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 }
// --------------------------------------------------------------------------- private long GetSamples(BinaryReader Source) { int DataIndex; // Using byte instead of char here to avoid mistaking range of bytes for unicode chars byte[] Data = new byte[251]; OggHeader Header = new OggHeader(); // Get total number of samples int result = 0; for (int index = 1; index <= 50; index++) { DataIndex = (int)(Source.BaseStream.Length - (/*Data.Length*/ 251 - 10) * index - 10); Source.BaseStream.Seek(DataIndex, SeekOrigin.Begin); Data = Source.ReadBytes(251); // Get number of PCM samples from last Ogg packet header for (int iterator = 251 - 10; iterator >= 0; iterator--) { char[] tempArray = new char[4] { (char)Data[iterator], (char)Data[iterator + 1], (char)Data[iterator + 2], (char)Data[iterator + 3] }; if (Utils.StringEqualsArr(OGG_PAGE_ID, tempArray)) { Source.BaseStream.Seek(DataIndex + iterator, SeekOrigin.Begin); Header.ID = Source.ReadChars(4); Header.StreamVersion = Source.ReadByte(); Header.TypeFlag = Source.ReadByte(); Header.AbsolutePosition = Source.ReadInt64(); Header.Serial = Source.ReadInt32(); Header.PageNumber = Source.ReadInt32(); Header.Checksum = Source.ReadInt32(); Header.Segments = Source.ReadByte(); Header.LacingValues = Source.ReadBytes(0xFF); return(Header.AbsolutePosition); } } } return(result); }
private void CalculateChecksumAndWriteOggHeaderToBinaryWriter(OggHeader oggHeader, byte[] data) { var oggHeaderBytes = ArrayPool <byte> .Shared.Rent(oggHeader.Size); try { var spanWriter = new SpanWriter(oggHeaderBytes); spanWriter.WriteOggHeader(oggHeader); var checkSum = OggCRC32.CalculateCRC(0, oggHeaderBytes.AsSpan().Slice(0, oggHeader.Size), data); spanWriter.Write(checkSum, OggHeader.CheckSumLocation); _writer.Write(oggHeaderBytes, 0, oggHeader.Size); } finally { ArrayPool <byte> .Shared.Return(oggHeaderBytes); } _writer.Write(data); }
public void WriteOggPage(OggHeaderType oggHeaderType, byte numberOfSegments, List <SegmentEntry> oggPages) { var data = oggPages.SelectMany(o => o.Data).ToArray(); _granulePosition += (ulong)oggPages.Sum(op => op.NumberOfSamples * op.NumberOfFrames); var oggHeader = new OggHeader { StreamVersion = 0, TypeFlag = oggHeaderType, GranulePosition = _granulePosition, Serial = _serial, PageNumber = _page, Checksum = 0, TotalSegments = numberOfSegments, SegmentTable = oggPages.SelectMany(o => o.SegmentBytes).ToArray() }; CalculateChecksumAndWriteOggHeaderToBinaryWriter(oggHeader, data); _writer.Flush(); _page++; }
// 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); }
// --------------------------------------------------------------------------- // Read total samples of OGG file, which are located on the very last page of the file private ulong getSamples(BufferedBinaryReader source) { OggHeader header = new OggHeader(); string headerId; byte typeFlag; byte[] lacingValues = new byte[255]; byte nbLacingValues = 0; long nextPageOffset = 0; // TODO - fine tune seekSize value int seekSize = (int)Math.Round(MAX_PAGE_SIZE * 0.75); if (seekSize > source.Length) { seekSize = (int)Math.Round(source.Length * 0.5); } source.Seek(-seekSize, SeekOrigin.End); if (!StreamUtils.FindSequence(source, Utils.Latin1Encoding.GetBytes(OGG_PAGE_ID))) { LogDelegator.GetLogDelegate()(Log.LV_ERROR, "No OGG header found; aborting read operation"); // Throw exception ? return(0); } source.Seek(-4, SeekOrigin.Current); // Iterate until last page is encountered do { if (source.Position + nextPageOffset + 27 > source.Length) // End of stream about to be reached => last OGG header did not have the proper type flag { break; } source.Seek(nextPageOffset, SeekOrigin.Current); headerId = Utils.Latin1Encoding.GetString(source.ReadBytes(4)); if (headerId.Equals(OGG_PAGE_ID)) { source.Seek(1, SeekOrigin.Current); typeFlag = source.ReadByte(); source.Seek(20, SeekOrigin.Current); nbLacingValues = source.ReadByte(); nextPageOffset = 0; source.Read(lacingValues, 0, nbLacingValues); for (int i = 0; i < nbLacingValues; i++) { nextPageOffset += lacingValues[i]; } } else { LogDelegator.GetLogDelegate()(Log.LV_ERROR, "Invalid OGG header found while looking for total samples; aborting read operation"); // Throw exception ? return(0); } } while (0 == (typeFlag & 0x04)); // 0x04 marks the last page of the logical bitstream // Stream is positioned at the end of the last page header; backtracking to read AbsolutePosition field source.Seek(-nbLacingValues - 21, SeekOrigin.Current); return(source.ReadUInt64()); }
// --------------------------------------------------------------------------- private long GetSamples(BinaryReader Source) { int DataIndex; // Using byte instead of char here to avoid mistaking range of bytes for unicode chars byte[] Data = new byte[251]; OggHeader Header = new OggHeader(); // Get total number of samples int result = 0; for (int index=1; index<=50; index++) { DataIndex = (int)(Source.BaseStream.Length - (/*Data.Length*/251 - 10) * index - 10); Source.BaseStream.Seek(DataIndex, SeekOrigin.Begin); Data = Source.ReadBytes(251); // Get number of PCM samples from last Ogg packet header for (int iterator=251 - 10; iterator>=0; iterator--) { char[] tempArray = new char[4] { (char)Data[iterator], (char)Data[iterator + 1], (char)Data[iterator + 2], (char)Data[iterator + 3] }; if ( Utils.StringEqualsArr(OGG_PAGE_ID,tempArray) ) { Source.BaseStream.Seek(DataIndex + iterator, SeekOrigin.Begin); Header.ID = Source.ReadChars(4); Header.StreamVersion = Source.ReadByte(); Header.TypeFlag = Source.ReadByte(); Header.AbsolutePosition = Source.ReadInt64(); Header.Serial = Source.ReadInt32(); Header.PageNumber = Source.ReadInt32(); Header.Checksum = Source.ReadInt32(); Header.Segments = Source.ReadByte(); Header.LacingValues = Source.ReadBytes(0xFF); return Header.AbsolutePosition; } } } return result; }
public static void Write(this BinaryWriter writer, OggHeader oggHeader) { oggHeader.Write(writer); }