I have multiple audio tracks and subtitles to extract in a single .mkv file. I'm new to ffmpeg commands, this is what I've tried (audio):
ffmpeg -i VIDEO.mkv -vn -acodec copy AUDIO.aac
It just extract 1 audio. What I want is tell ffmpeg to extract every single audio files and subtitle files to a destination, and keep the original name of each files and extensions. (Because I don't know which extension does the audio files are, sometimes maybe .flac or .aac).
I'm not sure about the solutions I'd found online, because it's quite complicated, and I need explanations to know how it's works, so that I can manipulate the command in the future. By the way, I planned to run the code from Windows CMD. Thanks.
There is no option yet in ffmpeg to automatically extract all streams into an appropriate container, but it is certainly possible to do manually.
You only need to know the appropriate containers for the formats you want to extract.
Default stream selection only chooses one stream per stream type, so you have to manually map each stream with the -map option.
1. Get input info
Using ffmpeg or ffprobe you can get the info in each individual stream, and there is a wide variety of formats (xml, json, cvs, etc) available to fit your needs.
ffmpeg example
ffmpeg -i input.mkv
The resulting output (I cut out some extra stuff, the stream numbers and format info are what is important):
Input #0, matroska,webm, from 'input.mkv':
Metadata:
Duration: 00:00:05.00, start: 0.000000, bitrate: 106 kb/s
Stream #0:0: Video: h264 (High 4:4:4 Predictive), yuv444p, 320x240 [SAR 1:1 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 50 tbc (default)
Stream #0:1: Audio: vorbis, 44100 Hz, mono, fltp (default)
Stream #0:2: Audio: aac, 44100 Hz, mono, fltp (default)
Stream #0:3: Audio: flac, 44100 Hz, mono, fltp (default)
Stream #0:4: Subtitle: ass (default)
ffprobe example
ffprobe -v error -show_entries stream=index,codec_name,codec_type input.mkv
The resulting output:
[STREAM]
index=0
codec_name=h264
codec_type=video
[/STREAM]
[STREAM]
index=1
codec_name=vorbis
codec_type=audio
[/STREAM]
[STREAM]
index=2
codec_name=aac
codec_type=audio
[/STREAM]
[STREAM]
index=3
codec_name=flac
codec_type=audio
[/STREAM]
[STREAM]
index=4
codec_name=ass
codec_type=subtitle
[/STREAM]
2. Extract the streams
Using the info from one of the commands above:
ffmpeg -i input.mkv \
-map 0:v -c copy video_h264.mkv \
-map 0:a:0 -c copy audio0_vorbis.oga \
-map 0:a:1 -c copy audio1_aac.m4a \
-map 0:a:2 -c copy audio2.flac \
-map 0:s -c copy subtitles.ass
In this case, the example above is the same as:
ffmpeg -i input.mkv \
-map 0:0 -c copy video_h264.mkv \
-map 0:1 -c copy audio0_vorbis.oga \
-map 0:2 -c copy audio1_aac.m4a \
-map 0:3 -c copy audio2.flac \
-map 0:4 -c copy subtitles.ass
I prefer the first example because the input file index:stream specifier:stream index is more flexible and efficient; it is also less prone to incorrect mapping.
See documentation on stream specifiers and the -map option to fully understand the syntax. Additional info is in the answer to FFmpeg mux video and audio (from another video) - mapping issue.
These examples will stream copy (re-mux) so no re-encoding will occur.
Container formats
A partial list to match the stream with the output extension for some common formats:
Video Format
Extensions
H.264
.mp4, .m4v, .mov, .h264, .264
H.265/HEVC
.mp4, .h265, .265
VP8/VP9
.webm
AV1
.mp4
MPEG-4
.mp4, .avi
MPEG-2
.mpg, .vob, .ts
DV
.dv, .avi, .mov
Theora
.ogv/.ogg
FFV1
.mkv
Almost anything
.mkv, .nut
Audio Format
Extensions
AAC
.m4a, .aac
MP3
.mp3
PCM
.wav
Vorbis
.oga/.ogg
Opus
.opus, .oga/.ogg, .mp4
FLAC
.flac, .oga/.ogg
Almost anything
.mka, .nut
Subtitle Format
Extensions
Subrip/SRT
.srt
SubStation Alpha/ASS
.ass
You would first list all the audio streams:
ffmpeg -i VIDEO.mkv
and then based on the output you can compile the command to extract the audio tracks individually.
Using some shell script you can then potentially automate this in a script file so that you can do it generically for any mkv file.
Subtitles are pretty much the same. The subtitles will be printed in the info and then you can extract them, similar to:
ffmpeg -threads 4 -i VIDEO.mkv -vn -an -codec:s:0.2 srt myLangSubtitle.srt
0.2 is the identifier that you have to read from the info.
I solved it like this:
ffprobe -show_entries stream=index,codec_type:stream_tags=language -of compact $video1 2>&1 | { while read line; do if $(echo "$line" | grep -q -i "stream #"); then echo "$line"; fi; done; while read -d $'\x0D' line; do if $(echo "$line" | grep -q "time="); then echo "$line" | awk '{ printf "%s\r", $8 }'; fi; done; }
Output:
Only set $video1 var before command.
Enjoy it!.
If someone steps in this question with a modern version of ffmpeg, it looks like they added the option there.
I needed to convert a file by maintaining all tracks:
ffmpeg -i "${input_file}" -vcodec hevc -crf 28 -map 0 "${output_file}"
To achieve what the original question asked, probably this could be used:
mappings="`ffmpeg -i \"${filein}\" |& awk 'BEGIN { i = 1 }; /Stream.*Audio/ {gsub(/^ *Stream #/, \"-map \"); gsub(/\(.*$/, \" -acodec mp3 audio\"i\".mp3\"); print; i +=1}'`"
ffmpeg -i "${input_file}" ${mappings}
The 1st line (mappings=...) extracts the existing audio streams and converts them in "-map X:Y -acodec mp3 FILENAME", while the 2nd one executes the extraction
The following script extracts all audio streams from files in current directory
ls |parallel "ffmpeg -i {} 2>&1 |\
sed -n 's/.*Stream \#\(.\+\)\:\(.\+\)\: Audio\: \([a-zA-Z0-9]\+\).*$/-map \1:\2 -c copy \"{.}.\1\2.\3\"/p' |\
xargs -n5 ffmpeg -i {} "
Related
I'm looking for a way to convert wav(16bit, 48kHz, LPCM) into an mxf file with timecode.
Since ffmpeg supports mxf, I'm trying, but I don't know the command.
ffmpeg -i ./input.wav [hh:mm:ss.ff, name1] [hh:mm:ss.ff, name2]... ./output.mxf
I'm expecting the above command, but does anyone know?
MXF is a pain
The default MXF muxer requires video.
The -timecode option with MXF requires video.
The mxf_opatom muxer allows just audio, but only mono with 48000 MHz sample rate, so each channel will need to be in its own MXF file.
Workaround 1: Pipe
ffmpeg -i input.wav -ar 48000 -c:a pcm_s16le -timecode 01:02:03:04 -f nut - | ffmpeg -i - -c:a pcm_s16le -f mxf_opatom output.mxf
I'm assuming your audio is mono (you didn't say what it is). If your input is multichannel then output each channel into its own file.
Use 01:02:03:04 for non-drop timecode, and 01:02:03.04 or 01:02:03;04 for drop.
Workaround 2: Dummy/blank video
Just ignore the video.
Non-drop timecode:
ffmpeg -f lavfi -i color=r=25 -i input.wav -timecode 01:02:03:04 -c:a copy -shortest output.mxf
Drop timecode:
ffmpeg -f lavfi -i color=r=30000/1001 -i input.wav -timecode 01:02:03.04 -c:a copy -shortest output.mxf
everybody here! So basically this is what I want to achieve:
I have a muted video about 3 minutes long.
I have a list of audio tracks in mp3 format (40 songs in a folder with duration 2 to 6 mins each one)
I want this video to play cycled automatically taking songs from playlist and injecting them to the video one by one. Every time a song finishes the next one from the list should start playing at the moment. Video continues playing and doesn't care duration of tracks.
I consider it as the first step on the way to broadcast radio with a video background on youtube in 24/7 mode with ability to put additional tracks to playlist without need to stop translation.
My problem is that I'm new in FFmpeg and I would appreciate any suggestions regarding which FFMpeg topic to start investigate with in order to achieve my goal
Use the concat demuxer
You can do live updates to the playlist for the concat demuxer, but each audio file must have the same attributes, the same number of streams, and all be the same format.
Create input.txt containing:
ffconcat version 1.0
file 'audio1.mp3'
file 'audio2.mp3'
file 'audio3.mp3'
file 'audio40.mp3'
All file names must be "safe" or it will fail with Unsafe file name. Basically no special characters in file names and only use absolute paths. See concat demuxer for more info.
Run ffmpeg to stream to YouTube:
ffmpeg -re -framerate 10 -loop 1 -i image.jpg -re -f concat -i input.txt -map 0:v -map 1:a -c:v libx264 -tune stillimage -vf format=yuv420p -c:a aac -g 20 -b:v 2000k -maxrate 2000k -bufsize 8000k -f flv rtmp://youtube
When you are ready to add new songs make temp.txt containing:
ffconcat version 1.0
file 'audio41.mp3'
file 'audio42.mp3'
file 'audio43.mp3'
Replace input.txt atomically:
mv temp.txt input.txt
See FFmpeg Wiki: Concatenate for lots more info.
If your audio files are not the same
The files listed in input.txt must all have the same:
Format (AAC, MP3, etc, but not mixed)
Sample rate (48000, 44100, etc)
Number of channels (mono, stereo, etc).
If they vary then you will have to pre-process them before adding them to the playlist. Bash example conforming each audio to stereo (-ac 2) with 44100 sample rate (-ar 44100) and save as AAC format in M4A container:
mkdir conformed
for f in *.mp3; do ffmpeg -i "$f" -map 0:a -ac 2 -ar 44100 -c:a aac "conformed/${f%.*}.m4a"; done
Outputting to AAC is recommended for streaming to YouTube.
If you do this then you can avoid re-encoding the audio in the ffmpeg command to YouTube. Just change -c:a aac to -c:a copy in step #2: Run ffmpeg to stream to YouTube.
I want to perform HLS segmentation and get audio and video in separated files for each segment from .mp4 video with audio. No need to adapt bitrate, because that will cause an extra delay.
I tried to use
ffmpeg -i videoInput.mp4 -c:v copy –c:a copy -hls_segment_type mpegts –map a:0 –map v:0 -hls_time 1 out.m3u8 audio.ts
Which generates the video files perfectly but just one audio file.
Thank you.
From the FFmpeg documentation I understand:
When there are two or more variant streams, the output filename
pattern must contain the string "%v", this string specifies the
position of variant stream index in the output media playlist
filenames. The string "%v" may be present in the filename or in the
last directory name containing the file. If the string is present
in the directory name, then sub-directories are created after
expanding the directory name pattern. This enables creation of
variant streams in subdirectories.
'''
For me:
ffmpeg -hide_banner -t 5:00 -i "$IN" -c copy -map a:0 -map v:0 -hls_segment_type mpegts -hls_time 1 -f hls -var_stream_map "a:0 v:0" v_%v/file.m3u8
creates both streams in v_0 & v_1.
I don't know the exact solution, but as a workaround, you can extract the audio first with:
ffmpeg -i video.mp4 -c:a copy -vn audio.aac
Then apply segmentation for video and audio files separately
Audio:
ffmpeg -i audio.aac -c:a copy -hls_segment_type mpegts -map a:0 -hls_time 1 out_aud.m3u8
Video:
ffmpeg -i video.mp4 -c:v copy -hls_segment_type mpegts -map v:0 -hls_time 1 out_vid.m3u8
I'm trying to calculate the audio + visual difference between a harshly compressed video file and one that hasn't been.
I'm using pipes because ultimately I wish this to take src from a camera stream.
I've managed to get the video results that I'm looking for, but I'm struggling with the audio.
I've added a line to invert the phase of the compressed audio, so that when they add up in the blend they should almost cancel each other out, but that doesn't happen.
ffmpeg -i input.avi -f avi -c:v libxvid -qscale:v 30 -c:a wmav1 - | \
ffmpeg -i - -f avi -af "aeval='-val(0)':c=same" - | \
ffmpeg -i input.avi -i - -filter_complex "blend=all_mode=difference" -c:v libx264 -crf 18 -f avi - | \
ffplay -
I can still hear all the audio, when what I should be hearing are solely compression artifacts. thx
To preface, I'm not sure your method would identify audio compression 'artifacts'
Your command doesn't perform any audio comparison, it only inverts a single channel. Also, the audio and video are compressed twice and the codecs the last ffmpeg command receives are the default AVI codecs of mpeg4 and mp3.
Use
ffmpeg -i input.avi -f matroska -c:v libxvid -qscale:v 30 -c:a wmav1 - |\
ffmpeg -i input.avi -i - -filter_complex "[0][1]blend=all_mode=difference;[1]aselect=gt(n\,0),asetpts=PTS-STARTPTS[1a];[0][1a]amerge,aeval=val(0)-val(1):c=mono" -c:v rawvideo -c:a pcm_s16le -f matroska - |\
ffplay -
I assume your audio is mono. If your audio has N channels, your aeval will need N expressions where the Mth expression is val(M-1)-val(N+M-1)
I also trim out the first encoded audio frame in order to mitigate encoder delay that Paul mentioned, and it seems to work here.
There might be some delay introduced with encoded audio samples. Also your command is incorrect.
I'm currently writing a small script that coverts an MP4 to Opus audio on the fly and sends it to Discord in golang. Initially my script would pass an MP4 as it was downloading to ffmpeg through stdin and then pass stdout to an Opus encoder, then to Discord (exactly like this). After learning I could build ffmpeg with Opus, I'd like to cut out the opus encoder I previous had and pass ffmpeg's output directly to Discord.
Previous, my ffmpeg command looked like this (with using the second opus encoder)
ffmpeg -i - -f s16le -ar 48000 -ac 2 pipe:1
Now, without the encoder and letting ffmpeg do all the work, this is what I've come up with so far.
ffmpeg -i - -f s16le -ar 48000 -ac 2 -acodec libopus -b:a 192k -vbr on -compression_level 10 pipe:1
With this command however the audio doesn't get accepted by Discord's server, meaning I'm suspecting opus audio isn't coming out the other end. No errors outputted. Have I done something wrong with ffmpeg that could of caused this?
Try
ffmpeg -i - -sample_fmt s16 -ar 48000 -ac 2 -acodec libopus -b:a 192k -vbr on -compression_level 10 -f opus pipe:1
You can't use -f s16le as that specifies an uncompressed output format (of a specific sample type), whereas you need a compressed data stream of a certain codec. Instead, you can use sample_fmt and -f opus