public MediaBox(IsochronousTrackInfo trackInfo) : this() { ulong scaledDuration = (ulong)TimeArithmetic.ConvertToTimeScale(trackInfo.TimeScale, trackInfo.DurationIn100NanoSecs); MediaHeaderBox = new MediaHeaderBox(this, scaledDuration, trackInfo.TimeScale); this.Size += MediaHeaderBox.Size; Codec codec = null; if (trackInfo.GetType() == typeof(RawAudioTrackInfo)) { RawAudioTrackInfo audioInfo = (RawAudioTrackInfo)trackInfo; codec = new Codec(CodecTypes.Audio); codec.PrivateCodecData = audioInfo.CodecPrivateData; } else if (trackInfo.GetType() == typeof(RawVideoTrackInfo)) { RawVideoTrackInfo videoInfo = (RawVideoTrackInfo)trackInfo; codec = new Codec(CodecTypes.Video); codec.PrivateCodecData = videoInfo.CodecPrivateData; } HandlerReferenceBox = new HandlerReferenceBox(this, codec); this.Size += HandlerReferenceBox.Size; MediaInformationBox = new MediaInformationBox(this, trackInfo); // MediaInformationBox.Size is indeterminate at this time; it is determined only during SampleTableBox.FinalizeBox }
public MovieMetadataBox(List <IsochronousTrackInfo> trackInfos, float rate, float volume, uint[] matrix) : base(BoxTypes.Movie) { // initialize movie duration to zero, then increment it for every slice that is written ulong scaledDuration = (ulong)TimeArithmetic.ConvertToTimeScale(trackInfos[0].MovieTimeScale, trackInfos[0].MovieDurationIn100NanoSecs); MovieHeaderBox = new MovieHeaderBox(trackInfos[0].MovieTimeScale, scaledDuration, rate, volume, matrix); this.Size += MovieHeaderBox.Size; TrackBoxes = new TrackBox[trackInfos.Count]; // may have more than 2 tracks // MovieExtendsBox should only exist if this is a fragment if (trackInfos[0].IsFragment) { MovieExtendsBox = new MovieExtendsBox(this, trackInfos); this.Size += MovieExtendsBox.Size; } }
public TrackBox(IsochronousTrackInfo trackInfo) : this() { float height = 0.0f; float width = 0.0f; if (trackInfo is RawVideoTrackInfo) { // set the TRACK width, which may differ from SampleDescription width and height, depending on Aspect Ratio RawVideoTrackInfo rvti = (RawVideoTrackInfo)trackInfo; height = rvti.Height; width = rvti.Width * ((float)rvti.AspectRatioX / (float)rvti.AspectRatioY); } ulong scaledDuration = (ulong)TimeArithmetic.ConvertToTimeScale(trackInfo.MovieTimeScale, trackInfo.DurationIn100NanoSecs); TrackHeaderBox = new TrackHeaderBox((uint)trackInfo.TrackID, scaledDuration, height, width); // TrackHeaderBox = new TrackHeaderBox((uint)trackInfo.TrackID, (trackInfo.Duration * oneSecondTicks) / trackInfo.TimeScale, height, width); this.Size += TrackHeaderBox.Size; // skip the TrackReferenceBox for now //TrackReferenceBox = new TrackReferenceBox((uint)trackInfo.TrackID); //this.Size += TrackReferenceBox.Size; #if EDTS_OUT EdtsBox = (EdtsBox)trackInfo.GetEdtsBox(); if (EdtsBox != null) { this.Size += EdtsBox.Size; EdtsBox.ScaleToTarget(trackInfo.MovieTimeScale, trackInfo.TimeScale); } #endif MediaBox = new MediaBox(trackInfo); // MediaBox.Size can only be determined during FinalizeBox // NOTE: NO Esds Box }
private uint SampleCountInLastBatch = 0; // last batch sample count for this trak box (this is independent of the other trak box) /// <summary> /// InitSampleTableBoxFromStreamLocations /// Initialize the boxes that point to where the payload bits are, without writing them out to final destination file yet. /// Major change (06/06/2012): favor creating new chunks over accumulating slices in a chunk. /// We take it to the extreme here, like VLC does it: we create a new chunk for every slice/sample. /// What this does is make the stsc box small, but the stco box very large. The advantage is that /// every slice now has an offset into mdat (and the slice crawler can't possibly go out of sync). /// </summary> /// <param name="streamLocations">List of StreamDataBlockInfo extracted from source stream, possibly using InitSampleStreamFromSampleTableBox above.</param> public void InitSampleTableBoxFromStreamLocations(List <StreamDataBlockInfo> streamLocations, ref ulong currMdatOffset) { // if this is the first call, create temp files if (SttsCountsWriter == null) { CreateTempFiles(); } if (CompositionTimeToSample == null && (CTTSOut) && (streamLocations.Any(d => (d.CTS > 0UL) || (d.SliceType == SliceType.BFrame)))) { CompositionTimeToSample = new CompositionTimeToSample(this); } if (streamLocations.Count == 0) { throw new Exception("InitSampleTableBoxFromStreamLocations: SampleStreamLocations list empty."); } bool needNewChunk = true; foreach (StreamDataBlockInfo sample in streamLocations) { uint scaledDuration = (uint)TimeArithmetic.ConvertToTimeScale(parent.parent.MediaHeaderBox.TimeScale, sample.SliceDuration); if (LastDuration == 0) { sampleCountInStts = 1; } else if (LastDuration == scaledDuration) { sampleCountInStts++; } else { WriteToSttsTempFile(); sampleCountInStts = 1; // this one for which duration is different counts as one } LastDuration = scaledDuration; //TimeTicks += sample.SliceDuration; if (sample.SliceType == SliceType.IFrame) { SyncSampleMapWriter.Write(SampleIndex); // if the SyncSampleMapStream has zero length when all is done, then its box should be null CurrSyncSampleMapCount++; } // compute CTTS from TimeStamp and CTS if (CompositionTimeToSample != null) { // CTS = Composition Time of the Sample, so these values are ever-increasing // CTTS = Composition Time relative to Time of the Sample, so these are really either 0 or some multiple of the typical sample duration // CTTS values for an i-frame, for example, is always zero, as its composition time relative to the sample: // CTTS-iframe = SampleTime - CTS = Always 0 if (sample.SliceType == SliceType.IFrame) { // relative time for an iframe is always 0 CompositionTimeToSample.AddEntry(0); LastSynchTime = 0; } else { //if (sample.TimeStampNew.HasValue) { // // relative time for a d-frame is always 0 // uint TimeFromLastSample = (uint)TimeArithmetic.ConvertToTimeScale(parent.parent.MediaHeaderBox.TimeScale, sample.SliceDuration); // CompositionTimeToSample.AddEntry((uint)TimeFromLastSample); //} else { // // this means we are a b-frame // CompositionTimeToSample.AddEntry((uint)uint.MaxValue); //} if (!sample.TimeStampNew.HasValue || sample.SliceType == SliceType.BFrame) { // this means we are a b-frame uint TimeFromLastSample = (uint)TimeArithmetic.ConvertToTimeScale(parent.parent.MediaHeaderBox.TimeScale, sample.SliceDuration); // we get further from a sync time for each consecutive b-frame we have // as you can see above if we encounter an i or d frame we snap back to a delta of 0 LastSynchTime += TimeFromLastSample; CompositionTimeToSample.AddEntry((uint)LastSynchTime); } else if (sample.TimeStampNew.HasValue) { // relative time for a d-frame is always 0 CompositionTimeToSample.AddEntry(0); LastSynchTime = 0; } } } // determine which chunk to put this sample in SampleSizeWriter.Write((uint)sample.SliceSize); SampleToChunkBox.SetFileOffsetForChunk(SampleIndex, (uint)sample.SliceSize, 1u /* (uint)streamLocations.Count */, needNewChunk, ref currMdatOffset); needNewChunk = true; // always create a new chunk, thereby having only a single slice in every chunk (as in VLC output) SampleIndex++; } // set last count SampleCountInLastBatch = (uint)streamLocations.Count; }