public static float CalculateTimeScale(MovieMetadataBox moovBox, TrackBox trackBox) { MovieHeaderBox headerBox = moovBox.MovieHeaderBox; ulong moovDuration = headerBox.Duration; uint moovTimeScale = headerBox.TimeScale; MediaHeaderBox mdhdBox = trackBox.MediaBox.MediaHeaderBox; ulong mediaDuration = mdhdBox.Duration; float mediaTimeScale = mdhdBox.TimeScale; // Note that time scales may differ between moov and each media (because sampling rate can differ?) moovDuration = moovDuration / moovTimeScale; mediaDuration = (ulong)(mediaDuration / mediaTimeScale); long diff = Math.Abs((long)moovDuration - (long)mediaDuration); if ((diff * diff) > (long)((moovDuration * moovDuration) / 100)) // must be within 1% { throw new Exception("Media Box Header inconsistent with Track Header"); } // scale to 10,000,000 ticks per second mediaTimeScale /= TimeSpan.FromSeconds(1.0).Ticks; if (mediaTimeScale == 0) { throw new Exception("MP4VideoTrack: media time scale is zero"); } return(mediaTimeScale); }
public static float CalculateTimeScale(MovieMetadataBox moovBox, TrackBox trackBox) { MovieHeaderBox headerBox = moovBox.MovieHeaderBox; ulong moovDuration = headerBox.Duration; uint moovTimeScale = headerBox.TimeScale; MediaHeaderBox mdhdBox = trackBox.MediaBox.MediaHeaderBox; ulong mediaDuration = mdhdBox.Duration; float mediaTimeScale = mdhdBox.TimeScale; // Note that time scales may differ between moov and each media (because sampling rate can differ?) moovDuration = moovDuration / moovTimeScale; mediaDuration = (ulong)(mediaDuration / mediaTimeScale); long diff = Math.Abs((long)moovDuration - (long)mediaDuration); if ((diff * diff) > (long)((moovDuration * moovDuration) / 100)) // must be within 1% throw new Exception("Media Box Header inconsistent with Track Header"); // scale to 10,000,000 ticks per second mediaTimeScale /= TimeSpan.FromSeconds(1.0).Ticks; if (mediaTimeScale == 0) throw new Exception("MP4VideoTrack: media time scale is zero"); return mediaTimeScale; }
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; } }
/// <summary> /// FinalizeStream /// 1. Write out ftyp /// 2. Finalize stbl box in each trak box (call mmb.FinalizeBox) /// 3. Calculate size of ftyp + moov + header of mdat boxes /// 4. Fix fileoffsets in stbl chunk boxes (NOTE: interleaving means we should have more than one chunk) /// 5. Write out all of moov box /// 6. Create and write out mdat header only, with correct byte count /// 7. Read from mdat sample file and write to destination mdat /// </summary> public override void FinalizeStream() { // step 0 // flush all remaining slices if (this.CachingEnabled) { foreach (GenericMediaTrack track in this.MediaTracks) { if ((track.SampleStreamLocations != null) && (track.SampleStreamLocations.Count > 0)) { ulong _currMDatOffset = CurrMDatOffset; track.TrackFormat.PrepareSampleWriting(track.SampleStreamLocations, ref _currMDatOffset); CurrMDatOffset = _currMDatOffset; this.WriteSamples(track.SampleStreamLocations.Cast <Slice>(), track.Codec.CodecType); } } } // set duration of movie (must be set to duration of longest track) TrackBox trkBox = this.mmb.TrackBoxes.FirstOrDefault(box => box.MediaBox.HandlerReferenceBox.HandlerType.Equals("vide")); decimal vDuration; if (trkBox == null) { vDuration = 0; } else { vDuration = (decimal)trkBox.MediaBox.MediaHeaderBox.Duration / trkBox.MediaBox.MediaHeaderBox.TimeScale; } trkBox = this.mmb.TrackBoxes.FirstOrDefault(box => box.MediaBox.HandlerReferenceBox.HandlerType.Equals("soun")); decimal aDuration; if (trkBox == null) { aDuration = 0; } else { aDuration = (decimal)trkBox.MediaBox.MediaHeaderBox.Duration / trkBox.MediaBox.MediaHeaderBox.TimeScale; } if (vDuration < aDuration) { this.mmb.MovieHeaderBox.Duration = (ulong)(aDuration * this.mmb.MovieHeaderBox.TimeScale); } else { this.mmb.MovieHeaderBox.Duration = (ulong)(vDuration * this.mmb.MovieHeaderBox.TimeScale); } // step 1 this.ftb.Write(m_writer); // step 2 this.mmb.FinalizeBox(); // step 3 int headerSize = (int)(this.ftb.Size + this.mmb.Size); // step 4 foreach (TrackBox tbox in this.mmb.TrackBoxes) { tbox.MediaBox.MediaInformationBox.SampleTableBox.ChunkOffSetBox.Fixup(headerSize); } // step 5 this.mmb.Write(m_writer); // step 6 this.MediaDataBoxList = new List <MediaDataBox>(); // there is only one mdat for non-fragmented streams. MediaDataBox mdat = new MediaDataBox(); // because the mdat is the last box in the file, we can set its size to 0 // if mdat is too large, set its size filed to zero: // this gets WMP to work, but VLC and QT doesn't like it. mdat.Size += (ulong)tempMdat.Length; if (mdat.Size > uint.MaxValue) { mdat.Size = 0UL; // += (ulong)tempMdat.Length; } this.MediaDataBoxList.Add(mdat); // step 7 tempMdat.Position = 0L; mdat.Write(m_writer, tempMdat); // close file Close(); }
public MediaBox(TrackBox inParent) : base(BoxTypes.Media) { parent = inParent; }
public override void Read(BoxReader reader) { long testpos = reader.BaseStream.Position; using (new SizeChecker(this, reader)) { base.Read(reader); MovieHeaderBox.Read(reader); // Don't assume any order in the boxes within this after the header. // (The order depends on the brand.) int tbcount = 0; TrackBox[] tmpTBs = new TrackBox[10]; while (reader.BaseStream.Position < (long)(this.Size + this.Offset)) { long pos = reader.BaseStream.Position; Box tmpBox = new Box(BoxTypes.Any); tmpBox.Read(reader); reader.BaseStream.Position = pos; if (tmpBox.Type == BoxTypes.Track) { TrackBox tb = new TrackBox(this, MovieHeaderBox.TimeScale); tb.Read(reader); tmpTBs[tbcount] = tb; tbcount++; } else if (tmpBox.Type == BoxTypes.MovieExtends) { MovieExtendsBox = new MovieExtendsBox(this); MovieExtendsBox.Read(reader); } else if (tmpBox.Type == BoxTypes.ObjectDescriptor) // iods { _objectDescriptor = new ObjectDescriptorBox(); _objectDescriptor.Read(reader); } else if (tmpBox.Type == BoxTypes.UserData) // udta { _userData = new UserDataBox(); _userData.Read(reader); } // some cases below for things we currently ignore, thus we skip over them... else { byte[] buffer = new byte[tmpBox.Size]; reader.Read(buffer, 0, (int)tmpBox.Size); //reader.BaseStream.Position = (long)(tmpBox.Size + tmpBox.Offset); // ignore for now Debug.WriteLine(string.Format("Unknown box type {0} in MovieMetadataBox (this), skipped", tmpBox.Type.ToString())); } } TrackBoxes = new TrackBox[tbcount]; for (int i = 0; i < tbcount; i++) { TrackBoxes[i] = tmpTBs[i]; } } }
public void AddTrackBox(TrackBox tbox) { TrackBoxes[MovieHeaderBox.NextTrackID - 1] = tbox; tbox.TrackHeaderBox.TrackID = MovieHeaderBox.NextTrackID; MovieHeaderBox.NextTrackID++; }