Browse Source

优化flv 编码,完善单元测试

tags/v1.1.0
waterliu99 5 years ago
parent
commit
12456e8e53
6 changed files with 201 additions and 379 deletions
  1. +117
    -290
      src/JT1078.Flv.Test/FlvEncoderTest.cs
  2. +3
    -0
      src/JT1078.Flv.Test/H264ToFlvTest.cs
  3. +54
    -60
      src/JT1078.Flv/FlvEncoder.cs
  4. +1
    -1
      src/JT1078.Flv/FlvTags.cs
  5. +24
    -27
      src/JT1078.Flv/JT1078.Flv.xml
  6. +2
    -1
      src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs

+ 117
- 290
src/JT1078.Flv.Test/FlvEncoderTest.cs View File

@@ -20,306 +20,133 @@ namespace JT1078.Flv.Test
[Fact] [Fact]
public void 测试第一帧的数据() public void 测试第一帧的数据()
{ {
JT1078Package Package = null;
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.txt"));
int mergeBodyLength = 0;
foreach (var line in lines)
FileStream fileStream = null;
try
{ {
var data = line.Split(',');
var bytes = data[6].ToHexBytes();
JT1078Package package = JT1078Serializer.Deserialize(bytes);
mergeBodyLength += package.DataBodyLength;
Package = JT1078Serializer.Merge(package);
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.txt"));
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.flv");
fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);

bool isNeedFirstHeadler = true;
FlvEncoder encoder = new FlvEncoder();
foreach (var line in lines)
{
var data = line.Split(',');
var bytes = data[6].ToHexBytes();
JT1078Package package = JT1078Serializer.Deserialize(bytes);
JT1078Package fullpackage = JT1078Serializer.Merge(package);
if (fullpackage != null)
{
var videoTag = encoder.EncoderVideoTag(fullpackage, isNeedFirstHeadler);
fileStream.Write(videoTag);
isNeedFirstHeadler = false;
}
}
} }
H264Decoder decoder = new H264Decoder();
//7 8 6 5 1 1 1 1 1 7 8 6 5 1 1 1 1
var nalus = decoder.ParseNALU(Package);
Assert.Equal(4, nalus.Count);
catch (Exception ex)
{


FlvEncoder encoder = new FlvEncoder();
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.flv");
if (File.Exists(filepath))
}
finally
{ {
File.Delete(filepath);
fileStream?.Close();
fileStream?.Dispose();
} }
FileStream fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
fileStream.Write(encoder.EncoderFlvHeader());
fileStream.Write(encoder.EncoderScriptTag());
fileStream.Write(encoder.EncoderVideoTag(Package, true));
fileStream.Close();
} }


//[Fact]
//public void 测试前几帧的数据()
//{
// FileStream fileStream = null;
// try
// {
// JT1078Package Package = null;
// List<H264NALU> h264NALULs = new List<H264NALU>();
// H264Decoder decoder = new H264Decoder();
// FlvEncoder encoder = new FlvEncoder();
// var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_2.txt"));
// foreach (var line in lines)
// {
// var data = line.Split(',');
// var bytes = data[6].ToHexBytes();
// JT1078Package package = JT1078Serializer.Deserialize(bytes);
// Package = JT1078Serializer.Merge(package);
// if (Package != null)
// {
// var tmp = decoder.ParseNALU(Package);
// if (tmp != null && tmp.Count > 0)
// {
// h264NALULs = h264NALULs.Concat(tmp).ToList();
// }
// }
// }

// var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_2.flv");
// if (File.Exists(filepath))
// {
// File.Delete(filepath);
// }
// fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
// fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer);
// var totalPage = (h264NALULs.Count + 10 - 1) / 10;
// for (var i = 0; i < totalPage; i++)
// {
// var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList());
// if (flv2.Length != 0)
// {
// fileStream.Write(flv2);
// }
// }
// }
// catch (Exception ex)
// {

// }
// finally
// {
// fileStream?.Close();
// fileStream?.Dispose();
// }
//}

//[Fact]
//public void 测试可以播放的Flv1()
//{
// FileStream fileStream = null;
// H264Decoder decoder = new H264Decoder();
// List<H264NALU> h264NALULs = new List<H264NALU>();
// FlvEncoder encoder = new FlvEncoder();
// try
// {
// var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_3.flv");
// var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_3.txt"));
// if (File.Exists(filepath))
// {
// File.Delete(filepath);
// }

// JT1078Package Package = null;

// foreach (var line in lines)
// {
// var data = line.Split(',');
// var bytes = data[6].ToHexBytes();
// JT1078Package package = JT1078Serializer.Deserialize(bytes);
// Package = JT1078Serializer.Merge(package);
// if (Package != null)
// {
// var tmp = decoder.ParseNALU(Package);
// if (tmp != null && tmp.Count > 0)
// {
// h264NALULs = h264NALULs.Concat(tmp).ToList();
// }
// }
// }

// fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
// fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer);
// var totalPage = (h264NALULs.Count + 10 - 1) / 10;
// for (var i = 0; i < totalPage; i++)
// {
// var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList());
// if (flv2.Length != 0)
// {
// fileStream.Write(flv2);
// }
// }
// }
// catch (Exception ex)
// {
// Assert.Throws<Exception>(() => { });
// }
// finally
// {
// fileStream?.Close();
// fileStream?.Dispose();
// }
//}

//[Fact]
//public void 测试可以播放的Flv2()
//{
// FileStream fileStream = null;
// H264Decoder decoder = new H264Decoder();
// List<H264NALU> h264NALULs = new List<H264NALU>();
// FlvEncoder encoder = new FlvEncoder();
// try
// {
// var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_4.flv");
// var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_4.txt"));
// if (File.Exists(filepath))
// {
// File.Delete(filepath);
// }

// JT1078Package Package = null;

// foreach (var line in lines)
// {
// var data = line.Split(',');
// var bytes = data[6].ToHexBytes();
// JT1078Package package = JT1078Serializer.Deserialize(bytes);
// Package = JT1078Serializer.Merge(package);
// if (Package != null)
// {
// var tmp = decoder.ParseNALU(Package);
// if (tmp != null && tmp.Count > 0)
// {
// h264NALULs = h264NALULs.Concat(tmp).ToList();
// }
// }
// }

// fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
// fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer);
// var totalPage = (h264NALULs.Count + 10 - 1) / 10;
// for (var i = 0; i < totalPage; i++)
// {
// var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList());
// if (flv2.Length != 0)
// {
// fileStream.Write(flv2);
// }
// }
// }
// catch (Exception ex)
// {
// Assert.Throws<Exception>(() => { });
// }
// finally
// {
// fileStream?.Close();
// fileStream?.Dispose();
// }
//}

//[Fact]
//public void 测试主次码流切换()
//{
// FileStream fileStream = null;
// H264Decoder decoder = new H264Decoder();
// List<H264NALU> h264NALULs = new List<H264NALU>();
// FlvEncoder encoder = new FlvEncoder();
// try
// {
// var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_5.flv");
// var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_5.txt"));
// if (File.Exists(filepath))
// {
// File.Delete(filepath);
// }

// JT1078Package Package = null;
[Fact]
public void 测试前几帧的数据()
{
FileStream fileStream = null;
try
{
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_2.txt"));
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_2.flv");
fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);

bool isNeedFirstHeadler = true;
FlvEncoder encoder = new FlvEncoder();
foreach (var line in lines)
{
var data = line.Split(',');
var bytes = data[6].ToHexBytes();
JT1078Package package = JT1078Serializer.Deserialize(bytes);
JT1078Package fullpackage = JT1078Serializer.Merge(package);
if (fullpackage != null)
{
var videoTag = encoder.EncoderVideoTag(fullpackage, isNeedFirstHeadler);
fileStream.Write(videoTag);
isNeedFirstHeadler = false;
}
}
}
catch (Exception ex)
{


// foreach (var line in lines)
// {
// var data = line.Split(',');
// var bytes = data[6].ToHexBytes();
// JT1078Package package = JT1078Serializer.Deserialize(bytes);
// Package = JT1078Serializer.Merge(package);
// if (Package != null)
// {
// var tmp = decoder.ParseNALU(Package);
// if (tmp != null && tmp.Count > 0)
// {
// h264NALULs = h264NALULs.Concat(tmp).ToList();
// }
// }
// }
// var tmp1 = h264NALULs.Where(w => w.NALUHeader.NalUnitType == 7).ToList();
// List<SPSInfo> tmpSpss = new List<SPSInfo>();
// List<ulong> times = new List<ulong>();
// List<ushort> lastIFrameIntervals = new List<ushort>();
// List<ushort> lastFrameIntervals = new List<ushort>();
// List<int> type = new List<int>();
// foreach (var item in h264NALULs)
// {
// //type.Add(item.NALUHeader.NalUnitType);
// times.Add(item.Timestamp);
// lastFrameIntervals.Add(item.LastFrameInterval);
// lastIFrameIntervals.Add(item.LastIFrameInterval);
// if (item.NALUHeader.NalUnitType == 7)
// {
// ExpGolombReader expGolombReader = new ExpGolombReader(item.RawData);
// tmpSpss.Add(expGolombReader.ReadSPS());
// }
// }
// fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
// var totalPage = (h264NALULs.Count + 10 - 1) / 10;
// for (var i = 0; i < totalPage; i++)
// {
// var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList());
// if (flv2.Length != 0)
// {
// //fileStream.Write(flv2);
// }
// }
// }
// catch (Exception ex)
// {
// Assert.Throws<Exception>(() => { });
// }
// finally
// {
// fileStream?.Close();
// fileStream?.Dispose();
// }
//}
}
finally
{
fileStream?.Close();
fileStream?.Dispose();
}
}


//[Fact]
//public void CreateScriptTagFrameTest()
//{
// FlvEncoder flvEncoder = new FlvEncoder();
// var hexData = flvEncoder.CreateScriptTagFrame(288, 352);
// Assert.Equal(151, hexData.Length);
//}
[Fact]
public void 测试可以播放的Flv3()
{
FileStream fileStream = null;
try
{
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_3.flv");
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_3.txt"));
fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);

bool isNeedFirstHeadler = true;
FlvEncoder encoder = new FlvEncoder();
foreach (var line in lines)
{
var data = line.Split(',');
var bytes = data[6].ToHexBytes();
JT1078Package package = JT1078Serializer.Deserialize(bytes);
JT1078Package fullpackage = JT1078Serializer.Merge(package);
if (fullpackage != null)
{
var videoTag = encoder.EncoderVideoTag(fullpackage, isNeedFirstHeadler);
fileStream.Write(videoTag);
isNeedFirstHeadler = false;
}
}
}
catch (Exception ex)
{
Assert.Throws<Exception>(() => { });
}
finally
{
fileStream?.Close();
fileStream?.Dispose();
}
}


//[Fact]
//public void CreateVideoTag0FrameTest()
//{
// FlvEncoder flvEncoder = new FlvEncoder();
// var hexData = flvEncoder.CreateVideoTag0Frame(
// new byte[] { 0x67, 0x4D, 0, 0x14, 0x95, 0xA8, 0x58, 0x25, 0x90 },
// new byte[] { 0x68, 0xEE, 0x3C, 0x80 },
// new SPSInfo { levelIdc = 0x14, profileIdc = 0x4d, profileCompat = 0 });
// Assert.Equal(40, hexData.Length);
//}
[Fact]
public void EncoderScriptTag()
{
FlvEncoder flvEncoder = new FlvEncoder();
var hexData = flvEncoder.EncoderScriptTag(new SPSInfo { width = 288, height = 352 });
Assert.Equal(155, hexData.Length);
}


//[Fact]
//public void GetFirstFlvFrameTest()
//{
// FlvEncoder flvEncoder = new FlvEncoder();
// string key = "test";
// var bufferFlvFrame = new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
// FlvEncoder.FirstFlvFrameCache.TryAdd(key, (2, new byte[] { 1, 2, 3, 4, 5, 6 }, true));
// var buffer = flvEncoder.GetFirstFlvFrame(key, bufferFlvFrame);
// //替换PreviousTagSize 4位的长度为首帧的 PreviousTagSize
// Assert.Equal(new byte[] { 1, 2, 3, 4, 5, 6, 0, 0, 0, 2, 0xE, 0xF }, buffer);
//}
[Fact]
public void EncoderFirstVideoTag()
{
FlvEncoder flvEncoder = new FlvEncoder();
var hexData = flvEncoder.EncoderFirstVideoTag(
new SPSInfo { levelIdc = 0x14, profileIdc = 0x4d, profileCompat = 0 },
new H264NALU { RawData = new byte[] { 0x67, 0x4D, 0, 0x14, 0x95, 0xA8, 0x58, 0x25, 0x90 } },
new H264NALU { RawData = new byte[] { 0x68, 0xEE, 0x3C, 0x80 } },
new H264NALU()
);
Assert.Equal(44, hexData.Length);
}
} }
} }

+ 3
- 0
src/JT1078.Flv.Test/H264ToFlvTest.cs View File

@@ -12,6 +12,9 @@ namespace JT1078.Flv.Test
{ {
public class H264ToFlvTest public class H264ToFlvTest
{ {
/// <summary>
/// jt1078 转成 H264数据包
/// </summary>
[Fact] [Fact]
public void Test_1() public void Test_1()
{ {


+ 54
- 60
src/JT1078.Flv/FlvEncoder.cs View File

@@ -22,7 +22,8 @@ namespace JT1078.Flv
/// </para> /// </para>
/// ///
/// 手动编码 /// 手动编码
/// 1、<see cref="EncoderFlvHeader"/>
/// 0、<see cref="EncoderFlvHeader"/>
/// 1、插入 PriviousTagSize =0 always equal 0
/// 2、<see cref="EncoderScriptTag"/> /// 2、<see cref="EncoderScriptTag"/>
/// 3、<see cref="EncoderFirstVideoTag"/> /// 3、<see cref="EncoderFirstVideoTag"/>
/// 4、<see cref="EncoderFirstAudioTag"/> /// 4、<see cref="EncoderFirstAudioTag"/>
@@ -36,11 +37,8 @@ namespace JT1078.Flv
/// </summary> /// </summary>
public class FlvEncoder : IDisposable public class FlvEncoder : IDisposable
{ {
uint previousTagSize;
FlvHeader flvHeader = new FlvHeader(true, true);
readonly FaacEncoder faacEncoder; readonly FaacEncoder faacEncoder;
readonly H264Decoder h264Decoder = new H264Decoder(); readonly H264Decoder h264Decoder = new H264Decoder();

public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false) public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false)
{ {
faacEncoder = new FaacEncoder(sampleRate, channels, sampleBit, adts); faacEncoder = new FaacEncoder(sampleRate, channels, sampleBit, adts);
@@ -48,32 +46,24 @@ namespace JT1078.Flv


/// <summary> /// <summary>
/// 编码flv头 /// 编码flv头
/// <para>
/// 注意:本方法已写入<see cref="previousTagSize"/>
/// </para>
/// </summary> /// </summary>
/// <param name="hasVideo"></param>
/// <param name="hasAudio"></param>
/// <param name="hasVideo">是否有视频</param>
/// <param name="hasAudio">是否有音频</param>
/// <returns></returns> /// <returns></returns>
public byte[] EncoderFlvHeader(bool hasVideo = true, bool hasAudio = false) public byte[] EncoderFlvHeader(bool hasVideo = true, bool hasAudio = false)
{ {
previousTagSize = 0;
flvHeader = new FlvHeader(hasVideo, hasAudio);
var flvHeader = new FlvHeader(hasVideo, hasAudio);
return flvHeader.ToArray().ToArray(); return flvHeader.ToArray().ToArray();
} }


/// <summary> /// <summary>
/// 编码脚本Tag /// 编码脚本Tag
/// <para>
/// 注意:本方法已写入<see cref="previousTagSize"/>
/// </para>
/// </summary> /// </summary>
/// <param name="width">视频宽度</param>
/// <param name="height">视频高度</param>
/// <param name="hasAudio">是否含有音频,如果有,则写入音频配置,后来发现即便是有音频,这里给<c>false</c>也没关系</param>
/// <param name="frameRate">帧率</param>
/// <param name="spsInfo">解析后的sps信息</param>
/// <param name="hasAudio">是否有音频</param>
/// <param name="frameRate">帧率 默认25d 即每秒25帧</param>
/// <returns></returns> /// <returns></returns>
public byte[] EncoderScriptTag(bool hasAudio = false, double frameRate = 25d)
public byte[] EncoderScriptTag(SPSInfo spsInfo, bool hasAudio = false, double frameRate = 25d)
{ {
byte[] buffer = FlvArrayPool.Rent(1024); byte[] buffer = FlvArrayPool.Rent(1024);
try try
@@ -93,8 +83,12 @@ namespace JT1078.Flv
new Amf3Metadata_VideoDataRate{Value = 0d}, new Amf3Metadata_VideoDataRate{Value = 0d},
new Amf3Metadata_VideoCodecId{Value = 7d}, new Amf3Metadata_VideoCodecId{Value = 7d},
new Amf3Metadata_FrameRate{Value = frameRate}, new Amf3Metadata_FrameRate{Value = frameRate},
new Amf3Metadata_Width(),
new Amf3Metadata_Height(),
new Amf3Metadata_Width(){
Value=spsInfo.width
},
new Amf3Metadata_Height(){
Value=spsInfo.height
},
} }
} }
}; };
@@ -105,11 +99,9 @@ namespace JT1078.Flv
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioSampleSize()); flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioSampleSize());
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioStereo()); flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioStereo());
} }
flvMessagePackWriter.WriteUInt32(previousTagSize);
flvMessagePackWriter.WriteFlvTag(flvTags); flvMessagePackWriter.WriteFlvTag(flvTags);
var data = flvMessagePackWriter.FlushAndGetArray();
previousTagSize = (uint)(flvTags.DataSize + 11);
return data;
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
return flvMessagePackWriter.FlushAndGetArray();
} }
finally finally
{ {
@@ -118,24 +110,19 @@ namespace JT1078.Flv
} }


/// <summary> /// <summary>
/// 编码首帧视频,即videoTag[0]
/// <para>
/// 注意:本方法已写入<see cref="previousTagSize"/>
/// </para>
/// 编码首帧视频,即videoTag[0]
/// </summary> /// </summary>
/// <param name="spsInfo">sps 解析后的数据</param>
/// <param name="sps"></param> /// <param name="sps"></param>
/// <param name="pps"></param> /// <param name="pps"></param>
/// <param name="sei"></param> /// <param name="sei"></param>
/// <returns></returns> /// <returns></returns>
public byte[] EncoderFirstVideoTag(H264NALU sps, H264NALU pps, H264NALU sei)
public byte[] EncoderFirstVideoTag(SPSInfo spsInfo, H264NALU sps, H264NALU pps, H264NALU sei)
{ {
byte[] buffer = FlvArrayPool.Rent(2048); byte[] buffer = FlvArrayPool.Rent(2048);
try try
{ {
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
var rawData = h264Decoder.DiscardEmulationPreventionBytes(sps.RawData);
ExpGolombReader h264GolombReader = new ExpGolombReader(rawData);
SPSInfo spsInfo = h264GolombReader.ReadSPS();
//flv body video tag //flv body video tag
//flv body tag header //flv body tag header
FlvTags flvTags = new FlvTags FlvTags flvTags = new FlvTags
@@ -163,11 +150,9 @@ namespace JT1078.Flv
SPSBuffer = sps.RawData SPSBuffer = sps.RawData
}; };
flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord; flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord;
flvMessagePackWriter.WriteUInt32(previousTagSize);
flvMessagePackWriter.WriteFlvTag(flvTags); flvMessagePackWriter.WriteFlvTag(flvTags);
var data = flvMessagePackWriter.FlushAndGetArray();
previousTagSize = (uint)(flvTags.DataSize + 11);
return data;
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
return flvMessagePackWriter.FlushAndGetArray();
} }
finally finally
{ {
@@ -177,10 +162,8 @@ namespace JT1078.Flv


/// <summary> /// <summary>
/// 编码首帧音频,即audioTag[0] /// 编码首帧音频,即audioTag[0]
/// <para>
/// 注意:本方法已写入<see cref="previousTagSize"/>
/// </para>
/// </summary> /// </summary>
/// <param name="timestamp"></param>
/// <returns></returns> /// <returns></returns>
public byte[] EncoderFirstAudioTag(ulong timestamp) public byte[] EncoderFirstAudioTag(ulong timestamp)
{ {
@@ -197,11 +180,9 @@ namespace JT1078.Flv
//flv body tag body //flv body tag body
AudioTagsData = new AudioTags(AACPacketType.AudioSpecificConfig) AudioTagsData = new AudioTags(AACPacketType.AudioSpecificConfig)
}; };
flvMessagePackWriter.WriteUInt32(previousTagSize);
flvMessagePackWriter.WriteFlvTag(flvTags); flvMessagePackWriter.WriteFlvTag(flvTags);
var data = flvMessagePackWriter.FlushAndGetArray();
previousTagSize = (uint)(flvTags.DataSize + 11);
return data;
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
return flvMessagePackWriter.FlushAndGetArray();
} }
finally finally
{ {
@@ -210,9 +191,9 @@ namespace JT1078.Flv
} }


/// <summary> /// <summary>
/// 编码非首帧视频
/// 编码视频
/// </summary> /// </summary>
/// <param name="package"></param>
/// <param name="package">1078包</param>
/// <param name="needVideoHeader">是否需要首帧视频</param> /// <param name="needVideoHeader">是否需要首帧视频</param>
/// <returns></returns> /// <returns></returns>
public byte[] EncoderVideoTag(JT1078Package package, bool needVideoHeader = false) public byte[] EncoderVideoTag(JT1078Package package, bool needVideoHeader = false)
@@ -231,14 +212,26 @@ namespace JT1078.Flv
nalus.Remove(sei); nalus.Remove(sei);
if (needVideoHeader) if (needVideoHeader)
{ {
var firstVideoTag = EncoderFirstVideoTag(sps, pps, sei);
//flv header
var flvHeader = EncoderFlvHeader(true, false);
flvMessagePackWriter.WriteArray(flvHeader);
// always 0
flvMessagePackWriter.WriteUInt32(0);
//解析sps
var rawData = h264Decoder.DiscardEmulationPreventionBytes(sps.RawData);
ExpGolombReader h264GolombReader = new ExpGolombReader(rawData);
SPSInfo spsInfo = h264GolombReader.ReadSPS();
//script tag
var scriptTag = EncoderScriptTag(spsInfo);
flvMessagePackWriter.WriteArray(scriptTag);
// first video tag
var firstVideoTag = EncoderFirstVideoTag(spsInfo, sps, pps, sei);
flvMessagePackWriter.WriteArray(firstVideoTag); flvMessagePackWriter.WriteArray(firstVideoTag);
} }
foreach (var naln in nalus) foreach (var naln in nalus)
{ {
flvMessagePackWriter.WriteUInt32(previousTagSize);
var videoTag = ConversionNaluToVideoTag(naln);
flvMessagePackWriter.WriteArray(videoTag);
var otherVideoTag = EncoderOtherVideoTag(naln);
flvMessagePackWriter.WriteArray(otherVideoTag);
} }
} }
return flvMessagePackWriter.FlushAndGetArray(); return flvMessagePackWriter.FlushAndGetArray();
@@ -280,15 +273,17 @@ namespace JT1078.Flv
} }
if (aacFrameData != null && aacFrameData.Any())//编码成功,此时为一帧aac音频数据 if (aacFrameData != null && aacFrameData.Any())//编码成功,此时为一帧aac音频数据
{ {
// PreviousTagSize
flvMessagePackWriter.WriteUInt32(previousTagSize);
// Data Tag Frame // Data Tag Frame
flvMessagePackWriter.WriteArray(ConversionAacDataToAudioTag((uint)package.Timestamp, aacFrameData));
flvMessagePackWriter.WriteArray(EncoderAacAudioTag((uint)package.Timestamp, aacFrameData));
} }
return flvMessagePackWriter.FlushAndGetArray(); return flvMessagePackWriter.FlushAndGetArray();
} }

byte[] ConversionNaluToVideoTag(H264NALU nALU)
/// <summary>
/// 编码非首帧视频
/// </summary>
/// <param name="nALU"></param>
/// <returns></returns>
public byte[] EncoderOtherVideoTag(H264NALU nALU)
{ {
byte[] buffer = FlvArrayPool.Rent(65535); byte[] buffer = FlvArrayPool.Rent(65535);
try try
@@ -330,9 +325,8 @@ namespace JT1078.Flv
// flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData); // flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData);
//} //}
flvMessagePackWriter.WriteFlvTag(flvTags); flvMessagePackWriter.WriteFlvTag(flvTags);
var data = flvMessagePackWriter.FlushAndGetArray();
previousTagSize = (uint)(flvTags.DataSize + 11);
return data;
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
return flvMessagePackWriter.FlushAndGetArray();
} }
finally finally
{ {
@@ -340,7 +334,7 @@ namespace JT1078.Flv
} }
} }


byte[] ConversionAacDataToAudioTag(uint timestamp, byte[] aacFrameData)
byte[] EncoderAacAudioTag(uint timestamp, byte[] aacFrameData)
{ {
byte[] buffer = FlvArrayPool.Rent(65535); byte[] buffer = FlvArrayPool.Rent(65535);
try try
@@ -358,7 +352,7 @@ namespace JT1078.Flv
AudioTagsData = new AudioTags(AACPacketType.AudioFrame, aacFrameData) AudioTagsData = new AudioTags(AACPacketType.AudioFrame, aacFrameData)
}; };
flvMessagePackWriter.WriteFlvTag(flvTags); flvMessagePackWriter.WriteFlvTag(flvTags);
previousTagSize = (uint)(flvTags.DataSize + 11);
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
return flvMessagePackWriter.FlushAndGetArray(); return flvMessagePackWriter.FlushAndGetArray();
} }
finally finally


+ 1
- 1
src/JT1078.Flv/FlvTags.cs View File

@@ -10,7 +10,7 @@ namespace JT1078.Flv
/// Tag Data部分大小 /// Tag Data部分大小
/// 3个字节 /// 3个字节
/// </summary> /// </summary>
public uint DataSize { get; set; }
public int DataSize { get; set; }
/// <summary> /// <summary>
/// Tag时间戳 /// Tag时间戳
/// 3个字节 /// 3个字节


+ 24
- 27
src/JT1078.Flv/JT1078.Flv.xml View File

@@ -214,15 +214,16 @@
</para> </para>
手动编码 手动编码
1、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"/>
2、<see cref="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(System.Boolean,System.Double)"/>
3、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFirstVideoTag(JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU)"/>
0、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"/>
1、插入 PriviousTagSize =0 always equal 0
2、<see cref="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(JT1078.Protocol.MessagePack.SPSInfo,System.Boolean,System.Double)"/>
3、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFirstVideoTag(JT1078.Protocol.MessagePack.SPSInfo,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU)"/>
4、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFirstAudioTag(System.UInt64)"/> 4、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFirstAudioTag(System.UInt64)"/>
5、<see cref="M:JT1078.Flv.FlvEncoder.EncoderVideoTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传false 5、<see cref="M:JT1078.Flv.FlvEncoder.EncoderVideoTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传false
6、<see cref="M:JT1078.Flv.FlvEncoder.EncoderAudioTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传false 6、<see cref="M:JT1078.Flv.FlvEncoder.EncoderAudioTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传false
自动编码 自动编码
1、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"/> 1、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"/>
2、<see cref="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(System.Boolean,System.Double)"/>
2、<see cref="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(JT1078.Protocol.MessagePack.SPSInfo,System.Boolean,System.Double)"/>
3、<see cref="M:JT1078.Flv.FlvEncoder.EncoderVideoTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传true 3、<see cref="M:JT1078.Flv.FlvEncoder.EncoderVideoTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传true
4、<see cref="M:JT1078.Flv.FlvEncoder.EncoderAudioTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传true 4、<see cref="M:JT1078.Flv.FlvEncoder.EncoderAudioTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传true
</summary> </summary>
@@ -230,34 +231,25 @@
<member name="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"> <member name="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)">
<summary> <summary>
编码flv头 编码flv头
<para>
注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/>
</para>
</summary> </summary>
<param name="hasVideo"></param>
<param name="hasAudio"></param>
<param name="hasVideo">是否有视频</param>
<param name="hasAudio">是否有音频</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(System.Boolean,System.Double)">
<member name="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(JT1078.Protocol.MessagePack.SPSInfo,System.Boolean,System.Double)">
<summary> <summary>
编码脚本Tag 编码脚本Tag
<para>
注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/>
</para>
</summary> </summary>
<param name="width">视频宽度</param>
<param name="height">视频高度</param>
<param name="hasAudio">是否含有音频,如果有,则写入音频配置,后来发现即便是有音频,这里给<c>false</c>也没关系</param>
<param name="frameRate">帧率</param>
<param name="spsInfo">解析后的sps信息</param>
<param name="hasAudio">是否有音频</param>
<param name="frameRate">帧率 默认25d 即每秒25帧</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:JT1078.Flv.FlvEncoder.EncoderFirstVideoTag(JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU)">
<member name="M:JT1078.Flv.FlvEncoder.EncoderFirstVideoTag(JT1078.Protocol.MessagePack.SPSInfo,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU)">
<summary> <summary>
编码首帧视频,即videoTag[0]
<para>
注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/>
</para>
编码首帧视频,即videoTag[0]
</summary> </summary>
<param name="spsInfo">sps 解析后的数据</param>
<param name="sps"></param> <param name="sps"></param>
<param name="pps"></param> <param name="pps"></param>
<param name="sei"></param> <param name="sei"></param>
@@ -266,17 +258,15 @@
<member name="M:JT1078.Flv.FlvEncoder.EncoderFirstAudioTag(System.UInt64)"> <member name="M:JT1078.Flv.FlvEncoder.EncoderFirstAudioTag(System.UInt64)">
<summary> <summary>
编码首帧音频,即audioTag[0] 编码首帧音频,即audioTag[0]
<para>
注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/>
</para>
</summary> </summary>
<param name="timestamp"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:JT1078.Flv.FlvEncoder.EncoderVideoTag(JT1078.Protocol.JT1078Package,System.Boolean)"> <member name="M:JT1078.Flv.FlvEncoder.EncoderVideoTag(JT1078.Protocol.JT1078Package,System.Boolean)">
<summary> <summary>
编码非首帧视频
编码视频
</summary> </summary>
<param name="package"></param>
<param name="package">1078包</param>
<param name="needVideoHeader">是否需要首帧视频</param> <param name="needVideoHeader">是否需要首帧视频</param>
<returns></returns> <returns></returns>
</member> </member>
@@ -288,6 +278,13 @@
<param name="needAacHeader">是否需要首帧音频</param> <param name="needAacHeader">是否需要首帧音频</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:JT1078.Flv.FlvEncoder.EncoderOtherVideoTag(JT1078.Protocol.H264.H264NALU)">
<summary>
编码非首帧视频
</summary>
<param name="nALU"></param>
<returns></returns>
</member>
<member name="P:JT1078.Flv.FlvTags.DataSize"> <member name="P:JT1078.Flv.FlvTags.DataSize">
<summary> <summary>
Tag Data部分大小 Tag Data部分大小


+ 2
- 1
src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs View File

@@ -33,7 +33,8 @@ namespace JT1078.Flv.MessagePack
//todo:VIDEODATA //todo:VIDEODATA
break; break;
} }
WriteInt24Return(GetCurrentPosition() - DataSizePosition -3-7, DataSizePosition);
tag.DataSize = GetCurrentPosition() - 11;
WriteInt24Return(tag.DataSize, DataSizePosition);
} }


public void WriteUInt24(uint value) public void WriteUInt24(uint value)


Loading…
Cancel
Save