本篇文章主要介绍了"【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
}