【ffmpeg】试图从raw buffer中读取数据的ff_read_packet 浅显分析

来源:互联网 发布:浙江科澜 知乎 编辑:程序博客网 时间:2024/06/05 11:38

故事的主角是ff_read_packet,显然,如今ff_read_packet已经不是对外的接口了,是ffmpeg内部使用的一个接口。

想要理解ff_read_packet,必须从之前的接口av_read_packet的设计意图着手:


#if FF_API_READ_PACKETint av_read_packet(AVFormatContext *s, AVPacket *pkt){    return ff_read_packet(s, pkt);}#endif



obsolete是废弃的,老式的,意思。

这个函数是用来从媒体文件中读取一个transport 包的,这里头参数AVFormatContext被认为是一个“媒体文件句柄”,所需要读取的包就是pkt,将会被这个函数填充。

因此应该传入的是包的地址。


返回值为0,表示成功,其他的AVERROR_XXXX都是错误.

#if FF_API_READ_PACKET/** * @deprecated use AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE to read raw * unprocessed packets * * Read a transport packet from a media file. * * This function is obsolete and should never be used. * Use av_read_frame() instead. * * @param s media file handle * @param pkt is filled * @return 0 if OK, AVERROR_xxx on error */attribute_deprecatedint av_read_packet(AVFormatContext *s, AVPacket *pkt);#endif

当然,这个接口已经被废弃了,对外的接口是av_read_frame,这俩肯定有不同的。

============


其实这个函数启动起来之后就是想方设法的读取一个包填充pkt,虽然人家有个for(;;)的无线循环,但是人家的目的只在“一个包”。

用for(;;)的目的在于,遇到一些情况(比如的得到的pkt有问题,需要重新读一个才能返回给上层),可以continue,重新来过,如果一直读不到一个合适的包,

那么这个函数会一直无限循环下去(当然,函数的调用者应该会有个判断吧,不会让这个函数一直for下去吧。),单从这个函数看,他是阻塞的。



函数包括两部分:

(1)当raw buffer 中有数据,那么从队头出队一个节点,将其中的pkt填充。

如果得到了这么一个pkt,是不是要立即退出for(;;)呢?

可以看到,只有当 

 if(st->request_probe <= 0){//-1是probe完成,0是不要probe

的时候,才回立即退出for(;;),这个request_probe和probe的过程,我不是很理解。

所以到底是不是从raw buffer中成功出队就一定会退出for,我不确定。


关于raw buffer的讨论,参见http://blog.csdn.net/commshare/article/details/16908917


(2)但是,如果从raw buffer没有成功出队,这说明raw buffer是空的,只有调用一个具体的读包函数,继续为能获取pkt而努力。

        //0表示成功,小于0表示错误,pkt就在这里获取到。        ret= s->iformat->read_packet(s, pkt);
读取一个pkt,这个pkt填充调用者所需要的pkt,但不一定就是返回给调用者的pkt,因为:

====

之后,需要经过一系列的检查,如果失败就continue,重新用read_packet来读取一个。

这个检查的过程,下面的代码都有,但我也不太清楚。

=======

如果可以成功的通过具体的read_packet得到一个正确的pkt,那么

   1) 当

        if(!pktl && st->request_probe <= 0)        {                   return ret;        }
就退出for,该pkt就是所需pkt,但是不入队列raw buffer中。


注意,这里显示(pktl为NULL) 并且(不进行probe)的时候,才直接退出for(意味着函数会退出)并且不加入到raw buffer中。

pktl为NULL,表示此时没能从raw buffer中取到结点(才执行的具体的read_packet的这个流程)。



    2) 不返回,而是加入raw buffer中,并继续for循环(意味着此次具体格式的read_packet是成功的,但是不把这次得到的pkt结果返回(不退出for,不退出函数))。

那么调用入队列函数,add_to_pktbuf 加入到raw buffer中。

关于这个入队列函数参见http://blog.csdn.net/commshare/article/details/16910051

那么这个pkt就是要填充的pkt,



===========================

int ff_read_packet(AVFormatContext *s, AVPacket *pkt)

{    int ret, i;    AVStream *st;   //endless loop ,这里头其实就是一个不断的从出队列一个pkt(raw buffer只要有数据)返回给上层和不断的读取数据加入到raw buffer(这个新读取的数据,会被返回给上层)    for(;;){        AVPacketList *pktl = s->raw_packet_buffer; //node        if (pktl) {                   av_log(s,AV_LOG_WARNING,"raw_packet_buffer %s","NOT NULL ");        //得到            *pkt = pktl->pkt; //get a pkt from raw pkt buffer            st = s->streams[pkt->stream_index];//judge stream type by pkt stream_indexs            if(st->request_probe <= 0){//-1是probe完成,0是不要probe s->raw_packet_buffer = pktl->next;                //归还队列一些空间                s->raw_packet_buffer_remaining_size += pkt->size;                av_free(pktl);                return 0;            }   printf("### st->request_probe > 0  \n");        }//end if(pktl)        //////////////raw_packet_buffer 里头没有,自己去读/////       //先赋个初值        pkt->data = NULL;        pkt->size = 0;//初始化一下        av_init_packet(pkt);        //0表示成功,小于0表示错误,pkt就在这里获取到。        ret= s->iformat->read_packet(s, pkt);        //表示错误了。        if (ret < 0) { //貌似返回的是0if (!pktl || ret == AVERROR(EAGAIN))                return ret;            for (i = 0; i < s->nb_streams; i++) {                st = s->streams[i];                if (st->probe_packets) {                    probe_codec(s, st, NULL);                }                av_assert0(st->request_probe <= 0);            }            //读取失败了,重新来过            continue;        }        //读取成功        //判断是不是要丢掉        if ((s->flags & AVFMT_FLAG_DISCARD_CORRUPT) &&            (pkt->flags & AV_PKT_FLAG_CORRUPT)) {            av_log(s, AV_LOG_WARNING,                   "Dropped corrupted packet (stream = %d)\n",                   pkt->stream_index);            av_free_packet(pkt);            continue;        }        if(!(s->flags & AVFMT_FLAG_KEEP_SIDE_DATA))            av_packet_merge_side_data(pkt);        if(pkt->stream_index >= (unsigned)s->nb_streams){ //也没走吧            av_log(s, AV_LOG_ERROR, "Invalid stream index %d\n", pkt->stream_index);            continue;        }        printf("###wrap_timestamp \n");        //得到读取到的这个包所在的stream,以为pkt获取时间戳        st= s->streams[pkt->stream_index];        pkt->dts = wrap_timestamp(st, pkt->dts);        pkt->pts = wrap_timestamp(st, pkt->pts);        force_codec_ids(s, st);        /* TODO: audio: time filter 音频要做时间过滤;        video: frame reordering (pts != dts) 视频帧的重排序(当视频帧的dts和pts不等的时候)*/        if (s->use_wallclock_as_timestamps) //这里好像没走    pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base);        if(!pktl && st->request_probe <= 0)        {            return ret;        }         //这是给raw buffer存入数据啊,看来raw buffer也是有大小限制的,就是raw_packet_buffer_remaining_size 这么大。        add_to_pktbuf(&s->raw_packet_buffer, pkt, &s->raw_packet_buffer_end);        s->raw_packet_buffer_remaining_size -= pkt->size;        probe_codec(s, st, pkt);    }}






int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    int ret, i;
    AVStream *st;


   //endless loop ,这里头其实就是一个不断的从出队列一个pkt(raw buffer只要有数据)返回给上层和不断的读取数据加入到raw buffer(这个新读取的数据,会被返回给上层)
    for(;;){


        AVPacketList *pktl = s->raw_packet_buffer; //node


        if (pktl) { 
        
          av_log(s,AV_LOG_WARNING,"raw_packet_buffer %s","NOT NULL ");
        //得到
            *pkt = pktl->pkt; //get a pkt from raw pkt buffer
            st = s->streams[pkt->stream_index];//judge stream type by pkt stream_indexs
            if(st->request_probe <= 0){//-1是probe完成,0是不要probe
s->raw_packet_buffer = pktl->next;
                //归还队列一些空间
                s->raw_packet_buffer_remaining_size += pkt->size;
                av_free(pktl);
                return 0; //成功
            }
  printf("### st->request_probe > 0  \n");
        }//end if(pktl)


        //////////////raw_packet_buffer 里头没有,自己去读/////
       //先赋个初值
        pkt->data = NULL;
        pkt->size = 0;
//初始化一下
        av_init_packet(pkt);
        //0表示成功,小于0表示错误,pkt就在这里获取到。
        ret= s->iformat->read_packet(s, pkt);


        //表示错误了。
        if (ret < 0) { //貌似返回的是0
if (!pktl || ret == AVERROR(EAGAIN))   //这是返回错误值
                     return ret;

            for (i = 0; i < s->nb_streams; i++) {
                st = s->streams[i];
                if (st->probe_packets) {
                    probe_codec(s, st, NULL);
                }
                av_assert0(st->request_probe <= 0);
            }
            //读取失败了,重新来过
            continue;
        }


        //读取成功,此时ret为0
        //判断是不是要丢掉
        if ((s->flags & AVFMT_FLAG_DISCARD_CORRUPT) &&
            (pkt->flags & AV_PKT_FLAG_CORRUPT)) {
            av_log(s, AV_LOG_WARNING,
                   "Dropped corrupted packet (stream = %d)\n",
                   pkt->stream_index);
            av_free_packet(pkt);
            continue;
        }


        if(!(s->flags & AVFMT_FLAG_KEEP_SIDE_DATA))
            av_packet_merge_side_data(pkt);


        if(pkt->stream_index >= (unsigned)s->nb_streams){ //也没走吧
            av_log(s, AV_LOG_ERROR, "Invalid stream index %d\n", pkt->stream_index);
            continue;
        }


        printf("###wrap_timestamp \n");
        //得到读取到的这个包所在的stream,以为pkt获取时间戳
        st= s->streams[pkt->stream_index];
        pkt->dts = wrap_timestamp(st, pkt->dts);
        pkt->pts = wrap_timestamp(st, pkt->pts);


        force_codec_ids(s, st);


        /* TODO: audio: time filter 音频要做时间过滤;
        video: frame reordering (pts != dts) 视频帧的重排序(当视频帧的dts和pts不等的时候)*/
        if (s->use_wallclock_as_timestamps) //这里好像没走
   pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base);

     //这个时候返回的,应该是0,代表成功获取并填充了一个pkt
        if(!pktl && st->request_probe <= 0)
        {
            return ret;  //这个时候返回的,应该是0
        }

       //这是给raw buffer入队,但是并不通知调用者,我们获取到了一个pkt,继续执行for

         //这是给raw buffer存入数据啊,看来raw buffer也是有大小限制的,就是raw_packet_buffer_remaining_size 这么大。
        add_to_pktbuf(&s->raw_packet_buffer, pkt, &s->raw_packet_buffer_end);
        s->raw_packet_buffer_remaining_size -= pkt->size;
        probe_codec(s, st, pkt);
    } //end for


}