从头用脚分析FFmpeg源码 - av_interleaved_write_frame | av_write_frame

news/2024/7/10 19:54:31 标签: ffmpeg, 音视频

本文所使用的是FFmpeg n4.4的源码,所有分析均来自博主瞎猜,如果有误,欢迎批评指正。

av_write_frame 作用

/**
 * Write a packet to an output media file.
 *
 * This function passes the packet directly to the muxer, without any buffering
 * or reordering. The caller is responsible for correctly interleaving the
 * packets if the format requires it. Callers that want libavformat to handle
 * the interleaving should call av_interleaved_write_frame() instead of this
 * function.
 */
int av_write_frame(AVFormatContext *s, AVPacket *pkt);

直接把AVPacket写到格式封装器中,输入的pkt必须有正确的时间戳和stream_index。写入的过程中不会有经过buffer和重排序。一些封装器可能在实现的内部对pkt增加引用。

av_interleaved_write_frame 作用

/**
 * Write a packet to an output media file ensuring correct interleaving.
 *
 * This function will buffer the packets internally as needed to make sure the
 * packets in the output file are properly interleaved in the order of
 * increasing dts. Callers doing their own interleaving should call
 * av_write_frame() instead of this function.
 *
 * Using this function instead of av_write_frame() can give muxers advance
 * knowledge of future packets, improving e.g. the behaviour of the mp4
 * muxer for VFR content in fragmenting mode.
 *
 * @return 0 on success, a negative AVERROR on error. Libavformat will always
 *         take care of freeing the packet, even if this function fails.
 *
 * @see av_write_frame(), AVFormatContext.max_interleave_delta
 */
int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);

这个函数的作用同上面的函数一样,都是写入pkt,不同之处在于调用av_interleaved_write_frame函数会写入buffer,然后重新根据时间戳设置写入的pkt顺序。

av_write_frame 源码

简单来说,就是调用write_packets_common函数,设置interleaved参数为0

int av_write_frame(AVFormatContext *s, AVPacket *in)
{
    AVPacket *pkt = s->internal->pkt;
    int ret;

	//刷新
    if (!in) {
        if (s->oformat->flags & AVFMT_ALLOW_FLUSH) {
            ret = s->oformat->write_packet(s, NULL);
            flush_if_needed(s);
            if (ret >= 0 && s->pb && s->pb->error < 0)
                ret = s->pb->error;
            return ret;
        }
        return 1;
    }

	// 未编码数据
    if (in->flags & AV_PKT_FLAG_UNCODED_FRAME) {
        pkt = in;
    } else {
        /* We don't own in, so we have to make sure not to modify it.
         * The following avoids copying in's data unnecessarily.
         * Copying side data is unavoidable as a bitstream filter
         * may change it, e.g. free it on errors. */
        av_packet_unref(pkt);
        pkt->buf  = NULL;
        pkt->data = in->data;
        pkt->size = in->size;
        ret = av_packet_copy_props(pkt, in);
        if (ret < 0)
            return ret;
        if (in->buf) {
            pkt->buf = av_buffer_ref(in->buf);
            if (!pkt->buf) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
        }
    }

	// av_interleaved_write_frame和av_write_frame函数都会调用这个函数,具体的区别在write_packets_common中实现
    ret = write_packets_common(s, pkt, 0/*non-interleaved*/);

fail:
    // Uncoded frames using the noninterleaved codepath are also freed here
    av_packet_unref(pkt);
    return ret;
}

av_interleaved_write_frame 源码

av_interleaved_write_frame 和 av_write_frame源码类似,调用write_packets_common,设置设置interleaved参数为1.

int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt)
{
    int ret;

    if (pkt) {
        ret = write_packets_common(s, pkt, 1/*interleaved*/);
        if (ret < 0)
            av_packet_unref(pkt);
        return ret;
    } else {
    	// interleaved_write_packet刷新,这个函数在write_packet_common中也有调用
        av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame FLUSH\n");
        return interleaved_write_packet(s, NULL, 1/*flush*/);
    }
}

write_packets_common 源码

检测pkt,如果有需要,在经过bsf的转化,然后调用write_packet_common函数,进行写操作。

static int write_packets_common(AVFormatContext *s, AVPacket *pkt, int interleaved)
{
    AVStream *st;
    //检测stream_index和对应的AVStream codec_type是否为AVMEDIA_TYPE_ATTACHMENT
    int ret = check_packet(s, pkt);
    if (ret < 0)
        return ret;
    st = s->streams[pkt->stream_index];

	//主要是版本兼容
    ret = prepare_input_packet(s, st, pkt);
    if (ret < 0)
        return ret;

	//检测oformat是否有实现check_bitstream。
	//主要是用来看编码是数据是否符合要求,比如AVCC和ANNEXB
    ret = check_bitstream(s, st, pkt);
    if (ret < 0)
        return ret;

	//不符合要求,去要对packet进行操作
    if (st->internal->bsfc) {
        return write_packets_from_bsfs(s, st, pkt, interleaved);
    } else {
    	//重要函数,通过这个函数写AVPacket
        return write_packet_common(s, st, pkt, interleaved);
    }
}

write_packets_from_bsfs 源码

主要作用就是对编码后的数据进行处理,比如AVCC到ANNEXB格式等,将处理后的AVPacket送到write_packet_common函数。

static int write_packets_from_bsfs(AVFormatContext *s, AVStream *st, AVPacket *pkt, int interleaved)
{
    AVBSFContext *bsfc = st->internal->bsfc;
    int ret;

    if ((ret = av_bsf_send_packet(bsfc, pkt)) < 0) {
        av_log(s, AV_LOG_ERROR,
                "Failed to send packet to filter %s for stream %d\n",
                bsfc->filter->name, st->index);
        return ret;
    }

    do {
        ret = av_bsf_receive_packet(bsfc, pkt);
        if (ret < 0) {
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                return 0;
            av_log(s, AV_LOG_ERROR, "Error applying bitstream filters to an output "
                   "packet for stream #%d: %s\n", st->index, av_err2str(ret));
            if (!(s->error_recognition & AV_EF_EXPLODE) && ret != AVERROR(ENOMEM))
                continue;
            return ret;
        }
        av_packet_rescale_ts(pkt, bsfc->time_base_out, st->time_base);
        //主要是这个函数,在write_packets_common函数中,也有调用,也就是说最终还是要调用write_packet_common
        ret = write_packet_common(s, st, pkt, interleaved);
        if (ret >= 0 && !interleaved) // a successful write_packet_common already unrefed pkt for interleaved
            av_packet_unref(pkt);
    } while (ret >= 0);

    return ret;
}

write_packet_common 源码

根据是否interleaved,分别调用interleaved_write_packet函数和write_packet函数

static int write_packet_common(AVFormatContext *s, AVStream *st, AVPacket *pkt, int interleaved)
{
    int ret;

    if (s->debug & FF_FDEBUG_TS)
        av_log(s, AV_LOG_DEBUG, "%s size:%d dts:%s pts:%s\n", __FUNCTION__,
               pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts));

    guess_pkt_duration(s, st, pkt);

#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX
    if ((ret = compute_muxer_pkt_fields(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))
        return ret;
#endif

    if (interleaved) {
        if (pkt->dts == AV_NOPTS_VALUE && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))
            return AVERROR(EINVAL);
        return interleaved_write_packet(s, pkt, 0);
    } else {
        return write_packet(s, pkt);
    }
}

interleaved_write_packet 源码

static int interleaved_write_packet(AVFormatContext *s, AVPacket *pkt, int flush)
{
    for (;; ) {
        AVPacket opkt;
        //写入buffer,然后从buffer中读取最前面的pkt
        int ret = interleave_packet(s, &opkt, pkt, flush);
        if (ret <= 0)
            return ret;

        pkt = NULL;

		//把获取的pkt写入
        ret = write_packet(s, &opkt);

        av_packet_unref(&opkt);

        if (ret < 0)
            return ret;
    }
}

interleave_packet 源码

static int interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, int flush)
{
	// oformat中自己实现了buffer和重排序函数
    if (s->oformat->interleave_packet) {
        return s->oformat->interleave_packet(s, out, in, flush);
    } else
    	// 公共oformat的buffer,重排序函数,重排序算法暂时就不分析了
        return ff_interleave_packet_per_dts(s, out, in, flush);
}

write_packet 源码

static int write_packet(AVFormatContext *s, AVPacket *pkt)
{
    int ret;

    // If the timestamp offsetting below is adjusted, adjust
    // ff_interleaved_peek similarly.
    // 输出偏移调整,output_ts_offset最终是在写pkt的时候发挥作用
    if (s->output_ts_offset) {
        AVStream *st = s->streams[pkt->stream_index];
        int64_t offset = av_rescale_q(s->output_ts_offset, AV_TIME_BASE_Q, st->time_base);

        if (pkt->dts != AV_NOPTS_VALUE)
            pkt->dts += offset;
        if (pkt->pts != AV_NOPTS_VALUE)
            pkt->pts += offset;
    }

	// 根据avoid_negative_ts字段,调整要写入的pts和dts
    if (s->avoid_negative_ts > 0) {
        AVStream *st = s->streams[pkt->stream_index];
        int64_t offset = st->internal->mux_ts_offset;
        int64_t ts = s->internal->avoid_negative_ts_use_pts ? pkt->pts : pkt->dts;

        if (s->internal->offset == AV_NOPTS_VALUE && ts != AV_NOPTS_VALUE &&
            (ts < 0 || s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO)) {
            s->internal->offset = -ts;
            s->internal->offset_timebase = st->time_base;
        }

        if (s->internal->offset != AV_NOPTS_VALUE && !offset) {
            offset = st->internal->mux_ts_offset =
                av_rescale_q_rnd(s->internal->offset,
                                 s->internal->offset_timebase,
                                 st->time_base,
                                 AV_ROUND_UP);
        }

        if (pkt->dts != AV_NOPTS_VALUE)
            pkt->dts += offset;
        if (pkt->pts != AV_NOPTS_VALUE)
            pkt->pts += offset;

        if (s->internal->avoid_negative_ts_use_pts) {
            if (pkt->pts != AV_NOPTS_VALUE && pkt->pts < 0) {
                av_log(s, AV_LOG_WARNING, "failed to avoid negative "
                    "pts %s in stream %d.\n"
                    "Try -avoid_negative_ts 1 as a possible workaround.\n",
                    av_ts2str(pkt->pts),
                    pkt->stream_index
                );
            }
        } else {
            av_assert2(pkt->dts == AV_NOPTS_VALUE || pkt->dts >= 0 || s->max_interleave_delta > 0);
            if (pkt->dts != AV_NOPTS_VALUE && pkt->dts < 0) {
                av_log(s, AV_LOG_WARNING,
                    "Packets poorly interleaved, failed to avoid negative "
                    "timestamp %s in stream %d.\n"
                    "Try -max_interleave_delta 0 as a possible workaround.\n",
                    av_ts2str(pkt->dts),
                    pkt->stream_index
                );
            }
        }
    }

	//这里是最主要的实现,一般来说都是编码后的数据,走s->oformat->write_packet,write_packet函数是对应各个封装器的实现。
    if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) {
        AVFrame **frame = (AVFrame **)pkt->data;
        av_assert0(pkt->size == sizeof(*frame));
        ret = s->oformat->write_uncoded_frame(s, pkt->stream_index, frame, 0);
    } else {
        ret = s->oformat->write_packet(s, pkt);
    }

    if (s->pb && ret >= 0) {
        flush_if_needed(s);
        if (s->pb->error < 0)
            ret = s->pb->error;
    }

    if (ret >= 0)
        s->streams[pkt->stream_index]->nb_frames++;

    return ret;
}


http://www.niftyadmin.cn/n/202664.html

相关文章

HAProxy TCP路由配置

haproxy是基于4层协议的转发负载工具&#xff0c;与nginx&#xff08;主7层&#xff09;一样&#xff0c;适合做tcp的负载均衡&#xff08;eg.mysql&#xff09;&#xff0c;使用该功能 主要分3步。 1.确认linux当前系统支持tcp转发 2.安装haproxy 3.配置文件 确认认linux当…

IDEA使用总结https://blog.csdn.net/qq_33417321/article/details/98884381

IDEA使用总结 麦芽糖0219 已于 2022-11-27 10:03:55 修改 832 收藏 6 分类专栏&#xff1a; 技术 开发工具 java 文章标签&#xff1a; IDEA 版权 技术 同时被 3 个专栏收录 39 篇文章2 订阅 订阅专栏 开发工具 7 篇文章0 订阅 订阅专栏 java 51 篇文章0 订阅 订阅专栏 …

MySQL开发04-MySQL优化器的提示功能

文章目录1、提示功能概述2、提示功能使用2.1、使用索引&#xff08;USE INDEX&#xff09;2.2、不使用索引&#xff08;IGNORE INDEX&#xff09;2.3、强制使用索引&#xff08;FORCE INDEX&#xff09;2.4、不使用查询缓存&#xff08;SQL_NO_CACHE&#xff09;2.5、使用查询缓…

Cannot read properties of undefined (reading ‘uri‘)

Cannot read properties of undefined (reading ‘uri’) 问题描述 Cursor更新后遇到如标题那样的问题&#xff0c;我直接百度没发现解决方法&#xff0c;遂到github的issue看了看&#xff01; 解决方法 有网友给了解决方法&#xff0c;如下图&#xff1a; 如果对你有帮助…

“三只青蛙”时间管理法(超级实用PMP知识)

如果你必须吃掉一只青蛙&#xff0c;不要长时间盯着它看。如果你必须连着吃掉三只青蛙&#xff0c;记得要先吃掉最大、最丑的那只。 ——博恩崔西 如今这个世界日益复杂&#xff0c;我们承受的压力越来越大。每天的时间似乎永远也不够用&#xff0c;不足以让我们完成所有想完成…

Data truncated for column

今天在启动项目的时候遇到一个问题&#xff0c;顺便记录一下 Cause: java.sql.SQLException: Data truncated for column create_time at row 1原因是&#xff1a; 1、datetime 类型在数据库中不能有默认值 2、当数据库字段的类型设置的过短时也会报这个错误

[WinError 183] 当文件已存在时,无法创建该文件

你写的程序重命名到相同名字了&#xff0c;看看哪个地方导致的改改就行了。

Linux系统常用命令大全

本教程将介绍Linux系统的基本操作&#xff0c;包括文件操作、用户管理和软件安装等。 1. 文件操作 1.1 查看文件内容 使用cat命令可以查看文件的内容&#xff0c;例如&#xff1a;cat file.txt 1.2 创建新文件 使用touch命令可以创建新文件&#xff0c;例如&#xff1a;to…