This is a series of articles. Follow the link here to get an overview over all articles.
Previously
on Using FFmpeg as a HLS streaming server (Part 2) – Enhanced HLS Segmentation
The livestream with constant segment duration including x264 video encoding and aac audio encoding is working well.
./ffmpeg -listen 1 -i rtmp://martin-riedl.de/stream01 \ -c:v libx264 -crf 21 -preset veryfast -g 25 -sc_threshold 0 \ -c:a aac -b:a 128k -ac 2 \ -f hls -hls_time 4 -hls_playlist_type event stream.m3u8
Now it’s time to support multiple quality versions. In my case it should be the full HD variant and a second SD variant. This article focuses on the video quality.
Multiple playlists
There will be a playlist with the segments for each quality level. All of this stream playlists are then listed in a master playlist. Since now, the master playlist will be used as video URL.
stream_%v.m3u8
%v must be added in the output stream name. This is a number counting from 0 for the stream variant. In my case 0 will be the lower quality, 1 the higher quality.
-master_pl_name master.m3u8
This will create the new master playlist that contains the list of streams.
Video-Bitrates
Again, Apple provides a list of recommendations for resolutions and the bitrates that should be used. I decided to use for full HD (1920×1080 6000kb/s) and for SD (960×540 2000kb/s). This replaces the -crf value, so this is no longer used and removed from our command.
-preset veryfast -g 25 -sc_threshold 0
This parameters stay as is and are used for all video variants.
-map v:0 -c:v:0 libx264 -b:v:0 2000k
-map v tells ffmpeg that a new video variant starts. x is the number of input video signal. Since I have only one video input, the index 0 is used here.
-c:v:x sets the codec for each variant. I will just use h264.
-b:v:0 sets the bitate for stream 0 (the lower quality).
Since I want to have two different bitrates, the following parameter sequence is used:
-map v:0 -c:v:0 libx264 -b:v:0 2000k
-map v:0 -c:v:1 libx264 -b:v:1 6000k
If you want to have more variants, just copy one of this lines and change the index.
Audio-Bitrates
Well, it’s possible to do the same as we have done with the video bitrates for the audio bitrate. But I want to have for both the same audio quality (since this is not using a lot of bandwidth).
-map a:0 -map a:0
Therefore, the map command must just be placed twice before the codec settings. If you have more streams, repeat this map command.
Bringing all together
-var_stream_map "v:0,a:0 v:1,a:1"
This tells FFmpeg what streams are combined together. A space separates each variant and everything that should be placed together is concatenated with a comma.
So the full command is now:
./ffmpeg -listen 1 -i rtmp://martin-riedl.de/stream01 \ -preset veryfast -g 25 -sc_threshold 0 \ -map v:0 -c:v:0 libx264 -b:v:0 2000k \ -map v:0 -c:v:1 libx264 -b:v:1 6000k \ -map a:0 -map a:0 -c:a aac -b:a 128k -ac 2 \ -f hls -hls_time 4 -hls_playlist_type event \ -master_pl_name master.m3u8 \ -var_stream_map "v:0,a:0 v:1,a:1" stream_%v.m3u8
I’m trying to apply the function of hls_flags delete_segments to that script, but does not work, not delete the files. In what part of the script should I write this function? Thanks in advance.
The cause could be the -hls_playlist_type event. This tells FFmpeg that you can always start the stream from the beginning.
If you want to delete older segments you should remove the event type.
From the documentation:
hls_playlist_type event: […]Forces hls_list_size to 0; the playlist can only be appended to.
Yes! you´re rigth! deleting hls_playlist_type event works fine!
Thanks!
Thanks for the great post. I’m trying to build a live streaming server. I was thinking of using ffmpeg to encode the RTMP input into multiple HLS playlists, and nginx to serve the files.
I’m using the following command:
ffmpeg -listen 1 -i rtmp://localhost:1935/stream01 \
-preset veryfast -g 25 -sc_threshold 0 \
-map v:0 -c:v:0 libx264 -b:v:0 3000000 \
-map v:0 -c:v:1 libx264 -b:v:1 1500000 \
-map v:0 -c:v:2 libx264 -b:v:2 750000 \
-map a:0 -map a:0 -map a:0 -c:a aac -b:a 128k -ac 2 \
-f hls -hls_time 4 -hls_flags delete_segments \
-master_pl_name master.m3u8 \
-var_stream_map “v:0,a:0 v:1,a:1 v:2,a:2” stream_%v.m3u8
The input is sent into the RTMP listener via this command:
ffmpeg -re -y -i ./20120626_181632.mp4 -vcodec libx264 -b:v 600k -r 25 \
-filter:v yadif -ab 64k -ac 1 -ar 44100 -f flv \
“rtmp://localhost:1935/stream01″
My question is: How do I scale the outputs?
I would like 1280×720, 640×480, and 320×240
My current master.m3u8 that is produced is this:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=3440800,RESOLUTION=1280×720,CODECS=”avc1.64001f,mp4a.40.2″
stream_0.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1790800,RESOLUTION=1280×720,CODECS=”avc1.64001f,mp4a.40.2″
stream_1.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=965800,RESOLUTION=1280×720,CODECS=”avc1.64001f,mp4a.40.2”
stream_2.m3u8
Here is information about my input file:
IT-18879:live-streaming-server findleyr$ ffprobe ./20120626_181632.mp4
ffprobe version 4.1 Copyright (c) 2007-2018 the FFmpeg developers
built with Apple LLVM version 10.0.0 (clang-1000.11.45.5)
configuration: –prefix=/usr/local/Cellar/ffmpeg/4.1_1 –enable-shared –enable-pthreads –enable-version3 –enable-hardcoded-tables –enable-avresample –cc=clang –host-cflags= –host-ldflags= –enable-ffplay –enable-gpl –enable-libmp3lame –enable-libopus –enable-libsnappy –enable-libtheora –enable-libvorbis –enable-libvpx –enable-libx264 –enable-libx265 –enable-libxvid –enable-lzma –enable-opencl –enable-videotoolbox
libavutil 56. 22.100 / 56. 22.100
libavcodec 58. 35.100 / 58. 35.100
libavformat 58. 20.100 / 58. 20.100
libavdevice 58. 5.100 / 58. 5.100
libavfilter 7. 40.101 / 7. 40.101
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 3.100 / 5. 3.100
libswresample 3. 3.100 / 3. 3.100
libpostproc 55. 3.100 / 55. 3.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from ‘./20120626_181632.mp4’:
Metadata:
major_brand : isom
minor_version : 0
compatible_brands: isom3gp4
creation_time : 2012-06-26T22:17:27.000000Z
Duration: 00:00:53.05, start: 0.000000, bitrate: 9815 kb/s
Stream #0:0(eng): Video: h264 (Baseline) (avc1 / 0x31637661), yuv420p, 1280×720, 9751 kb/s, SAR 1:1 DAR 16:9, 24.36 fps, 28.25 tbr, 90k tbn, 180k tbc (default)
Metadata:
creation_time : 2012-06-26T22:17:27.000000Z
handler_name : VideoHandle
encoder :
Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 16000 Hz, mono, fltp, 61 kb/s (default)
Metadata:
creation_time : 2012-06-26T22:17:27.000000Z
handler_name : SoundHandle
Sorry for the long post. Thanks again!
P.S. I tried adding the scale to each map like this “-map v:0 -c:v:0 libx264 -b:v:0 3000000 -s 1080×720” but only the final scale was used for all streams.
Have you checked the Part 4? – https://www.martin-riedl.de/2018/08/26/using-ffmpeg-as-a-hls-streaming-server-part-4-multiple-video-resolutions/
This article explains how you scale the video down using complex filter in FFmpeg.
Hi,
Im looking to create a Streaming server and the problem that im facing is this.
I start the Server using your tutorial, and then i start sending with ffmpeg a video. The problem is that when the video finish or its interupted the listener also disconnects and i need to reconect losing this way the posibility to have a continous hls output since its overwriting the existing one. Any ideas?
Im using http :
ffmpeg -listen 1 -i http://localhost:11000/live/a -preset veryfast -g 25 -sc_threshold 0 -map v:0 -c:v:0 libx264 -b:v:0 2000k -map v:0 -c:v:1 libx264
-b:v:1 6000k -map a:0 -map a:0 -c:a aac -b:a 128k -ac 2 -f hls -hls_time 4 -hls_playlist_type event -master_pl_name master.m3u8 -var_stream_map “v:0,a:0 v:1,a:1” new/str
eam_%v.m3u8
You can try to set the following parameters:
reconnect_at_eof
If set then eof is treated like an error and causes reconnection, this is useful for live / endless streams.
reconnect_streamed
If set then even streamed/non seekable streams will be reconnected on errors.
reconnect_delay_max
Sets the maximum delay in seconds after which to give up reconnecting
This could help, that the ffmpeg command never stops and instead reconnects to the input stream.
Be sure to set the parameters before the “-i” parameter:
ffmpeg -listen 1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 60 -i http://localhost:11000/live/a….
Hi , thanks for sharing your knowledge , very good
I am going to have NVOD channel ( a series of video files which continuously are streamed as a live channel)
i tried using “-f concat” option and used a file as an input ( include list of Videos ) but … the problem is that its not possible to append new Video file’s path to the input while ffmpeg is running.
could you help me to have nvod channel ?
Thanks in Advance
HI Again ,
my source has two Audio ( a below ), and i am going to have HLS .
Program 114
Stream #0:0[0x474]: Video: h264 (Main) ([27][0][0][0] / 0x001B), …
Stream #0:1[0x475](eng): Audio: aac_latm …
Stream #0:2[0x476](ara): Audio: aac_latm …
how can i provide two audio and one video in final m3u8 file? consider that users want to switch between the audio by player .
I have published a new article that answers your question: https://www.martin-riedl.de/2020/05/31/using-ffmpeg-as-a-hls-streaming-server-part-9-multiple-audio-languages/
I got this error do you know what’s i am wrong?
Unrecognized option ‘master_pl_name’.
Error splitting the argument list: Option not found
Maybe you’re using an old version of ffmpeg? The latest one ist 4.2.2. Have you tried with this one?
Hi, this is excellent series and thanks for sharing. I am however at a loss as to how do I publish the stream to the server. Running the command simple saves files .m3u8 and .ts locally on the drive. How do I make the output so that I can play it from the server where I am running the command?
The files are usually delivered over HTTP. So you need a webserver (like Nginx or Apache) that has access to the folder where your created files are stored. Using the URL to the m3u8 file you can play the video.
Thank You for you very usefull post about HLS and ffmpeg. I have one question, How to add second audio source to each video profile (960×540 and 1920×1080). Every time I have two video profiles and one audio track, I can’t choose the second audio track.
The input file has a structure:
input #0, mpegts, from ‘/home/tomek.ts’:
Duration: 00:11:00.84, start: 66167.954400, bitrate: 5156 kb/s
Program 1703
Metadata:
service_name : MiniMini+ HD
service_provider:
Stream #0:0[0xb05]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(tv, bt709, top first), 1920×1080 [SAR 1:1 DAR 16:9], 25 fps, 50 tbr, 90k tbn, 50 tbc
Stream #0:1[0xb06](pol): Audio: ac3 ([6][0][0][0] / 0x0006), 48000 Hz, stereo, fltp, 384 kb/s
Stream #0:2[0xb07](qaa): Audio: ac3 ([6][0][0][0] / 0x0006), 48000 Hz, stereo, fltp, 384 kb/s
Stream #0:3[0xb08](pol): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
My ffmpeg looks like:
ffmpeg -i /home/tomek.ts -filter_complex “[v:0]split=2[vtemp001][vout002];[vtemp001]scale=w=960:h=540[vout001]” -preset veryfast -g 25 -sc_threshold 0 -map [vout001] -c:v:0 libx264 -b:v:0 2000k -map [vout002] -c:v:1 libx264 -b:v:1 6000k -map a:0 -map a:0 -c:a aac -b:a 256k -ac 2 -f hls -hls_time 5 -hls_playlist_type event -master_pl_name master.m3u8 -var_stream_map “v:0,a:0 v:1,a:1” ffmpeg_t/stream_%v.m3u8
I hope I wrote it clearly
Jarek
I have published a new article that answers your question: https://www.martin-riedl.de/2020/05/31/using-ffmpeg-as-a-hls-streaming-server-part-9-multiple-audio-languages/
Hallo,
erst mal vielen Dank für die Anleitung. Wir haben gerade unser Internetradio auf hls umgestellt. War gar nichtr so einfach, da es kaum Deutsche Anleitungen gibt. Deine ist aber sehr ausführlich und gut verständlich.
Wir haben ffmpeg noch nicht implementiert. Wir haben momentan eine Verzögerung im Stream von 30-40 Sekunden. Hast Du einen Tip welche Einstellungen wir diesbezüglich ändern sollten? Chunk Sitze, Playlist_lengh und Segmment größe?
Gruß
Michael
Hallo Michael,
alle mir bekannten Informationen dazu habe ich gerade mal in einen neuen Artikel gepackt.
https://www.martin-riedl.de/2020/04/17/using-ffmpeg-as-a-hls-streaming-server-part-8-reducing-delay/
Gruß
Martin
I have hls as input and I want to produce mp4 out of it.
How can I set ffmpeg to use the best variant (or specified) only and do not download other media playlist of other variants?
I know just the manual way: First run FFmpeg without specifying an output:
./ffmpeg -i http://my.hls/master.m3u8
This prints all variants with a number (program ID). Choose the variant with the highest variant_bitrate and resolution.
Then run FFmpeg and specify the program index (use a number for $index):
./ffmpeg -i http://my.hls/master.m3u8 -map 0:v:$index -map 0:a:$index -c:v copy -c:a copy output.mp4
Thanks a lot, it saved me so much !
Thank you so much, this helped me immensely. Your materials were very helpful.