/// <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); }
/// <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); }
/// <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(); }
/// <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; } }
/// <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()); }
/// <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(); }
/// <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; } }