public static void unpackFrames(this Stream stream, ref Blob blob, ref LacedFrames laced) { eBlockFlags lacing = blob.lacing; if (lacing == eBlockFlags.NoLacing) { // laced.frames?.Clear(); laced.lacing = eLacingResult.NoLacing; return; } stream.Seek(blob.position, SeekOrigin.Begin); int lacingHeader; if (blob.length <= maxBuffer) { if (null != laced.buffer) { if (laced.buffer.Length < blob.length) { laced.buffer = new byte[blob.length]; } } else { laced.buffer = new byte[blob.length]; } stream.read(laced.buffer.AsSpan().Slice(0, blob.length)); lacingHeader = laced.buffer[0]; laced.lacing = eLacingResult.Buffered; } else { lacingHeader = stream.ReadByte(); if (lacingHeader < 0) { throw new EndOfStreamException(); } laced.lacing = eLacingResult.Large; } int lacedBlocksCount = lacingHeader + 1; laced.initialize(lacedBlocksCount); switch (lacing) { case eBlockFlags.FixedSize: int cbPayload = blob.length - 1; if (0 != cbPayload % lacedBlocksCount) { throw new ArgumentException($"Error in the MKV file, blob header specifies fixed size lacing with { lacedBlocksCount } laced frames, yet the block payload size { cbPayload } ain’t divisible"); } int cbFrame = cbPayload / lacedBlocksCount; for (int i = 0; i < lacedBlocksCount; i++) { laced.add(1 + cbFrame * i, cbFrame); } return; case eBlockFlags.EBML: if (laced.lacing == eLacingResult.Buffered) { unpackEbmlFromBuffer(lacedBlocksCount, laced.buffer.AsSpan().Slice(0, blob.length), ref laced); } else { unpackEbmlFromStream(lacedBlocksCount, stream, blob.length, ref laced); } break; default: throw new NotImplementedException($"{ lacing } lacing is not implemented"); } }
static void unpackEbmlFromBuffer(int lacedBlocksCount, ReadOnlySpan <byte> buffer, ref LacedFrames laced) { // The count is stored in 1 byte, the absolute max.limit is 256, totally fine for the stack. Span <int> sizes = stackalloc int[lacedBlocksCount]; int lacingHeaderBytes = 1; // The 1 byte is lacedBlocksCount we have already consumed from that buffer int cb; int prevSize = checked ((int)buffer.parseUint4(lacingHeaderBytes, out cb)); lacingHeaderBytes += cb; sizes[0] = prevSize; int combinedSize = prevSize; for (int i = 1; i < lacedBlocksCount - 1; i++) { int difference = checked ((int)buffer.parseUint4(lacingHeaderBytes, out cb)); lacingHeaderBytes += cb; prevSize += rangeShiftSignedInt(difference, cb); sizes[i] = prevSize; combinedSize += prevSize; } // The size of the last frame is deduced from the total size of the Block. sizes[lacedBlocksCount - 1] = buffer.Length - lacingHeaderBytes - combinedSize; int pos = lacingHeaderBytes; for (int i = 0; i < lacedBlocksCount; i++) { laced.add(pos, sizes[i]); pos += sizes[i]; } }
static void unpackEbmlFromStream(int lacedBlocksCount, Stream stream, int blobSize, ref LacedFrames laced) { Span <int> sizes = stackalloc int[lacedBlocksCount]; int lacingHeaderBytes = 1; // The 1 byte is lacedBlocksCount we have already read int cb; int prevSize = checked ((int)stream.readUint4(out cb)); lacingHeaderBytes += cb; sizes[0] = prevSize; int combinedSize = prevSize; for (int i = 1; i < lacedBlocksCount - 1; i++) { int difference = checked ((int)stream.readUint4(out cb)); lacingHeaderBytes += cb; prevSize += rangeShiftSignedInt(difference, cb); sizes[i] = prevSize; combinedSize += prevSize; } // The size of the last frame is deduced from the total size of the Block. sizes[lacedBlocksCount - 1] = blobSize - lacingHeaderBytes - combinedSize; int pos = lacingHeaderBytes; for (int i = 0; i < lacedBlocksCount; i++) { laced.add(pos, sizes[i]); pos += sizes[i]; } }