没有文档.... 只有自己东看西看,总结点东西。
打开视频:
1.首先是 libavformat/allformats.c 中的av_register_all();
这一步注册库中含有 的所有可用的文件格式和编码器,这样当打开一个文件时,它们才能够自动选择相应的文件格式和编码器。要注意你只需调用一次 av_register_all(),所以,尽可能的在你的初始代码中使用它。如果你愿意,你可以仅仅注册个人的文件格式和编码,不过,通常你不得不这么 做却没有什么原因。
2.打开文件 libavformat/utils.c 中的av_open_input_file函数.
int i = av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL);返回一个int , 不等于0则说明打开失败
传入pFormatCtx指针变量地址,会根据打开的文件来填充FormatContext(只填充头信息),传入时,它必须为NULL;
最 后三个参数描述了文件格式,缓冲区大小(size)和格式参数;我们通过简单地指明NULL或0告诉 libavformat 去自动探测文件格式并且使用默认的缓冲 区大小。
3.取出文件中的流信息 libavformat/utils.c 中的av_find_stream_info函数.
int i = av_find_stream_info(pFormatCtx) ; i<0出错
这一步会用有效的信息把 AVFormatContext 的流域(streams field)填满
4.现在pFormatCtx已经充满了流信息,接下来需要找出里面的第一种视频流,
int videoStream = -1;
for( i = 0; i < pFormatCtx->nb_streams; i++ )
{
if(pFormatCtx->streams->codec.codec_type==CODEC_TYPE_VIDEO)
{
videoStream=i;
break;
}
}
如果videoStream不等-1,说明找到了第一种视频流,这个videoStream应该是流的位置 streams[videoStream]
5得到视频流编码上下文的指针:
pCodecCtx=&pFormatCtx->streams[videoStream]->codec;
6.找到真正的解码器.
AVCodec *pCodec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
pCodec == NULL为出错
7.通知解码器我们能够处理截断的bit流--ie,
// bit流帧边界可以在包中
if(pCodec->capabilities & CODEC_CAP_TRUNCATED)
{
pCodecCtx->flags|=CODEC_FLAG_TRUNCATED;
}
8. 打开解码器 libavcodec/utils.c 中的avcodec_open函数
int i = avcodec_open(pCodecCtx, pCodec); i<0说明打开失败
9.给视频帧分配空间以便存储解码后的图片:
AVFrame *pFrame;
pFrame=avcodec_alloc_frame();
10. 别人封装好的一个函数,它返回下一帧
bool GetNextFrame(AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx,
int videoStream, AVFrame *pFrame)
{
static AVPacket packet;
static int bytesRemaining=0;
static uint8_t *rawData;
static bool fFirstTime=true;
Int bytesDecoded;
Int frameFinished;
// 我们第一次调用时,将 packet.data 设置为NULL指明它不用释放了
if(fFirstTime)
{
fFirstTime=false;
packet.data=NULL;
}
// 解码直到成功解码完整的一帧
while(true)
{
// 除非解码完毕,否则一直在当前包中工作
while(bytesRemaining > 0)
{
// 解码下一块数据
bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame,
&frameFinished, rawData, bytesRemaining);
// 出错了?
if(bytesDecoded < 0)
{
fprintf(stderr, "Error while decoding frame/n");
return false;
}
bytesRemaining-=bytesDecoded;
rawData+=bytesDecoded;
// 我们完成当前帧了吗?接着我们返回
if(frameFinished)
return true;
}
// 读取下一包,跳过所有不属于这个流的包
do
{
// 释放旧的包
if(packet.data!=NULL)
av_free_packet(&packet);
// 读取新的包
if(av_read_packet(pFormatCtx, &packet)<0)
goto loop_exit;
} while(packet.stream_index!=videoStream);
bytesRemaining=packet.size;
rawData=packet.data;
}
loop_exit:
// 解码最后一帧的余下部分
bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
rawData, bytesRemaining);
// 释放最后一个包
if(packet.data!=NULL)
av_free_packet(&packet);
return frameFinished!=0;
}
这个函数的好处是,不用理会 两帧之间的边界也可以在包的中间部分 这种情况
接下来我们要做的就是在一个循环中,调用 GetNextFrame () 直到它返回false。
while(GetNextFrame(pFormatCtx, pCodecCtx, videoStream, pFrame))
{
img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
// 处理视频帧(存盘等等)
DoSomethingWithTheImage(pFrameRGB);
}
RGB 图象pFrameRGB (AVFrame *类型)的空间分配如下:
AVFrame *pFrameRGB;
int numBytes;
uint8_t *buffer;
// 分配一个AVFrame 结构的空间
pFrameRGB=avcodec_alloc_frame();
if(pFrameRGB==NULL)
handle_error();
// 确认所需缓冲区大小并且分配缓冲区空间
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
pCodecCtx->height);
buffer=new uint8_t[numBytes];
// 在pFrameRGB中给图象位面赋予合适的缓冲区
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);
清除
好了,我们已经处理了我们的视频,现在需要做的就是清除我们自己的东西:
// 释放 RGB 图象
delete [] buffer;
av_free(pFrameRGB);
// 释放YUV 帧
av_free(pFrame);
// 关闭解码器(codec)
avcodec_close(pCodecCtx);
// 关闭视频文件
av_close_input_file(pFormatCtx);