av_read_frame
主要用到了如下函数。 read_from_packet_buffer,read_frame_internal(ff_read_packet(probe_codec),parse_packet(av_parser_parse2)。


与av_read_packet的区别是读出的是包,它可能是半帧或多帧,不保证帧的完整性。av_read_frame对av_read_packet进行了封装,使读出的数据总是完整的帧。
read_from_packet_buffer
read_from_packet_buffer函数比较简单,从AVPacketList里取出数据即可。若缓存中没有数据,av_read_frame函数则会调用read_frame_internal函数来获取数据。
read_frame_internal
read_frame_internal主要有以下两步,
(1)调用了ff_read_packet()从相应的AVInputFormat读取数据,如解析出TS的PES,一个PES包中可能有多个音频帧。

(2)如果媒体频流需要使用AVCodecParser,则调用parse_packet()解析相应的AVPacket,如调用h264_parser.c解析出一帧H264。

static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
{
    int ret = 0, i, got_packet = 0;
    AVDictionary *metadata = NULL;
    av_init_packet(pkt);

while (!got_packet && !s->internal->parse_queue) {

//parse_packet成功后,s->internal->parse_queue就不为空。

AVStream *st;

AVPacket cur_pkt;

。。。

}

//退出循环后,调用read_from_packet_buffer,从parse_queue中取出数据。

if (!got_packet && s->internal->parse_queue)
        ret = read_from_packet_buffer(&s->internal->parse_queue, &s->internal->parse_queue_end, pkt);

}

ff_read_packet
ff_read_packet函数也是会判断缓存是否有数据,若有则从缓存中取出,若没有,则调用demuxer的read_packet来读取数据。ff_read_packet()中最关键的地方就是调用了AVInputFormat的read_packet()方法。AVInputFormat的read_packet()是一个函数指针,指向当前的AVInputFormat的读取数据的函数,比如flv的flv_read_packet,flv_read_packet()的代码比较长,但是逻辑比较简单。它的主要功能就是根据FLV文件格式的规范,逐层解析Tag以及TagData,获取Tag以及TagData中的信息。
ff_read_packet的缓存和av_read_frame函数里的缓存是不一样的,ff_read_packet的缓存为AVFormatInternal::raw_packet_buffer,在av_read_frame函数里缓存则为
AVFormatInternal::packet_buffer,简单的理解为在codec被识别之前用raw_packet_buffer缓存,codec识别后用packet_buffer。

int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    int ret, i, err;
    AVStream *st;
     if (!genpts) {  
        ret = s->packet_buffer ?  
            read_from_packet_buffer(&s->packet_buffer, &s->packet_buffer_end, pkt) :  
            read_frame_internal(s, pkt);  
        if (ret < 0)  
            return ret;  
        goto return_packet;  
    }

for (;;) {
        AVPacketList *pktl = s->raw_packet_buffer;

//首先判断s->raw_packet_buffer中是否有数据,一般开始的时候是没有的
        if (pktl) {
            *pkt = pktl->pkt;
            st   = s->streams[pkt->stream_index];
            if (s->raw_packet_buffer_remaining_size <= 0)
                if ((err = probe_codec(s, st, NULL)) < 0)
                    return err;  //
            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;
            }
        }

pkt->data = NULL;
        pkt->size = 0;
        av_init_packet(pkt);
        ret = s->iformat->read_packet(s, pkt);

//如果没有数据,调用s->iformat->read_packet(s,pkt),对于ts流来讲:s->iformat->read_packet调用的是mpegts_read_packet(读出一个PES包),0表示成功,小于0表示错误,pkt就在这里获取到。 
        if (ret < 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 (pkt->stream_index >= (unsigned)s->nb_streams) {
            av_log(s, AV_LOG_ERROR, "Invalid stream index %d\n", pkt->stream_index);
            continue;
        }

st = s->streams[pkt->stream_index];

if (update_wrap_reference(s, st, pkt->stream_index, pkt) && st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) {
            // correct first time stamps to negative values
            if (!is_relative(st->first_dts))
                st->first_dts = wrap_timestamp(st, st->first_dts);
            if (!is_relative(st->start_time))
                st->start_time = wrap_timestamp(st, st->start_time);
            if (!is_relative(st->cur_dts))
                st->cur_dts = wrap_timestamp(st, st->cur_dts);
        }

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;
        //最后调用add_to_pktbuf将packet加入s->raw_packet_buffer中。
        add_to_pktbuf(&s->raw_packet_buffer, pkt, &s->raw_packet_buffer_end);//将packet加入到s->raw_packet_buffer
        s->raw_packet_buffer_remaining_size -= pkt->size;
      //raw buffer也是有大小限制的,就是raw_packet_buffer_remaining_size 这么大。  
        if ((err = probe_codec(s, st, pkt)) < 0)
            return err;
    }
}

probe_codec
probe_codec是用来探测codec的,probe_codec先是把pkt的数据打包进AVProbeData,然后调用set_codec_from_probe_data来进行探测的,set_codec_from_probe_data的基本思想是根据av_probe_input_format3函数返回的一个AVInputFormat格式来和fmt_id_type匹配得出的codec_id和type的。
parse_packet
parse_packet()给需要AVCodecParser的媒体流提供解析AVPacket的功能,最终调用了相应AVCodecParser的av_parser_parse2()函数,代码为s->parser->parser_parse,接着会调用具体的解析函数,如h264_parse(parse_nal_units),最终解析出来AVPacket。
h264解析
ff_h264_decode_seq_parameter_set  解析SPS。
ff_h264_decode_picture_parameter_set   解析PPS。
ff_h264_decode_sei                   解析SEI。
以上代码在libavcdec/h264_parser.c。

AVClass与AVOption
AVClass最主要的作用就是给结构体(例如AVFormatContext等)增加AVOption功能的支持。换句话说AVClass就是AVOption和目标结构体之间的“桥梁”。AVClass要求必须声明为目标结构体的第一个变量。 AVClass中有一个option数组用于存储目标结构体的所有的AVOption。举个例子,AVFormatContext结构体,AVClass和AVOption之间的关系如下图所示。

    图中AVFormatContext结构体的第一个变量为AVClass类型的指针av_class,它在AVFormatContext结构体初始化的时候,被赋值指向了全局静态变量av_format_context_class结构体(定义位于libavformat\options.c)。而AVClass类型的av_format_context_class结构体中的option变量指向了全局静态数组avformat_options(定义位于libavformat\options_table.h)。现在回到AVOption。其实除了可以对FFmpeg常用结构体AVFormatContext,AVCodecContext等进行赋值之外,还可以对它们的私有数据priv_data进行赋值。这个字段里通常存储了各种编码器特有的结构体。而这些结构体的定义在FFmpeg的SDK中是找不到的。例如使用libx264进行编码的时候,通过AVCodecContext的priv_data字段可以对X264Context结构体中的变量进行赋值,设置preset,profile等。使用libx265进行编码的时候,通过AVCodecContext的priv_data字段可以对libx265Context结构体中的变量进行赋值,设置preset,tune等。

AVProbeData
typedef struct AVProbeData {
    const char *filename;  //文件名
    unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. // buf存储用于推测AVInputFormat的媒体数据,最后还有个

mime_type保存媒体的类型。其中buf可以为空,但是其后面无论如何都需要 填充AVPROBE_PADDING_SIZE个0(AVPROBE_PADDING_SIZE取值为32,即32个0)。 */
    int buf_size;       /**< Size of buf except extra allocated bytes */
    const char *mime_type; /**< mime_type, when known. */
} AVProbeData;

avformat_open_input(init_input(av_probe_input_format3), s->iformat->read_header());
init_ input
if ((ret = init_input(s, filename)) < 0)  
        goto fail;  
    //执行完此函数后,s->pb和s->iformat都已经指向了有效实例.pb是用于读写数据的,它把媒体数据当做流来读写,不管是什么媒体格式,而iformat把pb读出来的流按某种媒体格式进行分析,也就是说pb在底层,iformat在上层。
主要分两种情况
1 知道s->pb,从s->pb得到s->iformat。
2 不知道s->pb,打开文件(avio_open),探测文件格式(av_probe_input_buffer)。
av_probe_input_format3
av_probe_input_format3()根据输入数据查找合适的AVInputFormat。输入的数据位于AVProbeData中。该函数最主要的部分是一个循环。该循环调用av_iformat_next()遍历FFmpeg中所有的AVInputFormat(av_register_all时组建),并根据以下规则确定AVInputFormat和输入媒体数据的匹配分数(score,反应匹配程度):
(1) 如果AVInputFormat中包含read_probe(),就调用read_probe()函数获取匹配分数(这一方法如果结果匹配的话,一般会获 得AVPROBE_SCORE_MAX的分值,即100分)。如果不包含该函数,就使用av_match_ext()函数比较输入媒体的扩展名和 AVInputFormat的扩展名是否匹配,如果匹配的话,设定匹配分数为 AVPROBE_SCORE_EXTENSION(AVPROBE_SCORE_EXTENSION取值为50,即50分)。
(2)使用av_match_name()比较输入媒体的mime_type和AVInputFormat的mime_type,如果匹配的话,设定匹配分数为AVPROBE_SCORE_MIME(AVPROBE_SCORE_MIME取值为75,即75分)。
(3)如果该AVInputFormat的匹配分数大于此前的最大匹配分数,则记录当前的匹配分数为最大匹配分数,并且记录当前的AVInputFormat为最佳匹配的AVInputFormat。

ffmpeg.c
parse_option()
    解析一个输入选项。具体的解析步骤不再赘述。parse_options()会循环调用parse_option()直到所有选项解析完毕。FFmpeg的每一个选项信息存储在一个OptionDef结构体中。

av_interleaved_write_frame与av_write_frame的区别?

av_interleaved_write_frame

/*

* Write apacket to an output media file ensuring correct interleaving.

* This function will buffer the packetsinternally as needed to make sure the

* packets in the output file are properlyinterleaved in the order of

* increasing dts. Callers doing their owninterleaving should call

* av_write_frame() instead of this function.

*/

int av_write_frame(AVFormatContext *s, AVPacket *pkt);

/*

*Write a packet to an output media file.

* Thisfunction passes the packet directly to the muxer, without any buffering

* orreordering. The caller is responsible for correctly interleaving the

*packets if the format requires it. Callers that want libavformat to handle

* theinterleaving should call av_interleaved_write_frame() instead of this

*function. */

av_interleaved_write_frame调用了interleave_packet,而av_write_frame没有调用,看看interleave_packet的代码:

static intinterleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, intflush)

{

if(s->oformat->interleave_packet) {

int ret =s->oformat->interleave_packet(s, out, in, flush);

if (in)

av_free_packet(in);

return ret;

} else

returnff_interleave_packet_per_dts(s, out, in, flush);

}

代码非常简单,如果AVOutputFormat有interleave_packet函数指针,则调用,如果没有则调用ff_interleave_packet_per_dts,看看ff_interleave_packet_per_dts的代码:

intff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out,

AVPacket *pkt,int flush)

{

AVPacketList *pktl;

int stream_count = 0;

int noninterleaved_count = 0;

int i, ret;

if (pkt) {//把pkt加入到AVPacketList中去

if ((ret = ff_interleave_add_packet(s,pkt, interleave_compare_dts)) < 0)

return ret;

}

for (i = 0; i < s->nb_streams; i++) {

if(s->streams[i]->last_in_packet_buffer) {

++stream_count;

} else if(s->streams[i]->codec->codec_type != AVMEDIA_TYPE_ATTACHMENT&&

s->streams[i]->codec->codec_id != AV_CODEC_ID_VP8 &&

s->streams[i]->codec->codec_id != AV_CODEC_ID_VP9) {

++noninterleaved_count;

}

}

if(s->internal->nb_interleaved_streams == stream_count)

flush = 1;

if (s->max_interleave_delta > 0&&

s->internal->packet_buffer&&

!flush &&

s->internal->nb_interleaved_streams ==stream_count+noninterleaved_count

) {

AVPacket *top_pkt =&s->internal->packet_buffer->pkt;

int64_t delta_dts = INT64_MIN;

int64_t top_dts =av_rescale_q(top_pkt->dts,

s->streams[top_pkt->stream_index]->time_base,

AV_TIME_BASE_Q);

for (i = 0; i < s->nb_streams;i++) {

int64_t last_dts;

const AVPacketList *last =s->streams[i]->last_in_packet_buffer;

if (!last)

continue;

last_dts =av_rescale_q(last->pkt.dts,

s->streams[i]->time_base,

AV_TIME_BASE_Q);

delta_dts = FFMAX(delta_dts,last_dts - top_dts);

// last_dts -top_dts为PTS差值。

}

/*dts间隔大于max_interleave_delta */

if (delta_dts >s->max_interleave_delta) {

av_log(s, AV_LOG_DEBUG,

"Delay between thefirst packet and last packet in the "

"muxing queue is%"PRId64" > %"PRId64": forcing output\n",

delta_dts,s->max_interleave_delta);

flush = 1;

}

}

if (stream_count && flush) {

AVStream *st;

pktl =s->internal->packet_buffer;

*out = pktl->pkt;

st  = s->streams[out->stream_index];

s->internal->packet_buffer =pktl->next;

if (!s->internal->packet_buffer)

s->internal->packet_buffer_end = NULL;

if (st->last_in_packet_buffer ==pktl)

st->last_in_packet_buffer =NULL;

av_freep(&pktl);

return 1;

} else {

av_init_packet(out);

return 0;

}

}

从代码中看到,函数ff_interleave_packet_per_dts调用ff_interleave_add_packet把pkt加入缓存,然后再从缓存中取出第一个pkt返回。

函数ff_interleave_add_packet把pkt经过排序(如果缓存中有pkt的话)加入到缓存中,这个排序是根据ff_interleave_packet_per_dts函数传入的compare(其实就是函数interleave_compare_dts)指针做的。

interleave_compare_dts函数其实就是在比较传入的2个pkt的dts值,其实就是比那个的dts更早,如果有音频还有audio_preload(音频预加载时间)的话,还需要把音频包的预加载时间算进去。

ffmpeg之av_read_frame相关推荐

  1. ffmpeg中av_read_frame 超时设置

    https://trac.ffmpeg.org/ ffmpeg wiki ffmpeg中avformat_open_input超时设置 这里有日志可以参考:日志 y也可以参考: https://blo ...

  2. ffmpeg中av_read_frame阻塞的原因与解决方案

    最近用到ffmpeg,想要av_read_frame立即返回,但是看网上的博客,都没有详细的解说,那就自己看源码. 两种方法: 设置回调打断函数和设置超时 超时,但是如果只设置该参数,依然会在av_r ...

  3. FFmpeg中调用av_read_frame函数导致的内存泄漏问题

    使用FFmpeg的av_read_frame函数后,每读完一个packet,必须调用av_packet_unref函数进行内存释放,否则会导致内存释泄漏. 在vs(博主所用的ffmpeg版本是3.4. ...

  4. FFmpeg源码分析:av_read_frame()读取音视频帧

    FFmpeg使用av_read_frame()方法读取音频流.视频流.字幕流,得到AVPacket数据包.FFmpeg官方提供的samples有使用示例,或者在ffplay.c代码中:打开文件/网络流 ...

  5. H.264 媒体流 AnnexB 和 AVCC 格式分析 及 FFmpeg 解析mp4的H.264码流方法

    H264码流分两种组织方式,一种是AnnexB格式,一种是AVCC格式. 作者:码农小明 来源:https://blog.csdn.net/shaosunrise/article/details/12 ...

  6. FFmpeg5.0源码阅读—— av_read_frame

      摘要:本文主要描述了FFmpeg中用于打开编解码器接口av_read_frame的具体调用流程,详细描述了该接口被调用时所作的具体工作.   关键字:ffmpeg.av_read_frame    ...

  7. RK3588 MPP解码句柄泄露问题记录

    1. 问题背景 最近在用瑞芯微3588开发板做一个视频处理的项目,前两天拷机发生了闪退,弹出的问题是"打开文件过多",经过初步排查定位到是MPP硬解码部分出的问题. 我的MPP解码 ...

  8. ffmpeg 源代码简单分析 : av_read_frame()

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  9. ffmpeg函数分析:av_read_frame()

    对于视频的编解码来说,要对数据进行解码,那么首先要获取视频帧的压缩数据. av_read_frame()的作用就是获取视频的数据. 注:av_read_frame()获取视频的一帧,不存在半帧说法.但 ...

最新文章

  1. 再学 GDI+[7]: DrawLines - 绘制一组直线
  2. R语言Logistic回归模型案例:低出生婴儿体重的影响因素分析(列线图、校准曲线)
  3. android内存加载dex,安卓8.1版本dex加载流程笔记--第一篇:oatfile,oatdexfile与dexfile...
  4. Oracle数据库中number类型在hibernate的引用
  5. java中的双与_java 双冒号是什么操作符?
  6. 大数据 数据库 评测_为什么腾讯QQ的大数据平台选择了这款数据库?
  7. php多应用,thinkphp6开启多应用模式
  8. html5 mid格式音乐,html5中audio支持音频格式的解决方法
  9. ThinkPad R400 Windows7 驱动安装
  10. 计算机职业规划书备选方案,职业规划书备选方案
  11. 1.3-----Simplify 3D切片软件简单设置
  12. .net 简单的后台合成图片
  13. unity3d开发AR/VR应用
  14. [轉載]房地产崩盘绝非戏言
  15. 如何制作电话号码二维码?
  16. LWN:Linux audio plugin APIs综述!
  17. Android系统设置settings应用学习(二)--源代码解析
  18. The Fool HDU - 6555
  19. 关于游戏介绍的HTML网页设计 HTML5期末考核大作业 HTML静态游戏网页作业 web前端开发技术 web课程设计 网页规划与设计
  20. 43款设计师必备英文设计字体-书法字体

热门文章

  1. ApiPost接口测试的用法之------Post
  2. 如何用浏览器测试post请求
  3. 债务纠纷案被录入终本库了怎么办?
  4. 1.2 Python介绍
  5. Android OkHttp 全面详解
  6. 单页应用首屏加载速度慢怎么解决?
  7. 虚拟桌面:一个简单的桌面管理工具
  8. 手游传奇刷元宝_传奇手游如何刷元宝
  9. CVE-2021-1647样本分析
  10. 听声变位测试软件,刺激战场:听声辩位其实有很大的学问,想了解的朋友请进来...