(转)ffmpeg 中 av_read_frame_internal分析
作者: chenwei1983 时间: 2012-3-5 04:21 PM
标题: ffmpeg 中 av_read_frame_internal分析 原出处:http://www.chinavideo.org/viewthread.php?action=printable&tid=13846
av_read_frame_internal 在ffmpeg中实现了将format格式的packet,最终转换成一帧帧的es流packet,并解析填充了packet的pts,dts,等信息,为最终解码提供了重要的数据,av_read_frame_internal,调用av_read_packet,每次只读取一个包,然后直到parser完这个包的所有数据,才开始读取下一个包,parser完的数据被保存在parser结构的数据缓冲中,这样即使av_read_packet读取的下一包和前一包的流不一样,由于parser也不一样,所以实现了av_read_frame_internal这个函数调用,可以解析出不同流的es流,而av_read_frame_internal函数除非出错否则必须解析出一帧数据才能返回
static int av_read_frame_internal(AVFormatContext *s, AVPacket *pkt)
{
AVStream *st;
int len, ret, i;
av_init_packet(pkt);
for(;;) {
//s->cur_st记录现在的stream指针,如果真,就需要接着分析packet数据,否则需要读包
st = s->cur_st;
if (st) {
//当流不需要parser或者parser为空时,即没有注册这种parser,就不要parser 包了,输出原始包数据
if (!st->need_parsing || !st->parser) {
/* no parsing needed: we just output the packet as is */
/* raw data support */
*pkt = s->cur_pkt;
compute_pkt_fields(s, st, NULL, pkt);//计算包的,其它信息
s->cur_st = NULL; //当输出一个帧数据时,需要将现在的流置成NULL,这是因为下一个包并不一定和现在是同一个流,下次调用av_read_frame_internal 时,也能够直接进入读包的那个分支
break;
//当现在的数据长度大于0,并且不是丢掉所有的包条件下,进去parser
} else if (s->cur_len > 0 && st->discard < AVDISCARD_ALL) {
//如果数据足够parser出一帧就会返回,注意ffmpeg 的parser设计得非常好,它并不需要每次从头将所有的流parser一遍,而只要每次parser新的数据就可以了,直到parser出一个完整帧出来
len = av_parser_parse(st->parser, st->codec, &pkt->data, &pkt->size,
s->cur_ptr, s->cur_len,
s->cur_pkt.pts, s->cur_pkt.dts);
s->cur_pkt.pts = AV_NOPTS_VALUE;
s->cur_pkt.dts = AV_NOPTS_VALUE;
/* increment read pointer */
//如果一个原始包里有多个帧,那就需要调用多次av_read_frame_internal
s->cur_ptr += len;
s->cur_len -= len;
/* return packet if any */
//当parser出一帧后,pkt->size大小为真
if (pkt->size) {
got_packet:
//填充pkt的其它信息
pkt->pos = s->cur_pkt.pos; // Isn't quite accurate but close.
pkt->duration = 0;
pkt->stream_index = st->index;
pkt->pts = st->parser->pts;
pkt->dts = st->parser->dts;
//注意这里使用这个destruct函数的含义,因为这里 pkt->data的指针并没有实际分配内存,而是指向了
parser结构中数据buffer,所以这里后面需要处理
pkt->destruct = av_destruct_packet_nofree;
compute_pkt_fields(s, st, st->parser, pkt);
//当需要产生通用的index时,并且是关键帧,那么需要将这一帧添加到index表中
if((s->iformat->flags & AVFMT_GENERIC_INDEX) && pkt->flags & PKT_FLAG_KEY){
ff_reduce_index(s, st->index);
av_add_index_entry(st, st->parser->frame_offset, pkt->dts,
0, 0, AVINDEX_KEYFRAME);
}
break;
}
} else {//s->cur_len == 0 ,当 s->cur_ptr 数据解析完了,则需要重新读取一个原始包来分析
/* free packet */
av_free_packet(&s->cur_pkt);
s->cur_st = NULL;
}
} else {//s->cur_st 为NULL时,读取一个原始包
/* read next packet */
ret = av_read_packet(s, &s->cur_pkt);
if (ret < 0) {
if (ret == AVERROR(EAGAIN))
return ret;
/* return the last frames, if any */
//输出最后的包
for(i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
if (st->parser && st->need_parsing) {
av_parser_parse(st->parser, st->codec,
&pkt->data, &pkt->size,
NULL, 0,
AV_NOPTS_VALUE, AV_NOPTS_VALUE);
if (pkt->size)
goto got_packet;
}
}
/* no more packets: really terminate parsing */
return ret;
}
if(s->cur_pkt.pts != AV_NOPTS_VALUE &&
s->cur_pkt.dts != AV_NOPTS_VALUE &&
s->cur_pkt.pts < s->cur_pkt.dts){//如果pts < dts 出错,返回
av_log(s, AV_LOG_WARNING, "Invalid timestamps stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d\n",
s->cur_pkt.stream_index,
s->cur_pkt.pts,
s->cur_pkt.dts,
s->cur_pkt.size);
// av_free_packet(&s->cur_pkt);
// return -1;
}
st = s->streams[s->cur_pkt.stream_index];
if(s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_DEBUG, "av_read_packet stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, flags=%d\n",
s->cur_pkt.stream_index,
s->cur_pkt.pts,
s->cur_pkt.dts,
s->cur_pkt.size,
s->cur_pkt.flags);
s->cur_st = st;//读取一个包后,设置好cur_ptr,cur_len,以便解析
s->cur_ptr = s->cur_pkt.data;//
s->cur_len = s->cur_pkt.size;//
// 当准备好解析数据时,如果parser为空则创建parser
if (st->need_parsing && !st->parser) {
st->parser = av_parser_init(st->codec->codec_id);
if (!st->parser) {
/* no parser available: just output the raw packets */
st->need_parsing = AVSTREAM_PARSE_NONE;
}else if(st->need_parsing == AVSTREAM_PARSE_HEADERS){
st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
}
if(st->parser && (s->iformat->flags & AVFMT_GENERIC_INDEX)){
st->parser->next_frame_offset=
st->parser->cur_offset= s->cur_pkt.pos;
}
}
}
}
if(s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_DEBUG, "av_read_frame_internal stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, flags=%d\n",
pkt->stream_index,
pkt->pts,
pkt->dts,
pkt->size,
pkt->flags);
return 0;
}
(转)ffmpeg 中 av_read_frame_internal分析的更多相关文章
- ffmpeg中AVOption的实现分析
[时间:2017-10] [状态:Open] [关键词:ffmpeg,avutil,AVOption] 0 引言 AVOptions提供了一种通用的options机制,可以用于任意特定结构的对象. 本 ...
- ffmpeg中av_log的实现分析
[时间:2017-10] [状态:Open] [关键词:ffmpeg,avutil,av_log, 日志输出] 0 引言 FFmpeg的libavutil中的日志输出的接口整体比较少,但是功能还是不错 ...
- ffmpeg中AVBuffer的实现分析
[时间:2017-10] [状态:Open] [关键词:ffmpeg,avutil,avbuffer, 引用计数] 0 引言 AVBuffer是ffmpeg提供的基于引用计数的智能指针的一个实现版本. ...
- Ffmpeg解析media容器过程/ ffmpeg 源代码简单分析 : av_read_frame()
ffmpeg 源代码简单分析 : av_read_frame() http://blog.csdn.net/leixiaohua1020/article/details/12678577 ffmpeg ...
- 最新版ffmpeg源码分析
最新版ffmpeg源码分析一:框架 (ffmpeg v0.9) 框架 最新版的ffmpeg中发现了一个新的东西:avconv,而且ffmpeg.c与avconv.c一个模样,一研究才发现是libav下 ...
- FFmpeg源代码简单分析:libavdevice的gdigrab
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:libavdevice的avdevice_register_all()
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:configure
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:makefile
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
随机推荐
- excel 添加换行符,去除换行符:
excel 中添加换行符: :alt+enter 去掉excel中的换行符有三种方法: 注:解决过程中翻阅其他博客,看到如下方式: 1.看到有的说全选后“取消自动换行”,保存后,再打开,依然存在换行符 ...
- Specular Aliasing与Specular Leaking
最近做高质量实时HDR PBR渲染中碰到了2个关键问题,若干思考如下: 问题1: 极高的动态范围HDR+高级BRDF+相对较低的采样率(比方说不考虑子像素的原始分辨率),在这3项因素的综合作用下,Sp ...
- 【教程】minicom使用教程
简介 Linux下的Minicom的功能与Windows下的超级终端功能相似,可以通过串口控制外部的硬件设备.适于在linux通过超级终端对嵌入式设备行管理.同样也可以使用minicom对外置Mode ...
- idea 实现热部署
Idea实现热部署需要以下几步 1.open module setting,设置path,使output path指向存放实时编译class的文件夹 2.设置Artifacts,选择exploded选 ...
- jQuery之自定义pagination控件
slpagination 效果: slpagination.js (function($) { $.fn.slpagination = function(options, params) { if ( ...
- Android背景颜色渐变
●使用XML的方式为背景添加渐变效果 1.在res/drawable文件夹里添加一个jbshape.xml文件,然后写入如下代码: <?xml version="1.0" e ...
- 封装篇——图片模块(Glide)
如今市面上差点儿全部的app都用到了图片,图片模块的开发是app开发中不可缺少的一块工作, 开源的力量是强大的.好多优秀的第三方项目能够任君使用,帮助我们提高效率.而且不须要反复造轮子,这边我採用的是 ...
- 【Unity笔记】静态碰撞体的陷阱
概念 静态碰撞体(Static Collider):物体勾选为静态Static,有Collider组件,无Rigidbody组件. 静态碰撞体的陷阱 Unity在游戏初始化时,会把所有的静态碰撞体合并 ...
- JVM内存的设置
一.JVM内存的设置的原理 默认的java虚拟机的大小比较小,在对大数据进行处理时java就会报错:java.lang.OutOfMemoryError. 设置jvm内存的方法,对于单独的.class ...
- gcc命令中参数c和o混合使用的详解
gcc -c a.c 编译成目标文件a.o gcc -o a a.o 生成执行文件a.exe gcc a.c 生成执行文件a.exe gcc -o a -c a.c 编译成目标文件a gcc -o a ...