Beispiel #1
0
        public static int[] GetBlockFrameLengths(EbmlLaceType laceType, int dataLength, Stream stm, out int bytesConsumed)
        {
            // Matroska uses 'lacing' to store more than one frame of data in a single block, thereby saving the overhead of a full block per frame
            //  this method determines the length of each frame so they can be re-separated and searched.  See the Matroska specs for details...
            bytesConsumed = 0;
            int laceFrameCount = 1;

            if (laceType != EbmlLaceType.None)
            {
                laceFrameCount = stm.ReadByte() + 1;
                bytesConsumed++;
            }

            int[] frameSizes = new int[laceFrameCount];
            for (int i = 0; i < laceFrameCount; i++)
            {
                if (laceType == EbmlLaceType.None)
                {
                    frameSizes[i] = dataLength;
                }
                else if (laceType == EbmlLaceType.Fixed)
                {
                    frameSizes[i] = dataLength / laceFrameCount;
                }
                else if (laceType == EbmlLaceType.Xiph)
                {
                    if (i < laceFrameCount - 1)
                    {
                        int nextByte;
                        do
                        {
                            nextByte = stm.ReadByte();
                            bytesConsumed++;
                            frameSizes[i] += nextByte;
                        } while (nextByte == 0xFF);
                    }
                    else
                    {
                        frameSizes[i] = dataLength - bytesConsumed;
                        for (int j = 0; j < i; j++)
                        {
                            frameSizes[i] -= frameSizes[j];
                        }
                    }
                }
                else                 // EbmlLaceType.Ebml
                {
                    int bc = 0;

                    if (i == 0)
                    {
                        frameSizes[i] = (int)GetEbmlUInt(stm, out bc);
                    }
                    else if (i < laceFrameCount - 1)
                    {
                        // convert UInt to SInt then add to previous
                        int len = (int)GetEbmlUInt(stm, out bc);
                        len          -= ((1 << (bc * 8 - (bc + 1))) - 1);
                        frameSizes[i] = frameSizes[i - 1] + len;
                    }
                    else
                    {
                        frameSizes[i] = dataLength - bytesConsumed;
                        for (int j = 0; j < i; j++)
                        {
                            frameSizes[i] -= frameSizes[j];
                        }
                    }

                    bytesConsumed += bc;
                }
            }

            return(frameSizes);
        }
Beispiel #2
0
        public bool Read()
        {
            Debug.Assert(readReady || (mode == EbmlReadMode.SRS && ElementType == EbmlElementType.Block), "Read() is invalid at this time", "MoveToChild(), ReadContents(), or SkipContents() must be called before Read() can be called again");

            long elementStartPos      = ebmlStream.Position;
            byte idLengthDescriptor   = 0;
            byte dataLengthDescriptor = 0;

            if (elementStartPos + 2 > fileLength)
            {
                return(false);
            }

            currentElement = null;
            readReady      = false;

            idLengthDescriptor = (byte)ebmlStream.ReadByte();
            elementHeader[0]   = idLengthDescriptor;
            idLengthDescriptor = EbmlHelper.GetUIntLength(idLengthDescriptor);
            ebmlStream.Read(elementHeader, 1, idLengthDescriptor - 1);
            dataLengthDescriptor = (byte)ebmlStream.ReadByte();
            elementHeader[idLengthDescriptor] = dataLengthDescriptor;
            dataLengthDescriptor = EbmlHelper.GetUIntLength(dataLengthDescriptor);
            ebmlStream.Read(elementHeader, idLengthDescriptor + 1, dataLengthDescriptor - 1);

            // these comparisons are ordered by the frequency with which they will be encountered to avoid unnecessary processing
            if (ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.Block, idLengthDescriptor) || ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.SimpleBlock, idLengthDescriptor))
            {
                ElementType = EbmlElementType.Block;
            }
            else if (ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.BlockGroup, idLengthDescriptor))
            {
                ElementType = EbmlElementType.BlockGroup;
            }
            else if (ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.Cluster, idLengthDescriptor))
            {
                ElementType = EbmlElementType.Cluster;
            }
            else if (ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.Timecode, idLengthDescriptor))
            {
                ElementType = EbmlElementType.Timecode;
            }
            else if (ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.Segment, idLengthDescriptor))
            {
                ElementType = EbmlElementType.Segment;
            }
            else if (ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.TimecodeScale, idLengthDescriptor))
            {
                ElementType = EbmlElementType.TimecodeScale;
            }
            else if (ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.Crc32, idLengthDescriptor))
            {
                ElementType = EbmlElementType.Crc32;
            }
            else if (ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.AttachmentList, idLengthDescriptor))
            {
                ElementType = EbmlElementType.AttachmentList;
            }
            else if (ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.Attachment, idLengthDescriptor))
            {
                ElementType = EbmlElementType.Attachment;
            }
            else if (ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.AttachedFileName, idLengthDescriptor))
            {
                ElementType = EbmlElementType.AttachedFileName;
            }
            else if (ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.AttachedFileData, idLengthDescriptor))
            {
                ElementType = EbmlElementType.AttachedFileData;
            }
            else if (mode == EbmlReadMode.SRS && ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.ReSample, idLengthDescriptor))
            {
                ElementType = EbmlElementType.ReSample;
            }
            else if (mode == EbmlReadMode.SRS && ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.ReSampleFile, idLengthDescriptor))
            {
                ElementType = EbmlElementType.ReSampleFile;
            }
            else if (mode == EbmlReadMode.SRS && ByteArrayComparer.AreEqual(elementHeader, EbmlElementIDs.ReSampleTrack, idLengthDescriptor))
            {
                ElementType = EbmlElementType.ReSampleTrack;
            }
            else
            {
                ElementType = EbmlElementType.Unknown;
            }

            long elementLength = EbmlHelper.GetEbmlUInt(elementHeader, idLengthDescriptor, dataLengthDescriptor);

            // sanity check on element length.  skip check on Segment element so we can still report expected size.  this is only applied on samples since a partial movie might still be useful
            long endOffset = elementStartPos + idLengthDescriptor + dataLengthDescriptor + elementLength;

            if (mode == EbmlReadMode.Sample && ElementType != EbmlElementType.Segment && endOffset > fileLength)
            {
                throw new InvalidDataException(string.Format("Invalid element length at 0x{0:x8}", elementStartPos));
            }

            if (ElementType != EbmlElementType.Block)
            {
                byte[] rawHeader = new byte[idLengthDescriptor + dataLengthDescriptor];
                Buffer.BlockCopy(elementHeader, 0, rawHeader, 0, idLengthDescriptor + dataLengthDescriptor);
                currentElement = new EbmlElement()
                {
                    ElementStartPos = elementStartPos, RawHeader = rawHeader, Length = elementLength
                };
            }
            else
            {
                // first thing in the block is the track number
                byte   trackDescriptor = (byte)ebmlStream.ReadByte();
                byte[] blockHeader     = new byte[4];
                blockHeader[0]  = trackDescriptor;
                trackDescriptor = EbmlHelper.GetUIntLength(trackDescriptor);

                // incredibly unlikely the track number is > 1 byte, but just to be safe...
                if (trackDescriptor > 1)
                {
                    byte[] newBlockHeader = new byte[trackDescriptor + 3];
                    newBlockHeader[0] = blockHeader[0];
                    ebmlStream.Read(newBlockHeader, 1, trackDescriptor - 1);
                    blockHeader = newBlockHeader;
                }

                int trackno = (int)EbmlHelper.GetEbmlUInt(blockHeader, 0, trackDescriptor);

                // read in time code (2 bytes) and flags (1 byte)
                ebmlStream.Read(blockHeader, trackDescriptor, 3);
                short timecode = (short)((blockHeader[blockHeader.Length - 3] << 8) + blockHeader[blockHeader.Length - 2]);

                // need to grab the flags (last byte of the header) to check for lacing
                EbmlLaceType laceType = (EbmlLaceType)(blockHeader[blockHeader.Length - 1] & (byte)EbmlLaceType.Ebml);

                int   dataLength = (int)elementLength - blockHeader.Length;
                int   bytesConsumed;
                int[] frameSizes = EbmlHelper.GetBlockFrameLengths(laceType, dataLength, ebmlStream, out bytesConsumed);
                if (bytesConsumed > 0)
                {
                    byte[] newBlockHeader = new byte[blockHeader.Length + bytesConsumed];
                    Buffer.BlockCopy(blockHeader, 0, newBlockHeader, 0, blockHeader.Length);
                    ebmlStream.Seek(-bytesConsumed, SeekOrigin.Current);
                    ebmlStream.Read(newBlockHeader, blockHeader.Length, bytesConsumed);
                    blockHeader = newBlockHeader;
                }

                elementLength -= blockHeader.Length;

                byte[] rawHeader = new byte[idLengthDescriptor + dataLengthDescriptor];
                Buffer.BlockCopy(elementHeader, 0, rawHeader, 0, idLengthDescriptor + dataLengthDescriptor);
                currentElement = new BlockElement()
                {
                    ElementStartPos = elementStartPos, RawHeader = rawHeader, Length = elementLength, FrameLengths = frameSizes, TrackNumber = trackno, Timecode = timecode, RawBlockHeader = blockHeader
                };
            }

            // the following line will write mkvinfo-like output from the parser (extremely useful for debugging)
            //Console.WriteLine("{0}: {1} bytes @ {2}", ElementType, elementLength, elementStartPos);

            return(true);
        }