Linux workstation : audio and video - How to alter audio and video files on a Linux workstation ?

mail

How to re-tag a .mkv file ?

Situation

When starting the playback of a video file, VLC displays its Title tag, read from the internal metadata. Turns out some metadata fields have which is why you'd like to fix these.

Details

VLC permits editing these fields (add file to playlist + select it + Ctrl-j + General tab), but changes made to .mkv files aren't permanent : they're lost when re-opening the file.

Solution

Tools

The mkvmerge and mkvpropedit binaries used hereafter are from the mkvtoolnix package (project homepage).
  • I've not spent much time with this program, but I don't get its logic : mkvpropedit can set values but has no easy method to read such values...
  • ... which can be done with a binary named mkvmerge
  • mkvtoolnix also has a GUI version, but it looks rather complex, and the CLI tools are enough for my current need anyway

Get file properties

mkvmerge -J movie.mkv | jq -r '.container.properties.title'

Set file properties

mkvpropedit --set "title=New title" movie.mkv
mail

How to mix an audio + a video track ?

Since
Code is worth 1000 words
let's dive into it :
youtubeVideoUrl='https://music.youtube.com/watch?v=JozAmXo2bDE'
audioFormatCode='251'; audioFile='audio.webm'
videoFormatCode='248'; videoFile='video.webm'

yt-dlp -f $audioFormatCode "$youtubeVideoUrl" -o "$audioFile"
yt-dlp -f $videoFormatCode "$youtubeVideoUrl" -o "$videoFile"

ffmpeg -i "$audioFile" -i "$videoFile" -c:a copy -c:v copy 'audio_video.webm'
Using different audio / video formats may require transcoding (which also depends on the format of the resulting video). For example, with :
  • 251 audio (webm)
  • 137 video (MP4)
  • and to get a MP4 output file
the audio stream needs to be converted into AAC and the merge command becomes : ffmpeg -i "$audioFile" -i "$videoFile" -c:a aac -c:v copy 'audio_video.mp4'
mail

How to concatenate audio / video files ?

ffmpeg -i concat:"file1|file2|file3" -c copy output
mail

How to convert audio files ?

WAV to MP3 :

for audioFile in *wav; do ffmpeg -i "$audioFile" -codec:a libmp3lame -b:a 256k "${audioFile%.*}.mp3"; done

WAV to FLAC (source) :

for audioFile in *wav; do ffmpeg -i "$audioFile" -af aformat=s16:44100 "${audioFile%.*}.flac"; done

This encodes to 16 bits at 44100 hertz.

mail

How to trim audio or video files ?

General case :

The command below will :
  1. "open" inputFile to do stuff with it
  2. "select" data starting at startTime of inputFile
  3. make this selection last for duration
  4. and copy the selection into outputFile

ffmpeg -i inputFile -ss startTime -t duration -codec copy outputFile

  • startTime and duration are in either a number of seconds, or in the hh:mm:ss[.xxx] format.
  • If no duration is specified, the process will continue until the end of the input file.

Cut a few seconds from the beginning of a video :

The command above may work pretty well. But, depending on the input file itself (I got this with a .avi file), it may end in a huge series of :

Non-monotonous DTS in output stream 0:1; previous: 103475, current: 34492; changing to 103476. This may result in incorrect timestamps in the output file.
+ audio and video not in sync anymore. This has to do with the file format itself (AVI container, mkv, mpg, mp4, ...) and codecs. see also

Re-encoding the audio track (here in mp3 format) fixed it :

ffmpeg -i input.avi -ss 15.5 -c:v copy -c:a mp3 output.avi

During the tests to find the right start time (-ss value), save time by not processing the input until the end :

ffmpeg -y -i input.avi -ss 15.5 -t 30 -c:v copy -c:a mp3 output.avi

TESTING :
outputDir='./output'; mkdir -p "$outputDir"; for input in *avi; do echo "$input"; ffmpeg -y -i "$input" -ss 15.5 -t 15 -c:v copy -c:a mp3 "$outputDir/$input"; done

REAL :
outputDir='./output'; mkdir -p "$outputDir"; for input in *avi; do echo "$input"; ffmpeg -i "$input" -ss 15.5 -c:v copy -c:a mp3 "$outputDir/$input"; done
mail

How to re-order the audio tracks of a .mkv file ?

Situation

I'd like to share a .mkv movie from my PC with MiniDLNA and play it on my TV. However, my TV automatically plays the 1st audio track (whatever language it is) and won't let me select another one.

Details

So far, I don't know whether this is :

Solution

Since I can't debug + fix the firmware of my TV, let's see what happens if I change the order of the audio tracks in the .mkv file :
  1. List all tracks of the movie file :
    ffmpeg -i input.mkv 2>&1 | grep Stream
    Stream #0.0(eng): Video: h264 (High), yuv420p, 1920x1080, PAR 1:1 DAR 16:9, 23.98 fps, 1k tbn, 47.95 tbc (default)
    Stream #0.1(eng): Audio: ac3, 48000 Hz, 5.1, fltp, 448 kb/s	my TV plays this audio track
    Stream #0.2(fre): Audio: ac3, 48000 Hz, 5.1, fltp, 448 kb/s	I want this one played
    Stream #0.3(fre): Subtitle: srt
    Stream #0.4(eng): Subtitle: srt
  2. Change the order of tracks using -map : ffmpeg can build a new multi-track output file by picking tracks from the specified input file :
    • the 1st map of the command line is for the 1st track of the output file
    • the 2nd map of the command line is for the 2nd track of the output file
    • and so on, you get it
    ffmpeg -i input.mkv -map 0:0 -map 0:2 -map 0:1 -c copy output.mkv
    This specifies the tracks of output.mkv to be :
    • Track 0 : track 0 of input.mkv (video)
    • Track 1 : track 2 of input.mkv (french audio)
    • Track 2 : track 1 of input.mkv (english audio)
    -c copy means the tracks must not be re-encoded but copied as-is.
  3. In output.mkv, we now have :
    ffmpeg -i output.mkv 2>&1 | grep Stream
    Stream #0.0(eng): Video: h264 (High), yuv420p, 1920x1080, PAR 1:1 DAR 16:9, 23.98 fps, 1k tbn, 47.95 tbc (default)
    Stream #0.1(fre): Audio: ac3, 48000 Hz, 5.1, fltp, 448 kb/s
    Stream #0.2(eng): Audio: ac3, 48000 Hz, 5.1, fltp, 448 kb/s
    We've not picked the subtitles tracks !
  4. Same as above, with ALL tracks :
    ffmpeg -i input.mkv -map 0:0 -map 0:2 -map 0:1 -map 0:3 -map 0:4 -c copy output.mkv
Last words : the whole operation lasted 1.5 minutes on my PC for a 3.9GB input file. It is actually more stressful for the HDD than for the CPU (30% of a core used).
mail

How to extract the audio track from a movie ?

There are several methods to get an audio track (source) :

As uncompressed audio :

ffmpeg -i movie.mp4 audio.wav

As MP3 :

  • one file : ffmpeg -i movie.mp4 -codec:a libmp3lame audio.mp3
  • more files :
    • for videoFile in *mp4; do ffmpeg -i "$videoFile" -codec:a libmp3lame "$videoFile".mp3; done
    • outputDir='./audio'; mkdir -p "$outputDir"; for videoFile in *mp4; do ffmpeg -i "$videoFile" -codec:a libmp3lame -b:a 256k "$outputDir/${videoFile%.*}.mp3"; done
Don't forget to specify the audio bitrate (defaults to 64kbit/s) with -b:a bitrate :
ffmpeg -i movie.mp4 -codec:a libmp3lame -b:a 256k audio.mp3
Check :
Get information (codec, bitrate, ...) about a media file

Only a snippet, as MP3 :

ffmpeg -y -ss 00:14:30 -i movie.avi -t 4.5 -codec:a libmp3lame audio.mp3

As Ogg Vorbis :

  • one file : ffmpeg -i movie.mp4 -codec:a libvorbis audio.ogg
  • more files : outputDir='./ogg'; mkdir -p "$outputDir"; for sourceFile in *mp4; do ffmpeg -i "$sourceFile" -codec:a libvorbis "$outputDir/${sourceFile%.*}.ogg"; done
For a reason unknown so far, this command _sometimes_ re-encodes the specified video file into "Ogg Theora" video format instead of extracting the audio track + converting it to Ogg Vorbis. I must have missed something important

Copy the audio track as-is with the map method :

This method should be preferred whenever possible since it only extracts the audio data with no reconversion :

  • preserves the original audio quality
  • doesn't produce an artificial "high quality" audio file from a lower quality source (which is what websites offering to download music from YouTube as MP3 do)
  • best possible audio quality vs file size ratio

  1. identify the audio track :
    Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 389 kb/s, 29.97 fps, 29.97 tbr, 90k tbn, 59.94 tbc (default)
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s (default)
  2. copy it into a new file :
    ffmpeg -i movie.mp4 -map 0:1 -c copy audio.mp4
  3. you can make sure this file as only 1 audio track :
    Stream #0:0(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s (default)
  4. Rename the audio file based on the type of audio format :
    mv audio.{mp4,aac}
mail

How to convert video files ?

Transcode files (details) :

From any format to MPEG 1 :
ffmpeg -i input.mp4 output.mpg
From any format to MPEG 2 :
ffmpeg -i input.mp4 -f dvd output.mpg
From any format to H.264 inside an MP4 container :
ffmpeg -i input.mp4 -strict experimental -vcodec libx264 -deinterlace -threads 0 output.mp4
-strict experimental was suggested by the software :
encoder 'aac' is experimental and might produce bad results.
Add '-strict experimental' if you want to use it.
From any format, convert video to H.264 + leave audio as-is, and save the result into an MP4 container :
ffmpeg -i input.mp4 -threads auto -c:a copy -vcodec libx264 output.mp4

Rotate a video :

Please note :
  • Videos shot with a smartphone have a Rotation metadata parameter. It is used by the built-in player to orient the video properly (source).
  • Looks like VLC ignores or misinterprets this parameter (?)
  • Despite all my efforts, I've not been able to rotate a video made with an Android phone with ffmpeg. Neither did others.
  • OpenShot did it very well with a few clicks.
  • Converting videos can be HARD on CPU, cause overheating (?) and make the system hang To workaround this, considering adjusting manually the CPU frequency.

90° :

https://stackoverflow.com/questions/3937387/rotating-videos-with-ffmpeg/9570992#9570992
	ffmpeg -i in.mov -vf "transpose=1" out.mov

For the transpose parameter you can pass:

0 = 90Counterclockwise and Vertical Flip (default)
1 = 90Clockwise
2 = 90Counterclockwise
3 = 90Clockwise and Vertical Flip

https://ffmpeg.org/ffmpeg-filters.html#transpose-1

180° (source) :

ffmpeg has no option to do so, but this can be achieved either with 2 consecutive 90° rotations, or with an horizontal flip followed by a vertical flip :
ffmpeg -i input.mp4 -vf "hflip,vflip" -codec:v libx264 -preset slow -crf 20 -codec:a copy flipped.mp4
ffmpeg -i input.mp4 -metadata:s:v rotate="0" -vf "hflip,vflip" -c:v libx264 -crf 23 -acodec copy output.mp4 (source)

Change a video resolution by percentage (source) :

To scale down the video to 25% of its original resolution :

ffmpeg -i inputFile -vf scale=iw*0.25:-1 -codec:a copy outputFile

-codec:a copy allows to stream copy the audio without re-encoding.