Esempio n. 1
0
        public override IEnumerable <Container.Track> GetTracks()
        {
            //For each packet a PID and StreamId was extracted.
            foreach (var pid in m_ProgramIds)
            {
                Sdp.MediaType mediaType = Sdp.MediaType.unknown;

                byte streamId = (byte)pid;

                if (Mpeg.StreamTypes.IsMpeg1or2AudioStream(streamId))
                {
                    mediaType = Sdp.MediaType.audio;
                }
                else if (Mpeg.StreamTypes.IsMpeg1or2VideoStream(streamId))
                {
                    mediaType = Sdp.MediaType.video;
                }
                else
                {
                    continue;  //Don't show unknown streams for now. (Could filter by Null only but then tables would be included, could also skip tables with a IsTable function...)
                }
                //Program name could be retieved if present

                //Todo track samples can be counted by adding logic in GetEnumerator..

                //Start and stop times should be defined already, just not stored, probably in a table.

                //Codec indication comes from the streamId or a table...

                yield return(new Container.Track(null, string.Empty, streamId, FileInfo.CreationTimeUtc, FileInfo.LastWriteTimeUtc, 0, 0, 0, TimeSpan.Zero, TimeSpan.Zero, 0, mediaType, BitConverter.GetBytes(0)));
            }
        }
Esempio n. 2
0
        public override IEnumerable <Container.Track> GetTracks()
        {
            //Use the StreamBound entries in the SystemsHeader to determine what media is contained.
            foreach (var entry in m_StreamBoundEntries)
            {
                Sdp.MediaType mediaType = Sdp.MediaType.unknown;

                switch (entry.Key)
                {
                case 0xB8:     //All Audio
                {
                    mediaType = Sdp.MediaType.audio;
                    break;
                }

                case 0xB9:     //All Video
                {
                    mediaType = Sdp.MediaType.video;
                    break;
                }

                case Mpeg.StreamTypes.PrivateStream1:     //0xBD
                {
                    //AC3 SubId From 0x80 to 0x87
                    //DTS SubId From 0x88 to 0x8F
                    //LPCM SubId From 0xAD to 0xA7
                    //Subtitles SubId From 0x20 to 0x3F
                    break;
                }

                default:
                {
                    if (Mpeg.StreamTypes.IsMpeg1or2AudioStream(entry.Key))
                    {
                        mediaType = Sdp.MediaType.audio;
                    }
                    else if (Mpeg.StreamTypes.IsMpeg1or2VideoStream(entry.Key))
                    {
                        mediaType = Sdp.MediaType.video;
                    }
                    break;
                }
                }

                //Give the System Header as the 'header'
                yield return(new Container.Track(entry.Value.Item3, string.Empty, entry.Key, DateTime.MinValue, DateTime.MinValue, 0, 0, 0, TimeSpan.Zero, TimeSpan.Zero, 0, mediaType, BitConverter.GetBytes(0)));
            }
        }
Esempio n. 3
0
        public override IEnumerable <Track> GetTracks()
        {
            if (m_Tracks != null)
            {
                foreach (Track track in m_Tracks)
                {
                    yield return(track);
                }
                yield break;
            }

            long position = Position;

            var tracks = new List <Track>();

            int trackId = 0;

            if (SubType == FourCharacterCode.WAVE)
            {
                if (false == m_SampleRate.HasValue)
                {
                    ParseFmt();
                }
                Track track = new Track(ReadChunk(FourCharacterCode.data), string.Empty, (int)(Length / BlockAlign), FileInfo.CreationTimeUtc, FileInfo.LastWriteTimeUtc,
                                        BlockAlign, 0, 0, TimeSpan.Zero, TimeSpan.FromSeconds(Length / (SampleRate * Channels * BitsPerSample / Common.Binary.BitsPerByte)),
                                        m_SampleRate.Value, Sdp.MediaType.audio, Common.Binary.GetBytes(m_Format.Value, Common.Binary.IsBigEndian), (byte)m_NumChannels.Value, (byte)m_BitsPerSample.Value,
                                        true);
                tracks.Add(track);
                yield return(track);
            }
            else
            {
                //strh has all track level info, strn has stream name..
                foreach (var strhChunk in ReadChunks(Root.Offset, FourCharacterCode.strh).ToArray())
                {
                    int offset = 0, sampleCount = TotalFrames, startTime = 0, timeScale = 0, duration = (int)Duration.TotalMilliseconds, width = Width, height = Height, rate = MicrosecondsPerFrame;

                    string trackName = string.Empty;

                    Sdp.MediaType mediaType = Sdp.MediaType.unknown;

                    byte[] codecIndication = Media.Common.MemorySegment.EmptyBytes;

                    byte channels = 0, bitDepth = 0;

                    //Expect 56 Bytes

                    FourCharacterCode fccType = (FourCharacterCode)Common.Binary.Read32(strhChunk.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 4;

                    switch (fccType)
                    {
                    case FourCharacterCode.iavs:
                    {
                        //Interleaved Audio and Video
                        //Should be audio and video samples together....?
                        //Things like this need a Special TrackType, MediaType doens't really cut it.
                        break;
                    }

                    case FourCharacterCode.vids:
                    {
                        //avg_frame_rate = timebase
                        mediaType = Sdp.MediaType.video;

                        sampleCount = ReadChunks(Root.Offset, ToFourCC(trackId.ToString("D2") + FourCharacterCode.dc.ToString()),
                                                 ToFourCC(trackId.ToString("D2") + FourCharacterCode.db.ToString())).Count();
                        break;
                    }

                    case FourCharacterCode.mids:     //Midi
                    case FourCharacterCode.auds:
                    {
                        mediaType = Sdp.MediaType.audio;

                        sampleCount = ReadChunks(Root.Offset, ToFourCC(trackId.ToString("D2") + FourCharacterCode.wb.ToString())).Count();

                        break;
                    }

                    case FourCharacterCode.txts:
                    {
                        sampleCount = ReadChunks(Root.Offset, ToFourCC(trackId.ToString("D2") + FourCharacterCode.tx.ToString())).Count();
                        mediaType   = Sdp.MediaType.text; break;
                    }

                    case FourCharacterCode.data:
                    {
                        mediaType = Sdp.MediaType.data; break;
                    }

                    default: break;
                    }

                    //fccHandler
                    codecIndication = strhChunk.Data.Skip(offset).Take(4).ToArray();

                    offset += 4 + (DWORDSIZE * 3);

                    //Scale
                    timeScale = Common.Binary.Read32(strhChunk.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 4;

                    //Rate
                    rate = Common.Binary.Read32(strhChunk.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 4;

                    //Defaults??? Should not be hard coded....
                    if (false == (timeScale > 0 && rate > 0))
                    {
                        rate      = 25;
                        timeScale = 1;
                    }

                    //Start
                    startTime = Common.Binary.Read32(strhChunk.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 4;

                    //Length of stream (as defined in rate and timeScale above)
                    duration = Common.Binary.Read32(strhChunk.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 4;

                    //SuggestedBufferSize

                    //Quality

                    //SampleSize

                    //RECT rcFrame (ushort left, top, right, bottom)

                    //Get strf for additional info.

                    switch (mediaType)
                    {
                    case Sdp.MediaType.video:
                    {
                        using (var strf = ReadChunk(FourCharacterCode.strf, strhChunk.Offset))
                        {
                            if (strf != null)
                            {
                                //BitmapInfoHeader
                                //Read 32 Width
                                width = (int)Common.Binary.ReadU32(strf.Data, 4, Media.Common.Binary.IsBigEndian);

                                //Read 32 Height
                                height = (int)Common.Binary.ReadU32(strf.Data, 8, Media.Common.Binary.IsBigEndian);

                                //Maybe...
                                //Read 16 panes

                                //Read 16 BitDepth
                                bitDepth = (byte)(int)Common.Binary.ReadU16(strf.Data, 14, Media.Common.Binary.IsBigEndian);

                                //Read codec
                                codecIndication = strf.Data.Skip(16).Take(4).ToArray();
                            }
                        }

                        break;
                    }

                    case Sdp.MediaType.audio:
                    {
                        //Expand Codec Indication based on iD?

                        using (var strf = ReadChunk(FourCharacterCode.strf, strhChunk.Offset))
                        {
                            if (strf != null)
                            {
                                //WaveFormat (EX)
                                codecIndication = strf.Data.Take(2).ToArray();
                                channels        = (byte)Common.Binary.ReadU16(strf.Data, 2, Media.Common.Binary.IsBigEndian);
                                bitDepth        = (byte)Common.Binary.ReadU16(strf.Data, 4, Media.Common.Binary.IsBigEndian);
                            }
                        }


                        break;
                    }

                    //text format....
                    default: break;
                    }

                    using (var strn = ReadChunk(FourCharacterCode.strn, strhChunk.Offset))
                    {
                        if (strn != null)
                        {
                            trackName = Encoding.UTF8.GetString(strn.Data, 8, (int)(strn.DataSize - 8));
                        }

                        //Variable BitRate must also take into account the size of each chunk / nBlockAlign * duration per frame.

                        Track created = new Track(strhChunk, trackName, ++trackId, Created, Modified, sampleCount, height, width,
                                                  TimeSpan.FromMilliseconds(startTime / timeScale),
                                                  mediaType == Sdp.MediaType.audio ?
                                                  TimeSpan.FromSeconds((double)duration / (double)rate) :
                                                  TimeSpan.FromMilliseconds((double)duration * m_MicroSecPerFrame.Value / (double)Media.Common.Extensions.TimeSpan.TimeSpanExtensions.MicrosecondsPerMillisecond),
                                                  rate / timeScale, mediaType, codecIndication, channels, bitDepth);

                        yield return(created);

                        tracks.Add(created);
                    }
                }
            }

            m_Tracks = tracks;

            Position = position;
        }
Esempio n. 4
0
        public override IEnumerable <Track> GetTracks()
        {
            if (m_Tracks != null)
            {
                foreach (Track track in m_Tracks)
                {
                    yield return(track);
                }
                yield break;
            }

            var tracks = new List <Track>();

            long position = Position;

            int trackId = 0;

            foreach (var asfObject in ReadObjects(Root.DataOffset, Identifier.StreamPropertiesObject).ToArray())
            {
                ulong sampleCount = 0, startTime = (ulong)PreRoll.TotalMilliseconds, timeScale = 1, duration = (ulong)Duration.TotalMilliseconds, width = 0, height = 0, rate = 0;

                string trackName = string.Empty;

                Sdp.MediaType mediaType = Sdp.MediaType.unknown;

                byte[] codecIndication = Media.Common.MemorySegment.EmptyBytes;

                byte channels = 0, bitDepth = 0;

                int offset = 0;

                //Would keep as GUID but can't switch on the Guid
                string mediaTypeName = ToTextualConvention(asfObject.Data, offset);//, noCorrection;

                offset += IdentifierSize * 2;

                //stream.Read(buffer, 0, IdentifierSize);

                //noCorrection = ToTextualConvention(buffer, 0);

                //if (noCorrection != "ASFNoErrorCorrection") throw new InvalidOperationException("Invalid ASFStreamPropertiesObject");

                //TimeOffset
                startTime = Common.Binary.ReadU64(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                offset += 8;

                int typeSpecDataLen, eccDataLen;

                typeSpecDataLen = Common.Binary.Read32(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                offset += 4;

                eccDataLen = Common.Binary.Read32(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                offset += 4;

                short flags = Common.Binary.Read16(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                offset += 2;

                trackId = (flags & 0x7f);

                bool encrypted = (flags & 0x8000) == 1;

                //Reserved
                offset += 4;

                if (asfObject.DataSize - offset < eccDataLen + typeSpecDataLen)
                {
                    throw new InvalidOperationException("Invalid ASFStreamPropertiesObject");
                }

                //Position At TypeSpecificData

                switch (mediaTypeName)
                {
                case "VideoMedia":
                {
                    //Read 32
                    //Read 32
                    //Read 8
                    //Read 16 SizeX
                    //Read 32 BytesPer BitmapInfoHeader
                    offset += 15;

                    mediaType = Sdp.MediaType.video;

                    //Read 32 Width
                    width = Common.Binary.ReadU32(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 4;

                    //Read 32 Height
                    height = Common.Binary.ReadU32(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 6;


                    //Maybe...
                    //Read 16 panes

                    //Read 16 BitDepth
                    bitDepth = (byte)Common.Binary.ReadU16(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 2;

                    codecIndication = asfObject.Data.Skip(offset).Take(4).ToArray();

                    offset += 4;

                    //32 image_size
                    //32 horizontal_pixels_per_meter
                    //32 vertical_pixels_per_meter
                    //32 used_colors_count
                    //32 important_colors_count

                    //Codec Specific Data (Varies)

                    break;
                }

                case "AudioMedia":
                {
                    mediaType = Sdp.MediaType.audio;
                    //WaveHeader ... Used also in RIFF
                    //16 format_tag
                    codecIndication = asfObject.Data.Skip(offset).Take(2).ToArray();
                    //Expand Codec Indication based on iD?

                    offset += 2;

                    //16 number_channels
                    channels = (byte)Common.Binary.ReadU16(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);
                    offset  += 2;

                    //32 samples_per_second
                    rate = Common.Binary.ReadU32(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 4;

                    //32 average_bytes_per_second
                    //16 block_alignment
                    offset += 6;

                    //16 bits_per_sample
                    bitDepth = (byte)Common.Binary.ReadU16(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);
                    break;
                }

                case "CommandMedia":
                {
                    mediaType = Sdp.MediaType.control;
                    break;
                }

                case "DegradableJPEGMedia":
                {
                    //Read 32 Width
                    width = Common.Binary.ReadU32(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 4;

                    //Read 32 Height
                    height = Common.Binary.ReadU32(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 4;

                    //Reserved32

                    mediaType       = Sdp.MediaType.video;
                    codecIndication = Encoding.UTF8.GetBytes("JFIF");
                    break;
                }

                case "JFIFMedia":
                {
                    //Read 32 Width
                    width = Common.Binary.ReadU32(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 4;

                    //Read 32 Height
                    height = Common.Binary.ReadU32(asfObject.Data, offset, Media.Common.Binary.IsBigEndian);

                    offset += 4;

                    //Reserved16

                    //Reserved16

                    //Reserved16

                    //InterchangeDataLength
                    //InterchangeData

                    mediaType       = Sdp.MediaType.video;
                    codecIndication = Encoding.UTF8.GetBytes("JFIF");

                    mediaType       = Sdp.MediaType.video;
                    codecIndication = Encoding.UTF8.GetBytes("JFIF");
                    break;
                }

                case "FileTransferMedia":
                case "BinaryData":
                {
                    // Web Stream Format Data Size 16
                    // Fixed Sample HEader Size 16
                    // Version Number 16
                    // Reserved 16

                    //OR

                    //Total Header Length 16
                    //Part Number 16
                    //Total Part Count 16
                    //Sample Type 16

                    //URL String (Varies)
                    mediaType = Sdp.MediaType.data;
                    break;
                }

                case "TextMedia":
                {
                    //Name,Value pairs
                    mediaType = Sdp.MediaType.text;
                    break;
                }
                }

                //Extension?
                //stream.Position += 16;

                //if (!IsBroadcast)
                //{
                //    //May have to adjust time...
                //    //m_PlayTime /  (10000000 / 1000) - m_StartTime;
                //}

                //Name comes from MetaData?

                //TODO Get Sample Count....

                //Parse Index....

                Track created = new Track(asfObject, trackName, trackId, Created, Modified, (int)sampleCount, (int)height, (int)width, TimeSpan.FromMilliseconds(startTime / timeScale), TimeSpan.FromMilliseconds(duration),
                                          //Frames Per Seconds
                                          // duration is in milliseconds, converted to seconds, scaled
                                          mediaType == Sdp.MediaType.video ?
                                          duration * Media.Common.Extensions.TimeSpan.TimeSpanExtensions.MicrosecondsPerMillisecond / Media.Common.Extensions.TimeSpan.TimeSpanExtensions.NanosecondsPerSecond * Media.Common.Extensions.TimeSpan.TimeSpanExtensions.MicrosecondsPerMillisecond
                    :
                                          rate,
                                          ///
                                          mediaType, codecIndication, channels, bitDepth);

                yield return(created);

                tracks.Add(created);
            }

            m_Tracks = tracks;

            Position = position;
        }
Esempio n. 5
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="header"></param>
 /// <param name="name"></param>
 /// <param name="id"></param>
 /// <param name="created"></param>
 /// <param name="modified"></param>
 /// <param name="sampleCount"></param>
 /// <param name="height"></param>
 /// <param name="width"></param>
 /// <param name="position"></param>
 /// <param name="duration"></param>
 /// <param name="frameRate"></param>
 /// <param name="mediaType">This could be a type defined either here or in Common to reduce the need to have SDP as a reference</param>
 /// <param name="codecIndication">There needs to be either a method on each IMediaContainer to get a 4cc or a common mapping.</param>
 /// May not always be present...
 /// <param name="channels"></param>
 /// <param name="bitDepth"></param>
 public Track(Node header, string name, int id, DateTime created, DateTime modified, long sampleCount, int height, int width, TimeSpan position, TimeSpan duration, double frameRate, Sdp.MediaType mediaType, byte[] codecIndication, byte channels = 0, byte bitDepth = 0, bool enabled = true)
 {
     this.Header          = header;
     this.Width           = width;
     this.Height          = height;
     this.Id              = id;
     this.Position        = position;
     this.Duration        = duration;
     this.Rate            = frameRate;
     this.MediaType       = mediaType;
     this.Name            = name;
     this.SampleCount     = sampleCount;
     this.CodecIndication = codecIndication;
     this.Channels        = channels;
     this.BitDepth        = bitDepth;
     this.Enabled         = enabled;
     //this.Volume = volume;
 }
Esempio n. 6
0
        //void ParseInfoPackage / SideMetaData (Optional)

        //void ParseSyncPoint

        public override IEnumerable <Track> GetTracks()
        {
            if (m_Tracks != null)
            {
                foreach (Track track in m_Tracks)
                {
                    yield return(track);
                }
                yield break;
            }

            long position = Position;

            ParseMainHeader();

            ParseStreamPackages();

            List <Track> tracks = new List <Track>();

            foreach (var streamId in m_StreamPackages.Keys)
            {
                //Parse the Stream Package

                Node streamPackage = m_StreamPackages[streamId];

                long timeBase = m_TimeBases[streamId];

                using (var stream = streamPackage.DataStream)
                {
                    int bytesRead = 0;

                    long tempStreamId = DecodeVariableLength(stream, out bytesRead);

                    if (tempStreamId != streamId)
                    {
                        throw new InvalidOperationException("Stream Package Mismatch");
                    }

                    long streamClass = DecodeVariableLength(stream, out bytesRead);

                    Sdp.MediaType mediaType = Sdp.MediaType.unknown;

                    byte[] codecIndication = new byte[4];

                    //if(streamClass > 3)

                    switch (streamClass)
                    {
                    case 0:
                    {
                        mediaType = Sdp.MediaType.video;
                        goto default;
                    }

                    case 1:
                    {
                        mediaType = Sdp.MediaType.audio;
                        goto default;
                    }

                    case 2:     //Subtitle
                    {
                        mediaType = Sdp.MediaType.text;
                        goto default;
                    }

                    case 3:     //Data
                    {
                        mediaType = Sdp.MediaType.data;
                        goto default;
                    }

                    default:
                    {
                        stream.Read(codecIndication, 0, (int)DecodeVariableLength(stream, out bytesRead));
                        break;
                    }
                    }

                    //timeBaseId
                    long tempTimeBase = DecodeVariableLength(stream, out bytesRead);

                    //if (tempTimeBase != timeBase) throw new InvalidOperationException("Stream Timebase Mismatch");

                    if (tempTimeBase != timeBase)
                    {
                        timeBase = m_TimeBases[(int)tempTimeBase];
                    }

                    long msb_pts_shift = DecodeVariableLength(stream, out bytesRead);
                    long pts_distance  = DecodeVariableLength(stream, out bytesRead);
                    long decode_delay  = DecodeVariableLength(stream, out bytesRead);
                    long stream_flags  = DecodeVariableLength(stream, out bytesRead);

                    long duration = msb_pts_shift * pts_distance;

                    duration += (int)m_HeaderOptions[streamId].Item6 << (int)msb_pts_shift;

                    //Skip extraData for codec
                    long extraDataSize = DecodeVariableLength(stream, out bytesRead);

                    if (extraDataSize > 0)
                    {
                        stream.Position += extraDataSize;
                    }

                    int width = 0, height = 0;

                    double rate = 0;

                    byte channels = 0, bitDepth = 0;

                    switch (mediaType)
                    {
                    case Sdp.MediaType.video:
                    {
                        //Already read
                        rate = pts_distance;

                        width  = (int)DecodeVariableLength(stream, out bytesRead);
                        height = (int)DecodeVariableLength(stream, out bytesRead);
                        //Aspect Ration (num/dum)

                        DecodeVariableLength(stream, out bytesRead);
                        DecodeVariableLength(stream, out bytesRead);

                        //csp type
                        bitDepth = (byte)DecodeVariableLength(stream, out bytesRead);
                        break;
                    }

                    case Sdp.MediaType.audio:
                    {
                        //Already read
                        duration = pts_distance;

                        //Rational
                        rate     = DecodeVariableLength(stream, out bytesRead);
                        rate    /= DecodeVariableLength(stream, out bytesRead);
                        channels = (byte)DecodeVariableLength(stream, out bytesRead);
                        break;
                    }
                    }

                    //Map codec indication to 4cc?

                    //foreach(var tag in  ReadTags(Frame))
                    //determine id from frame and count sample

                    //Or use pts_distance
                    int sampleCount = 0;

                    //Duration is off by about 30 seconds? must include rate or some conversion...

                    Track created = new Track(streamPackage, string.Empty, streamId, FileInfo.CreationTimeUtc, FileInfo.LastWriteTimeUtc, sampleCount, height, width, TimeSpan.FromMilliseconds(decode_delay), TimeSpan.FromSeconds(duration), rate, mediaType, codecIndication, channels, bitDepth);

                    yield return(created);

                    tracks.Add(created);
                }
            }

            m_Tracks = tracks;

            Position = position;
        }
Esempio n. 7
0
        public override IEnumerable <Track> GetTracks() //bool enabled tracks only?
        {
            if (m_Tracks != null)
            {
                foreach (Track track in m_Tracks)
                {
                    yield return(track);
                }
                yield break;
            }

            var tracks = new List <Track>();

            long position = Position;

            //Get Duration from mdhd, some files have more then one mdhd.
            if (false == m_Duration.HasValue)
            {
                ParseMovieHeader();
            }

            //traf should also be supported.
            foreach (var trakBox in ReadBoxes(Root.Offset, "trak").ToArray())
            {
                //Define variables which need to be reset for every track.

                int trackId = 0;

                ulong created = 0, modified = 0, duration = 0;

                int width = 0, height = 0;

                bool enabled = false, inMovie, inPreview;

                byte[] codecIndication = Media.Common.MemorySegment.EmptyBytes;

                float volume = m_Volume.Value;

                int offset = 0, version = 0, flags = 0;

                byte[] rawData;

                ulong trackTimeScale = m_TimeScale.Value, trackDuration = duration;

                DateTime trackCreated = m_Created.Value, trackModified = m_Modified.Value;

                Sdp.MediaType mediaType = Sdp.MediaType.unknown;

                string name = string.Empty;

                byte channels = 0, bitDepth = 0;

                double rate = 0;

                List <Tuple <long, long> > entries = new List <Tuple <long, long> >();

                List <long> offsets = new List <long>();

                List <int> sampleSizes = new List <int>();

                TimeSpan startTime = TimeSpan.Zero;

                //MAKE ONLY A SINGLE PASS HERE TO REDUCE IO
                using (var stream = trakBox.DataStream)
                {
                    int bytesRead = 0;

                    long length = 0, streamPosition = stream.Position, streamLength = stream.Length;

                    byte[] identifier;

                    //Note could use RawData from trakBox
                    //Would just need a way to ReadLength and Identifier from byte[] rather than Stream.

                    //This would also work but it would cause a seek
                    //foreach(var node in ReadBoxes(trakBox.DataOffset, trakBox.DataSize, null))

                    //While there is data in the stream
                    while (streamPosition < streamLength)
                    {
                        //Read the length
                        length = ReadLength(stream, out bytesRead);

                        streamPosition += bytesRead;

                        //Read the identifier
                        identifier = ReadIdentifier(stream);

                        bytesRead += IdentifierSize;

                        streamPosition += IdentifierSize;

                        length -= MinimumSize;

                        offset = 0;

                        string boxName = ToUTF8FourCharacterCode(identifier);

                        //Determine what to do
                        switch (boxName)
                        {
                        // Next Node has data
                        case "trak": continue;

                        case "elst":
                        {
                            rawData = new byte[length];

                            stream.Read(rawData, 0, (int)length);

                            List <Tuple <int, int, float> > edits = new List <Tuple <int, int, float> >();

                            //Skip Flags and Version
                            offset = LengthSize;

                            int entryCount = Common.Binary.Read32(rawData, offset, Common.Binary.IsLittleEndian);

                            offset += 4;

                            for (int i = 0; i < entryCount && offset < length; ++i)
                            {
                                //Edit Duration, MediaTime, Rate
                                edits.Add(new Tuple <int, int, float>(Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian),
                                                                      Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian),
                                                                      Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian) / ushort.MaxValue));
                            }

                            if (edits.Count > 0 && edits[0].Item2 > 0)
                            {
                                startTime = TimeSpan.FromMilliseconds(edits[0].Item2);
                            }

                            offset = (int)length;

                            goto default;
                        }

                        case "tkhd":    //tfhd
                        {
                            rawData = new byte[length];

                            stream.Read(rawData, 0, (int)length);

                            version = rawData[offset++];

                            flags = Common.Binary.Read24(rawData, offset, Common.Binary.IsLittleEndian);

                            offset += 3;

                            enabled = ((flags & 1) == flags);

                            inMovie = ((flags & 2) == flags);

                            inPreview = ((flags & 3) == flags);

                            if (version == 0)
                            {
                                created = Common.Binary.ReadU32(rawData, ref offset, Common.Binary.IsLittleEndian);

                                modified = Common.Binary.ReadU32(rawData, ref offset, Common.Binary.IsLittleEndian);
                            }
                            else
                            {
                                created = Common.Binary.ReadU64(rawData, ref offset, Common.Binary.IsLittleEndian);

                                modified = Common.Binary.ReadU64(rawData, ref offset, Common.Binary.IsLittleEndian);
                            }

                            trackId = Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian);

                            //Skip if not the first active track in the moov header..
                            //if (trackId < NextTrackId) continue;

                            //Skip
                            offset += 4;

                            //Get Duration
                            if (version == 0)
                            {
                                duration = Common.Binary.ReadU32(rawData, ref offset, Common.Binary.IsLittleEndian);
                            }
                            else
                            {
                                duration = Common.Binary.ReadU64(rawData, ref offset, Common.Binary.IsLittleEndian);
                            }

                            if (duration == 4294967295L)
                            {
                                duration = ulong.MaxValue;
                            }

                            //Reserved
                            offset += 8;

                            //int layer = Common.Binary.ReadU16(rawData, ref offset, Common.Binary.IsLittleEndian);

                            //int altGroup = Common.Binary.ReadU16(rawData, ref offset, Common.Binary.IsLittleEndian);

                            offset += 4;

                            volume = Common.Binary.ReadU16(rawData, ref offset, Common.Binary.IsLittleEndian) / 256;

                            //Skip int and Matrix
                            offset += 38;

                            //Width
                            width = Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian) / ushort.MaxValue;

                            //Height
                            height = Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian) / ushort.MaxValue;

                            offset = (int)length;

                            goto default;
                        }

                        case "mdhd":
                        {
                            rawData = new byte[length];

                            stream.Read(rawData, 0, (int)length);

                            version = rawData[offset++];

                            flags = Common.Binary.Read24(rawData, ref offset, Common.Binary.IsLittleEndian);

                            ulong mediaCreated, mediaModified, timescale, mediaduration;

                            if (version == 0)
                            {
                                mediaCreated = Common.Binary.ReadU32(rawData, ref offset, Common.Binary.IsLittleEndian);

                                mediaModified = Common.Binary.ReadU32(rawData, ref offset, Common.Binary.IsLittleEndian);

                                timescale = Common.Binary.ReadU32(rawData, ref offset, Common.Binary.IsLittleEndian);

                                mediaduration = Common.Binary.ReadU32(rawData, ref offset, Common.Binary.IsLittleEndian);
                            }
                            else
                            {
                                mediaCreated = Common.Binary.ReadU64(rawData, ref offset, Common.Binary.IsLittleEndian);

                                mediaModified = Common.Binary.ReadU64(rawData, ref offset, Common.Binary.IsLittleEndian);

                                timescale = Common.Binary.ReadU32(rawData, ref offset, Common.Binary.IsLittleEndian);

                                mediaduration = Common.Binary.ReadU64(rawData, ref offset, Common.Binary.IsLittleEndian);
                            }

                            trackTimeScale = timescale;

                            trackDuration = mediaduration;

                            trackCreated = IsoBaseDateUtc.AddMilliseconds(mediaCreated * Media.Common.Extensions.TimeSpan.TimeSpanExtensions.MicrosecondsPerMillisecond);

                            trackModified = IsoBaseDateUtc.AddMilliseconds(mediaModified * Media.Common.Extensions.TimeSpan.TimeSpanExtensions.MicrosecondsPerMillisecond);

                            offset = (int)length;

                            goto default;
                        }

                        case "stsd":
                        {
                            //H264
                            // stsd/avc1/avcC contains a field 'lengthSizeMinusOne' specifying the length. But the default is 4.

                            rawData = new byte[length];

                            stream.Read(rawData, 0, (int)length);

                            int sampleDescriptionCount = length > 0 ? Common.Binary.Read32(rawData, LengthSize, Common.Binary.IsLittleEndian) : 0;

                            offset = MinimumSize;

                            if (sampleDescriptionCount > 0)
                            {
                                for (int i = 0; i < sampleDescriptionCount; ++i)
                                {
                                    int len = Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian) - 4;

                                    var sampleEntry = rawData.Skip(offset).Take(len);
                                    offset += len;

                                    switch (mediaType)
                                    {
                                    case Sdp.MediaType.audio:
                                    {
                                        //Maybe == mp4a
                                        codecIndication = sampleEntry.Take(4).ToArray();

                                        //32, 16, 16 (dref index)
                                        version = Common.Binary.Read16(sampleEntry, 8, Common.Binary.IsLittleEndian);

                                        //Revision 16, Vendor 32

                                        //ChannelCount 16
                                        channels = (byte)Common.Binary.ReadU16(sampleEntry, 20, Common.Binary.IsLittleEndian);

                                        //SampleSize 16 (A 16-bit integer that specifies the number of bits in each uncompressed sound sample. Allowable values are 8 or 16. Formats using more than 16 bits per sample set this field to 16 and use sound description version 1.)
                                        bitDepth = (byte)Common.Binary.ReadU16(sampleEntry, 22, Common.Binary.IsLittleEndian);

                                        //CompressionId 16
                                        var compressionId = sampleEntry.Skip(24).Take(2);

                                        //Decode to a WaveFormatID (16 bit)
                                        int waveFormatId = Common.Binary.Read16(compressionId, 0, Common.Binary.IsLittleEndian);

                                        //The compression ID is set to -2 and redefined sample tables are used (see “Redefined Sample Tables”).
                                        if (-2 == waveFormatId)
                                        {
                                            //var waveAtom = ReadBox("wave", sampleDescriptionBox.Offset);
                                            //if (waveAtom != null)
                                            //{
                                            //    flags = Common.Binary.Read24(waveAtom.Raw, 9, Common.Binary.IsLittleEndian);
                                            //    //Extrack from flags?
                                            //}
                                        }                //If the formatId is known then use it
                                        else if (waveFormatId > 0)
                                        {
                                            codecIndication = compressionId.ToArray();
                                        }

                                        //@ 26

                                        //PktSize 16

                                        //sr 32

                                        rate = (double)Common.Binary.ReadU32(sampleEntry, 28, Common.Binary.IsLittleEndian) / 65536F;

                                        //@ 32

                                        if (version > 1)
                                        {
                                            //36 total

                                            rate     = BitConverter.Int64BitsToDouble(Common.Binary.Read64(sampleEntry, 32, Common.Binary.IsLittleEndian));
                                            channels = (byte)Common.Binary.ReadU32(sampleEntry, 40, Common.Binary.IsLittleEndian);

                                            //24 More Bytes
                                        }

                                        //else 16 more if version == 1
                                        //else 2 more if version == 0

                                        //@ esds for mp4a

                                        //http://www.mp4ra.org/object.html
                                        // @ +4 +4 +11 == ObjectTypeIndication

                                        break;
                                    }

                                    case Sdp.MediaType.video:
                                    {
                                        codecIndication = sampleEntry.Take(4).ToArray();

                                        //SampleEntry overhead = 8
                                        //Version, Revision, Vendor, TemporalQUal, SpacialQual, Width, Height, hRes,vRes, reversed, FrameCount, compressorName, depth, clrTbl, (extensions)

                                        //Width @ 28
                                        width = Common.Binary.ReadU16(sampleEntry, 28, Common.Binary.IsLittleEndian);
                                        //Height @ 30
                                        height = Common.Binary.ReadU16(sampleEntry, 30, Common.Binary.IsLittleEndian);

                                        //hres, vres, reserved = 12

                                        //FrameCount @ 44 (A 16-bit integer that indicates how many frames of compressed data are stored in each sample. Usually set to 1.)

                                        //@46

                                        //30 bytes compressor name (1 byte length) + 1

                                        //@78

                                        bitDepth = (byte)Common.Binary.ReadU16(sampleEntry, 78, Common.Binary.IsLittleEndian);

                                        //esds box for codec specific data.

                                        break;
                                    }
                                    }

                                    continue;
                                }
                            }

                            offset = (int)length;

                            goto default;
                        }

                        case "stts":
                        {
                            rawData = new byte[length];

                            stream.Read(rawData, 0, (int)length);

                            //Skip Flags and Version
                            offset = LengthSize;

                            int entryCount = Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian);

                            for (int i = 0; i < entryCount && offset < length; ++i)
                            {
                                //Sample Count Sample Duration
                                entries.Add(new Tuple <long, long>(Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian),
                                                                   Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian)));
                            }

                            offset = (int)length;

                            goto default;
                        }

                        case "stsz":
                        {
                            rawData = new byte[length];

                            stream.Read(rawData, 0, (int)length);

                            //Skip Flags and Version
                            offset = MinimumSize;

                            int defaultSize = Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian);

                            int count = Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian);

                            if (defaultSize == 0)
                            {
                                for (int i = 0; i < count && offset < length; ++i)
                                {
                                    sampleSizes.Add(Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian));
                                }
                            }

                            offset = (int)length;

                            goto default;
                        }

                        case "stco":
                        {
                            rawData = new byte[length];

                            stream.Read(rawData, 0, (int)length);

                            //Skip Flags and Version
                            offset = LengthSize;

                            int chunkCount = Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian);

                            for (int i = 0; i < chunkCount && offset < length; ++i)
                            {
                                offsets.Add((long)Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian));
                            }

                            offset = (int)length;

                            goto default;
                        }

                        case "st64":
                        {
                            //Could have Array return but would require cast...
                            //Memory segment and then read from would be best..
                            //bool TryRead(, out MemorySegment) but then how do you know the size etc?
                            //Could have a new type SizedArray which does this for you... (e.g. Array<T>)
                            //Todo, ReadSizedArrayAndElements(int elementSize, bool reverse = false)
                            //Todo, ReadSizedArrayAndElements(int elementSize, offset, bool reverse = false)
                            //Todo, ReadSizedArrayAndElements(int elementSize, offset, count, bool reverse = false)
                            //Todo, ReadSizedArrayElements(int elementSize, bool reverse = false)

                            rawData = new byte[length];

                            stream.Read(rawData, 0, (int)length);

                            //Skip Flags and Version
                            offset = MinimumSize;

                            int chunkCount = Common.Binary.Read32(rawData, ref offset, Common.Binary.IsLittleEndian);

                            for (int i = 0; i < chunkCount && offset < length; ++i)
                            {
                                offsets.Add(Common.Binary.Read64(rawData, ref offset, Common.Binary.IsLittleEndian));
                            }

                            //should asserte///

                            offset = (int)length;

                            goto default;
                        }

                        case "hdlr":
                        {
                            rawData = new byte[length];

                            stream.Read(rawData, 0, (int)length);

                            string comp = ToUTF8FourCharacterCode(rawData, LengthSize), sub = ToUTF8FourCharacterCode(rawData, LengthSize * 2);

                            switch (sub)
                            {
                            case "vide": mediaType = Sdp.MediaType.video; break;

                            case "soun": mediaType = Sdp.MediaType.audio; break;

                            case "text": mediaType = Sdp.MediaType.text; break;

                            case "tmcd": mediaType = Sdp.MediaType.timing; break;

                            default: break;
                            }

                            offset = (int)length;

                            goto default;
                        }

                        case "name":
                        {
                            rawData = new byte[length];

                            stream.Read(rawData, 0, (int)length);

                            name = Encoding.UTF8.GetString(rawData);

                            offset = (int)length;

                            goto default;
                        }

                        default:
                        {
                            //If the box was a parent continue parsing
                            if (BaseMediaReader.ParentBoxes.Contains(boxName))
                            {
                                continue;
                            }

                            //Determine how much to skip
                            long toMove = length - offset;

                            //Skip anything which remains if the offset was moved or the entire node if nothing was read.
                            if (toMove > 0)
                            {
                                streamPosition = stream.Position += toMove;
                            }
                            else
                            {
                                streamPosition += length;
                            }

                            continue;
                        }
                        }
                    }
                }

                TimeSpan calculatedDuration = TimeSpan.FromSeconds(trackDuration / (double)trackTimeScale);

                ulong sampleCount = (ulong)sampleSizes.Count();

                //If there are no samples defined and there are entries in the sample to time atom
                if (sampleCount == 0 && entries.Count > 0)
                {
                    //Use the count of entries from the sample to time atom as the count of samples.
                    sampleCount = (ulong)entries.Count;

                    //If there is only one entry use the value in the entry
                    if (sampleCount == 1)
                    {
                        sampleCount = (ulong)entries[0].Item1;
                    }
                }

                //TOdo calc methods in BaseMedia class with base times etc.. (will help with writers)

                rate = mediaType == Sdp.MediaType.audio ? trackTimeScale : (double)((double)sampleCount / ((double)trackDuration / trackTimeScale));

                Track createdTrack = new Track(trakBox, name, trackId, trackCreated, trackModified, (long)sampleCount, width, height, startTime, calculatedDuration, rate, mediaType, codecIndication, channels, bitDepth, enabled);

                createdTrack.Volume = volume;

                tracks.Add(createdTrack);

                yield return(createdTrack);
            }

            m_Tracks = tracks;

            Position = position;
        }