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))); } }
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))); } }
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; }
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; }
/// <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; }
//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; }
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; }