<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          FFMPEG開發(fā)快速入坑——音視頻混流處理

          共 3097字,需瀏覽 7分鐘

           ·

          2022-02-09 17:35

          本章節(jié)重點講解對于編碼后的音視頻包寫入mp4文件的處理,混流所有的API函數(shù)都屬于libavformat 庫。音視頻混流操作的流程比較簡單:

          1、創(chuàng)建一個新的媒體格式上下文 avformat_alloc_output_context2()

          2、根據(jù)音視頻編碼器信息,分別創(chuàng)建音頻流 和 視頻流 avformat_new_stream() 和 avcodec_parameters_from_context()

          3、打開文件IO操作 avio_open()

          4、寫入文件頭信息 avformat_write_header()

          5、循環(huán)交錯調(diào)用 av_interleaved_write_frame() 寫入音視頻幀數(shù)據(jù)。音視頻數(shù)據(jù)包寫入都是通過這個函數(shù),需要注意的是AVPacket中 stream_index 流索引值要設(shè)置對。在具體項目中通常都是交錯調(diào)用這個函數(shù)分別寫入的(并不需要1對1的交錯,通常是寫入一個視頻包,寫入幾個音頻包的交錯)

          6、寫入文件尾信息 av_write_trailer()

          7、關(guān)閉文件IO操作 avio_closep()、釋放媒體格式上下文 avformat_free_context()


          另外需要注意一點的是:

          打開媒體格式上下文后,如果輸出媒體格式有 AVFMT_GLOBALHEADER 這個標記,那么音視頻編碼器創(chuàng)建的時候也需要設(shè)置 AV_CODEC_FLAG_GLOBAL_HEADER 標記。即:音視頻編碼器創(chuàng)建時需要:

          if (m_pFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
          {
              m_ptrVideoEncCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
          }
          

          整個混流處理的示例代碼:

          AVFormatContext*  m_pFormatCtx    = nullptr;      // 媒體格式上下文
          bool              m_bGlobalHeader = true;         // 音視頻編解碼器是否需要標記 AV_CODEC_FLAG_GLOBAL_HEADER
          AVStream*         m_pVideoStream  = nullptr;      // 視頻流信息
          AVStream*         m_pAudioStream  = nullptr;      // 音頻流信息
          
          
          /**
            * @brief 打開音視頻混流器
            * @param 
            * @return 返回錯誤碼, 0 表示正常; < 0 表示錯誤碼
            *
            */
          int32_t MuxerOpen(
            const char*           pszFilePath,        // 要保存的媒體文件,通常是.mp4文件
            const AVCodecContext* pVideoEncCtx,       // 視頻編碼器上下文
            const AVCodecContext* pAudioEncCtx  )     // 音頻編碼器上下文
          {
            int res = 0;
          
          
            // 創(chuàng)建輸出流格式上下文
            res = avformat_alloc_output_context2(&m_pFormatCtx, nullptr, nullptr, pszFilePath);
            if (nullptr == m_pFormatCtx || res < 0)
            {
              LOGE(" [ERROR] fail to avformat_alloc_output_context2()\n");
              return -1;
            }
            if (m_pFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
            {
              m_bGlobalHeader = true;
            }
          
          
            // 創(chuàng)建寫入的視頻流
            m_pVideoStream = avformat_new_stream(m_pFormatCtx, nullptr);
            if (nullptr == m_pVideoStream)
            {
              LOGE(" [ERROR] fail to create video stream\n");
              avformat_free_context(m_pFormatCtx);
              return -2;
            }
            res = avcodec_parameters_from_context(m_pVideoStream->codecpar, pVideoEncCtx);
            if (res < 0)
            {
              LOGE(" [ERROR] fail to video avcodec_parameters_from_context(), res=%d\n", res);
              avformat_free_context(m_pFormatCtx);
              return -2;
            }
            m_pVideoStream->time_base = pVideoEncCtx->time_base;
          
          
            // 創(chuàng)建寫入的音頻流
            m_pAudioStream = avformat_new_stream(m_pFormatCtx, nullptr);
            if (nullptr == m_pAudioStream)
            {
              LOGE(" [ERROR] fail to create video stream\n");
              avformat_free_context(m_pFormatCtx);
              return -2;
            }
            res = avcodec_parameters_from_context(m_pAudioStream->codecpar, pAudioEncCtx);
            if (res < 0)
            {
              LOGE(" [ERROR] fail to audio avcodec_parameters_from_context(), res=%d\n", res);
              avformat_free_context(m_pFormatCtx);
              return -2;
            }
            m_pAudioStream->time_base = pVideoEncCtx->time_base;
          
          
            // 打開文件IO上下文
            res = avio_open(&m_pFormatCtx->pb, pszFilePath, AVIO_FLAG_WRITE);
            if (res < 0)
            {
              LOGE(" [ERROR] fail to avio_open(), res=%d\n", res);
              avformat_free_context(m_pFormatCtx);
              return -2;
            }
          
          
            //
            // 寫入文件頭信息
            //
            res = avformat_write_header(m_pFormatCtx, nullptr);
            if (res < 0)
            {
              LOGE(" [ERROR] fail to FF_avformat_write_header(), res=%d\n", res);
              avformat_free_context(m_pFormatCtx);
              return -3;
            }
          
            return 0;
          }
          
          
          /**
            * @brief  關(guān)閉音視頻混流器
            * @param  無
            * @return 無
            * 
            */
          void MuxerClose()
          {
            // 寫入尾信息
            if (m_pFormatCtx != nullptr)
            {
              av_write_trailer(m_pFormatCtx);
            }
          
            // 先關(guān)IO上下文
            if (m_pFormatCtx->pb != nullptr)
            {
              avio_closep(&m_pFormatCtx->pb);
              m_pFormatCtx->pb = nullptr;
            }
          
            // 再釋放媒體格式上下文
            if (m_pFormatCtx != nullptr)
            {
              avformat_free_context(m_pFormatCtx);
              m_pFormatCtx = nullptr;
            }
          
            // 流文件直接在 avformat_free_context()內(nèi)部已經(jīng)銷毀了
            m_pVideoStream = nullptr;  
            m_pAudioStream = nullptr;
           
          }
          
          
          /**
            * @brief  寫入編碼后的音頻或者視頻數(shù)據(jù)包
            * @param  無
            * @return 無
            * 
            */
          int32_t MuxerWrite(bool bVideoPkt, AVPacket* pInPacket)
          {
            // 設(shè)置寫入數(shù)據(jù)包的流索引
            if (bVideoPkt)
            {
              pInPacket->stream_index = m_pVideoStream->index;
            }
            else
            {
              pInPacket->stream_index = m_pAudioStream->index;
            }
          
            // 寫入媒體文件
            int res = av_interleaved_write_frame(m_pFormatCtx, pInPacket);
            return res;
          }
          


          文章系列目錄

          華叔-視覺魔術(shù)師:FFMPEG開發(fā)快速入坑——緒論

          瀏覽 17
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美99 | 亚洲无码视频看看 | 国产高清激情视频 | 操操逼网| 四虎永久影院 |