国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Qt-FFmpeg開發(fā)-音頻解碼為PCM文件(9)

這篇具有很好參考價值的文章主要介紹了Qt-FFmpeg開發(fā)-音頻解碼為PCM文件(9)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

音視頻/FFmpeg #Qt

Qt-FFmpeg開發(fā)-使用libavcodec API的音頻解碼示例(MP3轉(zhuǎn)pcm)

目錄
  • 音視頻/FFmpeg #Qt
  • Qt-FFmpeg開發(fā)-使用libavcodec API的音頻解碼示例(MP3轉(zhuǎn)pcm)
    • 1、概述
    • 2、實現(xiàn)效果
    • 3、主要代碼
    • 4、完整源代碼
更多精彩內(nèi)容
??個人內(nèi)容分類匯總 ??
??音視頻開發(fā) ??

1、概述

  • 最近研究了一下FFmpeg開發(fā),功能實在是太強大了,網(wǎng)上ffmpeg3、4的文章還是很多的,但是學(xué)習(xí)嘛,最新的還是不能放過,就選了一個最新的ffmpeg n5.1.2版本,和3、4版本api變化還是挺大的;
  • 這是一個libavcodec API示例;
  • 這里主要是研究FFmpeg官方示例產(chǎn)生的一個程序,官方示例可以看Examples;
  • 由于官方示例有一些小問題,編譯沒通過,并且是通過命令行執(zhí)行,不方便,這里通過修改為使用Qt實現(xiàn)這個音頻解碼為PCM文件的示例。

開發(fā)環(huán)境說明

  • 系統(tǒng):Windows10、Ubuntu20.04
  • Qt版本:V5.12.5
  • 編譯器:MSVC2017-64、GCC/G++64
  • FFmpeg版本:n5.1.2
    • 官方下載
    • 我使用的庫

2、實現(xiàn)效果

  1. 將.mp3文件解碼轉(zhuǎn)換為.pcm文件;(PCM數(shù)據(jù)時最原始的音頻數(shù)據(jù));
  2. 使用Qt重新實現(xiàn),方便操作,便于使用;
  3. 解決官方示例中解碼失敗程序會終止問題 ;
  4. 關(guān)鍵步驟加上詳細(xì)注釋,比官方示例更便于學(xué)習(xí)。
  • 實現(xiàn)效果如下:

    Qt-FFmpeg開發(fā)-音頻解碼為PCM文件(9)

3、主要代碼

  • 啥也不說了,直接上代碼,一切有注釋

  • widget.h文件

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QFile>
    #include <QWidget>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class Widget; }
    QT_END_NAMESPACE
    
    struct AVCodecParserContext;
    struct AVCodecContext;
    struct AVCodec;
    struct AVPacket;
    struct AVFrame;
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
    private slots:
    
        void on_but_in_clicked();
    
        void on_but_out_clicked();
    
        void on_but_start_clicked();
    
    private:
        int  initDecode();
        int  decode(QFile& fileOut);
        void showError(int err);
        void showLog(const QString& log);
    
    private:
        Ui::Widget *ui;
    
        AVCodecParserContext*   m_parserContex  = nullptr;             // 裸流解析器
        AVCodecContext*         m_context       = nullptr;             // 解碼器上下文
        const AVCodec*          m_codec         = nullptr;             // 音頻解碼器
        AVPacket*               m_packet        = nullptr;             // 未解碼的原始數(shù)據(jù)
        AVFrame*                m_frame         = nullptr;             // 解碼后的數(shù)據(jù)幀
    };
    #endif // WIDGET_H
    
    
  • widget.cpp文件文章來源地址http://www.zghlxwxcb.cn/news/detail-405070.html

    #include "widget.h"
    #include "ui_widget.h"
    #include <qfiledialog.h>
    #include <QDebug>
    #include <qthread.h>
    #include <qtimer.h>
    
    extern "C" {        // 用C規(guī)則編譯指定的代碼
    #include <libavutil/frame.h>
    #include <libavutil/mem.h>
    #include <libavcodec/avcodec.h>
    }
    
    #define AUDIO_INBUF_SIZE 20480
    #define AUDIO_REFILL_THRESH 4096
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        this->setWindowTitle(QString("使用libavcodec API的音頻解碼示例(mp3轉(zhuǎn)pcm) V%1").arg(APP_VERSION));
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    /**
     * @brief    自定義非阻塞延時
     * @param ms
     */
    void msleep(int ms)
    {
        QEventLoop loop;
        QTimer::singleShot(ms, &loop, SLOT(quit()));
        loop.exec();
    
    }
    
    void Widget::showLog(const QString &log)
    {
        ui->textEdit->append(log);
    }
    
    /**
     * @brief        顯示ffmpeg函數(shù)調(diào)用異常信息
     * @param err
     */
    void Widget::showError(int err)
    {
        static char m_error[1024];
        memset(m_error, 0, sizeof (m_error));        // 將數(shù)組置零
        av_strerror(err, m_error, sizeof (m_error));
        showLog(QString("Error:%1  %2").arg(err).arg(m_error));
    }
    
    /**
     * @brief 獲取輸入文件路徑
     */
    void Widget::on_but_in_clicked()
    {
        QString strName = QFileDialog::getOpenFileName(this, "選擇用于解碼的.mp3音頻文件~!", "/", "音頻 (*.mp3);");
        if(strName.isEmpty())
        {
            return;
        }
        ui->line_fileIn->setText(strName);
    }
    
    /**
     * @brief 獲取解碼后的原始音頻文件保存路徑
     */
    void Widget::on_but_out_clicked()
    {
        QString strName = QFileDialog::getSaveFileName(this, "解碼后數(shù)據(jù)保存到~!", "/", "原始音頻 (*.pcm);");
        if(strName.isEmpty())
        {
            return;
        }
        ui->line_fileOut->setText(strName);
    }
    
    void Widget::on_but_start_clicked()
    {
        int ret = initDecode();
        if(ret < 0)
        {
            showError(ret);
        }
    
        avcodec_free_context(&m_context);   // 釋放編解碼器上下文和與之相關(guān)的所有內(nèi)容,并將NULL寫入提供的指針。
        av_parser_close(m_parserContex);
        av_frame_free(&m_frame);
        av_packet_free(&m_packet);
    }
    
    QString get_format_from_sample_fmt(int fmt)
    {
        typedef struct sample_fmt_entry {
            enum AVSampleFormat sample_fmt;
            QString fmt_be;          // 大端模式指令
            QString fmt_le;          // 小端模式指令
        }sample_fmt_entry;
    
        sample_fmt_entry sample_fmt_entryes[] = {
            { AV_SAMPLE_FMT_U8,  "u8",    "u8"    },
            { AV_SAMPLE_FMT_S16, "s16be", "s16le" },
            { AV_SAMPLE_FMT_S32, "s32be", "s32le" },
            { AV_SAMPLE_FMT_FLT, "f32be", "f32le" },
            { AV_SAMPLE_FMT_DBL, "f64be", "f64le" },
        };
    
        for(int i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entryes); i++)
        {
            sample_fmt_entry entry = sample_fmt_entryes[i];
            if(fmt == entry.sample_fmt)
            {
                return AV_NE(entry.fmt_be, entry.fmt_le);   // AV_NE:判斷大小端
            }
        }
    
        return QString();
    }
    /**
     * @brief   開始解碼
     * @return
     */
    int Widget::initDecode()
    {
        QString strIn  = ui->line_fileIn->text();
        QString strOut = ui->line_fileOut->text();
        if(strIn.isEmpty() || strOut.isEmpty())
        {
            return AVERROR(ENOENT);        // 返回文件不存在的錯誤碼
        }
    
        m_packet = av_packet_alloc();      // 創(chuàng)建一個AVPacket
        if(!m_packet)
        {
            return AVERROR(ENOMEM);        // 返回?zé)o法分配內(nèi)存的錯誤碼
        }
    
        m_frame = av_frame_alloc();      // 創(chuàng)建一個AVFrame
        if(!m_frame)
        {
            return AVERROR(ENOMEM);        // 返回?zé)o法分配內(nèi)存的錯誤碼
        }
    
        // 通過ID查詢MPEG音頻解碼器
        m_codec = avcodec_find_decoder(AV_CODEC_ID_MP2);
        if(!m_codec)
        {
            return AVERROR(ENXIO);        // 找不到解碼器
        }
    
        m_parserContex = av_parser_init(m_codec->id);
        if(!m_parserContex)
        {
            return AVERROR(ENOMEM);        // 解析器初始化失敗
        }
    
        m_context = avcodec_alloc_context3(m_codec);  // 分配AVCodecContext并將其字段設(shè)置為默認(rèn)值
        if(!m_context)
        {
            return AVERROR(ENOMEM);        // 解碼器上下文創(chuàng)建失敗
        }
    
        // 使用給定的AVCodec初始化AVCodecContext。
        int ret = avcodec_open2(m_context, m_codec, nullptr);
        if(ret < 0)
        {
            return ret;
        }
    
        // 打開輸入文件
        QFile fileIn(strIn);
        if(!fileIn.open(QIODevice::ReadOnly))
        {
            return AVERROR(ENOENT);
        }
        // 打開輸出文件
        QFile fileOut(strOut);
        if(!fileOut.open(QIODevice::WriteOnly))
        {
            return AVERROR(ENOENT);
        }
    
        showLog("開始解碼!");
        msleep(1);
        QByteArray buf = fileIn.readAll();        // 讀取所有數(shù)據(jù)
        char inbuf[AUDIO_INBUF_SIZE];
        while(buf.count() > 0)
        {
            int len = (buf.count() <= AUDIO_INBUF_SIZE) ? buf.count() : AUDIO_INBUF_SIZE;
            memcpy(inbuf, buf.data(), len);
            // 解析數(shù)據(jù)包
            ret = av_parser_parse2(m_parserContex, m_context, &m_packet->data, &m_packet->size,
                                   reinterpret_cast<const uchar*>(inbuf),        // 這里不能直接使用buf.data(),否則會出現(xiàn)[mp2 @ 000001c8dbd40b00] Multiple frames in a packet.
                                   len,
                                   AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
            if(ret < 0)
            {
                break;
            }
            buf.remove(0, ret);  // 移除已解析的數(shù)據(jù)
    
            if(m_packet->size)
            {
                ret = decode(fileOut);
                if(ret < 0)
                {
    //                return ret;
                }
            }
        }
        m_packet->data = nullptr;
        m_packet->size = 0;
        decode(fileOut);               // 需要傳入空的數(shù)據(jù)幀才可以將解碼器中所有數(shù)據(jù)讀取出來
    
        enum AVSampleFormat sfmt = m_context->sample_fmt;
        // 檢查樣本格式是否為平面
        if(av_sample_fmt_is_planar(sfmt))
        {
            const char* name = av_get_sample_fmt_name(sfmt);  // 獲取音頻樣本格式名稱
            showLog(QString("警告:解碼器生成的樣本格式是平面格式(%1)。此示例將僅輸出第一個通道。").arg(name));
            sfmt = av_get_packed_sample_fmt(sfmt);   // 獲取樣本格式的替代格式
        }
    
        // 音頻通道數(shù)
    #if FF_API_OLD_CHANNEL_LAYOUT
        int channels = m_context->channels;
    #else
        int channels = m_context->ch_layout.nb_channels;
    #endif
        QString strFmt = get_format_from_sample_fmt(sfmt);
        if(!strFmt.isEmpty())
        {
            showLog(QString("使用下列命令播放輸出音頻文件!\n"
                            "ffplay -f %1 -ac %2 -ar %3 %4\n")
                            .arg(strFmt).arg(channels)
                            .arg(m_context->sample_rate).arg(strOut));
        }
    
        return 0;
    }
    
    /**
     * @brief           解碼并寫入文件
     * @param fileOut
     * @return
     */
    int Widget::decode(QFile &fileOut)
    {
        // 將包含壓縮數(shù)據(jù)的數(shù)據(jù)包發(fā)送到解碼器
        int ret = avcodec_send_packet(m_context, m_packet);   // 注意:官方Demo中這里如果返回值<0則終止程序,由于數(shù)據(jù)中有mp3文件頭,所以一開始會有返回值<0的情況
    
        // 讀取所有輸出幀(通??梢杂腥我鈹?shù)量的輸出幀
        while (ret >= 0)
        {
            // 讀取解碼后的數(shù)據(jù)幀
            int ret = avcodec_receive_frame(m_context, m_frame);
            if(ret == AVERROR(EAGAIN)   // 資源暫時不可用
            || ret == AVERROR_EOF)      // 文件末尾
            {
                return 0;
            }
            else if(ret < 0)
            {
                return ret;
            }
    
            // 返回每個樣本的字節(jié)數(shù)。例如格式為AV_SAMPLE_FMT_U8,則字節(jié)數(shù)為1字節(jié)
            int size = av_get_bytes_per_sample(m_context->sample_fmt);   // 返回值不會小于0
            for(int i = 0; i < m_frame->nb_samples; ++i)   // 音頻樣本數(shù)(采樣率)
            {
    #if FF_API_OLD_CHANNEL_LAYOUT
                for(int j = 0; j < m_context->channels; ++j)         // 5.1.2以后版本會棄用channels
    #else
                for(int j = 0; j < m_context->ch_layout.nb_channels; ++j)
    #endif
                {
                    fileOut.write((const char*)(m_frame->data[j] + size * i), size);
                }
            }
        }
        return 0;
    }
    
    

4、完整源代碼

  • github
  • gitee

到了這里,關(guān)于Qt-FFmpeg開發(fā)-音頻解碼為PCM文件(9)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 通過libfdk_aac編解碼器實現(xiàn)AAC音頻和PCM的編解碼

    了解如何使用libfdk_aac編解碼器在Windows環(huán)境下實現(xiàn)AAC音頻和PCM的編解碼,通過FFmpeg進行操作,提高音頻質(zhì)量和壓縮效率。

    2024年02月04日
    瀏覽(28)
  • 【FFmpeg+Qt開發(fā)】解碼流程 詳細(xì)分析+代碼示例

    【FFmpeg+Qt開發(fā)】解碼流程 詳細(xì)分析+代碼示例

    一、FFMPEG 概述 ?二、FFMPEG 解碼 2.1解碼流程 2.2解碼示例 FFmpeg 是一套可以用來記錄、轉(zhuǎn)換,數(shù)字音頻、視頻,并能將其轉(zhuǎn)化為流的開源計算機程序。 FFmpeg 采用 LGPL 或 GPL 許可證;它提供了錄制、轉(zhuǎn)換以及流化音視頻的完整解決案;它還包含了非常先進的音頻 視頻編解碼庫

    2024年01月23日
    瀏覽(18)
  • Qt基于FFmpeg解碼本地視頻生成H264文件并播放

    Qt基于FFmpeg解碼本地視頻生成H264文件并播放

    用eseye_u.exe 打開H264文件并播放 本文福利, 免費領(lǐng)取C++音視頻學(xué)習(xí)資料包、技術(shù)視頻 ,內(nèi)容包括(音視頻開發(fā),面試題, FFmpeg , webRTC , rtmp , hls , rtsp , ffplay , srs ) ↓↓↓↓↓↓ 見下面↓↓文章底部點擊免費領(lǐng)取↓↓ ? 三、核心代碼:? main中創(chuàng)建對象即可測試:

    2023年04月17日
    瀏覽(28)
  • Linux音頻處理:MP3解碼、PCM、播放PCM、ALSA(Advanced Linux Sound Architecture)、MPEG(Moving Picture Experts Group)

    將MP3音頻文件中的數(shù)字音頻數(shù)據(jù)轉(zhuǎn)換為可以播放或處理的音頻信號的過程。MP3(MPEG-1 Audio Layer 3)是一種常見的音頻壓縮格式,用于將音頻文件壓縮到較小的文件大小,同時保持相對高的音質(zhì)。 以下是MP3解碼的一般步驟: 讀取MP3文件 : 首先,需要讀取存儲在MP3文件中的音頻

    2024年02月03日
    瀏覽(18)
  • QT-播放原始PCM音頻流

    QT +=? multimedia audioplay.h audioplay.cpp

    2024年02月12日
    瀏覽(17)
  • Java Mp3轉(zhuǎn)化WAV/PCM音頻數(shù)據(jù),解碼詳細(xì)解析,提取每一幀數(shù)據(jù)集合/比特流/播放,一行代碼!

    Java Mp3轉(zhuǎn)化WAV/PCM音頻數(shù)據(jù),解碼詳細(xì)解析,提取每一幀數(shù)據(jù)集合/比特流/播放,一行代碼!

    大家好!我是原子君 1 .因為Java本身只支持,wav,缺少mp3的解碼器,所以Java自帶的無法對mp3進行處理,這種 MPEG-*音頻有損壓縮標(biāo)準(zhǔn)編碼 ,更不要說使用Java的音頻格式和音頻流就可以解決。 2 .所以本次轉(zhuǎn)換需要使用到colorful1.1這種純Java-Pc可跨平臺的工具框架。 注意:colorful只支持

    2024年02月15日
    瀏覽(90)
  • ffmpeg學(xué)習(xí)之音頻解碼數(shù)據(jù)

    ffmpeg學(xué)習(xí)之音頻解碼數(shù)據(jù)

    音頻數(shù)據(jù)經(jīng)過解碼后會被保存為,pcm數(shù)據(jù)格式。而對應(yīng)的處理流程如下所示。 avcodec_find_encoder()? avcodec_find_encoder_by_name()? avcodec_alloc_context3()? ?設(shè)置對應(yīng)音頻編碼的數(shù)據(jù)類型 設(shè)置編碼的frame的相關(guān)參數(shù) 整個代碼:

    2024年02月16日
    瀏覽(19)
  • FFmpeg 解碼 AAC 格式的音頻

    FFmpeg 解碼 AAC 格式的音頻

    FFmpeg 默認(rèn)是可以解碼 AAC 格式的音頻,但是如果需要獲取 PCM16 此類數(shù)據(jù)則需要經(jīng)過音頻轉(zhuǎn)碼。首先要打開解碼器,然后向解碼器發(fā)送 AAC 音頻幀(不帶 ADTS),然后從解碼器獲取解碼后的音頻幀,數(shù)據(jù)是 float 類型的,如果需要則進行轉(zhuǎn)碼流程將 float 轉(zhuǎn)成整型。 一、AAC 音頻

    2024年02月11日
    瀏覽(23)
  • Android FFmpeg 解碼 OpenSL ES 播放音頻

    ?在Android開發(fā)中,OpenSLES(Open Sound Library for Embedded Systems)是一個 C/C++ 音頻庫,提供了底層的音頻功能和處理接口。它是 Android 平臺上用于實現(xiàn)低延遲和高性能音頻功能的一種選擇。 本文的主線任務(wù)是描述 一個媒體文件通過 FFmpeg 解碼 后用 OpenSL ES 播放 音頻的過程 因為代碼

    2024年02月08日
    瀏覽(34)
  • FFmpeg音頻解碼流程詳解及簡單demo參考

    FFmpeg音頻解碼流程詳解及簡單demo參考

    ????????本文主要講解FFmpeg的音頻解碼具體流程,API使用。最后再以一個非常簡單的demo演示將一個mp3格式的音頻文件解碼為原始數(shù)據(jù)pcm文件。?本文主要基于FFmpeg音頻解碼新接口。 ???API接口簡單大體講解如下: ????????這一步是ffmpeg的任何程序的第一步都是需要先注

    2023年04月08日
    瀏覽(70)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包