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

3
我一直在尝试从.mov文件(QuickTime文件格式)的元数据中读取一些值,但成功有限。 我一直在使用以下链接作为参考:QuickTime文件格式规范介绍。我已经成功地定位并读取/计算了媒体持续时间,但似乎找不到存储比特率信息的原子(原子是文件内部的元数据块)。 如果有人能指出正确的原子来读取,我会很好读取它...即使在整个文档中“比特率”只提到了几次,我也无法找到它。
更新 >>>
根据@szatmary下面提供的非常有限的信息,我已经解析了相关的轨道原子中的样本大小原子和时间到样本原子,但得到了一些奇怪的值。 例如,当从多个具有恒定比特率的单个视频.mov文件中读取时,我一直得到一个样本大小值为1的值。相关文档(来自上面的链接)说:
样本大小 一个32位整数,指定样本大小。如果所有样本的大小相同,则该字段包含该大小值。如果将此字段设置为0,则样本具有不同的大小,并且这些大小存储在样本大小表中。
因此,该字段的值为1,这意味着所有样本的大小相同,并且“样本大小表”中的“条目数”字段与“时间到样本表”的单个条目中的“样本计数”字段匹配(一些非常大的数字)。文档指出:
...如果视频媒体具有恒定的帧速率,则此表将具有一个条目,并且计数将等于样本数。
因此,该视频具有恒定的比特率。但是,当从“样本大小表”中读取大小条目时,它们都是不同的和无意义的...有些是0,而其他一些则是非常大的数字,最高可达约40000。如果视频具有恒定比特率,为什么它们不同,或者在这种情况下我是否不应该读取它们?
我发现的另一个问题是,“时间到样本原子”的“时间到样本表”的单个条目具有以下值:

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

不幸的是,文档(来自上面的链接)在这里非常简略:

时间到样本表
定义媒体中每个样本的持续时间的表。每个表条目都包含一个计数字段和一个持续时间字段。

那么这些1值使用哪些单位 (Sample Duration & Sample Size)呢?

如有帮助计算正确的比特率,将不胜感激。请注意,我已经考虑了文件的大端性并在读取它们之前翻转了每个字段值的字节。


更新2 >>>

我已经成功计算出采样率的计算方法:

媒体持续时间 = 持续时间 / 时间刻度(来自电影头部原子轨道头部原子) 采样率 = 样本计数(来自时间到样本原子)/ 媒体持续时间

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


视频或音频的比特率?你知道有第三方工具可以为你完成所有这些吗? - Ňɏssa Pøngjǣrdenlarp
Windows资源管理器中显示的比特率...我认为这是音频比特率。也许我需要计算两个比特率来确保哪一个是正确的?是的,我知道有第三方工具可以做到这一点,但由于某些原因,我无法在这个特定项目上使用它们。此外,我已经编写了代码来解析各种Atom并跳过不需要的Atom...我只需要知道需要从哪些Atom读取哪些值。 - Sheridan
除了第三方工具之外,通过QT Atoms自己计算BR是您唯一可以接受的方式吗? - Ňɏssa Pøngjǣrdenlarp
3个回答

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

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

enter image description here

GetMediaProperties的结果:

enter image description here

返回结果:

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


前35个Shell扩展属性已经有很好的文档说明。我认为从Windows 7开始,这个数字增加到了291个(!)。其中许多是针对特定文件类型的,例如照片、电子邮件等。以下是一些可能会引起兴趣的属性:
282:数据速率
283:帧高度
284:帧速率
285:帧宽度
286:总比特率
数据速率(282)是视频比特率(与MediaInfo相匹配);总比特率(286)是音频和视频比特率的组合。

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论坛的在Windows 8中实例化Shell32.Shell对象的解决方案问题中找到了答案。

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

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


似乎Shell32返回的结果中有一些字符阻止了直接将其转换为整数值。对于比特率:
string bRate = myinfo["Bit rate"];       // get return val
bRate = new string(bRate.Where(char.IsDigit).ToArray());  // tidy up

int bitRate = Convert.ToInt32(bRate);

嗨@Plutonix,感谢您的回答。我本来希望得到一个基于Atom的解决方案,因为我已经完成了90%,但这肯定是一个有趣的替代方案。今天我不认为我有机会去尝试它,因为我必须今天处理其他事情,但我一定会尽快尝试它。 - Sheridan
我快速查看了这段代码,但不幸的是它在Windows 8上不能直接运行。然而,经过一番研究,我找到了一个适用于Windows 8的解决方案,因此我编辑了你的答案以提供这个“修复”方法。有了这个新代码,你的示例就可以正常工作了,所以除非在悬赏结束之前我得到一个完整的基于Atom的答案,否则我将授予你悬赏积分。 - Sheridan
我在瞎搞的时候发现了这个:((streamsizeKB * 1024) * 8) / durationSecs kindof = bitrate。我不知道你是否有一个流大小原子,并且存在一些注意事项。主要是它只适用于可变比特率。对于苹果文件和1GB MP4,它(有点)起作用。个人建议使用Shell32,这样任何错误的结果都无法通过资源管理器证明。可变比特率会使结果混乱:1GB文件的平均值为5000k,但最大值为9800k。 - Ňɏssa Pøngjǣrdenlarp
啊,好发现,我错过了那个额外的隐藏字符。看起来我将不得不使用这个解决方案来访问文件属性,但告诉我一些东西...在文件属性对话框的“详细信息”选项卡中的“音频”部分中的音频采样率属性可以在哪里找到?我在这个列表中找不到它。 - Sheridan
音频采样率不是Shell32/Win可以识别的其中之一。在您的Windows版本中可用的将在资源管理器详细视图中,右键单击标题并选择“更多...”。如果您有MediaTab,则它将报告所有媒体属性,但它使用第三方DLL;它可能有助于确定哪些原子代表什么。 - Ňɏssa Pøngjǣrdenlarp
显示剩余5条评论

1
如果您要阅读信息价值(您已经拥有szatmary的答案以获取更准确的信息),shell会通过Media Foundation MPEG-4 Property Handler类解析文件并读取元数据来报告此情况。这个的本机API入口点是PSLookupPropertyHandlerCLSID, 然后是对IPropertyStore接口的常规COM实例化,然后读取属性。即使您没有C#接口,您也可以通过P / Invoke和互操作层轻松获得此内容。您可以通过这个包装API的帮助应用程序轻松发现可以通过此方式读取的属性:FilePropertyStore( Win32, x64)。也就是说,您通过该应用程序看到的内容也可以通过提到的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`

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

1

这并没有被记录在任何地方。一般来说,存储可以通过其他值计算得出的值是不好的做法。而且,相同的视频比特率随时间可能会发生变化。您可以做的是将您感兴趣的帧的大小加起来,在stsz盒子中(在iso标准中,原子被称为盒子),并从stts盒子中获取样本持续时间进行计算。


谢谢您的回复。您能否在回答中详细说明我应该如何进行数学计算?我该如何从样本大小原子中计算出我感兴趣的帧,并将其与时间到样本原子中的持续时间联系起来? - Sheridan
我该如何确定我感兴趣的帧?我不知道你感兴趣的是哪些帧。也许全部都是吗?“我应该如何进行数学计算?”我猜你想要每秒的比特数,所以将设备比特除以秒。 - szatmary
拿一个具有恒定比特率的单个.mov视频文件作为例子...我应该在样本原子中寻找什么来计算比特率? - Sheridan
样本大小和持续时间。 - szatmary
请阅读我的更新并提供更多有用的信息。 - Sheridan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接