基本音視頻流的處理順序:
1).打開video.avi文件,讀取video_stream.
2).從video_stream讀取packet,分離frame
3).判斷frame是否完整,若不完整,轉到2
4).處理frame
5).轉到2
1.初始化ffmpeg
創建main.cpp,包含頭文件並初始化ffmpeg。
extern "C" //main.cpp文件包含C頭文件,否則查找不到函數的定義
{
#include <libavformat/avformat.h>
}
...
//register all all the muxers, demuxers and protocols
av_register_all();
...
av_register_all註冊所有格式的音視頻文件的編解碼器,這個函數只需要在主函數開始時調用一次。當然,如果你只想註冊單一格式的編解碼器,你可以單獨調用av_register_input_format/av_register_output_format.
2.打開文件
AVFormatContext *pFormatContext = nullptr;
char szFilePath[] = "input.mp4";
if(avformat_open_input(&pFormatContext, szFilePath, nullptr, nullptr) != 0)
{
return -1;
}
avformat_open_input讀取視頻文件的頭,並把文件格式信息保存到AVFormatContext中。第三、四個參數設爲null,表示函數自動檢測文件格式及操作。
3.查找流信息
//retrieve stream information
if(avformat_find_stream_info(pFormatContext, nullptr) < 0)
{
return -1;
}
4.輸出打印信息
avformat_find_stream_info填充pFormatContext->streams數據,可以用av_dump_format(pFormatContext, 0, szFilePath, 0)打印文件及流信息。
pFormatContext->streams是一組指針數組,數組大小爲pFormatContext->nb_streams。
5.查找視頻流數據
int videoStream = -1;
for(int i = 0; i < pFormatContext->nb_streams; ++i)
{
if(pFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStream = i;
break;
}
}
if(videoStream == -1)
{
return -1;
}
AVCodecContext *pCodecCtxOrig = pFormatContext->streams[videoStream]->codec;
6.打開解碼器
AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec == nullptr)
{
return -1;
}
AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec);
AVCodecContext *pCodecCtxOrig = nullptr;
if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0)
{
return -1;
}
if(avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
{
return -1;
}
注:不能直接操作AVCodecContext的視頻流數據,我們必須copy一份。
7.存儲數據
AVFrame *pFrame = av_frame_alloc();
AVFrame *pFrameRGB = av_frame_alloc();
if(pFrame == nullptr || pFrameRGB == nullptr)
{
return -1;
}
uint8_t *buffer = nullptr;
int numBytes = -1;
numBytes = avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
if(buffer == nullptr)
{
return -1;
}
avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);
PPM文件數據格式是24b-RGB格式,必須將視頻幀格式轉換成24b-RGB格式。
8.讀取數據
struct SwsContext *sws_ctx = nullptr;
int frameFinished = -1;
sws_ctx = sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
AV_PIX_FMT_RGB24,
SWS_BILINEAR,
nullptr,
nullptr,
nullptr);
int i = 0;
AVPacket packet;
while(av_read_frame(pFormatContext, &packet) >= 0)
{
if(packet.stream_index == videoStream)
{
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if(frameFinished)
{
sws_scale(sws_ctx, (const uint8_t *const*)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height,
pFrameRGB->data, pFrameRGB->linesize);
if(++i <= 5)
{
//save the frame to disk
saveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
}
}
}
}
9.保存數據
void saveFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
FILE *pFile = nullptr;
char szFilename[32];
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile = fopen(szFilename, "wb");
if(pFile == nullptr)
{
return;
}
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
for(int y = 0; y < height; ++y)
{
fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, width * 3, pFile);
}
fclose(pFile);
}
10.釋放內存數據
av_free_packet(&packet);
av_free(buffer);
av_free(pFrameRGB);
av_free(pFrame);
avcodec_close(pCodecCtx);
avcodec_close(pCodecCtxOrig);
avformat_close_input(&pFormatContext);