@@ -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); | |||||
} | |||||
} | } | ||||
} | } |
@@ -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() | ||||
{ | { | ||||
@@ -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 | ||||
@@ -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个字节 | ||||
@@ -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部分大小 | ||||
@@ -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) | ||||