3

I've been trying to read some values out of the metadata of a .mov file (QuickTime File Format) with limited success. I've been using the following link as a reference:

Introduction to QuickTime File Format Specification

I've managed to correctly locate and read out/calculate the media duration, but I can't seem to find which Atom the Bit Rate information is stored in. (Atoms are the internal blocks of metadata inside the file).

If anyone can point me to the correct Atom to read, I'll be alright reading it... I just can't seem to find it in the documentation even. "Bit Rate" is only mentioned a couple of times in the whole document.


UPDATE >>>

Going by the very limited information provided below by @szatmary, I have parsed the Sample Size Atom and the Time to Sample Atom from the relevant Track Atom, but am getting some bizarre values. For example, I keep getting a Sample Size value of 1 (when reading from multiple different single video .mov files with constant Bit Rates). The related documentation (from the above link) says:

Sample size
A 32-bit integer specifying the sample size. If all the samples are the same size, this field contains that size value. If this field is set to 0, then the samples have different sizes, and those sizes are stored in the sample size table.

So the field has the value of 1, which means that all samples have the same size, and the Number of entries [in the Sample Size Table] field matches that of the Sample Count field in the single entry of the Time to Sample Table (some very large number). The documentation states this:

... if a video media has a constant frame rate, this table would have one entry and the count would be equal to the number of samples.

So the video has a constant Bit Rate. However, when reading the size entries from the Sample Size Table, they are all different and non-sensical... some are 0, while others are very large numbers up to around 40000. Why are they different if the video has a constant Bit Rate, or should I not be reading them in this case?

Another issue that I have found is that the single entry in the Time to Sample Table of the Time to Sample Atom has the following values:

Sample Count: some very large number (expected)
Sample Duration: 1

Unfortunately the documentation (from the above link) is very light here:

Time-to-sample table
A table that defines the duration of each sample in the media. Each table entry contains a count field and a duration field.

So what units do these 1 values use (Sample Duration & Sample Size)?

Any further help with calculating the correct Bit Rate would be greatly appreciated. Please note that I have been taking the Big-Endian-ness of the file into consideration and reversing the bytes of each field value before reading them.


UPDATE 2 >>>

I have managed to work out that the Sampling Rate is calculated like this:

Media Duration = Duration / Timescale (from the Movie Header Atom or Track Header Atom) Sampling Rate = Sample Count (from the Time-to-Sample Atom) / Media Duration

I just need to crack the Bit Rate now and further help is needed.

Sheridan
  • 64,785
  • 21
  • 128
  • 175
  • Which bit rate, video or audio? Are you aware there is a 3rd party tool which will do all this for you? – Ňɏssa Pøngjǣrdenlarp Feb 11 '15 at 20:54
  • The Bit Rate that is shown in Windows Explorer... which I *think* is the Audio Bit Rate. Perhaps I need to calculate both to make sure which is the correct one? Yes I'm aware that there are third party tools that do this, but I am not able to use them on this particular project for reasons that are out of my control. Also, I have already written code that will parse the various Atoms and skip unwanted Atoms... I just need to know which values I need to read from which Atoms. – Sheridan Feb 12 '15 at 09:06
  • Third party tools aside, is calculating the BR yourself via QT Atoms the only way you are open to getting it? – Ňɏssa Pøngjǣrdenlarp Feb 12 '15 at 15:07

3 Answers3

3

This will get you what you want, "The Bit Rate that is shown in Windows Explorer", but not from the QT metadata. If it is not appropriate for some reason, maybe it will work as a fallback solution until you can work out the Atom based answer or as something to compare the QT Atom results to.

In short, if you want what Explorer shows, get it from Explorer:

// add reference to Microsoft Shell controls and Automation
// from the COM tab
using Shell32;
class ShellInfo
{

    // "columns" we want:
    // FileName = 0;
    const int PerceivedType = 9;
    // FileKind = 11;
    // MediaBitrate = 28;
    // MediaLength = 27;
    static int[] info = {0, 9, 11, 27, 28};

    // note: author and title also available

    public static Dictionary<string, string> GetMediaProperties(string file)
    {
        Dictionary<string, string> xtd = new Dictionary<string, string>();

        Shell32.Shell shell = new Shell32.Shell();
        Shell32.Folder folder;

        folder = shell.NameSpace(Path.GetDirectoryName(file));

        foreach (var s in folder.Items())
        {
            if (folder.GetDetailsOf(s, 0).ToLowerInvariant() ==
                    Path.GetFileName(file).ToLowerInvariant())
            {
                // see if it is video
                // possibly check FileKind ???
                if (folder.GetDetailsOf(s, PerceivedType).ToLowerInvariant() ==
                                "video")
                { 
                    // add just the ones we want using the array of col indices 
                    foreach (int n in info)
                    {
                        xtd.Add(folder.GetDetailsOf(folder.Items(), n),
                            folder.GetDetailsOf(s, n));
                    }                 
                }
                break;
            }
            // ToDo:  freak out when it is not a video or audio type
            // depending what you are trying to do
        }
        return xtd;

    }
}

Usage:

Dictionary<string, string> myinfo;
myinfo = ShellInfo.GetMediaProperties(filepath);

The test file is a sample QT mov from Apple's site, so there is nothing special about it. The view in Explorer:

enter image description here

The results from GetMediaProperties:

enter image description here

The BitRate returned also matched the Audio BitRate returned by MediaProps and MediaTab (both use MediaInfo.DLL to gather all media property values).


The first 35 Shell extended properties are pretty well documented. I think as of Windows 7, this goes to 291(!). Many are file type specific for photos, emails etc. A few which may be of interest:

282: Data rate
283: Frame height
284: Frame rate
285: Frame width
286: Total bitrate

Data rate (282) is the Video BitRate (matches MediaInfo) ; Total Bitrate (286) is the combined a/v bitrate.


Windows 8 (UPDATE)

While the above code appears to run OK on Windows 7, for computers running Windows 8, to avoid a System.InvalidCastException on the following line...:

Shell shell = new Shell();

... the following code will need to be run to instantiate the Shell and Folder COM objects:

Type shellType = Type.GetTypeFromProgID("Shell.Application");
Object shell = Activator.CreateInstance(shellType);
Folder folder = (Folder)shellType.InvokeMember("NameSpace", 
           BindingFlags.InvokeMethod, null, shell, 
           new object[] { Path.GetDirectoryName(file) });

Solution found in the Instantiate Shell32.Shell object in Windows 8 question on the Visual Studio Forum.

Also, on Windows 8, it appears that more attributes have been added so that the maximum index is now 309 (with a few empty entries) and the above mentioned attributes have different indices:

298: Data rate
299: Frame height
300: Frame rate
301: Frame width
303: Total bitrate


It seems the returns from Shell32 has some characters in it which prevent a simple and direct conversion to an int value. For the Bit Rate:

string bRate = myinfo["Bit rate"];       // get return val
bRate = new string(bRate.Where(char.IsDigit).ToArray());  // tidy up

int bitRate = Convert.ToInt32(bRate);
Ňɏssa Pøngjǣrdenlarp
  • 37,255
  • 11
  • 50
  • 147
  • Hi @Plutonix, thanks for your answer. I had hoped to get an Atom based solution seeing as I am already 90% of the way there, but this is certainly an interesting alternative. I don't think that I'll have a chance to get to this today as I have to work on something else today, but I'll definitely try it out as soon as possible. – Sheridan Feb 13 '15 at 09:14
  • I had a quick look at this code, but unfortunately it didn't work as is on Windows 8. After a little research however, I found a working solution for Windows 8, so I edited your answer to provide that 'fix'. With this new code, your example worked well, so unless I get a full Atom based answer before the bounty runs out, I will award you with the bounty points. – Sheridan Feb 13 '15 at 11:00
  • Its *really* odd about the location of some of those things changing in W8 from W7 - that is quite unlike MS. Thanks for the W8 info! – Ňɏssa Pøngjǣrdenlarp Feb 13 '15 at 14:26
  • Yeah, I thought so too, but I wouldn't put anything past Microsoft now... they've *really* gone down hill since Bill left. – Sheridan Feb 13 '15 at 14:48
  • I was dinking around and found this: `((streamsizeKB * 1024) * 8) / durationSecs kindof = bitrate`. I dont know if you have a streamsize atom and there are caveats. Mainly that it is only close for variable bitrates. Worked (sort of) for the apple file and a 1GB MP4. Personally I would use Shell32 so any bad results cant actually be proven via Explorer ;) Variable BRs muck it up bad: the 1GB file *avg* is 5000k but the max is 9800k – Ňɏssa Pøngjǣrdenlarp Feb 13 '15 at 16:14
  • Tell me something @Plutonix... if you put try to parse the Bit Rate in your example to an `int` (after removing the `kbps` text), do you get a `FormatException`? It's the strangest thing... removing the alphabetical characters leaves a numeric string that can't be parsed (in my test). – Sheridan Feb 13 '15 at 16:28
  • Yes, I got it too. The underlying char array is `{8206, 52, 51}` or `{"", "4", "3"}`. Same on the 1GB MP4 so it is not unique to a QT/MOV – Ňɏssa Pøngjǣrdenlarp Feb 13 '15 at 16:44
  • Ahhh, good catch, I missed that extra hidden character. It's starting to look like I'll have to use this solution to access the file attributes, but tell me something... where can the *Audio sample rate* attribute from the *Audio* section of the *Details* tab in the file properties dialog be found? I couldn't find it in this list. – Sheridan Feb 16 '15 at 12:08
  • Audio Sample Rate is not one of those which Shell32/Win picks up. Those available in your version of Windows would be in Explorer Details View, RightClick a header and pick *more...*. If you have MediaTab, it will report **all** media props but it uses a 3rdP DLL; it might help to identify which atoms mean what though. – Ňɏssa Pøngjǣrdenlarp Feb 16 '15 at 13:18
  • Oh that's a shame... I was really hoping to perform just one of these methods rather than both, but I guess that I'll still have to read the file header to extract that information. – Sheridan Feb 16 '15 at 13:25
1

Its not recorded anywhere. As a general rule, it is bad practice to store a value that can be calculated from other values. Plus bitrate can change over time with the same video. What you can do is add up the sizes of the frames you are interested in the stsz box (atoms are called boxes in the iso standard) and the sample durations from he stts box and to the math.

szatmary
  • 27,213
  • 7
  • 39
  • 54
  • Thanks for your response. Can you please go into some more detail in your answer as to how I should *do the Math*? How do I work out which frames I'm interested in from the Sample Size Atom and then how do I relate that to the durations in the Time to Sample Atom? – Sheridan Feb 09 '15 at 23:07
  • "How do I work out which frames I'm interested" I don't know what frames you are interested in. Maybe all of them? "how I should do the Math" you want bits per second I assume, so device bits by seconds. – szatmary Feb 10 '15 at 19:51
  • Take a single video .mov file with a constant Bit Rate for example... what should I look for in the Sample Atom(s) to calculate the Bit Rate? – Sheridan Feb 11 '15 at 10:21
  • The sample sizes and durations. – szatmary Feb 11 '15 at 19:21
  • Please read my updates and provide further useful information. – Sheridan Feb 12 '15 at 14:02
1

If you are OK to read informational value (you already have szatmary's answer for more accurate information), shell reports this by parsing the file and reading metadata through Media Foundation MPEG-4 Property Handler class.

Native API entry point for this is PSLookupPropertyHandlerCLSID and then regular COM instantiation for IPropertyStore interface and then reading the properties. Even if you don't have C# interface into this, you could easily get this through P/Invoke and interoperability layer.

The properties you can read this way are easily discovered by this helper app, wrapping the API: FilePropertyStore (Win32, x64). That is, what you see through the app is also available to you through the API mentioned.

enter image description here

Here is an excerpt from what it gets for a .MOV file (note PKEY_Audio_EncodingBitrate and PKEY_Video_EncodingBitrate):

## Property

 * `PKEY_Media_Duration`, Length: `855000000` (`VT_UI8`) // `855,000,000`
 * `PKEY_Audio_EncodingBitrate`, Bit rate: `43744` (`VT_UI4`) // `43,744`
 * `PKEY_Audio_ChannelCount`, Channels: `1` (`VT_UI4`) // `1`
 * `PKEY_Audio_Format`, Audio format: `{00001610-0000-0010-8000-00AA00389B71}` (`VT_LPWSTR`) // FourCC 0x00001610
 * `PKEY_Audio_SampleRate`, Audio sample rate: `32000` (`VT_UI4`) // `32,000`
 * `PKEY_Audio_SampleSize`, Audio sample size: `16` (`VT_UI4`) // `16`
 * `PKEY_Audio_StreamNumber`: `1` (`VT_UI4`) // `1`
 * `PKEY_Video_EncodingBitrate`, Data rate: `263352` (`VT_UI4`) // `263,352`
 * `PKEY_Video_FrameWidth`, Frame width: `640` (`VT_UI4`) // `640`
 * `PKEY_Video_FrameHeight`, Frame height: `480` (`VT_UI4`) // `480`

The method also works for other media file formats, getting data using the same keys through respective property handlers for other container formats.

Roman R.
  • 64,446
  • 5
  • 83
  • 139