How to encode audio once for multiple output videos with ffmpeg - ffmpeg

I would like to create several output videos with different resolutions but the same audio. Afaik audio encoding is an output option.
ffmpeg \
-hwaccel qsv -c:v h264_qsv \
-i <input> \
-filter_complex '[0:a]aformat=channel_layouts=stereo,aresample=async=1,asplit=3[a1][a2][a3];[0:v]vpp_qsv=detail=50:framerate=25,split=3[v1][v2][v3];[v2]vpp_qsv=width=1280[v2o];[v3]vpp_qsv=width=800[v3o]' \
-c:v h264_qsv -c:a aac -b:a 96k -map '[v1]' -map '[a1]' <output> \
-c:v h264_qsv -c:a aac -b:a 96k -map '[v2o]' -map '[a2]' <output> \
-c:v h264_qsv -c:a aac -b:a 96k -map '[v3o]' -map '[a3]' <output>
Above I have two redundant audio encodings.
How can I encode the audio just once and copy it for the different outputs?

Use the tee muxer:
ffmpeg \
-hwaccel qsv -c:v h264_qsv -i <input> \
-filter_complex '[0:a]aformat=channel_layouts=stereo,aresample=async=1[a];[0:v]vpp_qsv=detail=50:framerate=25,split=3[v1][v2][v3];[v2]vpp_qsv=width=1280[v2o];[v3]vpp_qsv=width=800[v3o]' \
-map '[v1]' -map '[v2o]' -map '[v3o]' -map '[a]' \
-c:v h264_qsv -c:a aac -b:a 96k -f tee -flags +global_header \
"[select=\'v:0,a\']output.mkv|[select=\'v:1,a\':f=flv:onfail=ignore]rtmp://server0/app/instance/playpath|[select=\'v:2,a\':movflags=+faststart]output.mp4"

I found a workaround with some overhead for muxing and de-muxing but savings in the long run:
ffmpeg -i <input> \
-af 'aformat=channel_layouts=stereo,aresample=async=1' \
-c:a libopus -b:a 64k -ar 48k \
-c:v copy \
-f mpegts - | \
ffmpeg \
-hwaccel qsv -c:v h264_qsv \
-f mpegts -i - \
-filter_complex '[0:v]vpp_qsv=detail=50:framerate=25,split=3[v][v2][v3];[v2]vpp_qsv=width=1280[720p];[v3]vpp_qsv=width=800[450p]' \
-map '[v]' -map 0:a -c:v h264_qsv -c:a copy <output> \
-map '[720p]' -map 0:a -c:v h264_qsv -c:a copy <output> \
-map '[450p]' -map 0:a -c:v h264_qsv -c:a copy <output>

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"

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

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.

Ffmpeg silent audio

I have a string that outputs 4x mp4.
I would like to add quiet audio to the outputs.
I have tried to insert anullsrc=cl=mono:sample_rate=48000 but don't really know where to insert it. It gives me an error.
ffmpeg -hwaccel_output_format cuda -i test.mxf -filter_complex "[0:v]yadif=1,format=yuv420p,split=4[vid1][vid2][vid3][vid4];[vid1]scale=-2:1080[1080];[vid2]scale=-2:432[432];[vid3]scale=-2:288[288];[vid4]scale=-2:216[216]" -map "[1080]" -map "[432]" -map "[288]" -map "[216]" -map 0:a:0 -c:v h264_nvenc -force_key_frames "expr:gte(t,n_forced*10)" -preset slow -rc vbr_hq -b:v:0 4.5M -b:v:1 1.5M -b:v:2 1.0M -b:v:3 0.5M -c:a aac -b:a 192k -f tee "[select=\'v:0,a\']1080.mp4|[select=\'v:1,a\']432.mp4|[select=\'v:2,a\']288.mp4|[select=\'v:3,a\']216.mp4"
You would add the anullsrc as a lavfi input and then map it.
You then either have to add -shortest or add -t X where X is the duration of the video.
ffmpeg -hwaccel_output_format cuda -i test.mxf -f lavfi -i "anullsrc=cl=mono:sample_rate=48000" -filter_complex "[0:v]yadif=1,format=yuv420p,split=4[vid1][vid2][vid3][vid4];[vid1]scale=-2:1080[1080];[vid2]scale=-2:432[432];[vid3]scale=-2:288[288];[vid4]scale=-2:216[216]" -map "[1080]" -map "[432]" -map "[288]" -map "[216]" -map 0:a:0? -map 1:a -c:v h264_nvenc -force_key_frames "expr:gte(t,n_forced*10)" -preset slow -rc vbr_hq -b:v:0 4.5M -b:v:1 1.5M -b:v:2 1.0M -b:v:3 0.5M -c:a aac -b:a 192k -shortest -f tee "[select=\'v:0,a\']1080.mp4|[select=\'v:1,a\']432.mp4|[select=\'v:2,a\']288.mp4|[select=\'v:3,a\']216.mp4"

FFmpeg filter complex audio

How do I add the aac to the filter_complex/split so the audio only is encoded once as the yadif?
ffmpeg -y -hwaccel cuvid -i test.mxf -filter_complex "[0:v]yadif=1,split=2[out1][out2]" -map "[out1]" -s 1920:1080 -c:v h264_nvenc -force_key_frames "expr:gte(t,n_forced*10)" -pix_fmt yuv420p -preset slow -rc vbr_hq -b:v 4.5M -map 0:1 -c:a aac -b:a 192k test2.mp4 -map "[out2]" -s 768:432 -c:v h264_nvenc -force_key_frames "expr:gte(t,n_forced*10)" -pix_fmt yuv420p -preset slow -rc vbr_hq -b:v 1.5M -map 0:1 -c:a aac -b:a 192k test3.mp4
Your video is being encoded twice which is unavoidable because you are outputting two different width x height. Your audio is the same for each output, so you can use the tee muxer to only encode audio once and put it in both outputs:
ffmpeg -hwaccel cuvid -i test.mxf -filter_complex "[0:v]yadif=1,format=yuv420p,split=2[vid1][vid2];[vid1]scale=-2:1080[1080];[vid2]scale=-2:432[432]" -map "[1080]" -map "[432]" -map 0:a:0 -c:v h264_nvenc -force_key_frames "expr:gte(t,n_forced*10)" -preset slow -rc vbr_hq -b:v:0 4.5M -b:v:1 1.5M -c:a aac -b:a 192k -f tee "[select=\'v:0,a\']1080.mp4|[select=\'v:1,a\']432.mp4"

ffmpeg wmv to mp4 and synchronous add a logo image

The script I use to add a logo:
ffmpeg -i input.mp4 -framerate 30000/1001 -loop 1 -i test.png \
-filter_complex "[1:v] fade=out:st=30:d=1:alpha=1 [ov]; \
[0:v][ov] overlay=10:10 [v]" -map "[v]" -map 0:a \
-c:v libx264 -c:a copy -shortest output.mp4
The command I use to convert video. (With this command, synchronize your webm and mp4 and get the picture.)
ffmpeg -i input.wmv -c:v libvpx -crf 10 -b:v 1M -c:a libvorbis \
outputwebm.webm -c:v libx264 -crf 35 outputmp4.mp4 \
-vf "thumbnail,scale=640:360" -frames:v 1 outputpng.png
I want to add the logo image as synchronous.
The command I tried:
ffmpeg -i input.wmv -c:v libvpx -crf 10 -b:v 1M \
-c:a libvorbis outputwebm.webm -c:v libx264 \
-crf 35 -framerate 30000/1001 -loop 1 -i test.png \
-filter_complex "[1:v] fade=out:st=30:d=1:alpha=1 [ov]; \
[0:v][ov] overlay=10:10 [v]" -map "[v]" -map 0:a \
-c:v libx264 -c:a copy -shortest outputmp4.mp4 \
-vf "thumbnail,scale=640:360" -frames:v 1 outputpng.png
Result:
Group all inputs at the front of the command and remove the encoding for the temp MP4 file.
ffmpeg -i input.wmv -framerate 30000/1001 -loop 1 -i test.png -c:v libvpx -crf 10 -b:v 1M -c:a libvorbis outputwebm.webm -filter_complex "[1:v] fade=out:st=30:d=1:alpha=1 [ov]; [0:v][ov] overlay=10:10 [v]" -map "[v]" -map 0:a -c:v libx264 -c:a copy -shortest outputmp4.mp4 -vf "thumbnail,scale=640:360" -frames:v 1 outputpng.png
If your PNG has greater resolution than the WMV then you'll need to map the video for the webm and png outputs.

Resources