Exemple #1
0
        /// <summary>
        ///    Reads the file with a specified read style.
        /// </summary>
        /// <param name="propertiesStyle">
        ///    A <see cref="ReadStyle" /> value specifying at what level
        ///    of accuracy to read the media properties, or <see
        ///    cref="ReadStyle.None" /> to ignore the properties.
        /// </param>
        private void Read(ReadStyle propertiesStyle)
        {
            long end;
            Dictionary <uint, Bitstream> streams = ReadStreams(null,
                                                               out end);
            List <ICodec> codecs = new List <ICodec>();

            InvariantStartPosition = end;
            InvariantEndPosition   = Length;

            foreach (uint id in streams.Keys)
            {
                tag.AddComment(id,
                               streams[id].Codec.CommentData);
                codecs.Add(streams[id].Codec);
            }

            if (propertiesStyle == ReadStyle.None)
            {
                return;
            }

            PageHeader last_header = LastPageHeader;

            TimeSpan duration = streams[last_header
                                        .StreamSerialNumber].GetDuration(
                last_header.AbsoluteGranularPosition);

            properties = new Properties(duration, codecs);
        }
Exemple #2
0
 /// <summary>
 ///    Checks whether or not the current instance is equal to
 ///    another instance of <see cref="PageHeader" />.
 /// </summary>
 /// <param name="other">
 ///    A <see cref="PageHeader" /> object to compare to the
 ///    current instance.
 /// </param>
 /// <returns>
 ///    A <see cref="bool" /> value indicating whether or not the
 ///    current instance is equal to <paramref name="other" />.
 /// </returns>
 /// <seealso cref="M:System.IEquatable`1.Equals" />
 public bool Equals(PageHeader other)
 {
     return(packet_sizes == other.packet_sizes &&
            version == other.version &&
            flags == other.flags &&
            absolute_granular_position ==
            other.absolute_granular_position &&
            stream_serial_number ==
            other.stream_serial_number &&
            page_sequence_number ==
            other.page_sequence_number &&
            size == other.size &&
            data_size == other.data_size);
 }
Exemple #3
0
        /// <summary>
        ///    Constructs and initializes a new instance of <see
        ///    cref="Page" /> with a specified header and packets.
        /// </summary>
        /// <param name="packets">
        ///    A <see cref="ByteVectorCollection" /> object containing
        ///    packets to use for the new instance.
        /// </param>
        /// <param name="header">
        ///    A <see cref="PageHeader"/> object to use as the header of
        ///    the new instance.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///    <paramref name="packets" /> is <see langword="null" />.
        /// </exception>
        public Page(ByteVectorCollection packets, PageHeader header)
            : this(header)
        {
            if (packets == null)
            {
                throw new ArgumentNullException("packets");
            }

            this.packets = new ByteVectorCollection(packets);

            List <int> packet_sizes = new List <int>();

            // Build a page from the list of packets.
            foreach (ByteVector v in packets)
            {
                packet_sizes.Add(v.Count);
            }

            header.PacketSizes = packet_sizes.ToArray();
        }
Exemple #4
0
        /// <summary>
        ///    Constructs and initializes a new instance of <see
        ///    cref="PageHeader" /> by copying the values from another
        ///    instance, offsetting the page number and applying new
        ///    flags.
        /// </summary>
        /// <param name="original">
        ///    A <see cref="PageHeader"/> object to copy the values
        ///    from.
        /// </param>
        /// <param name="offset">
        ///    A <see cref="uint"/> value specifying how much to offset
        ///    the page sequence number in the new instance.
        /// </param>
        /// <param name="flags">
        ///    A <see cref="PageFlags"/> value specifying the flags to
        ///    use in the new instance.
        /// </param>
        public PageHeader(PageHeader original, uint offset,
                          PageFlags flags)
        {
            version    = original.version;
            this.flags = flags;
            absolute_granular_position =
                original.absolute_granular_position;
            stream_serial_number = original.stream_serial_number;
            page_sequence_number =
                original.page_sequence_number + offset;
            size         = original.size;
            data_size    = original.data_size;
            packet_sizes = new List <int>();

            if (page_sequence_number == 0 &&
                (flags & PageFlags.FirstPacketContinued) == 0)
            {
                this.flags |= PageFlags.FirstPageOfStream;
            }
        }
Exemple #5
0
        /// <summary>
        ///    Repaginates the pages passed into the current instance to
        ///    handle changes made to the Xiph comment.
        /// </summary>
        /// <param name="change">
        ///    A <see cref="int" /> value reference containing the
        ///    the difference between the number of pages returned and
        ///    the number of pages that were added to the class.
        /// </param>
        /// <returns>
        ///    A <see cref="Page[]" /> containing the new page
        ///    collection.
        /// </returns>
        public Page[] Paginate(out int change)
        {
            // Ogg Pagination: Welcome to sucksville!
            // If you don't understand this, you're not alone.
            // It is confusing as Hell.

            // TODO: Document this method, in the mean time, there
            // is always http://xiph.org/ogg/doc/framing.html

            if (pages_read == 0)
            {
                change = 0;
                return(new Page[0]);
            }

            int count = pages_read;
            ByteVectorCollection packets = new ByteVectorCollection(
                this.packets);
            PageHeader  first_header = (PageHeader)first_page_header;
            List <Page> pages        = new List <Page>();
            uint        index        = 0;
            bool        bos          = first_header.PageSequenceNumber == 0;

            if (bos)
            {
                pages.Add(new Page(new ByteVectorCollection(packets[0]), first_header));
                index++;
                packets.RemoveAt(0);
                count--;
            }

            int lacing_per_page = 0xfc;

            if (count > 0)
            {
                int total_lacing_bytes = 0;

                for (int i = 0; i < packets.Count; i++)
                {
                    total_lacing_bytes += GetLacingValueLength(
                        packets, i);
                }

                lacing_per_page = Math.Min(total_lacing_bytes / count + 1, lacing_per_page);
            }

            int lacing_bytes_used             = 0;
            ByteVectorCollection page_packets = new ByteVectorCollection();
            bool first_packet_continued       = false;

            while (packets.Count > 0)
            {
                int  packet_bytes = GetLacingValueLength(packets, 0);
                int  remaining    = lacing_per_page - lacing_bytes_used;
                bool whole_packet = packet_bytes <= remaining;
                if (whole_packet)
                {
                    page_packets.Add(packets[0]);
                    lacing_bytes_used += packet_bytes;
                    packets.RemoveAt(0);
                }
                else
                {
                    page_packets.Add(packets[0].Mid(0, remaining * 0xff));
                    packets[0]         = packets[0].Mid(remaining * 0xff);
                    lacing_bytes_used += remaining;
                }

                if (lacing_bytes_used == lacing_per_page)
                {
                    pages.Add(new Page(page_packets,
                                       new PageHeader(first_header,
                                                      index, first_packet_continued ?
                                                      PageFlags.FirstPacketContinued :
                                                      PageFlags.None)));
                    page_packets      = new ByteVectorCollection();
                    lacing_bytes_used = 0;
                    index++;
                    count--;
                    first_packet_continued = !whole_packet;
                }
            }

            if (page_packets.Count > 0)
            {
                pages.Add(new Page(page_packets,
                                   new PageHeader(
                                       first_header.StreamSerialNumber,
                                       index, first_packet_continued ?
                                       PageFlags.FirstPacketContinued :
                                       PageFlags.None)));
                index++;
                count--;
            }
            change = -count;
            return(pages.ToArray());
        }
Exemple #6
0
 /// <summary>
 ///    Constructs and intializes a new instance of <see
 ///    cref="Page" /> with a specified header and no packets.
 /// </summary>
 /// <param name="header">
 ///    A <see cref="PageHeader"/> object to use as the header of
 ///    the new instance.
 /// </param>
 protected Page(PageHeader header)
 {
     this.header = header;
     packets     = new ByteVectorCollection();
 }
Exemple #7
0
        /// <summary>
        ///    Overwrites all page headers in a file starting at a
        ///    specified position, shifting the page sequence numbers
        ///    a set amount.
        /// </summary>
        /// <param name="file">
        ///    A <see cref="File" /> object containing the file to
        ///    update.
        /// </param>
        /// <param name="position">
        ///    A <see cref="long" /> value specify at what position to
        ///    start updating.
        /// </param>
        /// <param name="shiftTable">
        ///    A <see cref="T:System.Collections.Generic.IDictionary`2"
        ///    /> object where the key is the serial number of the
        ///    stream to update and the value is the amount to offset
        ///    the page sequence numbers in the stream.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///    <paramref name="file" /> or <paramref name="shiftTable"
        ///    /> is <see langword="null" />.
        /// </exception>
        /// <remarks>
        ///    When the number of pages in a stream changes, all
        ///    subsequent pages in the stream need to have their page
        ///    sequence number update in order to remain valid.
        ///    Additionally, when the page sequence number changes, the
        ///    page needs to have its checksum recomputed. This makes
        ///    for a costly recalculation if large comment data is
        ///    added.
        /// </remarks>
        public static void OverwriteSequenceNumbers(File file,
                                                    long position,
                                                    IDictionary <uint, int> shiftTable)
        {
            if (file == null)
            {
                throw new ArgumentNullException("file");
            }

            if (shiftTable == null)
            {
                throw new ArgumentNullException("shiftTable");
            }

            // Check to see if there are no changes to be made.
            bool done = true;

            foreach (KeyValuePair <uint, int> pair in shiftTable)
            {
                if (pair.Value != 0)
                {
                    done = false;
                    break;
                }
            }

            // If the file is fine, quit.
            if (done)
            {
                return;
            }

            while (position < file.Length - 27)
            {
                PageHeader header = new PageHeader(file, position);
                int        size   = (int)(header.Size + header.DataSize);

                if (shiftTable.ContainsKey(header.StreamSerialNumber) &&
                    shiftTable[header.StreamSerialNumber] != 0)
                {
                    file.Seek(position);
                    ByteVector page_data = file.ReadBlock(size);

                    ByteVector new_data = ByteVector.FromUInt(
                        (uint)(header.PageSequenceNumber +
                               shiftTable[header.StreamSerialNumber]),
                        false);

                    for (int i = 18; i < 22; i++)
                    {
                        page_data[i] = new_data[i - 18];
                    }
                    for (int i = 22; i < 26; i++)
                    {
                        page_data[i] = 0;
                    }

                    new_data.Add(ByteVector.FromUInt(
                                     page_data.Checksum, false));
                    file.Seek(position + 18);
                    file.WriteBlock(new_data);
                }
                position += size;
            }
        }