使用FFMPEG将视频文件连续流式传输到RTMP服务器

34

ffmpeg可以作为输入或输出处理RTMP流,运行良好。

我想要将一些视频(由Python脚本管理的动态播放列表)流式传输到RTMP服务器,目前我正在进行相当简单的操作:使用FFmpeg一个接一个地将我的视频流式传输到RTMP服务器,但是每次视频结束时都会导致连接中断,并且下一个视频开始时需要重新建立连接。

我希望能够连续地流式传输这些视频而不会有任何连接中断,以便正确地查看视频流。我使用此命令将我的视频流式传输到服务器:

ffmpeg -re -y -i myvideo.mp4 -vcodec libx264 -b:v 600k -r 25 -s 640x360 \
-filter:v yadif -ab 64k -ac 1 -ar 44100 -f flv \
"rtmp://mystreamingserver/app/streamName"

我在互联网上寻找了很多天的解决办法,发现一些人谈到使用命名管道作为ffmpeg的输入。我尝试过,但效果不佳,因为ffmpeg不仅在新视频到来时关闭RTMP流,而且也会自行关闭。

有没有什么方法可以实现这个功能?(用ffmpeg向RTMP服务器流式传输动态播放列表,而不中断连接)


1
使用命名管道是正确的方法。您能详细说明一下为什么这对您不起作用吗? - blahdiblah
1
@blahdiblah 我在ffmpeg中使用了命名管道作为输入,ffmpeg暂停并等待来自管道的一些数据,然后我尝试了cat video.mp4 > fifo,ffmpeg开始流式传输并在流式传输视频后退出。我认为我不知道如何正确使用命名管道,当我执行cat video1.mp4 video2.mp4 > fifo时,ffmpeg在流式传输第一个视频后显示错误stream 4,offset 0x1d83c:partial file。我知道这是我的错误,我必须以适当的方式通过管道传递视频数据。 - kketch
2
我成功地通过使用管道(例如vid1.mp4-> pipe1,vid2.mp4-> pipe2等)来流式传输静态视频播放列表中的每个视频。然后,我将它们写入名为“stream”的单个流中,如下所示:cat pipe1 pipe2 pipe3 > stream,并且我使用该流管道作为FFMPEG的输入来发布我的流。但是,由于我正在寻找一个动态播放列表,我该如何将更多的视频发送到“stream”管道以保持流的活动状态? 我还没有想出来。(请注意,除了第一个视频外,我必须使用tail命令剪切每个视频文件的元数据才能使其正常工作) - kketch
FFmpeg有一个新的“concat”协议,可能会有所帮助。 - rogerdpack
1
http://ffmpeg.org/trac/ffmpeg/wiki/How%20to%20concatenate%20(join,%20merge)%20media%20files - hinekyle
ffmpeg [NO -re] [blah blah] -f flv "namedpipe" ffmpeg -re -f flv -i "namedpipe" - Tanner
4个回答

11

更新(因为我无法删除已接受的答案):正确的解决方案是实现一个自定义的分离器,类似于拼接器。目前没有其他干净的方法。你必须动手编写代码!

以下是一种丑陋的hack。这是一种非常糟糕的方式,不要使用!

该解决方案使用拼接器并假设所有源媒体文件都使用相同的编解码器。本例基于MPEG-TS,但对于RTMP也可以执行相同的操作。

  1. 创建一个播放列表文件,其中包含大量条目点,以以下格式进行动态播放列表:

    file 'item_1.ts' file 'item_2.ts' file 'item_3.ts' [...] file 'item_[ENOUGH_FOR_A_LIFETIME].ts'

    这些文件只是占位符。

  2. 制作一个脚本,跟踪当前播放列表索引并即时创建符号链接 current_index + 1

    ln -s /path/to/what/to/play/next.ts item_1.ts

    ln -s /path/to/what/to/play/next.ts item_2.ts

    ln -s /path/to/what/to/play/next.ts item_3.ts

    [...]

  3. 开始播放 ffmpeg -f concat -i playlist.txt -c copy output -f mpegts udp://<ip>:<port>

  4. 被愤怒的系统管理员追赶和骂名


谢谢,刚刚看到你的回答。我早就通过hinekyle的评论弄明白了,尽管现在我对这种技巧不感兴趣。 - kketch
你能详细说明一下“编写一个脚本,跟踪您当前的播放列表索引,并即时为 current_index + 1 创建符号链接”的意思吗?这有什么目的? - chovy
尝试对.m3u8 24/7流做同样的事情。但是命令“-c copy output -f mpegts stream.m3u8”会抛出一个错误。 - chovy
2
@chovy 这是我发布的一个非常糟糕的回答,更像是一个玩笑。真的,不要像这样做,这是一个丑陋的黑客行为。我会删除它,以免其他人受到诱惑尝试它。 - aergistal
1
@aergistal 你好!你说这是一个丑陋的hack,但是似乎ffmpeg的维基实际上建议按照你描述的那样做https://trac.ffmpeg.org/wiki/Concatenate#Automaticallyappendingtothelistfile ,所以可能你的答案毕竟不错 :) - Edgar P-Yan

8
需要创建两个播放列表文件,并在每个文件的末尾指定另一个文件的链接。 list_1.txt
ffconcat version 1.0
file 'item_1.mp4'
file 'list_2.txt'

list_2.txt

ffconcat version 1.0
file 'item_2.mp4'
file 'list_1.txt'

现在您所需要做的就是动态更改下一个播放列表文件的内容。

1
如何使用ffmpeg执行? - chovy
@chovy 你可以使用 ffmpeg -f concat -i list_1.txt -c copy output -f [等等等等] - undefined

7
您可以将循环导入到缓冲区中,然后从该缓冲区导入到流实例中。在shell中,它看起来像这样:
#!/bin/bash

for i in *.mp4; do
        ffmpeg -hide_banner -nostats -i "$i" -c:v mpeg2video \
[proper settings] -f mpegts -
done | mbuffer -q -c -m 20000k | ffmpeg -hide_banner \ 
-nostats -re -fflags +igndts \ 
-thread_queue_size 512 -i pipe:0 -fflags +genpts \ 
[proper codec setting] -f flv rtmp://127.0.0.1/live/stream

当然,您可以使用任何类型的循环,包括遍历播放列表。
  • 我发现 mpeg 对于输入流比 x264 更加稳定。
  • 我不知道为什么,但是 mpeg 压缩至少需要 2 个线程。
  • 输入压缩需要比输出帧速率更快,这样我们才能获得足够快的新输入。
  • 由于时间戳不连续,因此我们必须跳过它们并在输出中生成一个新的时间戳。
  • 缓冲区大小需要足够大,以使循环有足够的时间来获取新片段。

这里有一个基于 Rust 的解决方案,它使用了这种技术:ffplayout

它使用 JSON 播放列表格式。播放列表是动态的,这样您就可以随时编辑当前播放列表并更改音轨或添加新音轨。


1
您提供的shell命令未按预期工作。存储在mbuffer内部的临时缓冲区正在播放,该临时缓冲区不断循环。如何清空缓冲区?我尝试添加了一个管道,但并没有取得太大的成功。有什么想法吗? - josh
请注意,mbuffer 并不是在 所有 平台上都可以轻松获得(可悲的是)。原帖没有指明他使用的操作系统。你可以在像 Ubuntu 这样的系统上轻松获得它(可能也可以在其他基于 Debian 的软件仓库中找到)。在其他平台上,它需要编译,这并不容易(作者自己也承认了这一点);例如,在 macOS 下,我无法将其编译成功(尽管我尝试了所有的技巧)。这真是遗憾,因为 mbuffer 看起来确实是完成这个任务的正确工具。另一方面,ffplayout 真是太棒了,谢谢你的建议 :-) - undefined
@GwynethLlewelyn,我更新了shell示例,不需要mbuffer。 - undefined

3
非常晚的回答,但我最近遇到了与上面发帖者完全相同的问题。
我通过使用OBS和OBS WebSockets插件解决了这个问题。
首先,像您现在一样设置您的RTMP流应用程序。 但是将其流式传输到本地RTMP流。
然后,使用本地RTMP作为源,将OBS加载此RTMP流作为VLC源图层。
然后(在您的应用程序中),使用OBS WebSockets插件,使您的VLC源在视频结束时切换到静态黑色视频或PNG文件。 然后在下一个视频开始时切换回RTMP流。 这将防止RTMP流在视频结束时停止。 OBS在短暂的过渡期间会变黑,但最终的OBS RTMP输出永远不会停止。
肯定有一种方法可以手动设置中间的RTMP服务器,将其推送到最终的RTMP服务器,但我发现使用OBS更容易,开销也小。
我希望这可以帮助其他人,这个解决方案对我来说非常有效。

我喜欢OBS,但考虑到以下问题:如果所有这些都发生在远程服务器上怎么办?OBS确实有一个命令行界面(CLI),但它仍然需要在系统上运行——这在所谓的“无头”服务器上可能是不可能的(即没有连接显示器)。 - Gwyneth Llewelyn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接