作者: 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分析的更多相关文章

  1. ffmpeg中AVOption的实现分析

    [时间:2017-10] [状态:Open] [关键词:ffmpeg,avutil,AVOption] 0 引言 AVOptions提供了一种通用的options机制,可以用于任意特定结构的对象. 本 ...

  2. ffmpeg中av_log的实现分析

    [时间:2017-10] [状态:Open] [关键词:ffmpeg,avutil,av_log, 日志输出] 0 引言 FFmpeg的libavutil中的日志输出的接口整体比较少,但是功能还是不错 ...

  3. ffmpeg中AVBuffer的实现分析

    [时间:2017-10] [状态:Open] [关键词:ffmpeg,avutil,avbuffer, 引用计数] 0 引言 AVBuffer是ffmpeg提供的基于引用计数的智能指针的一个实现版本. ...

  4. Ffmpeg解析media容器过程/ ffmpeg 源代码简单分析 : av_read_frame()

    ffmpeg 源代码简单分析 : av_read_frame() http://blog.csdn.net/leixiaohua1020/article/details/12678577 ffmpeg ...

  5. 最新版ffmpeg源码分析

    最新版ffmpeg源码分析一:框架 (ffmpeg v0.9) 框架 最新版的ffmpeg中发现了一个新的东西:avconv,而且ffmpeg.c与avconv.c一个模样,一研究才发现是libav下 ...

  6. FFmpeg源代码简单分析:libavdevice的gdigrab

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  7. FFmpeg源代码简单分析:libavdevice的avdevice_register_all()

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  8. FFmpeg源代码简单分析:configure

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  9. FFmpeg源代码简单分析:makefile

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

随机推荐

  1. 从强制解包看 Swift 的设计

    从强制解包看 Swift 的设计 不知道大家有没有发现,在一个 Objective-C 和 Swift 混编的 App 中,当把一个 OC 中的参数转到 Swift 时,Swift 会自动把这个变量进 ...

  2. 用ubuntu的grpb2引导Remix OS或Phoenix OS

    Remix OS游戏版,这里下载:http://youxi.jide.com/ 安装简单.我这里要解决的是安装后用ubunu的grub2菜单去引导它. 方法如下: 进入ubuntu系统里修改其grub ...

  3. Android 两级菜单栏

    这里来记录下,android 的两级菜单栏,就是顶部切换,和底部的切换.因为在这个上面整了太久的时间,所以特此记录下. 第一种: 先介绍一个网上别人写出来的效果吧,这个当时积分真的很高..CSDN30 ...

  4. Android Studio占用C盘内存

    使用Android Studio的时候,会发现,在各种下载导入的时候,C盘内存耗费的非常的快,于是我看了下配置.

  5. Java多线程——sychronized

    概述 关键字synchronized的作用是实现线程间的同步.它的工作是对同步的代码加锁,使得每一次,只能有一个线程进入同步块,从而保证线程间的安全性. 直接作用于实例方法(普通同步方法):对当前实例 ...

  6. AT91SAM9260EK-38k产生原理

    9260内部有5个内部计数器,分别为TIMER_CLOCK1 --- TIMER_CLOCK5.通过这5个时钟可以为各种内部设备提供时钟基准. 其中,红外发射38K方波,是通过CLOCK1计数产生. ...

  7. 【转】15个最受欢迎的Python开源框架

    以下是伯乐在线从GitHub中整理出的15个最受欢迎的Python开源框架.这些框架包括事件I/O,OLAP,Web开发,高性能网络通信,测试,爬虫等. Django: Python Web应用开发框 ...

  8. DLL入门

    DllTest工程 QMath.h #pragma once #ifdef API_EXPORT #define DLL_CLASS __declspec(dllexport) #define DLL ...

  9. Android开发,在Fragment中,隐藏或关闭软键盘(虚拟键盘)的方法

    网上可以搜到各种方法,实际测试中,我的开发用机中,仅仅下面这个方法有效,记录一下. //隐藏虚拟键盘      public static void HideKeyboard(View v)      ...

  10. 【LINUX】——linux如何使用Python创建一个web服务

    问:linux如何使用Python创建一个web服务? 答:一句话,Python! 一句代码: /usr/local/bin/python -m SimpleHTTPServer 8686 > ...