FFmpeg 推流到底做了什么?从 avformat_open_input 到 av_write_frame 的完整链路拆解
2026/8/9 11:38:54 网站建设 项目流程

目录

一、先给结论:推流只有 4 个阶段

二、完整调用流程(标准 RTMP 推流版)

Step 0:全局一次(程序生命周期)

Step 1:打开“输入源”(文件 / 设备)

Step 2:创建输出上下文(推流核心)

Step 3:为输出创建 AVStream(非常关键)

Step 4:打开 IO(真正开始 TCP / RTMP Handshake)

Step 5:写 Header(Metadata / FLV Tag / SPS/PPS)

Step 6:主循环 —— 读 Packet → 时间戳 → 写出去

6.1 读 Packet

6.2 重算时间戳(90% 推流问题在这里)

6.3 发送 Packet(真正 push)

6.4 限速(模拟实时)

Step 7:写尾(优雅断流)

三、最小函数调用清单

四、推流常见翻车Top3

1. 不改 time_base

2. 用 MP4 当推流格式

3. 编码参数没继承全

五、一句话工程总结


觉得有用,就请您帮忙点赞转发收藏吧,您的鼓励是我创作的动力,多谢看官。

由于能力水平有限,文中的错误或不严谨的地方在所难免,还请批评指正。

Fmpeg 推流本质是mux + 时间戳 + 网络 IO,不是编码活。核心链路就三步:

avformat_network_init(),再开输入拿AVStream;用avformat_alloc_output_context2("flv")建输出,avformat_new_stream()拷贝codecpar(别忘extradatatime_base),avio_open()起 TCP/RTMP 握手;接着avformat_write_header()写 Metadata/SPS-PPS;主循环里av_read_frame()读包,必须av_packet_rescale_ts()重算时基,再用av_interleaved_write_frame()往出扔;最后av_write_trailer()收尾。

90% 的坑都在:不改 time_base、用 MP4 推流、首帧非 keyframe。记住一句:推的是 AVPacket,调的是时间戳,header 写成功才算上线。

很多人第一次用 FFmpeg 推 RTMP / RTSP / SRT,都是抄一条命令:

ffmpeg -re -i test.mp4 -c copy -f flv rtmp://xxx/live/stream

然后一写代码就懵:

到底要调哪些函数?顺序能不能乱?为什么要 avformat_write_header?

这篇就用“推流视角”把 FFmpeg 的 API 调用链捋清楚,不啰嗦,按时间线来。


一、先给结论:推流只有 4 个阶段

准备输入 ↓ 打开输出(AVFormatContext *ofmt_ctx) ↓ 构造输出流(AVStream + codecpar) ↓ 写头 / 循环送 AVPacket / 写尾

核心函数其实不到 10 个。


二、完整调用流程(标准 RTMP 推流版)

下面假设你已经有一个本地文件 / 摄像头 / 解码后帧源,目标是rtmp://server/app/stream


Step 0:全局一次(程序生命周期)

avformat_network_init(); // RTMP/RTSP/UDP 必调

很多 bug 的根源:忘了这句,RTMP 连不上还不报错。


Step 1:打开“输入源”(文件 / 设备)

AVFormatContext *ifmt_ctx = nullptr; avformat_open_input(&ifmt_ctx, input_file, nullptr, nullptr); avformat_find_stream_info(ifmt_ctx, nullptr);

目的:

  • 拿到AVStream

  • 拿到codecpar(宽高、codec_tag、bitrate、time_base)

如果是“自己造帧推流”(采集→编码),这步跳过,后面手动建流。


Step 2:创建输出上下文(推流核心)

AVFormatContext *ofmt_ctx = nullptr; avformat_alloc_output_context2(&ofmt_ctx, nullptr, "flv", rtmp_url);

重点:

  • "flv"→ RTMP 必须

  • URL 决定 muxer(rtmp:// → librtmp / native)

等价命令行:-f flv


Step 3:为输出创建 AVStream(非常关键)

AVStream *in_vst = ifmt_ctx->streams[video_index]; AVStream *out_vst = avformat_new_stream(ofmt_ctx, nullptr); avcodec_parameters_copy(out_vst->codecpar, in_vst->codecpar); out_vst->codecpar->codec_tag = 0; // ★ muxer 自动处理 out_vst->time_base = in_vst->time_base;

经验点:

  • 不要 new AVCodecContext

  • 推流只用codecpar

  • codec_tag=0能救很多 FLV / TS 奇葩错误

音频同理再来一遍。


Step 4:打开 IO(真正开始 TCP / RTMP Handshake)

AVDictionary *opts = nullptr; av_dict_set(&opts, "rw_timeout", "3000000", 0); // 3s 超时 avio_open2(&ofmt_ctx->pb, rtmp_url, AVIO_FLAG_WRITE, nullptr, &opts);

到这里:

  • TCP 连了

  • RTMP connect / createStream 完成了


Step 5:写 Header(Metadata / FLV Tag / SPS/PPS)

AVDictionary *fopts = nullptr; av_dict_set(&fopts, "flush_packets", "1", 0); avformat_write_header(ofmt_ctx, &fopts);

推流灵魂函数:

格式

header 干了啥

FLV

Script Tag + onMetaData + AVC seq header

TS

PAT/PMT

MP4

moov(但 MP4 不适合真·推流)

header 没成功 = 播放端永远拉不开


Step 6:主循环 —— 读 Packet → 时间戳 → 写出去

6.1 读 Packet

AVPacket pkt; av_read_frame(ifmt_ctx, &pkt);

6.2 重算时间戳(90% 推流问题在这里)

if (pkt.stream_index == video_index) { pkt.stream_index = out_vst->index; // 关键:重新 scale time_base av_packet_rescale_ts(&pkt, ifmt_ctx->streams[pkt.stream_index]->time_base, out_vst->time_base); }

不 rescale:

  • VLC 卡 1 帧

  • ffplay 报Non-monotonic DTS

  • OBS 能播但 Chrome MSE 挂


6.3 发送 Packet(真正 push)

av_interleaved_write_frame(ofmt_ctx, &pkt); av_packet_unref(&pkt);

为什么不用av_write_frame

  • interleaved:自动 A/V 交错

  • RTMP / TS 必选

  • 除非你手动严格交错


6.4 限速(模拟实时)

av_usleep(interval_us); // -re 的本质

Step 7:写尾(优雅断流)

av_write_trailer(ofmt_ctx);
  • RTMP:发 deleteStream

  • FLV:flush

  • 不写 trailer 也没事,但服务器日志会丑


三、最小函数调用清单

avformat_network_init avformat_open_input avformat_find_stream_info avformat_alloc_output_context2 avformat_new_stream avcodec_parameters_copy avio_open avformat_write_header av_read_frame av_packet_rescale_ts av_interleaved_write_frame av_write_trailer avio_closep avformat_free_context

四、推流常见翻车Top3

1. 不改 time_base

现象:能推,拉流端卡死

解法:av_packet_rescale_ts


2. 用 MP4 当推流格式

现象:等半天才播

真相:moov 在尾,HTTP-FLV / RTMP 吃瘪

解法:flv / mpegts


3. 编码参数没继承全

extradata extradata_size profile level

H264 没拷 extradata → 播放端无解码参数


五、一句话工程总结

FFmpeg 推流不是“编码问题”,是 mux + timestamp + network 问题。

80% 代码在搬 AVPacket,20% 在修时间戳。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询