diff --git a/src/JT1078.FMp4.Test/Boxs/MediaHeaderBox_Test.cs b/src/JT1078.FMp4.Test/Boxs/MediaHeaderBox_Test.cs new file mode 100644 index 0000000..1013f8c --- /dev/null +++ b/src/JT1078.FMp4.Test/Boxs/MediaHeaderBox_Test.cs @@ -0,0 +1,39 @@ +using JT1078.FMp4.MessagePack; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; +using JT1078.Protocol.Extensions; + +namespace JT1078.FMp4.Test.Boxs +{ + public class MediaHeaderBox_Test + { + /// + /// 使用doc/video/demo.mp4 + /// + [Fact] + public void Test1() + { + //00 00 00 20--box size 32 + //6d 64 68 64--box type mdhd + //00--version + //00 00 00--flags + //d9 3e 0b 8e--creation_time + //d9 3e 0b 8e--modification_time + //00 00 bb 80--timescale + //00 05 28 00--duration + //55 c4--(pad(1) + language(15)) + //00 00--pre_defined + MediaHeaderBox movieHeaderBox = new MediaHeaderBox(version:0); + movieHeaderBox.CreationTime = 0xd93e0b8e; + movieHeaderBox.ModificationTime = 0xd93e0b8e; + movieHeaderBox.Timescale = 0x0000bb80; + movieHeaderBox.Duration = 0x00052800; + FMp4MessagePackWriter writer = new MessagePack.FMp4MessagePackWriter(new byte[0x6c]); + movieHeaderBox.ToBuffer(ref writer); + var hex = writer.FlushAndGetArray().ToHexString(); + Assert.Equal("000000206d64686400000000d93e0b8ed93e0b8e0000bb800005280055c40000".ToUpper(), hex); + } + } +} diff --git a/src/JT1078.FMp4.Test/Boxs/MovieHeaderBox_Test.cs b/src/JT1078.FMp4.Test/Boxs/MovieHeaderBox_Test.cs new file mode 100644 index 0000000..b62b7d2 --- /dev/null +++ b/src/JT1078.FMp4.Test/Boxs/MovieHeaderBox_Test.cs @@ -0,0 +1,48 @@ +using JT1078.FMp4.MessagePack; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; +using JT1078.Protocol.Extensions; + +namespace JT1078.FMp4.Test.Boxs +{ + public class MovieHeaderBox_Test + { + /// + /// 使用doc/video/demo.mp4 + /// + [Fact] + public void Test1() + { + //00 00 00 6c--box size + //6d 76 68 64--box type mvhd + //00--version + //00 00 00--flags + //d9 3e 0b 8e--creation time + //d9 3e 0b 8e--modification time + //00 01 5f 90--time scale + //00 09 c7 e4 --duration + //00 01 00 00--rate + //01 00--volume + //00 00 00 00 00 00 00 00 00 00--保留位 10位 + //00 01 00 00 00 00 00 00 00 00 00 00--matrix 视频变换矩阵36 -12 + //00 00 00 00 00 01 00 00 00 00 00 00--matrix 视频变换矩阵36 -24 + //00 00 00 00 00 00 00 00 40 00 00 00--matrix 视频变换矩阵36 -36 + //00 00 00 00 00 00 00 00--pre - defined 24 - 8 + //00 00 00 00 00 00 00 00--pre - defined 24 - 16 + //00 00 00 00 00 00 00 00--pre - defined 24 - 24 + //00 00 00 03--next track id + MovieHeaderBox movieHeaderBox = new MovieHeaderBox(version:0); + movieHeaderBox.CreationTime = 0xd93e0b8e; + movieHeaderBox.ModificationTime = 0xd93e0b8e; + movieHeaderBox.Timescale = 0x00015f90; + movieHeaderBox.Duration = 0x0009c7e4; + movieHeaderBox.NextTrackID = 3; + FMp4MessagePackWriter writer = new MessagePack.FMp4MessagePackWriter(new byte[0x6c]); + movieHeaderBox.ToBuffer(ref writer); + var hex = writer.FlushAndGetArray().ToHexString(); + Assert.Equal("00 00 00 6c 6d 76 68 64 00 00 00 00 d9 3e 0b 8e d9 3e 0b 8e 00 01 5f 90 00 09 c7 e4 00 01 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03".Replace(" ","").ToUpper(), hex); + } + } +} diff --git a/src/JT1078.FMp4.Test/Boxs/TrackHeaderBox_Test.cs b/src/JT1078.FMp4.Test/Boxs/TrackHeaderBox_Test.cs new file mode 100644 index 0000000..6d14d89 --- /dev/null +++ b/src/JT1078.FMp4.Test/Boxs/TrackHeaderBox_Test.cs @@ -0,0 +1,49 @@ +using JT1078.FMp4.MessagePack; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; +using JT1078.Protocol.Extensions; + +namespace JT1078.FMp4.Test.Boxs +{ + public class TrackHeaderBox_Test + { + /// + /// 使用doc/video/demo.mp4 + /// + [Fact] + public void Test1() + { + //00 00 00 5c--box size 92 + //74 6b 68 64--box type tkhd + //00--version + //00 00 01--flags + //d9 3e 0b 8e--creation_time + //d9 3e 0b 8e--modification_time + //00 00 00 01--track_ID + //00 00 00 00--reserved1 保留位1 + //00 09 ab 00--duration + //00 00 00 00 00 00 00 00-- reserved2 保留位2 + //00 00--layer + //00 00--alternate_group + //01 00--volume + //00 00-- reserved3 保留位3 + //00 01 00 00 00 00 00 00 00 00 00 00--matrix 视频变换矩阵36 -12 + //00 00 00 00 00 01 00 00 00 00 00 00--matrix 视频变换矩阵36 -24 + //00 00 00 00 00 00 00 00 40 00 00 00--matrix 视频变换矩阵36 -36 + //00 00 00 00--width + //00 00 00 00--height + TrackHeaderBox trackHeaderBox = new TrackHeaderBox(version:0,flags:1); + trackHeaderBox.CreationTime = 0xd93e0b8e; + trackHeaderBox.ModificationTime = 0xd93e0b8e; + trackHeaderBox.TrackID = 1; + trackHeaderBox.TrackIsAudio = true; + trackHeaderBox.Duration = 0x0009ab00; + FMp4MessagePackWriter writer = new MessagePack.FMp4MessagePackWriter(new byte[0x6c]); + trackHeaderBox.ToBuffer(ref writer); + var hex = writer.FlushAndGetArray().ToHexString(); + Assert.Equal("0000005c746b686400000001d93e0b8ed93e0b8e00000001000000000009ab00000000000000000000000000010000000001000000000000000000000000000000010000000000000000000000000000400000000000000000000000".ToUpper(), hex); + } + } +} diff --git a/src/JT1078.FMp4/Boxs/FileTypeBox.cs b/src/JT1078.FMp4/Boxs/FileTypeBox.cs index a0cedd9..65a2392 100644 --- a/src/JT1078.FMp4/Boxs/FileTypeBox.cs +++ b/src/JT1078.FMp4/Boxs/FileTypeBox.cs @@ -33,7 +33,7 @@ namespace JT1078.FMp4 public void ToBuffer(ref FMp4MessagePackWriter writer) { - ToBuffer(ref writer,out int sizePosition); + Start(ref writer); writer.WriteASCII(MajorBrand); writer.WriteASCII(MinorVersion); if(CompatibleBrands!=null && CompatibleBrands.Count > 0) @@ -43,7 +43,7 @@ namespace JT1078.FMp4 writer.WriteASCII(item); } } - writer.WriteUInt32Return((uint)(writer.GetCurrentPosition()- sizePosition), sizePosition); + End(ref writer); } } } diff --git a/src/JT1078.FMp4/Boxs/MediaHeaderBox.cs b/src/JT1078.FMp4/Boxs/MediaHeaderBox.cs index 09147c7..b2d3f5f 100644 --- a/src/JT1078.FMp4/Boxs/MediaHeaderBox.cs +++ b/src/JT1078.FMp4/Boxs/MediaHeaderBox.cs @@ -1,24 +1,50 @@ -using System; +using JT1078.FMp4.Interfaces; +using JT1078.FMp4.MessagePack; +using System; using System.Collections.Generic; using System.Text; namespace JT1078.FMp4 { - public class MediaHeaderBox : FullBox + public class MediaHeaderBox : FullBox, IFMp4MessagePackFormatter { public MediaHeaderBox(byte version, uint flags=0) : base("mdhd", version, flags) { } - public uint CreationTime { get; set; } - public uint ModificationTime { get; set; } + public ulong CreationTime { get; set; } + public ulong ModificationTime { get; set; } public uint Timescale { get; set; } - public uint Duration { get; set; } = 1; + public ulong Duration { get; set; } = 1; //public bool Pad { get; set; } /// /// ISO-639-2/T language code + /// ref:doc\fmp4\ISO Language Codes.txt /// und-undetermined /// - public string Language { get; set; } + public string Language { get; set; } = "und"; public ushort PreDefined { get; set; } + + public void ToBuffer(ref FMp4MessagePackWriter writer) + { + Start(ref writer); + WriterToBuffer(ref writer); + if (Version == 1) + { + writer.WriteUInt64(CreationTime); + writer.WriteUInt64(ModificationTime); + writer.WriteUInt32(Timescale); + writer.WriteUInt64(Duration); + } + else + { + writer.WriteUInt32((uint)CreationTime); + writer.WriteUInt32((uint)ModificationTime); + writer.WriteUInt32(Timescale); + writer.WriteUInt32((uint)Duration); + } + writer.WriteIso639(Language); + writer.WriteUInt16(PreDefined); + End(ref writer); + } } } diff --git a/src/JT1078.FMp4/Boxs/MovieHeaderBox.cs b/src/JT1078.FMp4/Boxs/MovieHeaderBox.cs index 13896d7..20ae6c0 100644 --- a/src/JT1078.FMp4/Boxs/MovieHeaderBox.cs +++ b/src/JT1078.FMp4/Boxs/MovieHeaderBox.cs @@ -1,18 +1,20 @@ -using System; +using JT1078.FMp4.Interfaces; +using JT1078.FMp4.MessagePack; +using System; using System.Collections.Generic; using System.Text; namespace JT1078.FMp4 { - public class MovieHeaderBox : FullBox + public class MovieHeaderBox : FullBox, IFMp4MessagePackFormatter { public MovieHeaderBox(byte version, uint flags=0) : base("mvhd", version, flags) { } - public uint CreationTime { get; set; } - public uint ModificationTime { get; set; } + public ulong CreationTime { get; set; } + public ulong ModificationTime { get; set; } public uint Timescale { get; set;} - public uint Duration { get; set; } + public ulong Duration { get; set; } public int Rate { get; set; } = 0x00010000; public short Volume { get; set; } = 0x0100; public byte[] Reserved1 { get; set; } = new byte[2]; @@ -20,5 +22,44 @@ namespace JT1078.FMp4 public int[] Matrix { get; set; }=new int [9]{ 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 }; public byte[] PreDefined { get; set; } = new byte[24]; public uint NextTrackID { get; set; }= uint.MaxValue; + public void ToBuffer(ref FMp4MessagePackWriter writer) + { + Start(ref writer); + WriterToBuffer(ref writer); + if (Version == 1) + { + writer.WriteUInt64(CreationTime); + writer.WriteUInt64(ModificationTime); + writer.WriteUInt32(Timescale); + writer.WriteUInt64(Duration); + } + else + { + writer.WriteUInt32((uint)CreationTime); + writer.WriteUInt32((uint)ModificationTime); + writer.WriteUInt32(Timescale); + writer.WriteUInt32((uint)Duration); + } + writer.WriteInt32(Rate); + writer.WriteInt16(Volume); + foreach(var r in Reserved1) + { + writer.WriteByte(r); + } + foreach (var r in Reserved2) + { + writer.WriteUInt32(r); + } + foreach(var m in Matrix) + { + writer.WriteInt32(m); + } + foreach (var pd in PreDefined) + { + writer.WriteByte(pd); + } + writer.WriteUInt32(NextTrackID); + End(ref writer); + } } } diff --git a/src/JT1078.FMp4/Boxs/TrackBox.cs b/src/JT1078.FMp4/Boxs/TrackBox.cs index a6ff84f..533e2af 100644 --- a/src/JT1078.FMp4/Boxs/TrackBox.cs +++ b/src/JT1078.FMp4/Boxs/TrackBox.cs @@ -10,8 +10,8 @@ namespace JT1078.FMp4 { } public TrackHeaderBox TrackHeaderBox { get; set; } - public TrackReferenceBox TrackReferenceBox { get; set; } - public EditBox EditBox { get; set; } - public MediaBox MediaBox { get; set; } + //不是必须的 public TrackReferenceBox TrackReferenceBox { get; set; } + //不是必须的 public EditBox EditBox { get; set; } + public MediaBox MediaBox { get; set; } } } diff --git a/src/JT1078.FMp4/Boxs/TrackHeaderBox.cs b/src/JT1078.FMp4/Boxs/TrackHeaderBox.cs index f1db694..96ea670 100644 --- a/src/JT1078.FMp4/Boxs/TrackHeaderBox.cs +++ b/src/JT1078.FMp4/Boxs/TrackHeaderBox.cs @@ -1,26 +1,71 @@ -using System; +using JT1078.FMp4.Interfaces; +using JT1078.FMp4.MessagePack; +using System; using System.Collections.Generic; using System.Text; namespace JT1078.FMp4 { - public class TrackHeaderBox : FullBox + public class TrackHeaderBox : FullBox, IFMp4MessagePackFormatter { public TrackHeaderBox(byte version, uint flags) : base("tkhd", version, flags) { } - public uint CreationTime { get; set; } - public uint ModificationTime { get; set; } + public ulong CreationTime { get; set; } + public ulong ModificationTime { get; set; } public uint TrackID { get; set; } public uint Reserved1 { get; set; } - public uint Duration { get; set; } + public ulong Duration { get; set; } public uint[] Reserved2 { get; set; } = new uint[2]; - public ushort Layer { get; set; } - public ushort AlternateGroup { get; set; } - public ushort Volume { get; set; } + public short Layer { get; set; } + public short AlternateGroup { get; set; } + public bool TrackIsAudio { get; set; } = false; public ushort Reserved3 { get; set; } public int[] Matrix { get; set; } = new int[9] { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 }; public uint Width { get; set; } public uint Height { get; set; } + public void ToBuffer(ref FMp4MessagePackWriter writer) + { + Start(ref writer); + WriterToBuffer(ref writer); + if (Version == 1) + { + writer.WriteUInt64(CreationTime); + writer.WriteUInt64(ModificationTime); + writer.WriteUInt32(TrackID); + writer.WriteUInt32(Reserved1); + writer.WriteUInt64(Duration); + } + else + { + writer.WriteUInt32((uint)CreationTime); + writer.WriteUInt32((uint)ModificationTime); + writer.WriteUInt32(TrackID); + writer.WriteUInt32(Reserved1); + writer.WriteUInt32((uint)Duration); + } + foreach(var r in Reserved2) + { + writer.WriteUInt32(r); + } + writer.WriteInt16(Layer); + writer.WriteInt16(AlternateGroup); + if (TrackIsAudio) + { + writer.WriteInt16(0x0100); + } + else + { + writer.WriteInt16(0); + } + writer.WriteUInt16(Reserved3); + foreach (var m in Matrix) + { + writer.WriteInt32(m); + } + writer.WriteUInt32(Width); + writer.WriteUInt32(Height); + End(ref writer); + } } } diff --git a/src/JT1078.FMp4/FullBox.cs b/src/JT1078.FMp4/FullBox.cs index 3c9cbc5..36e7df4 100644 --- a/src/JT1078.FMp4/FullBox.cs +++ b/src/JT1078.FMp4/FullBox.cs @@ -1,4 +1,6 @@ -using System; +using JT1078.FMp4.Interfaces; +using JT1078.FMp4.MessagePack; +using System; using System.Collections.Generic; using System.Text; @@ -19,5 +21,14 @@ namespace JT1078.FMp4 /// bit(24) /// public uint Flags { get; set; } + /// + /// + /// + /// + protected void WriterToBuffer(ref FMp4MessagePackWriter writer) + { + writer.WriteByte(Version); + writer.WriteUInt24(Flags); + } } } diff --git a/src/JT1078.FMp4/JT1078.FMp4.xml b/src/JT1078.FMp4/JT1078.FMp4.xml index 4f8615f..6ff981d 100644 --- a/src/JT1078.FMp4/JT1078.FMp4.xml +++ b/src/JT1078.FMp4/JT1078.FMp4.xml @@ -168,6 +168,7 @@ ISO-639-2/T language code + ref:doc\fmp4\ISO Language Codes.txt und-undetermined @@ -358,6 +359,12 @@ bit(24) + + + + + + @@ -368,7 +375,6 @@ ref - @@ -382,12 +388,17 @@ 盒子类型 - + + + + + + + - diff --git a/src/JT1078.FMp4/MessagePack/FMp4MessagePackWriter.cs b/src/JT1078.FMp4/MessagePack/FMp4MessagePackWriter.cs index 9dc34df..40bb399 100644 --- a/src/JT1078.FMp4/MessagePack/FMp4MessagePackWriter.cs +++ b/src/JT1078.FMp4/MessagePack/FMp4MessagePackWriter.cs @@ -28,6 +28,11 @@ namespace JT1078.FMp4.MessagePack BinaryPrimitives.WriteUInt16BigEndian(writer.Free, value); writer.Advance(2); } + public void WriteInt16(short value) + { + BinaryPrimitives.WriteInt16BigEndian(writer.Free, value); + writer.Advance(2); + } public void WriteInt32(int value) { BinaryPrimitives.WriteInt32BigEndian(writer.Free, value); @@ -44,6 +49,15 @@ namespace JT1078.FMp4.MessagePack writer.Advance(4); } + public void WriteUInt24(uint value) + { + var span = writer.Free; + span[0] = (byte)(value >> 16); + span[1] = (byte)(value >> 8); + span[2] = (byte)value; + writer.Advance(3); + } + public void WriteASCII(string value) { var data = Encoding.ASCII.GetBytes(value); @@ -88,7 +102,6 @@ namespace JT1078.FMp4.MessagePack /// /// ref - /// /// /// public void WriteIso639(string language) diff --git a/src/JT1078.FMp4/Mp4Box.cs b/src/JT1078.FMp4/Mp4Box.cs index 326f907..2bedc2d 100644 --- a/src/JT1078.FMp4/Mp4Box.cs +++ b/src/JT1078.FMp4/Mp4Box.cs @@ -1,4 +1,5 @@ -using JT1078.FMp4.MessagePack; +using JT1078.FMp4.Interfaces; +using JT1078.FMp4.MessagePack; using System; using System.Collections.Generic; using System.Text; @@ -9,7 +10,7 @@ namespace JT1078.FMp4 { //public const string UUID = "uuid"; - public const int FixedSizeLength = 4; + const int FixedSizeLength = 4; public Mp4Box(string boxType) { @@ -49,15 +50,25 @@ namespace JT1078.FMp4 ///// //public string UserType { get; set; } + private int sizePosition; + /// /// /// /// - /// - public void ToBuffer(ref FMp4MessagePackWriter writer, out int sizePosition) + public void Start(ref FMp4MessagePackWriter writer) { writer.Skip(FixedSizeLength, out sizePosition); writer.WriteASCII(BoxType); } + + /// + /// + /// + /// + public void End(ref FMp4MessagePackWriter writer) + { + writer.WriteUInt32Return((uint)(writer.GetCurrentPosition() - sizePosition), sizePosition); + } } }