目录
一、先给结论:推流只有 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(别忘extradata和time_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
推流只用
codecparcodec_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 DTSOBS 能播但 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 levelH264 没拷 extradata → 播放端无解码参数
五、一句话工程总结
FFmpeg 推流不是“编码问题”,是 mux + timestamp + network 问题。
80% 代码在搬 AVPacket,20% 在修时间戳。