Exemple #1
0
        /// <summary>
        ///    Constructs and initializes a new instance of <see
        ///    cref="IsoHandlerBox" /> with a provided header and
        ///    handler by reading the contents from a specified file.
        /// </summary>
        /// <param name="header">
        ///    A <see cref="BoxHeader" /> object containing the header
        ///    to use for the new instance.
        /// </param>
        /// <param name="file">
        ///    A <see cref="TagLib.File" /> object to read the contents
        ///    of the box from.
        /// </param>
        /// <param name="handler">
        ///    A <see cref="IsoHandlerBox" /> object containing the
        ///    handler that applies to the new instance.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///    <paramref name="file" /> is <see langword="null" />.
        /// </exception>
        public IsoHandlerBox(BoxHeader header, TagLib.File file,
                             IsoHandlerBox handler)
            : base(header, file, handler)
        {
            if (file == null)
            {
                throw new System.ArgumentNullException("file");
            }

            file.Seek(DataPosition + 4);
            ByteVector box_data = file.ReadBlock(DataSize - 4);

            handler_type = box_data.Mid(0, 4);

            int end = box_data.Find((byte)0, 16);

            if (end < 16)
            {
                end = box_data.Count;
            }
            name = box_data.ToString(StringType.UTF8, 16, end - 16);
        }
        /// <summary>
        ///    Constructs and initializes a new instance of <see
        ///    cref="AppleElementaryStreamDescriptor" /> with a provided
        ///    header and handler by reading the contents from a
        ///    specified file.
        /// </summary>
        /// <param name="header">
        ///    A <see cref="BoxHeader" /> object containing the header
        ///    to use for the new instance.
        /// </param>
        /// <param name="file">
        ///    A <see cref="TagLib.File" /> object to read the contents
        ///    of the box from.
        /// </param>
        /// <param name="handler">
        ///    A <see cref="IsoHandlerBox" /> object containing the
        ///    handler that applies to the new instance.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///    <paramref name="file" /> is <see langword="null" />.
        /// </exception>
        /// <exception cref="CorruptFileException">
        ///    Valid data could not be read.
        /// </exception>
        public AppleElementaryStreamDescriptor(BoxHeader header,
                                               TagLib.File file,
                                               IsoHandlerBox handler)
            : base(header, file, handler)
        {
            int        offset   = 0;
            ByteVector box_data = file.ReadBlock(DataSize);

            decoder_config = new ByteVector();

            // Elementary Stream Descriptor Tag
            if (box_data [offset++] == 3)
            {
                // We have a descriptor tag. Check that it's at
                // least 20 long.
                if (ReadLength(box_data, ref offset) < 20)
                {
                    throw new CorruptFileException(
                              "Insufficient data present.");
                }

                es_id           = box_data.Mid(offset, 2).ToUShort();
                offset         += 2;
                stream_priority = box_data [offset++];
            }
            else
            {
                // The tag wasn't found, so the next two byte
                // are the ID, and after that, business as
                // usual.
                es_id   = box_data.Mid(offset, 2).ToUShort();
                offset += 2;
            }

            // Verify that the next data is the Decoder
            // Configuration Descriptor Tag and escape if it won't
            // work out.
            if (box_data [offset++] != 4)
            {
                throw new CorruptFileException(
                          "Could not identify decoder configuration descriptor.");
            }

            // Check that it's at least 15 long.
            if (ReadLength(box_data, ref offset) < 15)
            {
                throw new CorruptFileException(
                          "Could not read data. Too small.");
            }

            // Read a lot of good info.
            object_type_id  = box_data [offset++];
            stream_type     = box_data [offset++];
            buffer_size_db  = box_data.Mid(offset, 3).ToUInt();
            offset         += 3;
            max_bitrate     = box_data.Mid(offset, 4).ToUInt();
            offset         += 4;
            average_bitrate = box_data.Mid(offset, 4).ToUInt();
            offset         += 4;

            // Verify that the next data is the Decoder Specific
            // Descriptor Tag and escape if it won't work out.
            if (box_data [offset++] != 5)
            {
                throw new CorruptFileException(
                          "Could not identify decoder specific descriptor.");
            }

            // The rest of the info is decoder specific.
            uint length = ReadLength(box_data, ref offset);

            decoder_config = box_data.Mid(offset, (int)length);
        }
Exemple #3
0
        /// <summary>
        ///    Saves the changes made in the current instance to the
        ///    file it represents.
        /// </summary>
        public override void Save()
        {
            if (udta_boxes.Count == 0)
            {
                IsoUserDataBox udtaBox = new IsoUserDataBox();
                udta_boxes.Add(udtaBox);
            }

            // Try to get into write mode.
            Mode = File.AccessMode.Write;
            try {
                FileParser parser = new FileParser(this);
                parser.ParseBoxHeaders();

                InvariantStartPosition = parser.MdatStartPosition;
                InvariantEndPosition   = parser.MdatEndPosition;

                long size_change    = 0;
                long write_position = 0;

                // To avoid rewriting udta blocks which might not have been modified,
                // the code here will work correctly if:
                // 1. There is a single udta for the entire file
                //   - OR -
                // 2. There are multiple utdtas, but only 1 of them contains the Apple ILST box.
                // We should be OK in the vast majority of cases
                IsoUserDataBox udtaBox = FindAppleTagUdta();
                if (null == udtaBox)
                {
                    udtaBox = new IsoUserDataBox();
                }
                ByteVector tag_data = udtaBox.Render();

                // If we don't have a "udta" box to overwrite...
                if (udtaBox.ParentTree == null ||
                    udtaBox.ParentTree.Length == 0)
                {
                    // Stick the box at the end of the moov box.
                    BoxHeader moov_header = parser.MoovTree [
                        parser.MoovTree.Length - 1];
                    size_change    = tag_data.Count;
                    write_position = moov_header.Position +
                                     moov_header.TotalBoxSize;
                    Insert(tag_data, write_position, 0);

                    // Overwrite the parent box sizes.
                    for (int i = parser.MoovTree.Length - 1; i >= 0;
                         i--)
                    {
                        size_change = parser.MoovTree [i
                                      ].Overwrite(this, size_change);
                    }
                }
                else
                {
                    // Overwrite the old box.
                    BoxHeader udta_header = udtaBox.ParentTree[udtaBox.ParentTree.Length - 1];
                    size_change = tag_data.Count -
                                  udta_header.TotalBoxSize;
                    write_position = udta_header.Position;
                    Insert(tag_data, write_position,
                           udta_header.TotalBoxSize);

                    // Overwrite the parent box sizes.
                    for (int i = udtaBox.ParentTree.Length - 2; i >= 0;
                         i--)
                    {
                        size_change = udtaBox.ParentTree [i
                                      ].Overwrite(this, size_change);
                    }
                }

                // If we've had a size change, we may need to adjust
                // chunk offsets.
                if (size_change != 0)
                {
                    // We may have moved the offset boxes, so we
                    // need to reread.
                    parser.ParseChunkOffsets();
                    InvariantStartPosition = parser.MdatStartPosition;
                    InvariantEndPosition   = parser.MdatEndPosition;

                    foreach (Box box in parser.ChunkOffsetBoxes)
                    {
                        IsoChunkLargeOffsetBox co64 =
                            box as IsoChunkLargeOffsetBox;

                        if (co64 != null)
                        {
                            co64.Overwrite(this,
                                           size_change,
                                           write_position);
                            continue;
                        }

                        IsoChunkOffsetBox stco =
                            box as IsoChunkOffsetBox;

                        if (stco != null)
                        {
                            stco.Overwrite(this,
                                           size_change,
                                           write_position);
                            continue;
                        }
                    }
                }

                TagTypesOnDisk = TagTypes;
            } finally {
                Mode = File.AccessMode.Closed;
            }
        }
Exemple #4
0
 /// <summary>
 ///    Constructs and initializes a new instance of <see
 ///    cref="IsoMetaBox" /> with a provided header and
 ///    handler by reading the contents from a specified file.
 /// </summary>
 /// <param name="header">
 ///    A <see cref="BoxHeader" /> object containing the header
 ///    to use for the new instance.
 /// </param>
 /// <param name="file">
 ///    A <see cref="TagLib.File" /> object to read the contents
 ///    of the box from.
 /// </param>
 /// <param name="handler">
 ///    A <see cref="IsoHandlerBox" /> object containing the
 ///    handler that applies to the new instance.
 /// </param>
 public IsoFreeSpaceBox(BoxHeader header, TagLib.File file,
                        IsoHandlerBox handler)
     : base(header, handler)
 {
     padding = DataSize;
 }
Exemple #5
0
        /// <summary>
        ///    Parses boxes for a specified range, looking for tags and
        ///    properties.
        /// </summary>
        /// <param name="start">
        ///    A <see cref="long" /> value specifying the seek position
        ///    at which to start reading.
        /// </param>
        /// <param name="end">
        ///    A <see cref="long" /> value specifying the seek position
        ///    at which to stop reading.
        /// </param>
        /// <param name="handler">
        ///    A <see cref="IsoHandlerBox" /> object that applied to the
        ///    range being searched.
        /// </param>
        private void ParseTagAndProperties(long start, long end,
                                           IsoHandlerBox handler, List <BoxHeader> parents)
        {
            BoxHeader header;

            for (long position = start; position < end;
                 position += header.TotalBoxSize)
            {
                header = new BoxHeader(file, position);
                ByteVector type = header.BoxType;

                if (type == BoxType.Moov)
                {
                    ParseTagAndProperties(header.HeaderSize + position,
                                          header.TotalBoxSize + position,
                                          handler,
                                          AddParent(parents, header));
                }
                else if (type == BoxType.Mdia ||
                         type == BoxType.Minf ||
                         type == BoxType.Stbl ||
                         type == BoxType.Trak)
                {
                    ParseTagAndProperties(
                        header.HeaderSize + position,
                        header.TotalBoxSize + position,
                        handler,
                        AddParent(parents, header));
                }
                else if (type == BoxType.Stsd)
                {
                    stsd_boxes.Add(BoxFactory.CreateBox(
                                       file, header, handler));
                }
                else if (type == BoxType.Hdlr)
                {
                    handler = BoxFactory.CreateBox(file,
                                                   header, handler) as
                              IsoHandlerBox;
                }
                else if (mvhd_box == null &&
                         type == BoxType.Mvhd)
                {
                    mvhd_box = BoxFactory.CreateBox(file,
                                                    header, handler) as
                               IsoMovieHeaderBox;
                }
                else if (type == BoxType.Udta)
                {
                    IsoUserDataBox udtaBox = BoxFactory.CreateBox(file,
                                                                  header, handler) as
                                             IsoUserDataBox;

                    // Since we can have multiple udta boxes, save the parent for each one
                    List <BoxHeader> new_parents = AddParent(
                        parents, header);
                    udtaBox.ParentTree = new_parents.ToArray();

                    udta_boxes.Add(udtaBox);
                }
                else if (type == BoxType.Mdat)
                {
                    mdat_start = position;
                    mdat_end   = position + header.TotalBoxSize;
                }

                if (header.TotalBoxSize == 0)
                {
                    break;
                }
            }
        }
Exemple #6
0
 /// <summary>
 ///    Constructs and initializes a new instance of <see
 ///    cref="FullBox" /> with a provided header, version, and
 ///    flags.
 /// </summary>
 /// <param name="header">
 ///    A <see cref="BoxHeader" /> object containing the header
 ///    to use for the new instance.
 /// </param>
 /// <param name="version">
 ///    A <see cref="byte" /> value containing the version of the
 ///    new instance.
 /// </param>
 /// <param name="flags">
 ///    A <see cref="byte" /> value containing the flags for the
 ///    new instance.
 /// </param>
 protected FullBox(BoxHeader header, byte version, uint flags)
     : base(header)
 {
     this.version = version;
     this.flags   = flags;
 }
 /// <summary>
 ///    Constructs and initializes a new instance of <see
 ///    cref="AppleAdditionalInfoBox" /> with a provided header
 ///    and handler by reading the contents from a specified
 ///    file.
 /// </summary>
 /// <param name="header">
 ///    A <see cref="BoxHeader" /> object containing the header
 ///    to use for the new instance.
 /// </param>
 /// <param name="file">
 ///    A <see cref="TagLib.File" /> object to read the contents
 ///    of the box from.
 /// </param>
 /// <param name="handler">
 ///    A <see cref="IsoHandlerBox" /> object containing the
 ///    handler that applies to the new instance.
 /// </param>
 /// <exception cref="ArgumentNullException">
 ///    <paramref name="file" /> is <see langword="null" />.
 /// </exception>
 public AppleAdditionalInfoBox(BoxHeader header, TagLib.File file, IsoHandlerBox handler) : base(header, handler)
 {
     // We do not care what is in this custom data section
     // see: https://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap2/qtff2.html
     Data = LoadData(file);
 }
Exemple #8
0
 /// <summary>
 ///    Constructs and initializes a new instance of <see
 ///    cref="IsoMetaBox" /> with a provided header and
 ///    handler by reading the contents from a specified file.
 /// </summary>
 /// <param name="header">
 ///    A <see cref="BoxHeader" /> object containing the header
 ///    to use for the new instance.
 /// </param>
 /// <param name="file">
 ///    A <see cref="TagLib.File" /> object to read the contents
 ///    of the box from.
 /// </param>
 /// <param name="handler">
 ///    A <see cref="IsoHandlerBox" /> object containing the
 ///    handler that applies to the new instance.
 /// </param>
 /// <exception cref="ArgumentNullException">
 ///    <paramref name="file" /> is <see langword="null" />.
 /// </exception>
 public IsoMetaBox(BoxHeader header, TagLib.File file,
                   IsoHandlerBox handler)
     : base(header, file, handler)
 {
     children = LoadChildren(file);
 }
Exemple #9
0
 /// <summary>
 ///    Constructs and initializes a new instance of <see
 ///    cref="AppleDataBox" /> with a provided header and handler
 ///    by reading the contents from a specified file.
 /// </summary>
 /// <param name="header">
 ///    A <see cref="BoxHeader" /> object containing the header
 ///    to use for the new instance.
 /// </param>
 /// <param name="file">
 ///    A <see cref="TagLib.File" /> object to read the contents
 ///    of the box from.
 /// </param>
 /// <param name="handler">
 ///    A <see cref="IsoHandlerBox" /> object containing the
 ///    handler that applies to the new instance.
 /// </param>
 /// <exception cref="ArgumentNullException">
 ///    <paramref name="file" /> is <see langword="null" />.
 /// </exception>
 public AppleDataBox(BoxHeader header, TagLib.File file,
                     IsoHandlerBox handler)
     : base(header, file, handler)
 {
     Data = LoadData(file);
 }
Exemple #10
0
        /// <summary>
        ///    Constructs and initializes a new instance of <see
        ///    cref="IsoMovieHeaderBox" /> with a provided header and
        ///    handler by reading the contents from a specified file.
        /// </summary>
        /// <param name="header">
        ///    A <see cref="BoxHeader" /> object containing the header
        ///    to use for the new instance.
        /// </param>
        /// <param name="file">
        ///    A <see cref="TagLib.File" /> object to read the contents
        ///    of the box from.
        /// </param>
        /// <param name="handler">
        ///    A <see cref="IsoHandlerBox" /> object containing the
        ///    handler that applies to the new instance.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///    <paramref name="file" /> is <see langword="null" />.
        /// </exception>
        public IsoMovieHeaderBox(BoxHeader header, TagLib.File file,
                                 IsoHandlerBox handler)
            : base(header, file, handler)
        {
            if (file == null)
            {
                throw new ArgumentNullException("file");
            }

            int        bytes_remaining = DataSize;
            ByteVector data;

            if (Version == 1)
            {
                // Read version one (large integers).
                data = file.ReadBlock(Math.Min(28,
                                               bytes_remaining));
                if (data.Count >= 8)
                {
                    creation_time = data.Mid(0,
                                             8).ToULong();
                }
                if (data.Count >= 16)
                {
                    modification_time = data.Mid(8,
                                                 8).ToULong();
                }
                if (data.Count >= 20)
                {
                    timescale = data.Mid(16, 4).ToUInt();
                }
                if (data.Count >= 28)
                {
                    duration = data.Mid(20, 8).ToULong();
                }
                bytes_remaining -= 28;
            }
            else
            {
                // Read version zero (normal integers).
                data = file.ReadBlock(Math.Min(16,
                                               bytes_remaining));
                if (data.Count >= 4)
                {
                    creation_time = data.Mid(0,
                                             4).ToUInt();
                }
                if (data.Count >= 8)
                {
                    modification_time = data.Mid(4,
                                                 4).ToUInt();
                }
                if (data.Count >= 12)
                {
                    timescale = data.Mid(8, 4).ToUInt();
                }
                if (data.Count >= 16)
                {
                    duration = data.Mid(12, 4).ToUInt();
                }
                bytes_remaining -= 16;
            }

            data = file.ReadBlock(Math.Min(6, bytes_remaining));
            if (data.Count >= 4)
            {
                rate = data.Mid(0, 4).ToUInt();
            }
            if (data.Count >= 6)
            {
                volume = data.Mid(4, 2).ToUShort();
            }
            file.Seek(file.Tell + 70);
            bytes_remaining -= 76;

            data = file.ReadBlock(Math.Min(4,
                                           bytes_remaining));

            if (data.Count >= 4)
            {
                next_track_id = data.Mid(0, 4).ToUInt();
            }
        }
Exemple #11
0
 /// <summary>
 ///    Constructs and initializes a new instance of <see
 ///    cref="Box" /> with a specified header.
 /// </summary>
 /// <param name="header">
 ///    A <see cref="BoxHeader" /> object describing the new
 ///    instance.
 /// </param>
 protected Box(BoxHeader header) : this(header, null)
 {
 }
Exemple #12
0
 /// <summary>
 ///    Constructs and initializes a new instance of <see
 ///    cref="Box" /> with a specified header and handler.
 /// </summary>
 /// <param name="header">
 ///    A <see cref="BoxHeader" /> object describing the new
 ///    instance.
 /// </param>
 /// <param name="handler">
 ///    A <see cref="IsoHandlerBox" /> object containing the
 ///    handler that applies to the new instance, or <see
 ///    langword="null" /> if no handler applies.
 /// </param>
 protected Box(BoxHeader header, IsoHandlerBox handler)
 {
     this.header        = header;
     this.data_position = header.Position + header.HeaderSize;
     this.handler       = handler;
 }
Exemple #13
0
        /// <summary>
        ///    Creates a box by reading it from a file given its header,
        ///    parent header, handler, and index in its parent.
        /// </summary>
        /// <param name="file">
        ///    A <see cref="TagLib.File" /> object containing the file
        ///    to read from.
        /// </param>
        /// <param name="header">
        ///    A <see cref="BoxHeader" /> object containing the header
        ///    of the box to create.
        /// </param>
        /// <param name="parent">
        ///    A <see cref="BoxHeader" /> object containing the header
        ///    of the parent box.
        /// </param>
        /// <param name="handler">
        ///    A <see cref="IsoHandlerBox" /> object containing the
        ///    handler that applies to the new box.
        /// </param>
        /// <param name="index">
        ///    A <see cref="int" /> value containing the index of the
        ///    new box in its parent.
        /// </param>
        /// <returns>
        ///    A newly created <see cref="Box" /> object.
        /// </returns>
        private static Box CreateBox(TagLib.File file,
                                     BoxHeader header,
                                     BoxHeader parent,
                                     IsoHandlerBox handler,
                                     int index)
        {
            // The first few children of an "stsd" are sample
            // entries.
            if (parent.BoxType == BoxType.Stsd &&
                parent.Box is IsoSampleDescriptionBox &&
                index < (parent.Box as IsoSampleDescriptionBox).EntryCount)
            {
                if (handler != null && handler.HandlerType == BoxType.Soun)
                {
                    return(new IsoAudioSampleEntry(header, file, handler));
                }
                else if (handler != null && handler.HandlerType == BoxType.Vide)
                {
                    return(new IsoVisualSampleEntry(header, file, handler));
                }
                else if (handler != null && handler.HandlerType == BoxType.Alis)
                {
                    if (header.BoxType == BoxType.Text)
                    {
                        return(new TextBox(header, file, handler));
                    }
                    else if (header.BoxType == BoxType.Url)
                    {
                        return(new UrlBox(header, file, handler));
                    }
                    // This could be anything, so just parse it
                    return(new UnknownBox(header, file, handler));
                }
                else
                {
                    return(new IsoSampleEntry(header,
                                              file, handler));
                }
            }

            // Standard items...
            ByteVector type = header.BoxType;

            if (type == BoxType.Mvhd)
            {
                return(new IsoMovieHeaderBox(header, file,
                                             handler));
            }
            else if (type == BoxType.Stbl)
            {
                return(new IsoSampleTableBox(header, file,
                                             handler));
            }
            else if (type == BoxType.Stsd)
            {
                return(new IsoSampleDescriptionBox(header,
                                                   file, handler));
            }
            else if (type == BoxType.Stco)
            {
                return(new IsoChunkOffsetBox(header, file,
                                             handler));
            }
            else if (type == BoxType.Co64)
            {
                return(new IsoChunkLargeOffsetBox(header, file,
                                                  handler));
            }
            else if (type == BoxType.Hdlr)
            {
                return(new IsoHandlerBox(header, file,
                                         handler));
            }
            else if (type == BoxType.Udta)
            {
                return(new IsoUserDataBox(header, file,
                                          handler));
            }
            else if (type == BoxType.Meta)
            {
                return(new IsoMetaBox(header, file, handler));
            }
            else if (type == BoxType.Ilst)
            {
                return(new AppleItemListBox(header, file,
                                            handler));
            }
            else if (type == BoxType.Data)
            {
                return(new AppleDataBox(header, file, handler));
            }
            else if (type == BoxType.Esds)
            {
                return(new AppleElementaryStreamDescriptor(
                           header, file, handler));
            }
            else if (type == BoxType.Free || type == BoxType.Skip)
            {
                return(new IsoFreeSpaceBox(header, file,
                                           handler));
            }
            else if (type == BoxType.Mean || type == BoxType.Name)
            {
                return(new AppleAdditionalInfoBox(header, file,
                                                  handler));
            }

            // If we still don't have a tag, and we're inside an
            // ItemListBox, load the box as an AnnotationBox
            // (Apple tag item).
            if (parent.BoxType == BoxType.Ilst)
            {
                return(new AppleAnnotationBox(header, file,
                                              handler));
            }

            // Nothing good. Go generic.
            return(new UnknownBox(header, file, handler));
        }
Exemple #14
0
 /// <summary>
 ///    Creates a box by reading it from a file given its header
 ///    and handler.
 /// </summary>
 /// <param name="file">
 ///    A <see cref="TagLib.File" /> object containing the file
 ///    to read from.
 /// </param>
 /// <param name="header">
 ///    A <see cref="BoxHeader" /> object containing the header
 ///    of the box to create.
 /// </param>
 /// <returns>
 ///    A newly created <see cref="Box" /> object.
 /// </returns>
 public static Box CreateBox(TagLib.File file, BoxHeader header)
 {
     return(CreateBox(file, header, null));
 }
Exemple #15
0
 /// <summary>
 ///    Creates a box by reading it from a file given its header
 ///    and handler.
 /// </summary>
 /// <param name="file">
 ///    A <see cref="TagLib.File" /> object containing the file
 ///    to read from.
 /// </param>
 /// <param name="header">
 ///    A <see cref="BoxHeader" /> object containing the header
 ///    of the box to create.
 /// </param>
 /// <param name="handler">
 ///    A <see cref="IsoHandlerBox" /> object containing the
 ///    handler that applies to the new box.
 /// </param>
 /// <returns>
 ///    A newly created <see cref="Box" /> object.
 /// </returns>
 public static Box CreateBox(TagLib.File file, BoxHeader header,
                             IsoHandlerBox handler)
 {
     return(CreateBox(file, header, BoxHeader.Empty,
                      handler, -1));
 }