void MetaDataHandler(AtomCallbackArgs a)
            {
                // see https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/Metadata/Metadata.html
                switch (a.TypeString)
                {
                case "keys":
                {
                    var version    = a.Reader.GetByte();
                    var flags      = a.Reader.GetBytes(3);
                    var entryCount = a.Reader.GetUInt32();
                    for (int i = 1; i <= entryCount; i++)
                    {
                        var keySize      = a.Reader.GetUInt32();
                        var keyValueSize = (int)keySize - 8;
                        var keyNamespace = a.Reader.GetUInt32();
                        var keyValue     = a.Reader.GetBytes(keyValueSize);
                        metaDataKeys.Add(Encoding.UTF8.GetString(keyValue));
                    }
                    break;
                }

                case "ilst":
                {
                    var directory = new QuickTimeMetadataHeaderDirectory();
                    // Iterate over the list of Metadata Item Atoms.
                    for (int i = 0; i < metaDataKeys.Count; i++)
                    {
                        long atomSize = a.Reader.GetUInt32();
                        if (atomSize < 24)
                        {
                            directory.AddError("Invalid ilist atom type");
                            a.Reader.Skip(atomSize - 4);
                            continue;
                        }
                        var atomType = a.Reader.GetUInt32();

                        // Indexes into the metadata item keys atom are 1-based (1…entry_count).
                        // atom type for each metadata item atom is the index of the key
                        if (atomType < 1 || atomType > metaDataKeys.Count)
                        {
                            directory.AddError("Invalid ilist atom type");
                            a.Reader.Skip(atomSize - 8);
                            continue;
                        }
                        var key = metaDataKeys[(int)atomType - 1];

                        // Value Atom
                        var typeIndicator   = a.Reader.GetUInt32();
                        var localeIndicator = a.Reader.GetUInt32();

                        // Data Atom
                        var dataTypeIndicator = a.Reader.GetUInt32();
                        if (!_supportedAtomValueTypes.Contains((int)dataTypeIndicator))
                        {
                            directory.AddError($"Unsupported type indicator \"{dataTypeIndicator}\" for key \"{key}\"");
                            a.Reader.Skip(atomSize - 20);
                            continue;
                        }
                        // Currently only the Default Country/Locale is supported
                        var dataLocaleIndicator = a.Reader.GetUInt32();
                        if (dataLocaleIndicator != 0)
                        {
                            directory.AddError($"Unsupported locale indicator \"{dataLocaleIndicator}\" for key \"{key}\"");
                            a.Reader.Skip(atomSize - 24);
                            continue;
                        }
                        var data = a.Reader.GetBytes((int)atomSize - 24);
                        if (directory.TryGetTag(key, out int tag))
                        {
                            DecodeData(data, (int)dataTypeIndicator, tag, directory);
                        }
                        else
                        {
                            directory.AddError($"Unsupported ilist key \"{key}\"");
                        }
                    }
                    directories.Add(directory);
                    break;
                }
                }
            }
Example #2
0
            void MetaDataHandler(AtomCallbackArgs a)
            {
                // see https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/Metadata/Metadata.html
                switch (a.TypeString)
                {
                case "keys":
                {
                    a.Reader.Skip(4);     // 1 byte version, 3 bytes flags
                    var entryCount = a.Reader.GetUInt32();
                    for (int i = 1; i <= entryCount; i++)
                    {
                        var keySize      = a.Reader.GetUInt32();
                        var keyValueSize = (int)keySize - 8;
                        a.Reader.Skip(4);     // uint32: key namespace
                        var keyValue = a.Reader.GetBytes(keyValueSize);
                        metaDataKeys.Add(Encoding.UTF8.GetString(keyValue));
                    }
                    break;
                }

                case "ilst":
                {
                    // Iterate over the list of Metadata Item Atoms.
                    for (int i = 0; i < metaDataKeys.Count; i++)
                    {
                        long atomSize = a.Reader.GetUInt32();
                        if (atomSize < 24)
                        {
                            GetMetaHeaderDirectory().AddError("Invalid ilist atom type");
                            a.Reader.Skip(atomSize - 4);
                            continue;
                        }
                        var atomType = a.Reader.GetUInt32();

                        // Indexes into the metadata item keys atom are 1-based (1…entry_count).
                        // atom type for each metadata item atom is the index of the key
                        if (atomType < 1 || atomType > metaDataKeys.Count)
                        {
                            GetMetaHeaderDirectory().AddError("Invalid ilist atom type");
                            a.Reader.Skip(atomSize - 8);
                            continue;
                        }
                        var key = metaDataKeys[(int)atomType - 1];

                        // Value Atom
                        a.Reader.Skip(8);     // uint32 type indicator, uint32 locale indicator

                        // Data Atom
                        var dataTypeIndicator = a.Reader.GetUInt32();
                        if (!_supportedAtomValueTypes.Contains((int)dataTypeIndicator))
                        {
                            GetMetaHeaderDirectory().AddError($"Unsupported type indicator \"{dataTypeIndicator}\" for key \"{key}\"");
                            a.Reader.Skip(atomSize - 20);
                            continue;
                        }

                        // locale not supported yet.
                        a.Reader.Skip(4);

                        var data = a.Reader.GetBytes((int)atomSize - 24);
                        if (QuickTimeMetadataHeaderDirectory.TryGetTag(key, out int tag))
                        {
                            object value = dataTypeIndicator switch
                            {
                                // UTF-8
                                1 => new StringValue(data, Encoding.UTF8),

                                // BE Float32 (used for User Rating)
                                23 => BitConverter.ToSingle(BitConverter.IsLittleEndian ? data.Reverse().ToArray() : data, 0),

                                // 13 JPEG
                                // 14 PNG
                                // 27 BMP
                                _ => data
                            };

                            GetMetaHeaderDirectory().Set(tag, value);
                        }
                        else
                        {
                            GetMetaHeaderDirectory().AddError($"Unsupported ilist key \"{key}\"");
                        }
                    }

                    break;
                }
                }
            }