From 715d0eb6779c906999d9d1ec06a091aafb1264d3 Mon Sep 17 00:00:00 2001
From: "SmallChi(Koike)" <564952747@qq.com>
Date: Tue, 22 Sep 2020 13:54:51 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0fmp4=E7=9B=92=E5=AD=90mvhd?=
=?UTF-8?q?=E3=80=81mdhd=E3=80=81tkhd=E7=9A=84=E7=BB=84=E5=8C=85=E5=8F=8A?=
=?UTF-8?q?=E5=AF=B9=E5=BA=94=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Boxs/MediaHeaderBox_Test.cs | 39 ++++++++++++
.../Boxs/MovieHeaderBox_Test.cs | 48 +++++++++++++++
.../Boxs/TrackHeaderBox_Test.cs | 49 +++++++++++++++
src/JT1078.FMp4/Boxs/FileTypeBox.cs | 4 +-
src/JT1078.FMp4/Boxs/MediaHeaderBox.cs | 38 ++++++++++--
src/JT1078.FMp4/Boxs/MovieHeaderBox.cs | 51 ++++++++++++++--
src/JT1078.FMp4/Boxs/TrackBox.cs | 6 +-
src/JT1078.FMp4/Boxs/TrackHeaderBox.cs | 61 ++++++++++++++++---
src/JT1078.FMp4/FullBox.cs | 13 +++-
src/JT1078.FMp4/JT1078.FMp4.xml | 17 +++++-
.../MessagePack/FMp4MessagePackWriter.cs | 15 ++++-
src/JT1078.FMp4/Mp4Box.cs | 19 ++++--
12 files changed, 327 insertions(+), 33 deletions(-)
create mode 100644 src/JT1078.FMp4.Test/Boxs/MediaHeaderBox_Test.cs
create mode 100644 src/JT1078.FMp4.Test/Boxs/MovieHeaderBox_Test.cs
create mode 100644 src/JT1078.FMp4.Test/Boxs/TrackHeaderBox_Test.cs
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);
+ }
}
}