浏览代码

添加mp4 单元测试

master
waterliu99 3 年前
父节点
当前提交
246a4bdd29
共有 4 个文件被更改,包括 125 次插入86 次删除
  1. +3
    -0
      src/JT1078.FMp4.Test/JT1078.FMp4.Test.csproj
  2. +24
    -2
      src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs
  3. +89
    -83
      src/JT1078.FMp4/FMp4Encoder.cs
  4. +9
    -1
      src/JT1078.FMp4/JT1078.FMp4.xml

+ 3
- 0
src/JT1078.FMp4.Test/JT1078.FMp4.Test.csproj 查看文件

@@ -40,6 +40,9 @@
<None Update="FMP4\fragmented_demo_trun.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="H264\1078视频数据.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="H264\index.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>


+ 24
- 2
src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs 查看文件

@@ -433,6 +433,28 @@ namespace JT1078.FMp4.Test
fileStream.Close();
}

[Fact]
public void Test3_1()
{
FMp4Encoder fMp4Encoder = new FMp4Encoder();
var packages = ParseNALUTests();
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_3.mp4");
if (File.Exists(filepath))
{
File.Delete(filepath);
}
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
//fragment moof n
bool flag = true;
foreach (var package in packages)
{
var buffer= fMp4Encoder.EncoderVideo(package, flag);
flag = false;
fileStream.Write(buffer);
}
fileStream.Close();
}

[Fact]
public void Test4()
{
@@ -471,6 +493,7 @@ namespace JT1078.FMp4.Test
{
if (nalus.Count > 0)
{

var otherBuffer = fMp4Encoder.EncoderOtherVideoBox(nalus);
fileStream.Write(otherBuffer);
nalus.Clear();
@@ -537,7 +560,7 @@ namespace JT1078.FMp4.Test
public JT1078Package ParseNALUTest()
{
JT1078Package Package = null;
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_1.txt"));
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_3.txt"));
int mergeBodyLength = 0;
foreach (var line in lines)
{
@@ -549,7 +572,6 @@ namespace JT1078.FMp4.Test
}
return Package;
}

public List<JT1078Package> ParseNALUTests()
{
List<JT1078Package> packages = new List<JT1078Package>();


+ 89
- 83
src/JT1078.FMp4/FMp4Encoder.cs 查看文件

@@ -39,6 +39,9 @@ namespace JT1078.FMp4
const uint TrunFlags = 0x205;
const uint SampleDescriptionIndex = 1;
const uint TrackID = 1;
//to-do:如何处理多个用户观看,多个设备播放
ulong samplesize = 0;
uint SequenceNumber = 0;

/// <summary>
///
@@ -63,13 +66,19 @@ namespace JT1078.FMp4
fileTypeBox.MajorBrand = "isom";
fileTypeBox.MinorVersion = "\0\0\u0002\0";
fileTypeBox.CompatibleBrands.Add("isom");
fileTypeBox.CompatibleBrands.Add("iso6");
fileTypeBox.CompatibleBrands.Add("iso2");
fileTypeBox.CompatibleBrands.Add("avc1");
fileTypeBox.CompatibleBrands.Add("mp41");
fileTypeBox.CompatibleBrands.Add("iso5");
fileTypeBox.CompatibleBrands.Add("iso6");
//fileTypeBox.CompatibleBrands.Add("isom");
//fileTypeBox.CompatibleBrands.Add("iso2");
//fileTypeBox.CompatibleBrands.Add("avc1");
//fileTypeBox.CompatibleBrands.Add("mp41");
//fileTypeBox.CompatibleBrands.Add("iso5");
//fileTypeBox.CompatibleBrands.Add("iso6");
fileTypeBox.ToBuffer(ref writer);
var data = writer.FlushAndGetArray();
samplesize += (ulong)data.Length;
return data;
}
finally
@@ -92,17 +101,19 @@ namespace JT1078.FMp4
var spsInfo = h264GolombReader.ReadSPS();
//moov
MovieBox movieBox = new MovieBox();
movieBox.MovieHeaderBox = new MovieHeaderBox(0, 2);
movieBox.MovieHeaderBox = new MovieHeaderBox(0, 0);
movieBox.MovieHeaderBox.CreationTime = 0;
movieBox.MovieHeaderBox.ModificationTime = 0;
movieBox.MovieHeaderBox.Duration = 0;
movieBox.MovieHeaderBox.Timescale = 1000;
movieBox.MovieHeaderBox.NextTrackID = 99;
movieBox.MovieHeaderBox.Rate = 0x00010000;//typically 1.0 媒体速率,这个值代表原始倍速
movieBox.MovieHeaderBox.Volume = 0x0100;// typically,1.0 full volume 媒体音量,这个值代表满音量
movieBox.MovieHeaderBox.NextTrackID = 2;
movieBox.TrackBox = new TrackBox();
movieBox.TrackBox.TrackHeaderBox = new TrackHeaderBox(0, 3);
movieBox.TrackBox.TrackHeaderBox.CreationTime = 0;
movieBox.TrackBox.TrackHeaderBox.ModificationTime = 0;
movieBox.TrackBox.TrackHeaderBox.TrackID = TrackID;
movieBox.TrackBox.TrackHeaderBox.TrackID = TrackID;//1
movieBox.TrackBox.TrackHeaderBox.Duration = 0;
movieBox.TrackBox.TrackHeaderBox.TrackIsAudio = false;
movieBox.TrackBox.TrackHeaderBox.Width = (uint)spsInfo.width;
@@ -137,21 +148,22 @@ namespace JT1078.FMp4
avc1.AVCConfigurationBox.SPSs = new List<byte[]>() { sps.RawData };
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries.Add(avc1);
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.TimeToSampleBox = new TimeToSampleBox();
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SyncSampleBox = new SyncSampleBox();
//movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SyncSampleBox = new SyncSampleBox();
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleToChunkBox = new SampleToChunkBox();
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleSizeBox = new SampleSizeBox();
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.ChunkOffsetBox = new ChunkOffsetBox();
movieBox.MovieExtendsBox = new MovieExtendsBox();
movieBox.MovieExtendsBox.TrackExtendsBoxs = new List<TrackExtendsBox>();
TrackExtendsBox trex = new TrackExtendsBox();
trex.TrackID = TrackID;
trex.DefaultSampleDescriptionIndex = SampleDescriptionIndex;
trex.TrackID = TrackID;//1
trex.DefaultSampleDescriptionIndex = SampleDescriptionIndex;//1
trex.DefaultSampleDuration = 0;
trex.DefaultSampleSize = 0;
trex.DefaultSampleFlags = 0;
movieBox.MovieExtendsBox.TrackExtendsBoxs.Add(trex);
movieBox.ToBuffer(ref writer);
var data = writer.FlushAndGetArray();
samplesize += (ulong)data.Length;
return data;
}
finally
@@ -164,93 +176,87 @@ namespace JT1078.FMp4
/// 编码其他视频数据盒子
/// </summary>
/// <returns></returns>
public byte[] EncoderOtherVideoBox(in List<H264NALU> nalus)
public byte[] EncoderOtherVideoBox(List<H264NALU> nalus)
{
byte[] buffer = FMp4ArrayPool.Rent(nalus.Sum(s => s.RawData.Length + s.StartCodePrefix.Length) + 4096);
byte[] buffer= buffer = FMp4ArrayPool.Rent(nalus.Sum(m=>m.RawData.Length + m.StartCodePrefix.Length) + 4096);
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
try
{
var truns = new List<TrackRunBox.TrackRunInfo>();
List<byte[]> rawdatas = new List<byte[]>();
uint iSize = 0;
ulong lastTimestamp = 0;
string key = string.Empty;
for (var i = 0; i < nalus.Count; i++)
{
var nalu = nalus[i];
if (string.IsNullOrEmpty(key))
{
key = nalu.GetKey();
}
rawdatas.Add(nalu.RawData);
if (nalu.DataType == Protocol.Enums.JT1078DataType.视频I帧)
{
iSize += (uint)(nalu.RawData.Length + nalu.StartCodePrefix.Length);
}
else
{
if (iSize > 0)
{
truns.Add(new TrackRunBox.TrackRunInfo()
{
SampleSize = iSize,
});
iSize = 0;
}
truns.Add(new TrackRunBox.TrackRunInfo()
{
SampleSize = (uint)(nalu.RawData.Length + nalu.StartCodePrefix.Length),
});
}
if (i == (nalus.Count - 1))
{
lastTimestamp = nalu.Timestamp;
}
}
if (TrackInfos.TryGetValue(key, out TrackInfo trackInfo))
{
if (trackInfo.SN == uint.MaxValue)
{
trackInfo.SN = 1;
}
trackInfo.SN++;
}
else
{
trackInfo = new TrackInfo { SN = 1, DTS = 0 };
TrackInfos.Add(key, trackInfo);
}
var movieFragmentBox = new MovieFragmentBox();
movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox();
movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = trackInfo.SN;
movieFragmentBox.TrackFragmentBox = new TrackFragmentBox();
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(TfhdFlags);
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = TrackID;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = SampleDescriptionIndex;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = DefaultSampleDuration;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = truns[0].SampleSize;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = DefaultSampleFlags;
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox();
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = trackInfo.DTS;
trackInfo.DTS += (ulong)(truns.Count * DefaultSampleDuration);
TrackInfos[key] = trackInfo;
FragmentBox fragmentBox = new FragmentBox();
fragmentBox.MovieFragmentBox = new MovieFragmentBox();
fragmentBox.MovieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox();
fragmentBox.MovieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = SequenceNumber;
fragmentBox.MovieFragmentBox.TrackFragmentBox = new TrackFragmentBox();
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(0x39);//TfhdFlags
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = TrackID;//1
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.BaseDataOffset = samplesize;//基于前面盒子的长度
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = SampleDescriptionIndex;
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = DefaultSampleDuration;
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = (uint)(nalus.Sum(m => m.RawData.Length + m.StartCodePrefix.Length));
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = DefaultSampleFlags;
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox();
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = nalus[0].Timestamp * 1000;
//trun
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: TrunFlags);
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = FirstSampleFlags;
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = truns;
movieFragmentBox.ToBuffer(ref writer);

var mediaDataBox = new MediaDataBox();
mediaDataBox.Data = rawdatas;
mediaDataBox.ToBuffer(ref writer);

fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: 0x5 );//TrunFlags
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = 0;// FirstSampleFlags;
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = new List<TrackRunBox.TrackRunInfo>();
fragmentBox.MovieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo());
fragmentBox.MediaDataBox = new MediaDataBox();
fragmentBox.MediaDataBox.Data = nalus.Select(s => s.RawData).ToList();
fragmentBox.ToBuffer(ref writer);
var data = writer.FlushAndGetArray();
samplesize += (ulong)data.Length;
SequenceNumber++;
return data;
}
finally
{
FMp4ArrayPool.Return(buffer);
}
}
/// <summary>
/// 编码mp4 视频
/// </summary>
/// <param name="package"></param>
/// <param name="needVideoHeader"></param>
/// <returns></returns>
public byte[] EncoderVideo(JT1078Package package, bool needVideoHeader = false) {
H264Decoder h264Decoder = new H264Decoder();
byte[] buffer = FMp4ArrayPool.Rent(package.Bodies.Length * 2 + 4096);
FMp4MessagePackWriter flvMessagePackWriter = new FMp4MessagePackWriter(buffer);
var nalus = h264Decoder.ParseNALU(package);
if (nalus != null && nalus.Count > 0)
{
try
{
var sei = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == NalUnitType.SEI);
var sps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == NalUnitType.SPS);
var pps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == NalUnitType.PPS);
nalus.Remove(sei);
nalus.Remove(sps);
nalus.Remove(pps);
if (needVideoHeader)
{
//mp4 header
//ftype
var ftypHeader = EncoderFtypBox();
flvMessagePackWriter.WriteArray(ftypHeader);
// moov
var moov = EncoderMoovBox(sps, pps);
flvMessagePackWriter.WriteArray(moov);
//解析sps
}

var otherVideoTag = EncoderOtherVideoBox(nalus);
flvMessagePackWriter.WriteArray(otherVideoTag);
}
finally
{
FMp4ArrayPool.Return(buffer);
}
}
var data = flvMessagePackWriter.FlushAndGetArray();
return data;
}

struct TrackInfo


+ 9
- 1
src/JT1078.FMp4/JT1078.FMp4.xml 查看文件

@@ -1337,12 +1337,20 @@
</summary>
<returns></returns>
</member>
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderOtherVideoBox(System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU}@)">
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderOtherVideoBox(System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU})">
<summary>
编码其他视频数据盒子
</summary>
<returns></returns>
</member>
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderVideo(JT1078.Protocol.JT1078Package,System.Boolean)">
<summary>
编码mp4 视频
</summary>
<param name="package"></param>
<param name="needVideoHeader"></param>
<returns></returns>
</member>
<member name="P:JT1078.FMp4.FullBox.Version">
<summary>
unsigned int(8)


正在加载...
取消
保存