Example #1
0
        public static DirectoryList ReadMetadata(Stream stream)
        {
            var directories  = new List <Directory>();
            var metaDataKeys = new List <string>();
            QuickTimeMetadataHeaderDirectory?metaHeaderDirectory = null;

            QuickTimeReader.ProcessAtoms(stream, Handler);

            return(directories);

            QuickTimeMetadataHeaderDirectory GetMetaHeaderDirectory()
            {
                if (metaHeaderDirectory == null)
                {
                    metaHeaderDirectory = new QuickTimeMetadataHeaderDirectory();
                    directories.Add(metaHeaderDirectory);
                }

                return(metaHeaderDirectory);
            }

            void TrakHandler(AtomCallbackArgs a)
            {
                switch (a.TypeString)
                {
                case "tkhd":
                {
                    var directory = new QuickTimeTrackHeaderDirectory();
                    directory.Set(QuickTimeTrackHeaderDirectory.TagVersion, a.Reader.GetByte());
                    directory.Set(QuickTimeTrackHeaderDirectory.TagFlags, a.Reader.GetBytes(3));
                    directory.Set(QuickTimeTrackHeaderDirectory.TagCreated, _epoch.AddTicks(TimeSpan.TicksPerSecond * a.Reader.GetUInt32()));
                    directory.Set(QuickTimeTrackHeaderDirectory.TagModified, _epoch.AddTicks(TimeSpan.TicksPerSecond * a.Reader.GetUInt32()));
                    directory.Set(QuickTimeTrackHeaderDirectory.TagTrackId, a.Reader.GetUInt32());
                    a.Reader.Skip(4L);
                    directory.Set(QuickTimeTrackHeaderDirectory.TagDuration, a.Reader.GetUInt32());
                    a.Reader.Skip(8L);
                    directory.Set(QuickTimeTrackHeaderDirectory.TagLayer, a.Reader.GetUInt16());
                    directory.Set(QuickTimeTrackHeaderDirectory.TagAlternateGroup, a.Reader.GetUInt16());
                    directory.Set(QuickTimeTrackHeaderDirectory.TagVolume, a.Reader.Get16BitFixedPoint());
                    a.Reader.Skip(2L);
                    directory.Set(QuickTimeTrackHeaderDirectory.TagMatrix, a.Reader.GetMatrix());
                    directory.Set(QuickTimeTrackHeaderDirectory.TagWidth, a.Reader.Get32BitFixedPoint());
                    directory.Set(QuickTimeTrackHeaderDirectory.TagHeight, a.Reader.Get32BitFixedPoint());
                    SetRotation(directory);
                    directories.Add(directory);
                    break;
                }
                }
            }
            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 #3
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;
                }
                }
            }