【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
}
- 【ffmpeg】试图从raw buffer中读取数据的ff_read_packet 浅显分析
- 读取数据的ff_read_packet 浅显分析
- ffmpeg 从内存中读取数据
- ffmpeg 从内存中读取数据
- ffmpeg 从内存中读取数据
- 读取raw文件夹的数据
- 从内存中读取数据到ffmpeg中
- 从raw中读取MP3文件
- muduo源码分析之使用封装的Buffer读取数据
- ffmpeg中读取数据的主要流程
- Android中从asset/raw拷贝数据的正确方式
- buffer cache实验9-从buffer caceh中读取数据块解析-从逻辑读到物理读
- 三层的浅显分析
- ffmpeg 从内存中读取数据(或将数据输出到内存)
- ffmpeg 从内存中读取数据(或将数据输出到内存)
- 对大数据信息挖掘、分析的浅显认识
- Android从assets和raw中读取txt文件
- 用输入输出流从res/raw中读取信息
- JAVA简介及视频教程
- C语言总结之函数的返回值
- structs2的学习笔记----(helloworld开发环境的搭建)
- ZOJ Alice's Print Service
- 用gdb配合内核转储文件瞬间定位段错误
- 【ffmpeg】试图从raw buffer中读取数据的ff_read_packet 浅显分析
- iOS 响应链
- TAM.c#
- 继承
- 关于C++中标准库类型:vector(上)
- 在创业型软件公司的收获
- 开博随笔
- java学习10-面向对象(继承)
- 寻找最小生成树——克鲁斯卡尔(Kruskal)算法