读取数据的ff_read_packet 浅显分析

 本篇文章主要介绍了"【ffmpeg】试图从raw buffer中读取数据的ff_read_packet 浅显分析",主要涉及到【ffmpeg】试图从raw buffer中读取数据的ff_read_packet 浅显分析方面的内容,对于【ffmpeg】试图从raw buffer中读取数据的ff_read_packet 浅显分析感兴趣的同学可以参考一下。

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

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

‘#if FF_API_READ_PACKET
int 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_deprecated
int 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) { //貌似返回的是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;
    }

    //读取成功
    //判断是不是要丢掉
    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

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值