private void readAllBlocks(BufferedBinaryReader source) { OggHeader header = new OggHeader(); // Start from the 1st page following headers byte typeFlag; byte nbLacingValues; byte[] lacingValues = new byte[255]; long nextPageOffset = 0; ulong lastAbsPosition = 0; source.Seek(info.SetupHeaderEnd, SeekOrigin.Begin); // Iterate until last page is encountered do { source.Seek(nextPageOffset, SeekOrigin.Current); header.ReadFromStream(source); nbLacingValues = header.Segments; typeFlag = header.TypeFlag; nextPageOffset = header.GetPageLength(); System.Console.WriteLine(header.AbsolutePosition - lastAbsPosition + ";" + nextPageOffset); lastAbsPosition = header.AbsolutePosition; } while (0 == (typeFlag & 0x04)); // 0x04 marks the last page of the logical bitstream }
// Specific implementation for OGG container (multiple pages with limited size) // TODO DOC // Simplified implementation of MetaDataIO tweaked for OGG-Vorbis specifics, i.e. // - tag spans over multiple pages, each having its own header // - last page may include whole or part of 3rd Vorbis header (setup header) public bool Write(BinaryReader r, BinaryWriter w, TagData tag) { bool result = true; int writtenPages = 0; long nextPageOffset = 0; // Read all the fields in the existing tag (including unsupported fields) ReadTagParams readTagParams = new ReadTagParams(true, true); readTagParams.PrepareForWriting = true; Read(r, readTagParams); // Get "unpaged" virtual stream to be written, containing the vorbis tag (=comment header) using (MemoryStream stream = new MemoryStream((int)(info.SetupHeaderEnd - info.CommentHeaderStart))) { stream.Write(Utils.Latin1Encoding.GetBytes(VORBIS_TAG_ID), 0, VORBIS_TAG_ID.Length); vorbisTag.Write(stream, tag); long newTagSize = stream.Position; int setupHeaderSize = (int)(info.SetupHeaderEnd - info.SetupHeaderStart); // Append the setup header in the "unpaged" virtual stream r.BaseStream.Seek(info.SetupHeaderStart, SeekOrigin.Begin); if (1 == info.SetupHeaderSpanPages) { StreamUtils.CopyStream(r.BaseStream, stream, setupHeaderSize); } else { // TODO - handle case where initial setup header spans across two pages LogDelegator.GetLogDelegate()(Log.LV_ERROR, "ATL does not yet handle the case where Vorbis setup header spans across two OGG pages"); return(false); } // Construct the entire segments table int commentsHeader_nbSegments = (int)Math.Ceiling(1.0 * newTagSize / 255); byte commentsHeader_remainingBytesInLastSegment = (byte)(newTagSize % 255); int setupHeader_nbSegments = (int)Math.Ceiling(1.0 * setupHeaderSize / 255); byte setupHeader_remainingBytesInLastSegment = (byte)(setupHeaderSize % 255); byte[] entireSegmentsTable = new byte[commentsHeader_nbSegments + setupHeader_nbSegments]; for (int i = 0; i < commentsHeader_nbSegments - 1; i++) { entireSegmentsTable[i] = 255; } entireSegmentsTable[commentsHeader_nbSegments - 1] = commentsHeader_remainingBytesInLastSegment; for (int i = commentsHeader_nbSegments; i < commentsHeader_nbSegments + setupHeader_nbSegments - 1; i++) { entireSegmentsTable[i] = 255; } entireSegmentsTable[commentsHeader_nbSegments + setupHeader_nbSegments - 1] = setupHeader_remainingBytesInLastSegment; int nbPageHeaders = (int)Math.Ceiling((commentsHeader_nbSegments + setupHeader_nbSegments) / 255.0); int totalPageHeadersSize = (nbPageHeaders * 27) + setupHeader_nbSegments + commentsHeader_nbSegments; // Resize the whole virtual stream once and for all to avoid multiple reallocations while repaging stream.SetLength(stream.Position + totalPageHeadersSize); /// Repage comments header & setup header within the virtual stream stream.Seek(0, SeekOrigin.Begin); OggHeader header = new OggHeader() { ID = OGG_PAGE_ID, StreamVersion = info.CommentHeader.StreamVersion, TypeFlag = 0, AbsolutePosition = ulong.MaxValue, Serial = info.CommentHeader.Serial, PageNumber = 1, Checksum = 0 }; int segmentsLeftToPage = commentsHeader_nbSegments + setupHeader_nbSegments; int bytesLeftToPage = (int)newTagSize + setupHeaderSize; int pagedSegments = 0; int pagedBytes = 0; long position; BinaryWriter virtualW = new BinaryWriter(stream); IList <KeyValuePair <long, int> > pageHeaderOffsets = new List <KeyValuePair <long, int> >(); // Repaging while (segmentsLeftToPage > 0) { header.Segments = (byte)Math.Min(255, segmentsLeftToPage); header.LacingValues = new byte[header.Segments]; if (segmentsLeftToPage == header.Segments) { header.AbsolutePosition = 0; // Last header page has its absolutePosition = 0 } Array.Copy(entireSegmentsTable, pagedSegments, header.LacingValues, 0, header.Segments); position = stream.Position; // Push current data to write header StreamUtils.CopySameStream(stream, stream.Position, stream.Position + header.GetHeaderSize(), bytesLeftToPage); stream.Seek(position, SeekOrigin.Begin); pageHeaderOffsets.Add(new KeyValuePair <long, int>(position, header.GetPageLength() + header.GetHeaderSize())); header.WriteToStream(virtualW); stream.Seek(header.GetPageLength(), SeekOrigin.Current); pagedSegments += header.Segments; segmentsLeftToPage -= header.Segments; pagedBytes += header.GetPageLength(); bytesLeftToPage -= header.GetPageLength(); header.PageNumber++; if (0 == header.TypeFlag) { header.TypeFlag = 1; } } writtenPages = header.PageNumber - 1; // Generate CRC32 of created pages uint crc; byte[] data; foreach (KeyValuePair <long, int> kv in pageHeaderOffsets) { crc = 0; stream.Seek(kv.Key, SeekOrigin.Begin); data = new byte[kv.Value]; stream.Read(data, 0, kv.Value); crc = OggCRC32.CalculateCRC(crc, data, (uint)kv.Value); stream.Seek(kv.Key + 22, SeekOrigin.Begin); // Position of CRC within OGG header virtualW.Write(crc); } /// Insert the virtual paged stream into the actual file long oldHeadersSize = info.SetupHeaderEnd - info.CommentHeaderStart; long newHeadersSize = stream.Length; if (newHeadersSize > oldHeadersSize) // Need to build a larger file { StreamUtils.LengthenStream(w.BaseStream, info.CommentHeaderEnd, (uint)(newHeadersSize - oldHeadersSize)); } else if (newHeadersSize < oldHeadersSize) // Need to reduce file size { StreamUtils.ShortenStream(w.BaseStream, info.CommentHeaderEnd, (uint)(oldHeadersSize - newHeadersSize)); } // Rewrite Comment and Setup headers w.BaseStream.Seek(info.CommentHeaderStart, SeekOrigin.Begin); stream.Seek(0, SeekOrigin.Begin); StreamUtils.CopyStream(stream, w.BaseStream); nextPageOffset = info.CommentHeaderStart + stream.Length; } // If the number of written pages is different than the number of previous existing pages, // all the next pages of the file need to be renumbered, and their CRC accordingly recalculated if (writtenPages != info.CommentHeaderSpanPages + info.SetupHeaderSpanPages - 1) { OggHeader header = new OggHeader(); byte[] data; uint crc; do { w.BaseStream.Seek(nextPageOffset, SeekOrigin.Begin); header.ReadFromStream(r); if (header.IsValid()) { // Rewrite page number writtenPages++; w.BaseStream.Seek(nextPageOffset + 18, SeekOrigin.Begin); w.Write(writtenPages); // Rewrite CRC w.BaseStream.Seek(nextPageOffset, SeekOrigin.Begin); data = new byte[header.GetHeaderSize() + header.GetPageLength()]; r.Read(data, 0, data.Length); // Checksum has to include its own location, as if it were 0 data[22] = 0; data[23] = 0; data[24] = 0; data[25] = 0; crc = OggCRC32.CalculateCRC(0, data, (uint)data.Length); r.BaseStream.Seek(nextPageOffset + 22, SeekOrigin.Begin); // Position of CRC within OGG header w.Write(crc); // To the next header nextPageOffset += data.Length; } else { LogDelegator.GetLogDelegate()(Log.LV_ERROR, "Invalid OGG header found; aborting writing operation"); // Throw exception ? return(false); } } while (0 == (header.TypeFlag & 0x04)); // 0x04 marks the last page of the logical bitstream } return(result); }