Пример #1
0
        public OggPage(ReadOnlyMemory <byte> data, int pageIndex, ulong granulePosition, int serialNumber, OggPageFlags flags = OggPageFlags.None)
        {
            //Create a header
            using (MemoryStream mem = new MemoryStream())
                using (BinaryWriter writer = new BinaryWriter(mem))
                {
                    writer.Write(Encoding.ASCII.GetBytes(Magic));
                    writer.Write(OggVersion);

                    writer.Write((byte)flags);

                    writer.Write(granulePosition);

                    writer.Write(serialNumber);

                    writer.Write(pageIndex);

                    long checksumPos = mem.Position;
                    writer.Write((int)0); //checksum

                    byte tableLength = (byte)(Math.Ceiling(data.Length / 255.0));

                    if (data.Length % 255 == 0)
                    {
                        //edge case which needs to add an extra byte
                        tableLength++;
                    }

                    writer.Write(tableLength);

                    int length = data.Length;
                    while (length > 254)
                    {
                        writer.Write((byte)255);
                        length -= 255;
                    }

                    writer.Write((byte)length);

                    long headerLength = mem.Position;

                    writer.Write(data.Span);

                    mem.Position = 0;
                    uint crc = OggCRC32.Compute(mem);

                    mem.Position = checksumPos;
                    writer.Write(crc);

                    //cut off the data
                    mem.SetLength(headerLength);

                    Header = mem.ToArray();
                    Data   = data;
                }
        }
Пример #2
0
        // 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);
        }