FFMPEG開(kāi)發(fā)快速入坑——音視頻混流處理
共 3097字,需瀏覽 7分鐘
·
2022-02-09 17:35
本章節(jié)重點(diǎn)講解對(duì)于編碼后的音視頻包寫(xiě)入mp4文件的處理,混流所有的API函數(shù)都屬于libavformat 庫(kù)。音視頻混流操作的流程比較簡(jiǎn)單:
1、創(chuàng)建一個(gè)新的媒體格式上下文 avformat_alloc_output_context2()
2、根據(jù)音視頻編碼器信息,分別創(chuàng)建音頻流 和 視頻流 avformat_new_stream() 和 avcodec_parameters_from_context()
3、打開(kāi)文件IO操作 avio_open()
4、寫(xiě)入文件頭信息 avformat_write_header()
5、循環(huán)交錯(cuò)調(diào)用 av_interleaved_write_frame() 寫(xiě)入音視頻幀數(shù)據(jù)。音視頻數(shù)據(jù)包寫(xiě)入都是通過(guò)這個(gè)函數(shù),需要注意的是AVPacket中 stream_index 流索引值要設(shè)置對(duì)。在具體項(xiàng)目中通常都是交錯(cuò)調(diào)用這個(gè)函數(shù)分別寫(xiě)入的(并不需要1對(duì)1的交錯(cuò),通常是寫(xiě)入一個(gè)視頻包,寫(xiě)入幾個(gè)音頻包的交錯(cuò))
6、寫(xiě)入文件尾信息 av_write_trailer()
7、關(guān)閉文件IO操作 avio_closep()、釋放媒體格式上下文 avformat_free_context()
另外需要注意一點(diǎn)的是:
打開(kāi)媒體格式上下文后,如果輸出媒體格式有 AVFMT_GLOBALHEADER 這個(gè)標(biāo)記,那么音視頻編碼器創(chuàng)建的時(shí)候也需要設(shè)置 AV_CODEC_FLAG_GLOBAL_HEADER 標(biāo)記。即:音視頻編碼器創(chuàng)建時(shí)需要:
if (m_pFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
{
m_ptrVideoEncCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
整個(gè)混流處理的示例代碼:
AVFormatContext* m_pFormatCtx = nullptr; // 媒體格式上下文
bool m_bGlobalHeader = true; // 音視頻編解碼器是否需要標(biāo)記 AV_CODEC_FLAG_GLOBAL_HEADER
AVStream* m_pVideoStream = nullptr; // 視頻流信息
AVStream* m_pAudioStream = nullptr; // 音頻流信息
/**
* @brief 打開(kāi)音視頻混流器
* @param
* @return 返回錯(cuò)誤碼, 0 表示正常; < 0 表示錯(cuò)誤碼
*
*/
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)建寫(xiě)入的視頻流
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)建寫(xiě)入的音頻流
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;
// 打開(kāi)文件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;
}
//
// 寫(xiě)入文件頭信息
//
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 無(wú)
* @return 無(wú)
*
*/
void MuxerClose()
{
// 寫(xiě)入尾信息
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 寫(xiě)入編碼后的音頻或者視頻數(shù)據(jù)包
* @param 無(wú)
* @return 無(wú)
*
*/
int32_t MuxerWrite(bool bVideoPkt, AVPacket* pInPacket)
{
// 設(shè)置寫(xiě)入數(shù)據(jù)包的流索引
if (bVideoPkt)
{
pInPacket->stream_index = m_pVideoStream->index;
}
else
{
pInPacket->stream_index = m_pAudioStream->index;
}
// 寫(xiě)入媒體文件
int res = av_interleaved_write_frame(m_pFormatCtx, pInPacket);
return res;
}
文章系列目錄
華叔-視覺(jué)魔術(shù)師:FFMPEG開(kāi)發(fā)快速入坑——緒論