Hi There,
I’m trying to script the extraction of video tracks from various MKVs. While I can hardcode an output name the application MKVCleaver seems to be able to passthrough the container format to the filename. e.g. “moveiname.h265”.
Is there a way for mkextract to name the output file based off that container format? As:
mkvextract “$moviename.mkv” tracks 0:
Fails. And:
mkvextract tracks “$moviename.mkv” 0:outname
Just creates a file called outname.
Anyone have any tips or ideas?
Thanks!
Welcome!
There’s no automatic way to do that solely with mkvextract
. It simply doesn’t contain similar functionality.
Hey mate,
Thanks for the quick reply!
I sussed a workaround. With the --identify flag you can evaluate against a list of formats.
#0. Evaluate Video Stream Container Format
Write-Output $m0_1
$containerFormatEval = mkvmerge --identify "$moviename.mkv"
if($containerFormatEval -like '*H.265*') {
$videostream="extracted_video_stream.h265"
$movieout="compressed_video_stream.h265"
Write-Output $m0_2
} else {
exit 1
}
# 1. Extract Video Stream
Write-Output $m1_1
mkvextract "$moviename.mkv" tracks 0:$videostream
Write-Output $m1_2
That seems to have solved my problem! Now I guess as time goes on I’d add format mappings for like AV1.
That’s certainly a good way to go. I can also recommend the JSON identification mode (mkvmerge --identification-mode json --identify yourfile.mkv
, or its shortcut, mkvmerge -J yourfile.mkv
) which outputs much more information & uses JSON as the portable, structured format for the output. PowerShell can easily read JSON:
$ident = .\mkvmerge.exe -J Z:\home\mosu\prog\video\data\v.mkv | ConvertFrom-JSON
The JSON identification output also contains a numerical value for the container type, which is guaranteed to remain stable even if new supported container formats are added. The textual names used for the containers aren’t guaranteed to be stable, making the container type number a much better attribute to compare. It can be accessed via $ident.container.properties.container_type
. The list of container types can be found here (unfortunately you’ll have to count yourself; e.g. 17 is Matroska).
Hey Moritz,
That is a hugely better way to go! The excerpt now looks like:
$codecFormatEval = mkvmerge -J "movie.mkv" | ConvertFrom-JSON
If ($codecFormatEval.tracks.codec.GetValue('0') = 'HEVC/H.265/MPEG-H') {$fileExtension= '.h.265'}
ElseIf ($codecFormatEval.tracks.codec.GetValue('0') = 'AV1') {$fileExtension= '.av1'}
ElseIf ($codecFormatEval.tracks.codec.GetValue('0') = 'AVC/H.264/MPEG-4p10') {$fileExtension= '.h.264'}
I guess writing a check to verify and map against the 18 video codecs in this file would be the best way to go?
GitLab codec.cpp file
Really appreciate the support mate!
Probably, yeah. The tracks[…].codec
value you’re testing against is the first value in all the .emplace_back(…)
lines in codec.cpp, as you’ve probably figured out already.
Note though that there’s no guarantee that your video track will always be the first track in the file! It can be any track, and mkvmerge
does not sort tracks by type for the purpose of identification. Meaning the 0
in your $codecFormatEval.tracks.codec.GetValue('0')
might be wrong. It would be better to loop through the tracks & find the first one for which tracks
→ type
is video
& use its corresponding codec
value.
Haha, oh man that was so challenging, I can’t code but I tried my best.
The code that matters is below (part of a larger script that automatically compresses MKVs with Dolby Vision data without loosing the original DV metadata)
It now exits out if theres more or less than 1 video track. It will dynamically map the trackID, codec and respective filetype to variables for later use. Easy to scale as more codec/filetypes are required.
I think it’s way more resilient with your feedback implemented. This was a rabbit hole I was not expecting, if you have any more thoughts feel free to share!
Tools like MKVToolNix, dovi_tool and nvenc have absolutely made my life easier, scripting them up just has made it next level!
$codecHashTable= @{
'HEVC/H.265/MPEG-H'='.h265'
'AVC/H.264/MPEG-4p10'='.h264'
'AV1'='.av1'
}
# Evaluate Video Stream JSON
$codecFormatEval = mkvmerge -J "$moviename.mkv" | ConvertFrom-JSON
$countVideoTracks=($codecFormatEval.tracks.type -eq 'video').Count
# Exit if video tracks is not equal to 1
if ($countVideoTracks -ne 1){
{exit 1}
}
# Loop through all tracks in MKV
# Extract the TrackID and codec where the track is a video type
# Map codec to fileExtension with HashTable
foreach ($track in $codecFormatEval.tracks) {
if ($track.type -eq 'video') {
$trackID = $track.id
$trackCodec =$track.codec
$fileExtension=$codecArray[$trackCodec]
break
}
}
# Extract Video Stream
mkvextract "$moviename.mkv" tracks $trackID':'$videostream$fileExtension
I’m super impressed & happy that you’re tackling this problem instead of just saying “don’t know how, don’t want to learn”. Giving these things a serious try, failing but trying again, slowly expanding on what’s there, finally succeeding — that’s how you build knowledge & grow.
mkvmerge
’s interfaces was explicitly designed to be fully scriptable, and I’m really glad when it pays off for people. The code looks good, it even handles “no video track found? exiting!” case, very nice!
I really appreciate the positive feedback mate! Effort really makes the world go round, soon I’ll upload this code to Github and see if I can author a bash equivalent, should help a few people out.
You’ve done so well with MKVToolNix and the underlying components, even for myself, they’re clear and comprehensible to work with. My Plex library and I thank you!
If you’re looking into bash
or other shells & want to do JSON processing, I suggest you look into using the jq
tool. A very powerful tool for reading JSON, transforming it, extracting information for it.
Duely noted, I’ll take a note of it when I start digging into bash, thanks Moritz!