zoukankan      html  css  js  c++  java
  • (转)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 ",
                        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 ",
                        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 ",
                pkt->stream_index,
                pkt->pts,
                pkt->dts,
                pkt->size,
                pkt->flags);

        return 0;

  • 相关阅读:
    C语言I博客作业04
    C语言I博客作业03
    C语言1博客作业02
    作业1
    C语言||作业01
    C语言寒假大作战04
    C语言寒假大作战03
    C语言寒假大作战02
    C语言寒假大作战01
    笔记本
  • 原文地址:https://www.cnblogs.com/lihaiping/p/4484904.html
Copyright © 2011-2022 走看看