/// <summary> /// Constructor to use when building the box from scratch. /// NOTE: We don't compute the Size of this box in this constructor. /// The Size of this box is computed during FinalizeBox. /// NOTE: The ordering of the sub-boxes is not determined in the constructor. /// Writing out the sub-boxes (see the Write method below) determines the order of sub-boxes. /// </summary> /// <param name="inParent">MediaInformationBox</param> /// <param name="trackInfo">IsochronousTrackInfo</param> public SampleTableBox(MediaInformationBox inParent, IsochronousTrackInfo trackInfo) : this(inParent) { CTTSOut = trackInfo.CTTSOut; fragmented = trackInfo.IsFragment; SampleDescriptionsBox = new SampleDescriptionsBox(this, trackInfo); DecodingTimeToSampleBox = new DecodingTimeToSampleBox(this); SampleToChunkBox = new SampleToChunkBox(this); SampleSizeBox = new SampleSizeBox(this); ChunkOffSetBox = new ChunkOffSetBox(this); if ((trackInfo is RawVideoTrackInfo) && !fragmented) { SyncSampleMapBox = new SyncSampleMapBox(); //CompositionTimeToSample = new CompositionTimeToSample(this); } }
/// <summary> /// Read - read in the SampleTableBox from the input MP4 file. /// Sub-boxes can come in in any order. /// </summary> /// <param name="reader">BoxReader</param> public override void Read(BoxReader reader) { using (new SizeChecker(this, reader)) { base.Read(reader); while (reader.BaseStream.Position < (long)(this.Size + this.Offset)) { long pos = reader.BaseStream.Position; Box test = new Box(BoxTypes.Any); test.Read(reader); reader.BaseStream.Position = pos; if (test.Type == BoxTypes.TimeToSample) { this.DecodingTimeToSampleBox = new DecodingTimeToSampleBox(this); DecodingTimeToSampleBox.Read(reader); } else if (test.Type == BoxTypes.CompositionOffset) { this.CompositionTimeToSample = new CompositionTimeToSample(this); CompositionTimeToSample.Read(reader); } else if (test.Type == BoxTypes.SampleDescription) { this.SampleDescriptionsBox = new SampleDescriptionsBox(this); SampleDescriptionsBox.Read(reader); } else if (test.Type == BoxTypes.SampleToChunk) { this.SampleToChunkBox = new SampleToChunkBox(this); SampleToChunkBox.Read(reader); } else if (test.Type == BoxTypes.SampleSize) { this.SampleSizeBox = new SampleSizeBox(this); SampleSizeBox.Read(reader); } else if (test.Type == BoxTypes.ChunkOffset) // FIXME: this can be a "co64" box { this.ChunkOffSetBox = new ChunkOffSetBox(this); ChunkOffSetBox.Read(reader); } else if (test.Type == BoxTypes.SyncSampleMap) { this.SyncSampleMapBox = new SyncSampleMapBox(); SyncSampleMapBox.Read(reader); } else { reader.BaseStream.Position = (long)(test.Size + test.Offset); // skip unknown box Debug.WriteLine(string.Format("Unknown box type {0} in SampleTableBox (stbl), skipped", test.Type.ToString())); } } // end of while if (SampleToChunkBox != null) { SampleToChunkBox.CheckIntegrityOfChunkData(); } } }
/// <summary> /// InitSampleStreamFromSampleTableBox /// The idea is to collect information on slices starting from startindex to endIndex. /// This is a fairly complex method that traverses all boxes read from this SampleTableBox. /// </summary> /// <param name="sampleTimeScale">uint - sample time scale</param> /// <param name="startIndex">int - start index</param> /// <param name="endIndex">int - end index</param> /// <param name="lastEnd">ref ulong</param> /// <returns></returns> public List <StreamDataBlockInfo> InitSampleStreamFromSampleTableBox(uint sampleTimeScale, int startIndex, int endIndex, ref ulong lastEnd) { List <StreamDataBlockInfo> SampleStreamLocations = new List <StreamDataBlockInfo>(); // local vars DecodingTimeToSampleBox stts = this.DecodingTimeToSampleBox; if (stts == null) { throw new Exception("SampleTableBox.DecodingTimeToSampleBox missing for track"); } uint sampleCount = 0; ulong timeT = 0; ulong currScaledT = 0; ulong prevScaledT = 0; ulong endT = 0; uint[] counts = stts.SampleCount; uint[] deltaTimes = stts.SampleDelta; ulong currOffset = 0UL; SampleSizeBox stsz = this.SampleSizeBox; uint sampleSize = stsz.SampleSize; uint totalSamples = stsz.SampleCount; uint[] sizeArray = stsz.SampleSizeArray; int sampleCountInList = 0; if ((this.SampleDescriptionsBox == null) || (this.SampleDescriptionsBox.EntryCount != 1)) { throw new Exception("SampleTableBox.SampleDescriptionsBox error"); } BoxType sampleDescriptionBoxType = this.SampleDescriptionsBox.Entries[0].Type; // initialize (set) cttsIndex to the value that corresponds to startIndex int k = 0; int cttsIndex = 0; if (CompositionTimeToSample != null && CompositionTimeToSample.EntryCount > 0) { int sliceIndex = 1; while (cttsIndex < CompositionTimeToSample.SampleOffset.Length) { if (sliceIndex == startIndex) { break; } k++; if (k == CompositionTimeToSample.SampleCount[cttsIndex]) { k = 0; // begin counting from zero again cttsIndex++; } sliceIndex++; } } for (int i = 0; i < stts.EntryCount; i++) { for (int j = 0; j < counts[i]; j++) { currScaledT = (ulong)((timeT * (ulong)TimeSpan.FromSeconds(1.0).Ticks) / sampleTimeScale); if ((sampleCount + 1 >= startIndex) && (sampleCount + 1 <= endIndex)) { StreamDataBlockInfo data = new StreamDataBlockInfo(); data.index = (int)sampleCount; data.TimeStampNew = currScaledT; data.SliceDuration = (uint)((deltaTimes[i] * TimeSpan.TicksPerSecond) / sampleTimeScale) + 1; data.SliceSize = (int)sampleSize; if (sampleSize == 0) { data.SliceSize = (int)sizeArray[sampleCount]; } data.StreamOffset = (ulong)this.SampleToChunkBox.GetFileOffset((uint)(sampleCount + 1)); data.SliceType = sampleDescriptionBoxType == BoxTypes.Mp4a ? SliceType.MP4A : ((this.SyncSampleMapBox == null) || this.SyncSampleMapBox.IsIFrame(sampleCount + 1) ? SliceType.IFrame : SliceType.DFrame); // if necessary, increment cttsIndex if (CompositionTimeToSample != null && CompositionTimeToSample.EntryCount > 0) { k++; data.NonQuickTimeCTTS = CompositionTimeToSample.SampleOffset[cttsIndex]; if (k == CompositionTimeToSample.SampleCount[cttsIndex]) { k = 0; // begin counting from zero again cttsIndex++; } } SampleStreamLocations.Add(data); sampleCountInList++; } if (sampleCount + 1 > endIndex) { endT = prevScaledT; break; } // close of if (currScaledT > endTimeJustBeforeIFrame) // keep track of offset if (sampleSize > 0) { currOffset += sampleSize; } else { if (sampleCount > totalSamples) { throw new Exception("SampleTableBox error: sample count inconsistency bet. stts and stsz"); } currOffset += sizeArray[sampleCount]; } prevScaledT = currScaledT; timeT += deltaTimes[i]; sampleCount++; } // end of for j if (endT > 0UL) // end sample found { break; } } // end of for i if (endT == 0UL) // if we did not find end, endTime would not be set { lastEnd = currScaledT; } else { lastEnd = endT; } return(SampleStreamLocations); }
/// <summary> /// GetStartAndEndIndex /// Given a start time and an end time, determine the start slice index and end slice index. /// </summary> /// <param name="edtsBox"EditsBox></param> /// <param name="sampleTimeScale">uint - sample time scale</param> /// <param name="startTime">ulong - start time</param> /// <param name="endTime">ulong - end time</param> /// <param name="startIndex">out param: start index</param> /// <param name="endIndex">out param: end index</param> private void GetStartAndEndIndex(EdtsBox edtsBox, uint sampleTimeScale, ulong startTime, ulong endTime, out uint startIndex, out uint endIndex) { startIndex = 0; endIndex = 0; ulong ticksDuration = (ulong)TimeArithmetic.ConvertToStandardUnit(sampleTimeScale, parent.parent.MediaHeaderBox.Duration); if (edtsBox != null) { ticksDuration = (ulong)(edtsBox.GetEditTrackDuration(sampleTimeScale) * (decimal)TimeSpan.TicksPerSecond); } if (ticksDuration < startTime) { return; } DecodingTimeToSampleBox stts = this.DecodingTimeToSampleBox; SyncSampleMapBox stss = this.SyncSampleMapBox; uint sampleCount = 0; ulong timeT = 0; ulong currScaledT = 0; ulong prevScaledT = 0; uint[] counts = stts.SampleCount; uint[] deltaTimes = stts.SampleDelta; bool startSet = false; for (int i = 0; i < stts.EntryCount; i++) { for (int j = 0; j < counts[i]; j++) { if ((currScaledT >= startTime) && (!startSet) && ((stss == null) || (stss.IsIFrame(sampleCount + 1)))) { startSet = true; startIndex = sampleCount + 1; } if (((stss == null) || stss.IsIFrame(sampleCount + 2)) && (currScaledT > endTime)) { endIndex = sampleCount + 1; break; } // close of if (currScaledT > endTime) prevScaledT = currScaledT; timeT += deltaTimes[i]; sampleCount++; currScaledT = (ulong)TimeArithmetic.ConvertToStandardUnit(sampleTimeScale, timeT); } // end of for j if (endIndex > 0) // end sample found { break; } } // end of for i if ((endIndex == 0) && startSet) // end sample not found { endIndex = sampleCount + 1; } }
/// <summary> /// Read - read in the SampleTableBox from the input MP4 file. /// Sub-boxes can come in in any order. /// </summary> /// <param name="reader">BoxReader</param> public override void Read(BoxReader reader) { using (new SizeChecker(this, reader)) { base.Read(reader); while (reader.BaseStream.Position < (long)(this.Size + this.Offset)) { long pos = reader.BaseStream.Position; Box test = new Box(BoxTypes.Any); test.Read(reader); reader.BaseStream.Position = pos; if (test.Type == BoxTypes.TimeToSample) { this.DecodingTimeToSampleBox = new DecodingTimeToSampleBox(this); DecodingTimeToSampleBox.Read(reader); } else if (test.Type == BoxTypes.CompositionOffset) { this.CompositionTimeToSample = new CompositionTimeToSample(this); CompositionTimeToSample.Read(reader); } else if (test.Type == BoxTypes.SampleDescription) { this.SampleDescriptionsBox = new SampleDescriptionsBox(this); SampleDescriptionsBox.Read(reader); } else if (test.Type == BoxTypes.SampleToChunk) { this.SampleToChunkBox = new SampleToChunkBox(this); SampleToChunkBox.Read(reader); } else if (test.Type == BoxTypes.SampleSize) { this.SampleSizeBox = new SampleSizeBox(this); SampleSizeBox.Read(reader); } else if (test.Type == BoxTypes.ChunkOffset) { // FIXME: this can be a "co64" box this.ChunkOffSetBox = new ChunkOffSetBox(this); ChunkOffSetBox.Read(reader); } else if (test.Type == BoxTypes.SyncSampleMap) { this.SyncSampleMapBox = new SyncSampleMapBox(); SyncSampleMapBox.Read(reader); } else { reader.BaseStream.Position = (long)(test.Size + test.Offset); // skip unknown box Debug.WriteLine(string.Format("Unknown box type {0} in SampleTableBox (stbl), skipped", test.Type.ToString())); } } // end of while if (SampleToChunkBox != null) SampleToChunkBox.CheckIntegrityOfChunkData(); } }