using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace MatrixIO.IO.Bmff.Boxes { /// /// Track Run Box ("trun") /// [Box("trun", "Track Run Box")] public class TrackRunBox : FullBox, ITableBox { public TrackRunBox() : base() { } public TrackRunBox(Stream stream) : base(stream) { } internal override ulong CalculateSize() { ulong calculatedSize = base.CalculateSize() + 4; if ((Flags & TrackRunFlags.DataOffsetPresent) == TrackRunFlags.DataOffsetPresent) calculatedSize += 4; if ((Flags & TrackRunFlags.FirstSampleFlagsPresent) == TrackRunFlags.FirstSampleFlagsPresent) calculatedSize += 4; ulong entryLength = 0; if ((Flags & TrackRunFlags.SampleDurationPresent) == TrackRunFlags.SampleDurationPresent) entryLength += 4; if ((Flags & TrackRunFlags.SampleSizePresent) == TrackRunFlags.SampleSizePresent) entryLength += 4; if ((Flags & TrackRunFlags.SampleFlagsPresent) == TrackRunFlags.SampleFlagsPresent) entryLength += 4; if ((Flags & TrackRunFlags.SampleCompositionTimeOffsetsPresent) == TrackRunFlags.SampleCompositionTimeOffsetsPresent) entryLength += 4; return calculatedSize + (entryLength * (ulong)_Entries.Count); } protected override void LoadFromStream(Stream stream) { base.LoadFromStream(stream); uint sampleCount = stream.ReadBEUInt32(); if ((Flags & TrackRunFlags.DataOffsetPresent) == TrackRunFlags.DataOffsetPresent) DataOffset = stream.ReadBEInt32(); if ((Flags & TrackRunFlags.FirstSampleFlagsPresent) == TrackRunFlags.FirstSampleFlagsPresent) FirstSampleFlags = new SampleFlags(stream.ReadBEUInt32()); for (uint i = 0; i < sampleCount; i++) { TrackRunEntry entry = new TrackRunEntry(); if ((Flags & TrackRunFlags.SampleDurationPresent) == TrackRunFlags.SampleDurationPresent) entry.SampleDuration = stream.ReadBEUInt32(); if ((Flags & TrackRunFlags.SampleSizePresent) == TrackRunFlags.SampleSizePresent) entry.SampleSize = stream.ReadBEUInt32(); if ((Flags & TrackRunFlags.SampleFlagsPresent) == TrackRunFlags.SampleFlagsPresent) entry.SampleFlags = new SampleFlags(stream.ReadBEUInt32()); if ((Flags & TrackRunFlags.SampleCompositionTimeOffsetsPresent) == TrackRunFlags.SampleCompositionTimeOffsetsPresent) entry.SampleCompositionTimeOffset = stream.ReadBEUInt32(); _Entries.Add(entry); } } protected override void SaveToStream(Stream stream) { if (DataOffset.HasValue) Flags |= TrackRunFlags.DataOffsetPresent; if (FirstSampleFlags != null) Flags |= TrackRunFlags.FirstSampleFlagsPresent; foreach (var entry in _Entries) { // TODO: There must be a better way... probably involves changing TrackRunEntry if (entry.SampleDuration.HasValue) Flags |= TrackRunFlags.SampleDurationPresent; if (entry.SampleSize.HasValue) Flags |= TrackRunFlags.SampleSizePresent; if (entry.SampleFlags != null) Flags |= TrackRunFlags.SampleFlagsPresent; if (entry.SampleCompositionTimeOffset.HasValue) Flags |= TrackRunFlags.SampleCompositionTimeOffsetsPresent; } base.SaveToStream(stream); stream.WriteBEUInt32((uint)_Entries.Count); if (DataOffset.HasValue) stream.WriteBEInt32(DataOffset.Value); if (FirstSampleFlags != null) stream.WriteBEUInt32(FirstSampleFlags._flags); foreach (TrackRunEntry entry in _Entries) { if ((Flags & TrackRunFlags.SampleDurationPresent) == TrackRunFlags.SampleDurationPresent) stream.WriteBEUInt32(entry.SampleDuration.HasValue ? entry.SampleDuration.Value : 0); if ((Flags & TrackRunFlags.SampleSizePresent) == TrackRunFlags.SampleSizePresent) stream.WriteBEUInt32(entry.SampleSize.HasValue ? entry.SampleSize.Value : 0); if ((Flags & TrackRunFlags.SampleFlagsPresent) == TrackRunFlags.SampleFlagsPresent) stream.WriteBEUInt32(entry.SampleFlags != null ? entry.SampleFlags._flags : 0); if ((Flags & TrackRunFlags.SampleCompositionTimeOffsetsPresent) == TrackRunFlags.SampleCompositionTimeOffsetsPresent) stream.WriteBEUInt32(entry.SampleCompositionTimeOffset.HasValue ? entry.SampleCompositionTimeOffset.Value : 0); } } private new TrackRunFlags Flags { get { return (TrackRunFlags)_Flags; } set { _Flags = (uint)value; } } public int? DataOffset { get; set; } public SampleFlags FirstSampleFlags { get; set; } private IList _Entries = Portability.CreateList(); public IList Entries { get { return _Entries; } } [FlagsAttribute] public enum TrackRunFlags : uint { DataOffsetPresent = 0x000001, FirstSampleFlagsPresent = 0x000004, SampleDurationPresent = 0x000100, SampleSizePresent = 0x000200, SampleFlagsPresent = 0x000400, SampleCompositionTimeOffsetsPresent = 0x000800, } public class TrackRunEntry { public TrackRunEntry() { } public uint? SampleDuration { get; set; } public uint? SampleSize { get; set; } public SampleFlags SampleFlags { get; set; } public uint? SampleCompositionTimeOffset { get; set; } } } }