无从告白 發表於 2025-10-8 12:10:00

FFmpeg开发笔记(八十一)FFmpeg代码对RTSP和RTMP的推流区别

<span id="cke_bm_434S">《FFmpeg开发实战:从零基础到短视频上线》一书的“10.2.2 &nbsp;FFmpeg向网络推流”介绍了如何使用FFmpeg代码向网络推送视频流,当时的例程采用了RTSP方式推流,在向RTSP地址时推流是正常的,但向RTMP地址推流时出现了问题,下面就介绍了如何使用FFmpeg代码向RTMP地址推送视频流。</span>
<h1>一、FFmpeg推流环境的区别</h1>
<p>首先注意RTSP推流地址以“rtsp://”开头,且RTSP地址的默认端口号为8554。而RTMP推流地址以“rtmp://”开头,且RTMP地址的默认端口号为1935。<br>
其次注意FFmpeg从6.1开始对RTMP协议做了增强支持,主要是支持HEVC、VP9和AV1等编码格式通过RTMP协议进行推流,所以建议将编译环境的FFmpeg版本升级到6.1或者更高版本。&nbsp;</p>
<h1>二、FFmpeg推流代码的适配</h1>
<p>FFmpeg推流代码对于RTSP地址和RTMP地址主要有下列两点适配区别:<br>
1、调用avformat_alloc_output_context2函数分配音视频文件封装实例的时候,第三个输入参数对于RTSP地址而言要填rtsp,对于RTMP地址而言要填flv。比如以下代码通过判断推流地址的协议类型来决定avformat_alloc_output_context2的第三个参数要填何值。</p>
<div class="cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected" data-cke-display-name="代码段" data-cke-filter="off" data-cke-widget-id="8" data-cke-widget-wrapper="1">
<pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22int%20ret%20%3D%200%3B%5Cn%2F%2F%20%E5%88%86%E9%85%8D%E9%9F%B3%E8%A7%86%E9%A2%91%E6%96%87%E4%BB%B6%E7%9A%84%E5%B0%81%E8%A3%85%E5%AE%9E%E4%BE%8B%EF%BC%88%E6%B3%A8%E6%84%8Frtmp%E5%8D%8F%E8%AE%AE%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0%E5%A1%ABflv%EF%BC%8Crtsp%E5%8D%8F%E8%AE%AE%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0%E5%A1%ABrtsp%EF%BC%89%5Cnif%20(strstr(dest_name%2C%20%5C%22rtmp%5C%22)%20!%3D%20NULL)%20%7B%5Cn%C2%A0%20%C2%A0%20ret%20%3D%20avformat_alloc_output_context2(%26out_fmt_ctx%2C%20NULL%2C%20%5C%22flv%5C%22%2C%20dest_name)%3B%5Cn%7D%20else%20%7B%5Cn%C2%A0%20%C2%A0%20ret%20%3D%20avformat_alloc_output_context2(%26out_fmt_ctx%2C%20NULL%2C%20%5C%22rtsp%5C%22%2C%20dest_name)%3B%5Cn%7D%5Cnif%20(ret%20%3C%200)%20%7B%5Cn%C2%A0%20%C2%A0%20av_log(NULL%2C%20AV_LOG_ERROR%2C%20%5C%22Can't%20alloc%20output_file%20%25s.%5C%5Cn%5C%22%2C%20dest_name)%3B%5Cn%C2%A0%20%C2%A0%20return%20-1%3B%5Cn%7D%5Cnav_log(NULL%2C%20AV_LOG_INFO%2C%20%5C%22Success%20open%20push%20url%20%25s.%5C%5Cn%5C%22%2C%20dest_name)%3B%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">int ret = 0;
// 分配音视频文件的封装实例(注意rtmp协议的第三个参数填flv,rtsp协议的第三个参数填rtsp)
if (strstr(dest_name, "rtmp") != NULL) {
&nbsp; &nbsp; ret = avformat_alloc_output_context2(&amp;out_fmt_ctx, NULL, "flv", dest_name);
} else {
&nbsp; &nbsp; ret = avformat_alloc_output_context2(&amp;out_fmt_ctx, NULL, "rtsp", dest_name);
}
if (ret &lt; 0) {
&nbsp; &nbsp; av_log(NULL, AV_LOG_ERROR, "Can't alloc output_file %s.\n", dest_name);
&nbsp; &nbsp; return -1;
}
av_log(NULL, AV_LOG_INFO, "Success open push url %s.\n", dest_name);</code></pre>
</div>
<p>2、调用avformat_alloc_output_context2函数之后,还要依据推流地址的协议类型来决定是否接着调用avio_open函数打开输出流,对于RTSP地址而言不必调用avio_open,对于RTMP地址而言必须调用avio_open。具体的判断与调用代码如下所示:</p>
<div class="cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected" data-cke-display-name="代码段" data-cke-filter="off" data-cke-widget-id="7" data-cke-widget-wrapper="1">
<pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22%2F%2F%20%E6%89%93%E5%BC%80%E8%BE%93%E5%87%BA%E6%B5%81%EF%BC%88%E6%B3%A8%E6%84%8Frtsp%E6%8E%A8%E6%B5%81%E4%B8%8D%E8%A6%81%E8%B0%83%E7%94%A8avio_open%EF%BC%8C%E4%BD%86rtmp%E6%8E%A8%E6%B5%81%E8%A6%81%E8%B0%83%E7%94%A8avio_open%EF%BC%89%5Cnif%20(strstr(dest_name%2C%20%5C%22rtmp%5C%22)%20!%3D%20NULL)%20%7B%5Cn%C2%A0%20%C2%A0%20ret%20%3D%20avio_open(%26out_fmt_ctx-%3Epb%2C%20dest_name%2C%20AVIO_FLAG_READ_WRITE)%3B%5Cn%C2%A0%20%C2%A0%20if%20(ret%20%3C%200)%20%7B%5Cn%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20av_log(NULL%2C%20AV_LOG_ERROR%2C%20%5C%22Can't%20open%20output_file%20%25s.%5C%5Cn%5C%22%2C%20dest_name)%3B%5Cn%C2%A0%20%C2%A0%20%C2%A0%20%C2%A0%20return%20-1%3B%5Cn%C2%A0%20%C2%A0%20%7D%5Cn%7D%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">// 打开输出流(注意rtsp推流不要调用avio_open,但rtmp推流要调用avio_open)
if (strstr(dest_name, "rtmp") != NULL) {
&nbsp; &nbsp; ret = avio_open(&amp;out_fmt_ctx-&gt;pb, dest_name, AVIO_FLAG_READ_WRITE);
&nbsp; &nbsp; if (ret &lt; 0) {
&nbsp; &nbsp; &nbsp; &nbsp; av_log(NULL, AV_LOG_ERROR, "Can't open output_file %s.\n", dest_name);
&nbsp; &nbsp; &nbsp; &nbsp; return -1;
&nbsp; &nbsp; }
}</code></pre>
</div>
<p>按照以上两点代码修改了《FFmpeg开发实战:从零基础到短视频上线》随书源码第十章的推流程序代码chapter10/pushvideo.c后,先按照之前的文章《详解MediaMTX的推拉流》启动电脑本地的流媒体服务器MediaMTX,再执行下面的编译命令。</p>
<div class="cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected" data-cke-display-name="代码段" data-cke-filter="off" data-cke-widget-id="6" data-cke-widget-wrapper="1">
<pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22gcc%20pushvideo.c%20-o%20pushvideo%20-I%2Fusr%2Flocal%2Fffmpeg%2Finclude%20-L%2Fusr%2Flocal%2Fffmpeg%2Flib%20-lavformat%20-lavdevice%20-lavfilter%20-lavcodec%20-lavutil%20-lswscale%20-lswresample%20-lpostproc%20-lm%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">gcc pushvideo.c -o pushvideo -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib -lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswscale -lswresample -lpostproc -lm</code></pre>
</div>
<p>编译完成后执行以下命令启动测试程序,期望把2018.mp4推给RTMP协议的推流地址rtmp://127.0.0.1:1935/stream。</p>
<div class="cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected" data-cke-display-name="代码段" data-cke-filter="off" data-cke-widget-id="5" data-cke-widget-wrapper="1">
<pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22.%2Fpushvideo%20..%2Ffile%2F2018.mp4%20rtmp%3A%2F%2F127.0.0.1%3A1935%2Fstream%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">./pushvideo ../file/2018.mp4 rtmp://127.0.0.1:1935/stream</code></pre>
</div>
<p>接着打开另一个MSYS窗口,同样进入该书第十章的源码目录chapter10,执行下面的编译命令。</p>
<div class="cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected" data-cke-display-name="代码段" data-cke-filter="off" data-cke-widget-id="4" data-cke-widget-wrapper="1">
<pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22gcc%20pullvideo.c%20-o%20pullvideo%20-I%2Fusr%2Flocal%2Fffmpeg%2Finclude%20-L%2Fusr%2Flocal%2Fffmpeg%2Flib%20-I%2Fusr%2Flocal%2Fsdl2%2Finclude%20-L%2Fusr%2Flocal%2Fsdl2%2Flib%20-lsdl2%20-lavformat%20-lavdevice%20-lavfilter%20-lavcodec%20-lavutil%20-lswscale%20-lswresample%20-lpostproc%20-lm%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">gcc pullvideo.c -o pullvideo -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib -I/usr/local/sdl2/include -L/usr/local/sdl2/lib -lsdl2 -lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswscale -lswresample -lpostproc -lm</code></pre>
</div>
<p>编译完成后执行以下命令启动测试程序,期望从http://127.0.0.1:8888/stream/index.m3u8拉取视频流并弹窗播放。</p>
<div class="cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected" data-cke-display-name="代码段" data-cke-filter="off" data-cke-widget-id="3" data-cke-widget-wrapper="1">
<pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22.%2Fpullvideo%20http%3A%2F%2F127.0.0.1%3A8888%2Fstream%2Findex.m3u8%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">./pullvideo http://127.0.0.1:8888/stream/index.m3u8</code></pre>
</div>
<p>然后果真弹出一个SDL窗口,正在播放从HLS服务拉取的视频画面,说明修改后的推流代码成功支持了RTMP协议。</p>
<p><span class="cke_widget_wrapper cke_widget_inline cke_widget_image cke_image_nocaption cke_widget_selected" data-cke-display-name="图像" data-cke-filter="off" data-cke-widget-id="2" data-cke-widget-wrapper="1"><span class="cke_reset cke_widget_drag_handler_container"><span class="cke_image_resizer" title="点击并拖拽以改变尺寸"><span class="cke_widget_edit_container" title="编辑图片"><img src="https://img2024.cnblogs.com/blog/729938/202510/729938-20251005160718459-2109946152.png"></span></span></span></span></p>
<p>更多详细的FFmpeg开发知识参见<span class="cke_widget_wrapper cke_widget_inline cke_widget_csdnlink cke_widget_selected" data-cke-display-name="a" data-cke-filter="off" data-cke-widget-id="1" data-cke-widget-wrapper="1">《FFmpeg开发实战:从零基础到短视频上线》一书。</span></p><br><br>
来源:https://www.cnblogs.com/aqi00/p/19126670
頁: [1]
查看完整版本: FFmpeg开发笔记(八十一)FFmpeg代码对RTSP和RTMP的推流区别