I have this code :

track1=$(mkvmerge -I sample.mkv | sed -ne '/^Track ID [0-9]*: audio ([^)]*).* language:eng.*/ { s/^[^0-9]*\([0-9]*\):.*/\1/;H }; $ { g;s/[^0-9]/,/g;s/^,//;p }' | cut -c1)

It will retrieve the 1 track matching language english.

I want to make a script to compare between same language audio tracks, and find the one with the highest quality.

But mkvinfo, mkvmerge and mediainfo doesn't seem to be able to retrieve the information about a given track ID, and I don't know how to parse their full output to retrieve only what I need.

How to do this? I need to retrieve everything that could be needed to guess quality, like format, bit rate, number of channels, size and store it in a variable like track1channels.

I'm open to other solutions that solve this difficult issue.

  • You can get that info using ffprobe. For example: ffprobe -v error -show_entries streams /path/to/file
    – Tigger
    Jan 6 '18 at 4:25
  • It doesn't seem easier to parse ffprobe :(
    – Freedo
    Jan 6 '18 at 4:53

There's probably many different ways to do it, depending on what tool you want to use to parse the output. One way is to make mkvmerge produce JSON, and then parse it with jq. For example, to get all audio tracks:

mkvmerge --identify --identification-format json sample.mkv | jq '.tracks[] | select(.type=="audio")'

You can specifiy all sorts of criteria in jq, for example .id=="2" for track 2 etc. Maybe you can even do the comparison/sorting with jq, depending on what you want to do, see man jq for details.

You can also store some filtered output from jq in a shell variable, and the use multiple jq calls to extract all the fields into other shell variables. (Possibly there's a way to do that in parallel, but I don't know it).


As for the bitrate: I tried with a sample mkv which contains AAC audio, but neither mediainfo nor mkvinfo give any bitrate purely for audio in the first place. Possibly one could calculate it from other information, like total bits used for this track and total duration, but I am not familiar enough with the internals of a mkv container to pinpoint which number is which.

The .tracks[] | select(.type=="audio") reads "stream all information in the track field, and then select those which have an type field which equals audio". Say you get something like

  "codec": "AAC",
  "id": 1,
  "properties": {
    "audio_channels": 2,
    "audio_sampling_frequency": 44100,
    "codec_id": "A_AAC",
    "codec_private_data": "1210",
    "codec_private_length": 2,
    "default_duration": 23219954,
    "default_track": true,
    "enabled_track": true,
    "forced_track": true,
    "language": "und",
    "minimum_timestamp": 0,
    "number": 2,
    "uid": 2897612726
  "type": "audio"

then save it into a file or variable. Pipe this into a second jq command like jq '.properties.audio_channels' to get subfields. I am not sure how you intend to loop over multiple tracks or what you want to do, but you can do a lot just with jq queries alone.


To get id, codec name, codec id, and number of channels on a single line, do something like

jq '[[.id, .codec, .properties.codec_id, .properties.audio_channels] | map(tostring) | join(",")] | join("\n")'

on the saved value (or add to the original expression).

The outer [...] captures the stream of JSON records, the inner [...] constructs a list which can be joined with a comma after converting numbers to strings, and then the outer list is also joined by newlines. I guess one could get rid of the quotes with commandline options, if necessary.

Also look into sort if you want to sort by number of channels first, etc.

This is really turning into a "how do I use jq properly" question, so maybe google a jq tutorial, or make a new question/new questions?

  • Just look at some JSON example, e.g. the above output. It's really obvious.
    – dirkt
    Jan 8 '18 at 7:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.