一:介绍

1.1 ffmpeg 的 Mux 主要分为 三步操作:

avformat_write_header : 写文件头

av_write_frame/av_interleaved_write_frame: 写packet

av_write_trailer : 写文件尾

本文主要分析 av_write_frame/av_interleaved_write_frame

可额外参考: http://blog.csdn.net/leixiaohua1020/article/details/44199673

1.2 av_write_frame

参考官网说明 :http://ffmpeg.org/doxygen/3.1/group__lavf__encoding.html#gaa85cc1774f18f306cd20a40fc50d0b36

av_write_frame 直接将包写进Mux,没有缓存和重新排序,一切都需要用户自己设置。

  • Packet's stream_index field must be set to the index of the corresponding stream ins->streams.
  • The timestamps (pts, dts) must be set to correct values in the stream's timebase (unless the output format is flagged with the AVFMT_NOTIMESTAMPS flag, then they can be set to AV_NOPTS_VALUE). The dts for subsequent packets passed to this function must be strictly increasing when compared in their respective timebases (unless the output format is flagged with the AVFMT_TS_NONSTRICT, then they merely have to be nondecreasing).duration) should also be set if known.

1.3 av_interleaved_write_frame

参考官网说明: http://ffmpeg.org/doxygen/3.1/group__lavf__encoding.html#ga37352ed2c63493c38219d935e71db6c1

av_interleaved_write_frame 将对 packet 进行缓存和 pts 检查,这是区别于 av_write_frame 的地方。

  • If the packet is reference-counted, this function will take ownership of this reference and unreference it later when it sees fit. The caller must not access the data through this reference after this function returns. If the packet is not reference-counted, libavformat will make a copy.
  • This parameter can be NULL (at any time, not just at the end), to flush the interleaving queues.
  • Packet's stream_index field must be set to the index of the corresponding stream ins->streams.
  • The timestamps (pts, dts) must be set to correct values in the stream's timebase (unless the output format is flagged with the AVFMT_NOTIMESTAMPS flag, then they can be set to AV_NOPTS_VALUE). The dts for subsequent packets in one stream must be strictly increasing (unless the output format is flagged with the AVFMT_TS_NONSTRICT, then they merely have to be nondecreasing).duration) should also be set if known.

二:分析

2.1 av_write_frame

int av_write_frame(AVFormatContext *s, AVPacket *pkt)
{int ret;//对包预处理ret = prepare_input_packet(s, pkt);if (ret < 0)return ret;//空包,flushif (!pkt) {if (s->oformat->flags & AVFMT_ALLOW_FLUSH) {if (!s->internal->header_written) {ret = s->internal->write_header_ret ? s->internal->write_header_ret : write_header_internal(s);if (ret < 0)return ret;}ret = s->oformat->write_packet(s, NULL);if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)avio_flush(s->pb);if (ret >= 0 && s->pb && s->pb->error < 0)ret = s->pb->error;return ret;}return 1;}//计算包信息
#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTXret = compute_muxer_pkt_fields(s, s->streams[pkt->stream_index], pkt);if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))return ret;
#endif//写包ret = write_packet(s, pkt);if (ret >= 0 && s->pb && s->pb->error < 0)ret = s->pb->error;if (ret >= 0)s->streams[pkt->stream_index]->nb_frames++;return ret;
}

下面来看看具体做了什么。

prepare_input_packet(检查流通道 和 包的pts,dts是否符合):

static int prepare_input_packet(AVFormatContext *s, AVPacket *pkt)
{int ret;//检查流通道ret = check_packet(s, pkt);if (ret < 0)return ret;//检查PTS , DTS
#if !FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX/* sanitize the timestamps */if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) {AVStream *st = s->streams[pkt->stream_index];/* when there is no reordering (so dts is equal to pts), but* only one of them is set, set the other as well */if (!st->internal->reorder) {if (pkt->pts == AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE)pkt->pts = pkt->dts;if (pkt->dts == AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE)pkt->dts = pkt->pts;}/* check that the timestamps are set */if (pkt->pts == AV_NOPTS_VALUE || pkt->dts == AV_NOPTS_VALUE) {av_log(s, AV_LOG_ERROR,"Timestamps are unset in a packet for stream %d\n", st->index);return AVERROR(EINVAL);}/* check that the dts are increasing (or at least non-decreasing,* if the format allows it */if (st->cur_dts != AV_NOPTS_VALUE &&((!(s->oformat->flags & AVFMT_TS_NONSTRICT) && st->cur_dts >= pkt->dts) ||st->cur_dts > pkt->dts)) {av_log(s, AV_LOG_ERROR,"Application provided invalid, non monotonically increasing ""dts to muxer in stream %d: %" PRId64 " >= %" PRId64 "\n",st->index, st->cur_dts, pkt->dts);return AVERROR(EINVAL);}if (pkt->pts < pkt->dts) {av_log(s, AV_LOG_ERROR, "pts %" PRId64 " < dts %" PRId64 " in stream %d\n",pkt->pts, pkt->dts, st->index);return AVERROR(EINVAL);}}
#endifreturn 0;
}

compute_muxer_pkt_fields (计算duration ,pts,dts等):

static int compute_muxer_pkt_fields(AVFormatContext *s, AVStream *st, AVPacket *pkt)
{int delay = FFMAX(st->codecpar->video_delay, st->internal->avctx->max_b_frames > 0);int num, den, i;int frame_size;if (!s->internal->missing_ts_warning &&!(s->oformat->flags & AVFMT_NOTIMESTAMPS) &&(pkt->pts == AV_NOPTS_VALUE || pkt->dts == AV_NOPTS_VALUE)) {av_log(s, AV_LOG_WARNING,"Timestamps are unset in a packet for stream %d. ""This is deprecated and will stop working in the future. ""Fix your code to set the timestamps properly\n", st->index);s->internal->missing_ts_warning = 1;}if (s->debug & FF_FDEBUG_TS)av_log(s, AV_LOG_TRACE, "compute_muxer_pkt_fields: pts:%s dts:%s cur_dts:%s b:%d size:%d st:%d\n",av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), delay, pkt->size, pkt->stream_index);if (pkt->duration < 0 && st->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) {av_log(s, AV_LOG_WARNING, "Packet with invalid duration %"PRId64" in stream %d\n",pkt->duration, pkt->stream_index);pkt->duration = 0;}/* duration field */if (pkt->duration == 0) {ff_compute_frame_duration(s, &num, &den, st, NULL, pkt);if (den && num) {pkt->duration = av_rescale(1, num * (int64_t)st->time_base.den * st->codec->ticks_per_frame, den * (int64_t)st->time_base.num);}}if (pkt->pts == AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE && delay == 0)pkt->pts = pkt->dts;//XXX/FIXME this is a temporary hack until all encoders output ptsif ((pkt->pts == 0 || pkt->pts == AV_NOPTS_VALUE) && pkt->dts == AV_NOPTS_VALUE && !delay) {static int warned;if (!warned) {av_log(s, AV_LOG_WARNING, "Encoder did not produce proper pts, making some up.\n");warned = 1;}pkt->dts =
//        pkt->pts= st->cur_dts;pkt->pts = st->priv_pts->val;}//calculate dts from ptsif (pkt->pts != AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY) {st->pts_buffer[0] = pkt->pts;for (i = 1; i < delay + 1 && st->pts_buffer[i] == AV_NOPTS_VALUE; i++)st->pts_buffer[i] = pkt->pts + (i - delay - 1) * pkt->duration;for (i = 0; i<delay && st->pts_buffer[i] > st->pts_buffer[i + 1]; i++)FFSWAP(int64_t, st->pts_buffer[i], st->pts_buffer[i + 1]);pkt->dts = st->pts_buffer[0];}if (st->cur_dts && st->cur_dts != AV_NOPTS_VALUE &&((!(s->oformat->flags & AVFMT_TS_NONSTRICT) &&st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE &&st->codecpar->codec_type != AVMEDIA_TYPE_DATA &&st->cur_dts >= pkt->dts) || st->cur_dts > pkt->dts)) {av_log(s, AV_LOG_ERROR,"Application provided invalid, non monotonically increasing dts to muxer in stream %d: %s >= %s\n",st->index, av_ts2str(st->cur_dts), av_ts2str(pkt->dts));return AVERROR(EINVAL);}if (pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->pts < pkt->dts) {av_log(s, AV_LOG_ERROR,"pts (%s) < dts (%s) in stream %d\n",av_ts2str(pkt->pts), av_ts2str(pkt->dts),st->index);return AVERROR(EINVAL);}if (s->debug & FF_FDEBUG_TS)av_log(s, AV_LOG_TRACE, "av_write_frame: pts2:%s dts2:%s\n",av_ts2str(pkt->pts), av_ts2str(pkt->dts));st->cur_dts = pkt->dts;st->priv_pts->val = pkt->dts;/* update pts */switch (st->codec->codec_type) {case AVMEDIA_TYPE_AUDIO:frame_size = (pkt->flags & AV_PKT_FLAG_UNCODED_FRAME) ?((AVFrame *)pkt->data)->nb_samples :av_get_audio_frame_duration(st->codec, pkt->size);/* HACK/FIXME, we skip the initial 0 size packets as they are most* likely equal to the encoder delay, but it would be better if we* had the real timestamps from the encoder */if (frame_size >= 0 && (pkt->size || st->priv_pts->num != st->priv_pts->den >> 1 || st->priv_pts->val)) {frac_add(st->priv_pts, (int64_t)st->time_base.den * frame_size);}break;case AVMEDIA_TYPE_VIDEO:frac_add(st->priv_pts, (int64_t)st->time_base.den * st->time_base.num);break;}return 0;
}

write_packet(写包):

static int write_packet(AVFormatContext *s, AVPacket *pkt)
{int ret, did_split;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;}if (s->avoid_negative_ts > 0) {AVStream *st = s->streams[pkt->stream_index];int64_t offset = st->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->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->dts),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);}}}did_split = av_packet_split_side_data(pkt);if (!s->internal->header_written) {ret = s->internal->write_header_ret ? s->internal->write_header_ret : write_header_internal(s);if (ret < 0)goto fail;}if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) {AVFrame *frame = (AVFrame *)pkt->data;av_assert0(pkt->size == UNCODED_FRAME_PACKET_SIZE);ret = s->oformat->write_uncoded_frame(s, pkt->stream_index, &frame, 0);av_frame_free(&frame);} else {ret = s->oformat->write_packet(s, pkt);}if (s->pb && ret >= 0) {if (s->flush_packets && s->flags & AVFMT_FLAG_FLUSH_PACKETS)avio_flush(s->pb);if (s->pb->error < 0)ret = s->pb->error;}fail:if (did_split)av_packet_merge_side_data(pkt);return ret;
}

写包调用了AVOutputFormat 的 write_packet 。与write_header类似,具体的写包实现在各个封装的 AVOutputFormat 中。对此处写包未做详细分析...(-.-)...(/飙泪)。

2.2 av_interleaved_write_frame

int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt)
{int ret, flush = 0, i;//对包预处理ret = prepare_input_packet(s, pkt);if (ret < 0)goto fail;//bitstream filters (过滤器,也是区别于av_write_frame 的地方)if (pkt) {AVStream *st = s->streams[pkt->stream_index];if (s->oformat->check_bitstream) {if (!st->internal->bitstream_checked) {if ((ret = s->oformat->check_bitstream(s, pkt)) < 0)goto fail;else if (ret == 1)st->internal->bitstream_checked = 1;}}for (i = 0; i < st->internal->nb_bsfcs; i++) {AVBSFContext *ctx = st->internal->bsfcs[i];if (i > 0) {AVBSFContext* prev_ctx = st->internal->bsfcs[i - 1];if (prev_ctx->par_out->extradata_size != ctx->par_in->extradata_size) {if ((ret = avcodec_parameters_copy(ctx->par_in, prev_ctx->par_out)) < 0)goto fail;}}// TODO: when any bitstream filter requires flushing at EOF, we'll need to// flush each stream's BSF chain on write_trailer.if ((ret = av_bsf_send_packet(ctx, pkt)) < 0) {av_log(ctx, AV_LOG_ERROR,"Failed to send packet to filter %s for stream %d",ctx->filter->name, pkt->stream_index);goto fail;}// TODO: when any automatically-added bitstream filter is generating multiple// output packets for a single input one, we'll need to call this in a loop// and write each output packet.if ((ret = av_bsf_receive_packet(ctx, pkt)) < 0) {if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return 0;av_log(ctx, AV_LOG_ERROR,"Failed to send packet to filter %s for stream %d",ctx->filter->name, pkt->stream_index);goto fail;}if (i == st->internal->nb_bsfcs - 1) {if (ctx->par_out->extradata_size != st->codecpar->extradata_size) {if ((ret = avcodec_parameters_copy(st->codecpar, ctx->par_out)) < 0)goto fail;}}}if (s->debug & FF_FDEBUG_TS)av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame size:%d dts:%s pts:%s\n",pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts));#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTXif ((ret = compute_muxer_pkt_fields(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))goto fail;
#endifif (pkt->dts == AV_NOPTS_VALUE && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) {ret = AVERROR(EINVAL);goto fail;}} else {av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame FLUSH\n");flush = 1;}//写包for (;; ) {AVPacket opkt;//此处也是与av_write_frame的区别int ret = interleave_packet(s, &opkt, pkt, flush);if (pkt) {memset(pkt, 0, sizeof(*pkt));av_init_packet(pkt);pkt = NULL;}if (ret <= 0) //FIXME cleanup needed for ret<0 ?return ret;//写入ret = write_packet(s, &opkt);if (ret >= 0)s->streams[opkt.stream_index]->nb_frames++;av_packet_unref(&opkt);if (ret < 0)return ret;if(s->pb && s->pb->error)return s->pb->error;}
fail:av_packet_unref(pkt);return ret;
}

prepare_input_packet 函数与 av_write_frame 的一致,主要是 bitstream filters 和 interleave_packet 的区别:

static int interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, int flush)
{if (s->oformat->interleave_packet) {int ret = s->oformat->interleave_packet(s, out, in, flush);if (in)av_packet_unref(in);return ret;} elsereturn ff_interleave_packet_per_dts(s, out, in, flush);
}

后续可参考 : http://blog.csdn.net/dancing_night/article/details/46469865

ffmpeg——av_write_frame/av_interleaved_write_frame写文件包相关推荐

  1. mark:ffmpeg的av_interleaved_write_frame写失败

    1.首先需要确认返回码含义,可使用如下打印 int ret = av_interleaved_write_frame(rtmp_format_context, pkt); if (ret < 0 ...

  2. FFmpeg源代码:avformat_write_header/av_write_frame/av_interleaved_write_frame/av_write_trailer

    跟写视频文件相关的三个配套接口是 avformat_write_header()用于写视频文件头 av_write_frame()用于写视频数据 av_interleaved_write_frame( ...

  3. ffmpeg——av_write_trailer写文件尾

    ffmpeg 的 Mux 主要分为 三步操作: avformat_write_header : 写文件头 av_write_frame/av_interleaved_write_frame: 写pac ...

  4. 松下plc FP-XH写的4轴控制程序,其中两个轴做直线插补,已经在12台设备上稳定运行,程序文件包含有plc程序触摸屏程序

    松下plc FP-XH写的4轴控制程序,其中两个轴做直线插补,已经在12台设备上稳定运行,程序文件包含有plc程序触摸屏程序,设备电气图纸清单,还有设备的清晰运行视频,能够帮助你很好的理解程序运行流程 ...

  5. ffmpeg转码本地文件(一)

    ffmpeg转码本地文件(一) 实现目标:输入本地文件,实现本地文件转码,里面包含mux层转码,codec层转码,视频格式转换,音频重采样等功能,功能点请看注释,注意:注释很重要. #ifndef _ ...

  6. Hhadoop-2.7.0中HDFS写文件源码分析(二):客户端实现(1)

    一.综述 HDFS写文件是整个Hadoop中最为复杂的流程之一,它涉及到HDFS中NameNode.DataNode.DFSClient等众多角色的分工与合作. 首先上一段代码,客户端是如何写文件的: ...

  7. 模块的四种形式 模块的调用 循环导入问题 模块的搜索路径 py文件的两种用途 编译python文件 包...

    目录 模块的四种形式 什么是模块 模块的四种形式 自定义模块 第三方模块 内置模块 包 使用模块的好处 模块的调用 循环导入问题 模块的搜索路径 py文件的两种用途 编译python文件 包 什么是包 ...

  8. 把Java程序打包成jar文件包并执行

    把Java程序打包成jar文件包并执行 1.首先要确认自己写的程序有没有报错. 2.第一次我写的是Web Project到现在,我一直没有执行成功,所以最好创建的是java Project 打包步骤: ...

  9. upload-labs_pass10_点空点绕过_pass11_双写文件扩展名

    关于靶场说几点:单纯用phpstudy 可能无法复现所有的漏洞,而且phpstudy中的php可能是线程不安全的,所以建议大家在自己本机或者虚拟机的中亲自搭建一下apache和php的环境,便于复现u ...

最新文章

  1. mysql timestamp 晚8小时_mysql插入timeStamp类型数据时间相差8小时的解决办法
  2. 谈谈DictionaryT1,T2和ListT的问题
  3. android 解决小米手机上选择照片路径为null的问题
  4. 微信小程序入门三: 简易form、本地存储
  5. 无法向会话状态服务器发出会话状态请求。
  6. weex官方demo weex-hackernews代码解读(上)
  7. 搞懂激活函数(Sigmoid/ReLU/LeakyReLU/PReLU/ELU)
  8. caffe安装简易教程
  9. R语言--异常值检测
  10. 通过httpurlconnection发带图片的文件
  11. 计算机视觉SIFT算法详解
  12. 微信开发工具button跳转页面_微信小程序按钮点击跳转页面详解
  13. 服务器响应图像的生成
  14. 语音合成第一篇-入门
  15. 酒水知识(六大基酒之白兰地_Brandy)
  16. Redis 设计与实现 读书笔记(菜鸟版)
  17. Android实现电量控制降低耗电
  18. DirectX图形开发(一)-基本概念
  19. 海思3516DV300对接IMX327-60fps
  20. unity三维地图的经纬度如何在二维地图上表示_三维GIS与游戏引擎的跨界融合,打造数字化孪生的平行世界...

热门文章

  1. 面试官:介绍下Spring MVC
  2. 【数据恢复方法】误删了Word文档如何恢复?
  3. 机器人佐德_恐龙战队系列:忍者超级佐德,战斗机器人和设定图
  4. 白话ETH2.0:你要了解的都在这里
  5. [mfc] SetWindowPos函数
  6. .NET Core/.NET5/.NET6 开源项目汇总9:客户端跨平台UI框架
  7. 图片格式转换怎么做?不如试试这三个方法
  8. 初识HTML5(通俗易懂)
  9. MobileNeXt:Rethinking Bottleneck Structure for Efficient Mobile Network Design
  10. ROS学习笔记-ROS订阅和发布节点