如何从.mov视频文件头(QuickTime文件格式)中读取比特率信息?

时间:2015-02-09 16:54:57

标签: c# video quicktime bitrate

我一直试图从.mov文件(QuickTime文件格式)的元数据中读取一些值,但成效有限。我一直在使用以下链接作为参考:

  

Introduction to QuickTime File Format Specification

我设法正确定位和读出/计算媒体持续时间,但我似乎无法找到存储比特率信息的原子。(原子是内部元数据的内部块文件)。

如果有人能指出正确的Atom阅读,我会好好阅读它...我甚至无法在文档中找到它。 "比特率"在整篇文档中只提到了几次。


更新>>>

根据@szatmary提供的非常有限的信息,我已经从相关的 Track Atom解析了样本大小原子样本原子的时间 ,但我得到了一些奇怪的价值观。例如,我不断获得样本大小1(当从具有常量比特率的多个不同单个视频.mov文件中读取时)。相关文档(来自上面的链接)说:

  

样本量
  一个32位整数,指定样本大小。如果所有样本的大小相同,则此字段包含该大小值。如果此字段设置为0,则样本具有不同的大小,并且这些大小存储在样本大小表中。

因此该字段的值为1,这意味着所有样本的大小相同,条目数 [在样本大小表]字段与 Time to Sample Table (一些非常大的数字)的单个条目中的 Sample Count 字段匹配。文档说明了这一点:

  

...如果视频媒体具有恒定的帧速率,则该表将具有一个条目,并且计数将等于样本的数量。

因此视频具有恒定的比特率。但是,当从样本大小表中读取大小条目时,它们都是不同的且非感性的......有些是0,而有些是非常大的数字,高达40000左右。为什么它们不同如果视频具有恒定的比特率,或者在这种情况下我不应该读取它们吗?

我发现的另一个问题是 Time to Sample Atom Time to Sample Table 中的单个条目具有以下值:

  

样本计数:一些非常大的数字(预期)
  样本持续时间:1

不幸的是,文档(来自上面的链接)非常简单:

  

采样时间表
  一个表,用于定义介质中每个样本的持续时间。每个表条目都包含计数字段和持续时间字段。

那么这些1值使用哪些单位(示例持续时间& 样本大小)?

任何有关计算正确比特率的进一步帮助将不胜感激。请注意,我一直在考虑文件的 Big-Endian-ness 并在读取之前反转每个字段值的字节。


更新2>>>

我已经设法计算出采样率的计算如下:

  

媒体持续时间=持续时间/时间刻度(来自电影标题原子跟踪标题原子)   采样率=样本计数(来自时间到样本原子)/媒体持续时间

我现在只需要破解比特率,需要进一步的帮助。

3 个答案:

答案 0 :(得分:3)

这将为您提供您想要的 “Windows资源管理器中显示的比特率”,而不是来自QT元数据。如果由于某种原因它不合适,它可能会作为一个后备解决方案,直到您可以计算出基于Atom的答案或者将QT Atom结果与之比较。

简而言之,如果您想要资源管理器显示的内容,请从资源管理器中获取:

// 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;

    }
}

用法:

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

测试文件是来自Apple网站的QT mov示例,因此没有什么特别之处。资源管理器中的视图:

  

enter image description here

GetMediaProperties的结果:

enter image description here

返回的BitRate还匹配MediaPropsMediaTab返回的音频比特率(两者都使用MediaInfo.DLL来收集所有媒体属性值。)


前35个Shell扩展属性已有详细记录。我认为从Windows 7开始,这就是291(!)。许多是特定于照片,电子邮件等的文件类型。一些可能感兴趣的文件:

  

282:数据率
  283:框架高度
  284:帧率
  285:框架宽度
  286:总比特率

数据速率(282)是视频比特率(匹配MediaInfo);总比特率(286)是组合的a / v比特率。


Windows 8(更新)

虽然上面的代码似乎在Windows 7上运行正常,但对于运行Windows 8的计算机,以避免在以下行中出现System.InvalidCastException ...:

Shell shell = new Shell();

...需要运行以下代码来实例化ShellFolder COM对象:

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) });
  

在Visual Studio论坛上的Instantiate Shell32.Shell object in Windows 8问题中找到了解决方案。

此外,在Windows 8上,似乎添加了更多属性,因此最大索引现在为309(带有一些空条目),并且上述属性具有不同的索引:

  

298:数据率
  299:框架高度
  300:帧率
  301:框架宽度
  303:总比特率


Shell32的返回似乎有一些字符阻止了简单直接转换为int值。对于比特率:

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

int bitRate = Convert.ToInt32(bRate);

答案 1 :(得分:1)

它没有记录在任何地方。作为一般规则,存储可以从其他值计算的值是不好的做法。加上比特率可以随着时间的推移改变相同的视频。您可以做的是在stsz框中添加您感兴趣的框架的大小(原子在iso标准中称为框)以及从stts框到数学的样本持续时间。

答案 2 :(得分:1)

如果您可以阅读信息值(您已经获得了szatmary的答案以获得更准确的信息),shell会通过解析文件并通过Media Foundation MPEG-4 Property Handler类读取元数据来报告此情况。

本地API入口点是PSLookupPropertyHandlerCLSID,然后是IPropertyStore接口的常规COM实例化,然后读取属性。即使您没有C#接口,也可以通过P / Invoke和互操作性层轻松实现这一点。

此帮助应用程序很容易发现您可以通过这种方式阅读的属性,包装API:FilePropertyStoreWin32x64)。也就是说,通过应用程序看到的内容也可以通过提到的API获得。

enter image description here

以下摘录自.MOV文件的内容(注意PKEY_Audio_EncodingBitratePKEY_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`

该方法也适用于其他媒体文件格式,通过相应的属性处理程序为其他容器格式使用相同的密钥获取数据。