Esempio n. 1
0
        public static FileType GetFileType(FileInfo file)
        {
            byte[] markerMKV = new byte[] { 0x1A, 0x45, 0xDF, 0xA3 };
            byte[] markerAVI = new byte[] { 0x52, 0x49, 0x46, 0x46 };
            byte[] markerRAR = new byte[] { 0x52, 0x61, 0x72, 0x21 };

            byte[] fileHeader = new byte[4];
            using (FileStream fs = file.OpenRead())
                fs.Read(fileHeader, 0, 4);

            if (ByteArrayComparer.AreEqual(fileHeader, markerRAR))
            {
                using (RarStream rs = new RarStream(file.FullName))
                    rs.Read(fileHeader, 0, 4);
            }

            if (ByteArrayComparer.AreEqual(fileHeader, markerMKV))
            {
                return(FileType.MKV);
            }
            else if (ByteArrayComparer.AreEqual(fileHeader, markerAVI))
            {
                return(FileType.AVI);
            }
            else
            {
                return(FileType.Unknown);
            }
        }
Esempio n. 2
0
        public static void FindSampleStreams(SortedList <int, TrackData> tracks, FileInfo inFile)
        {
            Stream fs;

            if (RarFileNameComparer.IsRarFile(inFile.Name))
            {
                fs = new RarStream(inFile.FullName);
            }
            else
            {
                fs = inFile.OpenRead();
            }

            using (EbmlReader rdr = new EbmlReader(fs, EbmlReadMode.MKV))
            {
                int  clustercount = 0;
                bool done         = false;
                while (rdr.Read() && !done)
                {
                    switch (rdr.ElementType)
                    {
                    case EbmlElementType.Segment:
                    case EbmlElementType.BlockGroup:
                        rdr.MoveToChild();
                        break;

                    case EbmlElementType.Cluster:
                        // simple progress indicator since this can take a while (cluster is good because they're about 1mb each)
                        Console.Write("\b{0}", Program.spinners[clustercount++ % Program.spinners.Length]);
                        rdr.MoveToChild();
                        break;

                    case EbmlElementType.Block:
                        if (!tracks.ContainsKey(rdr.Block.TrackNumber))
                        {
                            tracks.Add(rdr.Block.TrackNumber, new TrackData()
                            {
                                TrackNumber = (ushort)rdr.Block.TrackNumber
                            });
                        }

                        TrackData track = tracks[rdr.Block.TrackNumber];

                        // it's possible the sample didn't require or contain data for all tracks in the main file
                        //  if that happens, we obviously don't want to try to match the data
                        if (track.SignatureBytes != null && (track.MatchOffset == 0 || track.CheckBytes.Length < track.SignatureBytes.Length))
                        {
                            // here, the data we're looking for might not start in the first frame (lace) of the block, so we need to check them all
                            byte[] buff   = rdr.ReadContents();
                            int    offset = 0;
                            for (int i = 0; i < rdr.Block.FrameLengths.Length; i++)
                            {
                                if (track.CheckBytes != null && track.CheckBytes.Length < track.SignatureBytes.Length)
                                {
                                    byte[] checkBytes = new byte[Math.Min(track.SignatureBytes.Length, rdr.Block.FrameLengths[i] + track.CheckBytes.Length)];
                                    Buffer.BlockCopy(track.CheckBytes, 0, checkBytes, 0, track.CheckBytes.Length);
                                    Buffer.BlockCopy(buff, offset, checkBytes, track.CheckBytes.Length, checkBytes.Length - track.CheckBytes.Length);

                                    if (ByteArrayComparer.AreEqual(track.SignatureBytes, checkBytes, checkBytes.Length))
                                    {
                                        track.CheckBytes = checkBytes;
                                    }
                                    else
                                    {
                                        // it was only a partial match.  start over
                                        track.CheckBytes  = null;
                                        track.MatchOffset = 0;
                                        track.MatchLength = 0;
                                    }
                                }

                                // this is a bit weird, but if we had a false positive match going and discovered it above, we check this frame again
                                //  to see if it's the start of a new match (rare problem, but it can happen with subtitles especially)
                                if (track.CheckBytes == null)
                                {
                                    byte[] checkBytes = new byte[Math.Min(track.SignatureBytes.Length, rdr.Block.FrameLengths[i])];
                                    Buffer.BlockCopy(buff, offset, checkBytes, 0, checkBytes.Length);

                                    if (ByteArrayComparer.AreEqual(track.SignatureBytes, checkBytes, checkBytes.Length))
                                    {
                                        track.CheckBytes  = checkBytes;
                                        track.MatchOffset = rdr.Block.ElementStartPos + rdr.Block.RawHeader.Length + rdr.Block.RawBlockHeader.Length + offset;
                                        track.MatchLength = Math.Min(track.DataLength, rdr.Block.FrameLengths[i]);
                                    }
                                }
                                else
                                {
                                    track.MatchLength += Math.Min(track.DataLength - track.MatchLength, rdr.Block.FrameLengths[i]);
                                }

                                offset += rdr.Block.FrameLengths[i];
                            }
                        }
                        else if (track.MatchLength < track.DataLength)
                        {
                            track.MatchLength += Math.Min(track.DataLength - track.MatchLength, rdr.Element.Length);
                            rdr.SkipContents();

                            bool tracksDone = true;
                            foreach (TrackData t in tracks.Values)
                            {
                                if (t.MatchLength < t.DataLength)
                                {
                                    tracksDone = false;
                                    break;
                                }
                            }
                            done = tracksDone;
                        }
                        else
                        {
                            rdr.SkipContents();
                        }
                        break;

                    default:
                        rdr.SkipContents();
                        break;
                    }
                }
            }

            Console.Write('\b');
        }
Esempio n. 3
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);
        }
Esempio n. 4
0
        public static void FindSampleStreams(SortedList <int, TrackData> tracks, FileInfo inFile)
        {
            Stream fs;

            if (RarFileNameComparer.IsRarFile(inFile.Name))
            {
                fs = new RarStream(inFile.FullName);
            }
            else
            {
                fs = inFile.OpenRead();
            }

            using (RiffReader rdr = new RiffReader(fs, RiffReadMode.AVI))
            {
                int  blockcount = 0;
                bool done       = false;
                while (rdr.Read() && !done)
                {
                    if (rdr.ChunkType == RiffChunkType.List)
                    {
                        rdr.MoveToChild();
                    }
                    else                     // normal chunk
                    {
                        if (rdr.ChunkType == RiffChunkType.Movi)
                        {
                            if (++blockcount % 15 == 0)
                            {
                                Console.Write("\b{0}", Program.spinners[blockcount % Program.spinners.Length]);
                            }

                            int trackno = rdr.MoviChunk.StreamNumber;
                            if (!tracks.ContainsKey(trackno))
                            {
                                tracks.Add(trackno, new TrackData());
                            }

                            TrackData track = tracks[trackno];
                            track.TrackNumber = (byte)trackno;

                            if (track.MatchOffset == 0 || track.CheckBytes.Length < track.SignatureBytes.Length)
                            {
                                // it's possible the sample didn't require or contain data for all tracks in the main file
                                //  if that happens, we obviously don't want to try to match the data
                                if (track.SignatureBytes != null)
                                {
                                    if (track.CheckBytes != null && track.CheckBytes.Length < track.SignatureBytes.Length)
                                    {
                                        byte[] checkBytes = new byte[Math.Min(track.SignatureBytes.Length, rdr.MoviChunk.Length + track.CheckBytes.Length)];
                                        track.CheckBytes.CopyTo(checkBytes, 0);
                                        Buffer.BlockCopy(rdr.ReadContents(), 0, checkBytes, track.CheckBytes.Length, checkBytes.Length - track.CheckBytes.Length);

                                        if (ByteArrayComparer.AreEqual(track.SignatureBytes, checkBytes, checkBytes.Length))
                                        {
                                            track.CheckBytes = checkBytes;
                                        }
                                        else
                                        {
                                            // it was only a partial match.  start over
                                            track.CheckBytes  = null;
                                            track.MatchOffset = 0;
                                            track.MatchLength = 0;
                                        }
                                    }

                                    // this is a bit weird, but if we had a false positive match going and discovered it above, we check this frame again
                                    //  to see if it's the start of a new match (probably will never happen with AVI, but it does in MKV, so just in case...)
                                    if (track.CheckBytes == null)
                                    {
                                        byte[] chunkBytes = rdr.ReadContents();

                                        byte searchByte = track.SignatureBytes[0];
                                        int  foundPos   = -1;
                                        while ((foundPos = Array.IndexOf <byte>(chunkBytes, searchByte, foundPos + 1)) > -1)
                                        {
                                            byte[] checkBytes = new byte[Math.Min(track.SignatureBytes.Length, chunkBytes.Length - foundPos)];
                                            Buffer.BlockCopy(chunkBytes, foundPos, checkBytes, 0, checkBytes.Length);

                                            if (ByteArrayComparer.AreEqual(track.SignatureBytes, checkBytes, checkBytes.Length))
                                            {
                                                track.CheckBytes  = checkBytes;
                                                track.MatchOffset = rdr.Chunk.ChunkStartPos + rdr.Chunk.RawHeader.Length + foundPos;
                                                track.MatchLength = Math.Min(track.DataLength, chunkBytes.Length - foundPos);
                                                break;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        track.MatchLength += Math.Min(track.DataLength - track.MatchLength, rdr.MoviChunk.Length);
                                    }
                                }
                            }
                            else if (track.MatchLength < track.DataLength)
                            {
                                track.MatchLength += Math.Min(track.DataLength - track.MatchLength, rdr.MoviChunk.Length);

                                bool tracksDone = true;
                                foreach (TrackData t in tracks.Values)
                                {
                                    if (t.MatchLength < t.DataLength)
                                    {
                                        tracksDone = false;
                                        break;
                                    }
                                }
                                done = tracksDone;
                            }

                            rdr.SkipContents();
                        }
                        else
                        {
                            rdr.SkipContents();
                        }
                    }
                }
            }

            Console.Write('\b');
        }