@@ -20,306 +20,133 @@ namespace JT1078.Flv.Test | |||
[Fact] | |||
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 | |||
{ | |||
/// <summary> | |||
/// jt1078 转成 H264数据包 | |||
/// </summary> | |||
[Fact] | |||
public void Test_1() | |||
{ | |||
@@ -22,7 +22,8 @@ namespace JT1078.Flv | |||
/// </para> | |||
/// | |||
/// 手动编码 | |||
/// 1、<see cref="EncoderFlvHeader"/> | |||
/// 0、<see cref="EncoderFlvHeader"/> | |||
/// 1、插入 PriviousTagSize =0 always equal 0 | |||
/// 2、<see cref="EncoderScriptTag"/> | |||
/// 3、<see cref="EncoderFirstVideoTag"/> | |||
/// 4、<see cref="EncoderFirstAudioTag"/> | |||
@@ -36,11 +37,8 @@ namespace JT1078.Flv | |||
/// </summary> | |||
public class FlvEncoder : IDisposable | |||
{ | |||
uint previousTagSize; | |||
FlvHeader flvHeader = new FlvHeader(true, true); | |||
readonly FaacEncoder faacEncoder; | |||
readonly H264Decoder h264Decoder = new H264Decoder(); | |||
public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false) | |||
{ | |||
faacEncoder = new FaacEncoder(sampleRate, channels, sampleBit, adts); | |||
@@ -48,32 +46,24 @@ namespace JT1078.Flv | |||
/// <summary> | |||
/// 编码flv头 | |||
/// <para> | |||
/// 注意:本方法已写入<see cref="previousTagSize"/> | |||
/// </para> | |||
/// </summary> | |||
/// <param name="hasVideo"></param> | |||
/// <param name="hasAudio"></param> | |||
/// <param name="hasVideo">是否有视频</param> | |||
/// <param name="hasAudio">是否有音频</param> | |||
/// <returns></returns> | |||
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(); | |||
} | |||
/// <summary> | |||
/// 编码脚本Tag | |||
/// <para> | |||
/// 注意:本方法已写入<see cref="previousTagSize"/> | |||
/// </para> | |||
/// </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> | |||
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); | |||
try | |||
@@ -93,8 +83,12 @@ namespace JT1078.Flv | |||
new Amf3Metadata_VideoDataRate{Value = 0d}, | |||
new Amf3Metadata_VideoCodecId{Value = 7d}, | |||
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_AudioStereo()); | |||
} | |||
flvMessagePackWriter.WriteUInt32(previousTagSize); | |||
flvMessagePackWriter.WriteFlvTag(flvTags); | |||
var data = flvMessagePackWriter.FlushAndGetArray(); | |||
previousTagSize = (uint)(flvTags.DataSize + 11); | |||
return data; | |||
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11)); | |||
return flvMessagePackWriter.FlushAndGetArray(); | |||
} | |||
finally | |||
{ | |||
@@ -118,24 +110,19 @@ namespace JT1078.Flv | |||
} | |||
/// <summary> | |||
/// 编码首帧视频,即videoTag[0] | |||
/// <para> | |||
/// 注意:本方法已写入<see cref="previousTagSize"/> | |||
/// </para> | |||
/// 编码首帧视频,即videoTag[0] | |||
/// </summary> | |||
/// <param name="spsInfo">sps 解析后的数据</param> | |||
/// <param name="sps"></param> | |||
/// <param name="pps"></param> | |||
/// <param name="sei"></param> | |||
/// <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); | |||
try | |||
{ | |||
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 tag header | |||
FlvTags flvTags = new FlvTags | |||
@@ -163,11 +150,9 @@ namespace JT1078.Flv | |||
SPSBuffer = sps.RawData | |||
}; | |||
flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord; | |||
flvMessagePackWriter.WriteUInt32(previousTagSize); | |||
flvMessagePackWriter.WriteFlvTag(flvTags); | |||
var data = flvMessagePackWriter.FlushAndGetArray(); | |||
previousTagSize = (uint)(flvTags.DataSize + 11); | |||
return data; | |||
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11)); | |||
return flvMessagePackWriter.FlushAndGetArray(); | |||
} | |||
finally | |||
{ | |||
@@ -177,10 +162,8 @@ namespace JT1078.Flv | |||
/// <summary> | |||
/// 编码首帧音频,即audioTag[0] | |||
/// <para> | |||
/// 注意:本方法已写入<see cref="previousTagSize"/> | |||
/// </para> | |||
/// </summary> | |||
/// <param name="timestamp"></param> | |||
/// <returns></returns> | |||
public byte[] EncoderFirstAudioTag(ulong timestamp) | |||
{ | |||
@@ -197,11 +180,9 @@ namespace JT1078.Flv | |||
//flv body tag body | |||
AudioTagsData = new AudioTags(AACPacketType.AudioSpecificConfig) | |||
}; | |||
flvMessagePackWriter.WriteUInt32(previousTagSize); | |||
flvMessagePackWriter.WriteFlvTag(flvTags); | |||
var data = flvMessagePackWriter.FlushAndGetArray(); | |||
previousTagSize = (uint)(flvTags.DataSize + 11); | |||
return data; | |||
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11)); | |||
return flvMessagePackWriter.FlushAndGetArray(); | |||
} | |||
finally | |||
{ | |||
@@ -210,9 +191,9 @@ namespace JT1078.Flv | |||
} | |||
/// <summary> | |||
/// 编码非首帧视频 | |||
/// 编码视频 | |||
/// </summary> | |||
/// <param name="package"></param> | |||
/// <param name="package">1078包</param> | |||
/// <param name="needVideoHeader">是否需要首帧视频</param> | |||
/// <returns></returns> | |||
public byte[] EncoderVideoTag(JT1078Package package, bool needVideoHeader = false) | |||
@@ -231,14 +212,26 @@ namespace JT1078.Flv | |||
nalus.Remove(sei); | |||
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); | |||
} | |||
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(); | |||
@@ -280,15 +273,17 @@ namespace JT1078.Flv | |||
} | |||
if (aacFrameData != null && aacFrameData.Any())//编码成功,此时为一帧aac音频数据 | |||
{ | |||
// PreviousTagSize | |||
flvMessagePackWriter.WriteUInt32(previousTagSize); | |||
// Data Tag Frame | |||
flvMessagePackWriter.WriteArray(ConversionAacDataToAudioTag((uint)package.Timestamp, aacFrameData)); | |||
flvMessagePackWriter.WriteArray(EncoderAacAudioTag((uint)package.Timestamp, aacFrameData)); | |||
} | |||
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); | |||
try | |||
@@ -330,9 +325,8 @@ namespace JT1078.Flv | |||
// flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData); | |||
//} | |||
flvMessagePackWriter.WriteFlvTag(flvTags); | |||
var data = flvMessagePackWriter.FlushAndGetArray(); | |||
previousTagSize = (uint)(flvTags.DataSize + 11); | |||
return data; | |||
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11)); | |||
return flvMessagePackWriter.FlushAndGetArray(); | |||
} | |||
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); | |||
try | |||
@@ -358,7 +352,7 @@ namespace JT1078.Flv | |||
AudioTagsData = new AudioTags(AACPacketType.AudioFrame, aacFrameData) | |||
}; | |||
flvMessagePackWriter.WriteFlvTag(flvTags); | |||
previousTagSize = (uint)(flvTags.DataSize + 11); | |||
flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11)); | |||
return flvMessagePackWriter.FlushAndGetArray(); | |||
} | |||
finally | |||
@@ -10,7 +10,7 @@ namespace JT1078.Flv | |||
/// Tag Data部分大小 | |||
/// 3个字节 | |||
/// </summary> | |||
public uint DataSize { get; set; } | |||
public int DataSize { get; set; } | |||
/// <summary> | |||
/// Tag时间戳 | |||
/// 3个字节 | |||
@@ -214,15 +214,16 @@ | |||
</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)"/> | |||
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 | |||
自动编码 | |||
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 | |||
4、<see cref="M:JT1078.Flv.FlvEncoder.EncoderAudioTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传true | |||
</summary> | |||
@@ -230,34 +231,25 @@ | |||
<member name="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"> | |||
<summary> | |||
编码flv头 | |||
<para> | |||
注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/> | |||
</para> | |||
</summary> | |||
<param name="hasVideo"></param> | |||
<param name="hasAudio"></param> | |||
<param name="hasVideo">是否有视频</param> | |||
<param name="hasAudio">是否有音频</param> | |||
<returns></returns> | |||
</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> | |||
编码脚本Tag | |||
<para> | |||
注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/> | |||
</para> | |||
</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> | |||
</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> | |||
编码首帧视频,即videoTag[0] | |||
<para> | |||
注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/> | |||
</para> | |||
编码首帧视频,即videoTag[0] | |||
</summary> | |||
<param name="spsInfo">sps 解析后的数据</param> | |||
<param name="sps"></param> | |||
<param name="pps"></param> | |||
<param name="sei"></param> | |||
@@ -266,17 +258,15 @@ | |||
<member name="M:JT1078.Flv.FlvEncoder.EncoderFirstAudioTag(System.UInt64)"> | |||
<summary> | |||
编码首帧音频,即audioTag[0] | |||
<para> | |||
注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/> | |||
</para> | |||
</summary> | |||
<param name="timestamp"></param> | |||
<returns></returns> | |||
</member> | |||
<member name="M:JT1078.Flv.FlvEncoder.EncoderVideoTag(JT1078.Protocol.JT1078Package,System.Boolean)"> | |||
<summary> | |||
编码非首帧视频 | |||
编码视频 | |||
</summary> | |||
<param name="package"></param> | |||
<param name="package">1078包</param> | |||
<param name="needVideoHeader">是否需要首帧视频</param> | |||
<returns></returns> | |||
</member> | |||
@@ -288,6 +278,13 @@ | |||
<param name="needAacHeader">是否需要首帧音频</param> | |||
<returns></returns> | |||
</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"> | |||
<summary> | |||
Tag Data部分大小 | |||
@@ -33,7 +33,8 @@ namespace JT1078.Flv.MessagePack | |||
//todo:VIDEODATA | |||
break; | |||
} | |||
WriteInt24Return(GetCurrentPosition() - DataSizePosition -3-7, DataSizePosition); | |||
tag.DataSize = GetCurrentPosition() - 11; | |||
WriteInt24Return(tag.DataSize, DataSizePosition); | |||
} | |||
public void WriteUInt24(uint value) | |||