33

ffmpeg handles RTMP streaming as input or output, and it's working well.

I want to stream some videos (a dynamic playlist managed by a python script) to a RTMP server, and i'm currently doing something quite simple: streaming my videos one by one with FFMPEG to the RTMP server, however this causes a connection break every time a video end, and the stream is ready to go when the next video begins.

I would like to stream those videos without any connection breaks continuously, then the stream could be correctly viewed.

I use this command to stream my videos one by one to the server

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"

I looked for some workarounds over the internet for many days, and i found some people talking about using a named pipe as input in ffmpeg, I've tried it and it didn't work well since ffmpeg does not only close the RTMP stream when a new video comes but also closes itself.

Is there any way to do this ? (stream a dynamic playlist of videos with ffmpeg to RTMP server without connection breaks

user123456
  • 364
  • 1
  • 3
  • 16
kketch
  • 673
  • 1
  • 7
  • 10
  • 1
    Using a named pipe is the right way to go. Can you elaborate on how that didn't work for you? – blahdiblah Jul 25 '12 at 22:13
  • 1
    @blahdiblah I used the named pipe as an input in ffmpeg, ffmpeg pause and wait for some data from the pipe, then i've tried `cat video.mp4 > fifo` and ffmpeg start streaming and quit after streaming the video. I think i dont know how to use the named pipe the right way, when i did `cat video1.mp4 video2.mp4 > fifo` ffmpeg show the error `stream 4, offset 0x1d83c: partial file` after streaming the first video. I know this is me doing it wrong, i have to pass the video data in a proper way through the pipe. – kketch Jul 26 '12 at 07:41
  • 2
    I managed to stream a static playlist of videos by using for each video a pipe (ex vid1.mp4 -> pipe1, vid2.mp4 -> pipe2 etc). Then i write into a single stream named pipe "stream" this way `cat pipe1 pipe2 pipe3 > stream`, and i use the stream pipe as input in FFMPEG to publish my stream, but since i looking for a dynamic playlist **how can i send more videos to the "stream" pipe in order to keep alive the stream ?** I haven't figure it out yet. (Note, except for the first video, i had to cut the metadata of each video file using tail command for getting this working) – kketch Jul 28 '12 at 14:53
  • ffmpeg has a new "concat" protocol that...might...help – rogerdpack Oct 13 '12 at 17:21
  • 1
    http://ffmpeg.org/trac/ffmpeg/wiki/How%20to%20concatenate%20(join,%20merge)%20media%20files – hinekyle Feb 18 '13 at 15:17
  • ffmpeg [NO -re] [blah blah] -f flv "namedpipe" ffmpeg -re -f flv -i "namedpipe" – Tanner Sep 06 '13 at 22:28

4 Answers4

11

Update (as I can't delete the accepted answer): the proper solution is to implement a custom demuxer, similar to the concat one. There's currently no other clean way. You have to get your hands dirty and code!

Below is an ugly hack. This is a very bad way to do it, just don't!

The solution uses the concat demuxer and assumes all your source media files use the same codec. The example is based on MPEG-TS but the same can be done for RTMP.

  1. Make a playlist file holding a huge list of entry points for you dynamic playlist with the following format:

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

    These files are just placeholders.

  2. Make a script that keeps track of you current playlist index and creates symbolic links on-the-fly for 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. Start playing ffmpeg -f concat -i playlist.txt -c copy output -f mpegts udp://<ip>:<port>

  4. Get chased and called names by an angry system administrator

aergistal
  • 29,947
  • 5
  • 70
  • 92
  • thanks, just saw your answer. I figured it out with hinekyle comment earlier, even if I am not interested in these kind of hacks now – kketch Sep 01 '15 at 13:59
  • Can you elaborate on what you mean by `Make a script that keeps track of you current playlist index and creates symbolic links on-the-fly for current_index + 1`? What purpose is this for? – chovy Dec 10 '16 at 02:08
  • Trying to do same thing for .m3u8 24/7 stream. But the command `-c copy output -f mpegts stream.m3u8` throws an error. – chovy Dec 10 '16 at 02:56
  • 1
    @chovy this is a very bad answer on my part which I posted more like a joke. Really, don't do it like this, it's an ugly hack. I'll delete it so nobody else gets tempted to try it. – aergistal Dec 10 '16 at 09:37
  • 1
    @aergistal Hi! You sad that this is an ugly hack, but it seems that ffmpeg’s wiki actually suggest to do just as you described https://trac.ffmpeg.org/wiki/Concatenate#Automaticallyappendingtothelistfile , so probably your answer is not a bad one after all :) – Edgar P-Yan Jun 03 '23 at 00:06
8

Need to create two playlist files and at the end of each file specify a link to another file.

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'

Now all you need is to dynamically change the contents of the next playlist file.

bnku
  • 89
  • 1
  • 3
7

You can pipe your loop to a buffer, and from this buffer you pipe to your streaming instance.

In shell it would look like:

#!/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

Of course you can use any kind of loop, also looping through a playlist.

  • I figure out that mpeg is a bit more stabile, then x264 for the input stream.
  • I don't know why, but minimum 2 threads for the mpeg compression works better.
  • the input compression need to be faster then the output frame rate, so we get fast enough new input.
  • Because of the non-continuing timestamp we have to skip them and generate a new one in the output.
  • The buffer size needs to be big enough for the loop to have enough time to get the new clip.

Here is a Rust based solution, which uses this technique: ffplayout

This uses a JSON playlist format. The Playlist is dynamic, in that way that you can edit always the current playlist and change tracks or add new ones.

jb_alvarado
  • 169
  • 3
  • 11
  • 1
    The shell command that you have provided does not work as expected. A temporary buffer stored inside mbuffer is playing. The temporary buffer keeps looping.how to flush the buffer? I tried adding a pipe too with not much success. Any ideas? – josh Oct 23 '18 at 05:39
3

Very Late Answer, but I recently ran into the exact same issue as the poster above.

I solved this problem by using OBS and the OBS websockets plugin.

First, set your RTMP streaming app as you have it now. but stream to a LOCAL RTMP stream.

Then have OBS load this RTMP stream as a VLC source layer with the local RTMP as the source.

then (in your app), using the OBS websockets plugin, have your VLC source switch to a static black video or PNG file when the video ends. Then switch back to the RTMP stream once the next video starts. This will prevent the RTMP stream from stopping when the video ends. OBS will go black durring the short transition, but the final OBS RTMP output will never stop.

There is surely a way to do this with manually setting up a intermediate RTMP server that pushes to a final RTMP server, but I find using OBS to be easier, with little overhead.

I hope this helps others, this solutions has been working incredible for me.

Dani
  • 1,220
  • 7
  • 22
  • I love OBS, but consider the following issue: what if all of this is happening on a remote server? OBS _does_ have a CLI, but it still needs to be running on the system — which might not be possible on a so-called "headless" server (i.e. without an attached display). – Gwyneth Llewelyn Aug 15 '23 at 09:03