Ffmpeg - How can I create HLS multiple language streams, in multiple qualities? - ffmpeg

Preface
I'm working on converting videos from 4k to multiple qualities with multiple languages but am having issues with the multiple languages overlaying, sometimes losing quality and sometimes being out of sync. (this is less of a problem in the German audio, as this is voice over anyhow)
We as a team are complete noobs in terms of Video / Audio + HLS -- I'm a front end developer who has no experience of this so apologies if my question is poorly phrased
Videos
I have the video in a 4k format and have removed the original sound as I have English and German audio files that need to be overlayed. I am then taking these files and throwing them together into a .ts file like this:
$ ffmpeg -i ep03-ns-4k.mp4 -i nkit-ep3-de-output.m4a -i nkit-ep3-en-output.m4a \
> -thread 0 -muxdelay 0 -y \
> -map 0:v -map 1 -map 2 -movflags +faststart -refs 1 \
> -vcodec libx264 -acodec aac -profile:v baseline -level 30 -ar 44100 -ab 64k -f mpegts out.ts
This outputs a 4k out.ts video, with both audio tracks playing.
The hard part
This is where I'm finding it tricky, I now need to convert this single file into multiple quality levels (480, 720, 1080, 1920) and I attempt this with the following command:
ffmpeg -hide_banner -y -i out.ts \
-crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -ar 48000 \
-map 0:v:0 -map 0:v:0 -map 0:v:0 -map 0:v:0 \
-c:v:0 h264 -profile:v:0 main -filter:v:0 "scale=w=848:h=480:force_original_aspect_ratio=decrease" -b:v:0 1400k -maxrate:v:0 1498k -bufsize:v:0 2100k \
-c:v:1 h264 -profile:v:1 main -filter:v:1 "scale=w=1280:h=720:force_original_aspect_ratio=decrease" -b:v:1 2800k -maxrate:v:1 2996k -bufsize:v:1 4200k \
-c:v:2 h264 -profile:v:2 main -filter:v:2 "scale=w=1920:h=1080:force_original_aspect_ratio=decrease" -b:v:2 5600k -maxrate:v:2 5992k -bufsize:v:2 8400k \
-c:v:3 h264 -profile:v:3 main -filter:v:3 "scale=w=3840:h=1920:force_original_aspect_ratio=decrease" -b:v:3 11200k -maxrate:v:3 11984k -bufsize:v:3 16800k \
-var_stream_map "v:0 v:1 v:2 v:3" \
-master_pl_name master.m3u8 \
-f hls -hls_time 4 -hls_playlist_type vod -hls_list_size 0 \
-hls_segment_filename "%v/episode-%03d.ts" "%v/episode.m3u8"
This creates the required qualities, but I'm now at a loss of how this might work with the audio
Audio
For the audio I run this command:
ffmpeg -i out.ts -threads 0 -muxdelay 0 -y -map 0:a:0 -codec copy -f segment -segment_time 4 -segment_list_size 0 -segment_list audio-de/audio-de.m3u8 -segment_format mpegts audio-de/audio-de_%d.aac
ffmpeg -i out.ts -threads 0 -muxdelay 0 -y -map 0:a:1 -codec copy -f segment -segment_time 4 -segment_list_size 0 -segment_list audio-en/audio-en.m3u8 -segment_format mpegts audio-en/audio-en_%d.aac
This creates the required audio segments.
The question
I realise this is quite an ask, but is there anything wrong with our inputs? Is there a way that this can be done a bit more streamlined?
Any answers are greatly appreciated.

Lets say you have:
VideoA
AudioB-> Language 1
AudioC-> Language 2
AudioD-> Language 3
Although it can be done all together, it is better to use different commands for each language instance.
Note that the following are schematics only- some values and parameters will need to be filled in by you. However, this provides a scheme of how to connect the entities. Also I have simply set the size, and NOT used a scale filter. You can use a scale filter instead. Filters will go in place of the size parameter (-s 1280x720 etc).
ffmpeg -i VideoA -i AudioB -map [0:v] -map [1:a] -s 1280x720 -acodec aac -b:a 128k \
-vcodec libx264 -pix_fmt yuv420p [your other parameters go here] -movflags +faststart \
OutputAB_720p.mp4 -map [0:v] -map [1:a] -s 1920x1080 -acodec aac -b:a 128k -vcodec \
libx264 -pix_fmt yuv420p [your other parameters go here] -movflags +faststart \
OutputAB_1080p.mp4
The above shows a scheme for 2 resolutions, 720p and 1080p, merging VideoA with AudioB. To do the same scheme for AudioC you would repeat:
ffmpeg -i VideoA -i AudioC -map [0:v] -map [1:a] -s 1280x720 -acodec aac -b:a 128k \
-vcodec libx264 -pix_fmt yuv420p [your other parameters go here] -movflags +faststart \
OutputAC_720p.mp4 -map [0:v] -map [1:a] -s 1920x1080 -acodec aac -b:a 128k -vcodec \
libx264 -pix_fmt yuv420p [your other parameters go here] -movflags +faststart \
OutputAC_1080p.mp4
You could put all the inputs together:
ffmpeg -i VideoA -i AudioB -i AudioC -i AudioD
and accordingly map each for every language:
-map [0:v] -map [1:a]
-map [0:v] -map [2:a]
-map [0:v] -map [3:a]
etc.
But I feel such long commands that will result make it difficult to read, maintain and correct.

Related

FFMPEG stream to multiples servers using the same -filter_complex options

I want to stream a video to two rtmp servers, I have some options like scaling the resolution from 1080p to 576p or adding a logo. These options are serving in the first rtmp server which the signal was sent, but in the second rtmp it is sending 1080p without any of these options, what am I doing wrong?
ffmpeg -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect 1 -reconnect_delay_max 4 -i video.mp4 -i hello.jpg -filter_complex "overlay=1650:950,scale=1024:576" -vcodec libx264 -preset veryfast -b:v 1300k -acodec aac -b:a 128k -f flv rtmp://test -vcodec libx264 -preset veryfast -b:v 1300k -acodec aac -b:a 128k -f flv rtmp://test2
Unlike input streams, you can only consume a filtergraph output stream only once, and the first rtmp output is snatching it up. If you want to use it on both outputs, split the output of the filter:
ffmpeg -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect 1 -reconnect_delay_max 4 \
-i video.mp4 -i hello.jpg \
-filter_complex "overlay=1650:950,scale=1024:576,split=2[v1][v2]" \
-map [v1] -map 0:a -vcodec libx264 -preset veryfast -b:v 1300k -acodec aac -b:a 128k \
-f flv rtmp://test \
-map [v2] -map 0:a -vcodec libx264 -preset veryfast -b:v 1300k \
-acodec aac -b:a 128k -f flv rtmp://test2
Another, likely preferred, option if you are outputting identical streams is to use tee muxer. It should look something like this:
ffmpeg -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect 1 -reconnect_delay_max 4 \
-i video.mp4 -i hello.jpg \
-filter_complex "overlay=1650:950,scale=1024:576[vout]" \
-map [vout] -map 0:a -vcodec libx264 -preset veryfast -b:v 1300k -acodec aac -b:a 128k \
-f tee "[f=flv]rtmp://test|[f=flv] rtmp://test2"

How to stream to multiple destinations with ffmpeg

I want to stream my video to 4 destinations. My input signal needs to be recoded to "H.264 AAC", so I want to send it to my server. This works already.
Client -> Server with ffmpeg -> Destinations
Now I have a performance problem: One should get the stream in 1080p and two in 720p.
So it would make sense to first get the stream in the desired formats H.264 1080p and AAC with 30 FPS and then calculate the stream once, send it 1:1 to the two HD targets.
and create a 720p stream in parallel and send it to the two remaining destinations.
What is the best way to do this on a Ubuntu 16.04 machine?
My previous approach:
ffmpeg -i rtmp://livestream.domain.example/live/<key> \
-threads 2 -s hd1080 -preset veryfast -f flv rtmp://destination1.example/live2/<key> \
-threads 2 -s hd1080 -preset veryfast -f flv rtmp://destination2.example/live2/<key> \
-threads 1 -s hd720 -c:v libx264 -c:a aac -preset veryfast -r 30 -g 60 -b:v 3000k -f flv rtmp://destination3.example/x/<key> \
-threads 1 -s hd720 -c:v libx264 -preset veryfast -c:a aac -f flv 'rtmps://destination4.exmple/rtmp/<key>'
You can see the repetitions in the code. :-/
Use the tee muxer:
ffmpeg -i rtmp://livestream.domain.example/live/<key> \
-filter_complex "[0:v]scale=-2:1080,fps=30,split=outputs=2[1080a][1080b];[0:v]scale=-2:720,fps=30,split=outputs=2[720a][720b]" \
-map "[1080a]" -map "[1080b]" -map "[720a]" -map "[720b]" -map 0:a \
-c:v libx264 -c:a aac -preset veryfast -g 60 -b:v 3000k -maxrate 3000k -bufsize 6000k -f tee \
"[select=\'v:0,a\':f=flv:onfail=ignore]rtmp://destination1.example/live2/<key>| \
[select=\'v:1,a\':f=flv:onfail=ignore]rtmp://destination2.example/live2/<key>| \
[select=\'v:2,a\':f=flv:onfail=ignore]rtmp://destination3.example/live2/<key>| \
[select=\'v:3,a\':f=flv:onfail=ignore]rtmp://destination4.example/live2/<key>"

How to concatenate and output various video bitrates and a standalone audio file?

How to concatenate and output various video bitrates and a standalone audio file in ffmpeg?
My requirement is:
I have 4 input files.
Need to stitch all 4 files into single segment.
Need output with four different video bit rates: 500k, 800k 1000k 1500k
Along with that I need to extract only audio from stitched file.
So my output will be 4 different video bitrate + 1 audio only file.
tee muxer
Most efficient method is to use the tee muxer (more examples) to avoid unnecessarily encoding the audio for each output, but it is complicated to use:
ffmpeg -i 1.mp4 -i 2.mp4 -filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[v][aud];[v]split=4[v0][v1][v2][v3]" -map "[v0]" -b:v:0 500k -map "[v1]" -b:v:1 800k -map "[v2]" -b:v:2 1000k -map "[v3]" -b:v:3 1500k -map "[aud]" -c:v libx264 -c:a aac -f tee "[select=\'v:0,aud\':movflags=faststart]500.mp4|[select=\'v:1,aud\':movflags=faststart]800.mp4|[select=\'v:2,aud\':movflags=faststart]1000.mp4|[select=\'v:3,aud\':movflags=faststart]1500.mp4|[select=aud:movflags=faststart]audio.m4a"
This example method doesn't perform two-passes which you should do when using your old school method of manually choosing the bitrate for non-streaming outputs. See FFmpeg Wiki: H.264.
simpler but less efficient method
You can do a much less complicated command but it will be less efficient because it will separately encode the audio for each output. Possibly worth the tradeoff of less complexity.
ffmpeg -i 1.mp4 -i 2.mp4 -filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[v][a];[v]split=4[v0][v1][v2][v3];[a]asplit=4[a0][a1][a2][a3]" -map "[v0]" -map "[a0]" -b:v 500k -movflags +faststart 500.mp4 -map "[v1]" -map "[a1]" -c:v libx264 -c:a aac -b:v 800k -movflags +faststart 800.mp4 -map "[v2]" -map "[a2]" -b:v 1000k -movflags +faststart 1000.mp4 -map "[v3]" -map "[a3]" -b:v 1500k -movflags +faststart 1500.mp4
But since you're wanting to target a specific bitrate you should perform two passes:
ffmpeg -y -i 1.mp4 -i 2.mp4 -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v];[v]split=4[v0][v1][v2][v3]" -map "[v0]" -b:v 500k -pass 1 -passlogfile 500 -f mp4 /dev/null -map "[v1]" -c:v libx264 -c:a aac -b:v 800k -pass 1 -passlogfile 800 -f mp4 /dev/null -map "[v2]" -c:v libx264 -c:a aac -b:v 1000k -pass 1 -passlogfile 1000 -f mp4 /dev/null -map "[v3]" -c:v libx264 -c:a aac -b:v 1500k -pass 1 -passlogfile 1500 -f mp4 /dev/null
ffmpeg -y -i 1.mp4 -i 2.mp4 -filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[v][a];[v]split=4[v0][v1][v2][v3];[a]asplit=5[a0][a1][a2][a3][a4]" -map "[v0]" -map "[a0]" -c:v libx264 -c:a aac -b:v 500k -pass 2 -passlogfile 500 -movflags +faststart 500.mp4 -map "[v1]" -map "[a1]" -c:v libx264 -c:a aac -b:v 800k -pass 2 -passlogfile 800 -movflags +faststart 800.mp4 -map "[v2]" -map "[a2]" -c:v libx264 -c:a aac -b:v 1000k -pass 2 -passlogfile 1000 -movflags +faststart 1000.mp4 -map "[v3]" -map "[a3]" -c:v libx264 -c:a aac -b:v 1500k -pass 2 -passlogfile 1500 -movflags +faststart 1500.mp4 -map "[a4]" -movflags +faststart audio.m4a
If you're using Windows replace /dev/null with NUL in the examples above.

FFmpeg split and concatenate issues

I am trying to use FFmpeg to split different clips, concatenate them, and then reencode the concatenated stream. Here is the command line that I would like to use with 2 input clips (actually I would like to use more than 2, but 2 would suffice for illustrating this problem) as example:
./ffmpeg -y -noautorotate -ss 4.9 -i in0.ts -noautorotate -i in1.ts \
-threads 0 -map_chapters -1 -write_tmcd 0 \
-metadata location= -max_muxing_queue_size 2000 -f mp4 \
-movflags faststart -filter_complex "[0:v:0]yadif=deint=interlaced,scale=1280:720:flags=bicubic,setdar=1.7777778[v0];[1:v:0]yadif=deint=interlaced,scale=1280:720:flags=bicubic,setdar=1.7777778[v1];[v0][0:a:0][v1][1:a:0]concat=n=2:v=1:a=1[cat_v][cat_a]"
-map "[cat_a]" -acodec aac -ac 2 -ar 44100 -b:a 160k -async 1
-sn -map "[cat_v]" -vcodec libx264 -profile:v baseline -level 4 -b:v \
5400k -preset medium -x264opts ref=3:keyint=90 \
-r 30000/1001 -vsync 1 -metadata:s:v rotate= -pix_fmt yuv420p outputfile01.mp4
But the FFmpeg hangs and is stuck at frame 0. The in0.ts has its last key frame at 4s. If I were to change the -ss 4.9 to -ss X where X <= 4.0, then there is no issue.
My FFmpeg version is 3.3. I am aware that this problem does not exist in FFmpeg 4.0.x onwards or in FFmpeg 3.2.x but exists in 3.3.x and 3.4.x. Can someone help me understand exactly what bug has been introduced in 3.3.x and 3.4.x that there is this problem?
-ss before -i relies on seeking using the demuxer. For files with inter-coded video streams, the seek target will be a keyframe. The callback seek function in MPEG-TS demuxer returns the first keyframe after the specified point.
BTW, I can reproduce the effect with the latest builds. Why do you say the behaviour doesn't occur in 4.0 or 3.2?
To achieve the intended result, you can use the trim filters,
./ffmpeg -y -noautorotate -i in0.ts -noautorotate -i in1.ts -filter_complex "[0:v:0]yadif=deint=interlaced,trim=4.9,setpts=PTS-STARTPTS,scale=1280:720:flags=bicubic,setdar=1.7777778[v0];[1:v:0]yadif=deint=interlaced,scale=1280:720:flags=bicubic,setdar=1.7777778[v1];[0:a:0]atrim=4.9,asetpts=PTS-STARTPTS[a0];[v0][a0][v1][1:a:0]concat=n=2:v=1:a=1[cat_v][cat_a]" -sn -map "[cat_a]" -async 1 -ac 2 -ar 44100 -c:a aac -b:a 160k -map "[cat_v]" -r 30000/1001 -vsync 1 -pix_fmt yuv420p -c:v libx264 -threads 0 -profile:v baseline -level:v 4 -b:v 5400k -preset medium -x264opts ref=3:keyint=90 -map_chapters -1 -metadata location= -metadata:s:v rotate= -max_muxing_queue_size 2000 -f mp4 -write_tmcd 0 -movflags faststart outputfile01.mp4

FFmpeg Live Stream - Loop Video?

I am trying to stream a video loop to justin.tv using FFmpeg? I have managed to loop an image sequence and combine it with line in audio:
ffmpeg -loop 1 -i imageSequence%04d.jpg -f alsa -ac 2 -ar 22050 -ab 64k \
-i pulse -acodec adpcm_swf -r 10 -vcodec flv \
-f flv rtmp://live.justin.tv/app/<yourStreamKeyHere>
Is it possible to do this with a video file?
Definitely possible. In the recent versions of ffmpeg they have added a -stream_loop flag that allows you to loop the input as many times as required.
The gotcha is that if you don't regenerate the pts from the source, ffmpeg will drop frames after the first loop (as the timestamp will suddenly go back in time). To avoid this, you need to tell ffmpeg to generate the pts so you get an increasing timestamp between loops. This is done with the +genpts call (it has to be before the -i arg).
Here's an example ffmpeg call (replace $F with your input file). This example generates two output streams and the -stream_loop -1 argument tells ffmpeg to continuously loop the input. The output in this case is for a similar stream broadcast ingest (MetaCDN), adjust accordingly to your requirements.
ffmpeg -threads 2 -re -fflags +genpts -stream_loop -1 -i $F \
-s 640x360 -ac 2 -f flv -vcodec libx264 -profile:v baseline -b:v 600k -maxrate 600k -bufsize 600k -r 24 -ar 44100 -g 48 -c:a libfdk_aac -b:a 64k "rtmp://publish.live.metacdn.com/2050C7/dfsdfsd/lowquality_664?hello&adbe-live-event=lowquality_" \
-s 1920x1080 -ac 2 -f flv -vcodec libx264 -profile:v baseline -b:v 2000k -maxrate 2000k -bufsize 2000k -r 24 -ar 44100 -g 48 -c:a libfdk_aac -b:a 64k "rtmp://publish.live.metacdn.com/2050C7/dfsdfsd/highquality_2064?mate&adbe-live-event=highquality_"
Sinclair Media has found a solution by using the lavfi filter and appending :loop=0 to the file name :
This is untested:
ffmpeg -f lavfi -re -i movie=StreamTest.avi:loop=0 \
-acodec libfaac -b:a 64k -pix_fmt yuv420p -vcodec libx264 \
-x264opts level=41 -r 25 -profile:v baseline -b:v 1500k \
-maxrate 2000k -force_key_frames 50 -s 640×360 -map 0 -flags \
-global_header -f segment -segment_list index_1500.m3u8 \
-segment_time 10 -segment_format mpeg_ts \
-segment_list_type m3u8 segmented.ts
But it should create a local "index_1500.m3u8" file that streams the video in "StreamTest.avi".
I just reuse the Rob's answers with a few of modifications in order to provide a file to live streaming
ffmpeg -threads 2 -re -fflags +genpts -stream_loop -1 -i gvf.mp4 -c copy -f mpegts -mpegts_service_id 102 -metadata service_name=My_channel -metadata service_provider=My_Self -max_interleave_delta 0 -use_wallclock_as_timestamps 1 -flush_packets 1 "udp://233.0.0.1:1001?localaddr=10.60.4.237&pkt_size=188"

Resources