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:
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.
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:
The results from GetMediaProperties
:
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);
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.
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.
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.
来源:https://stackoverflow.com/questions/28415030/how-to-read-the-bit-rate-information-from-a-mov-video-file-header-quicktime-fi