FFmpeg5.0源码阅读——av_interleaved_write_frame

摘要 :本文主要详细描述FFmpeg中封装时写packet到媒体文件的函数av_interleaved_write_frame的实现。

关键字av_interleaved_write_frame

读者须知:读者需要熟悉ffmpeg的基本使用。

1 基本调用流程

av_interleaved_write_frame的基本调用流程图如下。

首先就是根据输入数据是否为空选择调用的函数,如果为空就会调用interleaved_write_packet刷新数据,否则调用write_packets_common写数据。

write_packets_common中,check_packet检查输入的数据和期望写入的媒体流是否能够对上。prepare_input_packet对输入数据进行修正,如果pts和dts其中之一为NOPTS则设置为对方的值,以及如果设置了is_intra_only则每一帧都会设置标志位AV_PKT_FLAG_KEY。而check_bitstream就是调用s->oformat->check_bitstream检查流是否符合对应的格式。最后才是调用write_packet_common进行写数据。如果有设置filter的话就调用write_packets_from_bsfs处理。

write_packet_common会根据输入的参数是否需要交织存储来调用具体的函数写packet。非交织的情况下就会调用write_packet,该函数内部实际调用的s->oformat->write_packets->oformat->write_uncoded_frame写文件,后者处理裸流。

interleaved_write_packet内,如果AVOuputFormat设置了对应的函数指针则直接调用s->oformat->interleave_packet写文件,否则就用FFmpeg提供的ff_interleave_packet_per_dts。我们重点看下这个函数实现。

2 ff_interleave_packet_per_dts

音视频交织就是,将音频数据和视频数据存储到文件时,按照几帧音频几帧视频的方式存储,这样在处理流数据时就不会发生频繁的seek导致一些性能问题。音视频交织的视频对于网络播放也比较友好。

ff_interleave_packet_per_dts只是针对当前的两个流的packet的时间戳进行比较避免在文件存储过程中距离太远导致解封转时要频繁seek文件。最终封装文件写入到磁盘还是需要write_packet。该函数首先将送入的pkt插入到缓存队列中,然后在从当前缓存队列中选出一帧返回调用write_packet进行写入。

在看ff_interleave_add_packet函数的实现之前,我们先简单看下帧比较函数interleave_compare_dts的实现,该函数用来比较两个packet的dts。如果非音频流就是调用的av_compare_ts进行比较,否则会根据当前音频流是否有preload去除preload的偏移:

c 复制代码
int preload  = st ->codecpar->codec_type == AVMEDIA_TYPE_AUDIO;
int preload2 = st2->codecpar->codec_type == AVMEDIA_TYPE_AUDIO;
if (preload != preload2) {
    int64_t ts, ts2;
    preload  *= s->audio_preload;
    preload2 *= s->audio_preload;
    //preload不同时需要减掉preload的偏移
    ts = av_rescale_q(pkt ->dts, st ->time_base, AV_TIME_BASE_Q) - preload;
    ts2= av_rescale_q(next->dts, st2->time_base, AV_TIME_BASE_Q) - preload2;
    if (ts == ts2) {
        ts  = ((uint64_t)pkt ->dts*st ->time_base.num*AV_TIME_BASE - (uint64_t)preload *st ->time_base.den)*st2->time_base.den
            - ((uint64_t)next->dts*st2->time_base.num*AV_TIME_BASE - (uint64_t)preload2*st2->time_base.den)*st ->time_base.den;
        ts2 = 0;
    }
    comp = (ts2 > ts) - (ts2 < ts);
}

重点就是下面的代码,从当前buffer中找到当前帧的插入位置然后插入到packet的链表中。

c 复制代码
if (st->internal->last_in_packet_buffer) {
    next_point = &(st->internal->last_in_packet_buffer->next);
} else {
    next_point = &s->internal->packet_buffer;
}
//省略部分代码.......
if (*next_point) {
    if (chunked && !(pkt->flags & CHUNK_START))
        goto next_non_null;

    if (compare(s, &s->internal->packet_buffer_end->pkt, pkt)) {
        while (   *next_point
                && ((chunked && !((*next_point)->pkt.flags&CHUNK_START))
                    || !compare(s, &(*next_point)->pkt, pkt)))
            next_point = &(*next_point)->next;
        if (*next_point)
            goto next_non_null;
    } else {
        next_point = &(s->internal->packet_buffer_end->next);
    }
}

插入成功后回到ff_interleave_packet_per_dts中,从当前的packet链表的头结点拿到一阵返回给write_packet写入。

相关推荐
-凌凌漆-12 小时前
【Qt】【C++】虚析构函数及 virtual ~Base() = default
java·c++·qt
wang_chao11812 小时前
RK3399平台ffmpeg-VPU硬编码录制USB摄像头视频、H264或MJPEG编码
ffmpeg·音视频
励志不掉头发的内向程序员12 小时前
STL库——AVL树
开发语言·c++·学习
汉克老师18 小时前
第十四届蓝桥杯青少组C++选拔赛[2023.2.12]第二部分编程题(5、机甲战士)
c++·算法·蓝桥杯·01背包·蓝桥杯c++·c++蓝桥杯
Mr_Xuhhh19 小时前
项目需求分析(2)
c++·算法·leetcode·log4j
PAK向日葵20 小时前
【C/C++】面试官:手写一个memmove,要求性能尽可能高
c语言·c++·面试
Jared_devin21 小时前
二叉树算法题—— [蓝桥杯 2019 省 AB] 完全二叉树的权值
数据结构·c++·算法·职场和发展·蓝桥杯
搞全栈小苏21 小时前
基于Qt QML和C++的MQTT测试客户端(CMakeLists实现)
xml·c++·qt
啊?啊?21 小时前
18 从对象内存到函数调用:C++ 虚函数表原理(继承覆盖 / 动态绑定)+ 多态实战
开发语言·c++·多态原理
bkspiderx21 小时前
C++标准库:文件流类
开发语言·c++