using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
namespace MatrixIO.IO.Bmff.Boxes
{
///
/// Track Fragment Random Access Box ("tfra")
///
[Box("tfra", "Track Fragment Random Access Box")]
public class TrackFragmentRandomAccessBox : FullBox, ITableBox
{
public TrackFragmentRandomAccessBox() : base() { }
public TrackFragmentRandomAccessBox(Stream stream) : base(stream) { }
internal override ulong CalculateSize()
{
ulong entries = (ulong)Entries.Count;
return base.CalculateSize()
+ 4 + 4 + 4
+ (entries * (ulong)(Version == 0x01 ? 16 : 8))
+ (entries * (ulong)SizeOfTrafNumber)
+ (entries * (ulong)SizeOfTrunNumber)
+ (entries * (ulong)SizeOfSampleNumber);
}
protected override void LoadFromStream(Stream stream)
{
base.LoadFromStream(stream);
TrackID = stream.ReadBEUInt32();
Reserved = stream.ReadBytes(3);
_SizeOf = stream.ReadOneByte();
uint NumberOfEntries = stream.ReadBEUInt32();
for (uint i = 0; i < NumberOfEntries; i++)
{
TrackFragmentEntry entry = new TrackFragmentEntry();
if (Version == 0x01)
{
entry.Time = stream.ReadBEUInt64();
entry.MoofOffset = stream.ReadBEUInt64();
}
else
{
entry.Time = stream.ReadBEUInt32();
entry.MoofOffset = stream.ReadBEUInt32();
}
if (SizeOfTrafNumber == 1) entry.TrafNumber = stream.ReadOneByte();
else if (SizeOfTrafNumber == 2) entry.TrafNumber = stream.ReadBEUInt16();
else if (SizeOfTrafNumber == 3) entry.TrafNumber = stream.ReadBEUInt24();
else entry.TrafNumber = stream.ReadBEUInt32();
if (SizeOfTrunNumber == 1) entry.TrunNumber = stream.ReadOneByte();
else if (SizeOfTrunNumber == 2) entry.TrunNumber = stream.ReadBEUInt16();
else if (SizeOfTrunNumber == 3) entry.TrunNumber = stream.ReadBEUInt24();
else entry.TrunNumber = stream.ReadBEUInt32();
if (SizeOfSampleNumber == 1) entry.SampleNumber = stream.ReadOneByte();
else if (SizeOfSampleNumber == 2) entry.SampleNumber = stream.ReadBEUInt16();
else if (SizeOfSampleNumber == 3) entry.SampleNumber = stream.ReadBEUInt24();
else entry.SampleNumber = stream.ReadBEUInt32();
_Entries.Add(entry);
}
}
protected override void SaveToStream(Stream stream)
{
if ((from entry in _Entries select Math.Max(entry.Time, entry.MoofOffset)).Max() > UInt32.MaxValue) Version = 1;
else Version = 0;
uint MaxTrafNumber = (from entry in _Entries select entry.TrafNumber).Max();
if (MaxTrafNumber > 16777215) SizeOfTrafNumber = 4;
else if (MaxTrafNumber > UInt16.MaxValue) SizeOfTrafNumber = 3;
else if (MaxTrafNumber > Byte.MaxValue) SizeOfTrafNumber = 2;
else SizeOfTrafNumber = 1;
uint MaxTrunNumber = (from entry in _Entries select entry.TrunNumber).Max();
if (MaxTrunNumber > 16777215) SizeOfTrunNumber = 4;
else if (MaxTrunNumber > UInt16.MaxValue) SizeOfTrunNumber = 3;
else if (MaxTrunNumber > Byte.MaxValue) SizeOfTrunNumber = 2;
else SizeOfTrunNumber = 1;
uint MaxSampleNumber = (from entry in _Entries select entry.SampleNumber).Max();
if (MaxSampleNumber > 16777215) SizeOfSampleNumber = 4;
else if (MaxSampleNumber > UInt16.MaxValue) SizeOfSampleNumber = 3;
else if (MaxSampleNumber > Byte.MaxValue) SizeOfSampleNumber = 2;
else SizeOfSampleNumber = 1;
base.SaveToStream(stream);
stream.WriteBEUInt32(TrackID);
stream.Write(Reserved, 0, 3);
stream.WriteOneByte(_SizeOf);
stream.WriteBEUInt32((uint)_Entries.Count);
foreach(var entry in _Entries)
{
if (Version == 0x01)
{
stream.WriteBEUInt64(entry.Time);
stream.WriteBEUInt64(entry.MoofOffset);
}
else
{
stream.WriteBEUInt32((uint)entry.Time);
stream.WriteBEUInt32((uint)entry.MoofOffset);
}
if (SizeOfTrafNumber == 1) stream.WriteOneByte((byte)entry.TrafNumber);
else if (SizeOfTrafNumber == 2) stream.WriteBEUInt16((ushort)entry.TrafNumber);
else if (SizeOfTrafNumber == 3) stream.WriteBEUInt24((uint)entry.TrafNumber);
else stream.WriteBEUInt32((uint)entry.TrafNumber);
if (SizeOfTrunNumber == 1) stream.WriteOneByte((byte)entry.TrunNumber);
else if (SizeOfTrunNumber == 2) stream.WriteBEUInt16((ushort)entry.TrunNumber);
else if (SizeOfTrunNumber == 3) stream.WriteBEUInt24((uint)entry.TrunNumber);
else stream.WriteBEUInt32((uint)entry.TrunNumber);
if (SizeOfSampleNumber == 1) stream.WriteOneByte((byte)entry.SampleNumber);
else if (SizeOfSampleNumber == 2) stream.WriteBEUInt16((ushort)entry.SampleNumber);
else if (SizeOfSampleNumber == 3) stream.WriteBEUInt24((uint)entry.SampleNumber);
else stream.WriteBEUInt32((uint)entry.SampleNumber);
}
}
public uint TrackID { get; set; }
private byte[] Reserved = new byte[4];
private byte _SizeOf;
public sbyte SizeOfTrafNumber
{
get
{
return (sbyte)(((_SizeOf & 0x30) >> 4) + 1);
}
private set
{
if (value < 1 || value > 4) throw new OverflowException("SizeOfTrafNumber must be a value between 1 and 4.");
_SizeOf = (byte)((_SizeOf & 0xCF) | (((SizeOfTrafNumber - 1) & 0x30) << 4));
}
}
public sbyte SizeOfTrunNumber
{
get
{
return (sbyte)(((_SizeOf & 0x30) >> 4) + 1);
}
private set
{
if (value < 1 || value > 4) throw new OverflowException("SizeOfTrunNumber must be a value between 1 and 4.");
_SizeOf = (byte)((_SizeOf & 0xF3) | ((SizeOfSampleNumber - 1) & 0x03));
}
}
public sbyte SizeOfSampleNumber
{
get
{
return (sbyte)(((_SizeOf & 0x30) >> 4) + 1);
}
private set
{
if (value < 1 || value > 4) throw new OverflowException("SizeOfSampleNumber must be a value between 1 and 4.");
_SizeOf = (byte)((_SizeOf & 0xFC) | ((SizeOfTrunNumber - 1) & 0x0C << 2));
}
}
public class TrackFragmentEntry
{
public TrackFragmentEntry() { }
public ulong Time { get; set; }
public ulong MoofOffset { get; set; }
public uint TrafNumber { get; set; }
public uint TrunNumber { get; set; }
public uint SampleNumber { get; set; }
}
private IList _Entries = Portability.CreateList();
public IList Entries
{
get
{
return _Entries;
}
}
}
}