|
|
@@ -45,175 +45,7 @@ namespace JT1078.Flv |
|
|
|
{ |
|
|
|
logger = loggerFactory.CreateLogger("FlvEncoder"); |
|
|
|
} |
|
|
|
internal byte[] CreateScriptTagFrame(int width, int height, double frameRate = 25d) |
|
|
|
{ |
|
|
|
byte[] buffer = FlvArrayPool.Rent(1024); |
|
|
|
try |
|
|
|
{ |
|
|
|
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); |
|
|
|
//flv body script tag |
|
|
|
//flv body tag header |
|
|
|
FlvTags flvTags = new FlvTags(); |
|
|
|
flvTags.Type = TagType.ScriptData; |
|
|
|
flvTags.Timestamp = 0; |
|
|
|
flvTags.TimestampExt = 0; |
|
|
|
flvTags.StreamId = 0; |
|
|
|
//flv body tag body |
|
|
|
flvTags.DataTagsData = new Amf3(); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas = new List<IAmf3Metadata>(); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Duration |
|
|
|
{ |
|
|
|
Value = 0d |
|
|
|
}); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_VideoDataRate |
|
|
|
{ |
|
|
|
Value = 0d |
|
|
|
}); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_VideoCodecId |
|
|
|
{ |
|
|
|
Value = 7d |
|
|
|
}); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_FrameRate |
|
|
|
{ |
|
|
|
Value = frameRate |
|
|
|
}); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Width |
|
|
|
{ |
|
|
|
Value = width |
|
|
|
}); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Height |
|
|
|
{ |
|
|
|
Value = height |
|
|
|
}); |
|
|
|
flvMessagePackWriter.WriteFlvTag(flvTags); |
|
|
|
return flvMessagePackWriter.FlushAndGetArray(); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
FlvArrayPool.Return(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
internal byte[] CreateVideoTag0Frame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo) |
|
|
|
{ |
|
|
|
byte[] buffer = FlvArrayPool.Rent(2048); |
|
|
|
try |
|
|
|
{ |
|
|
|
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); |
|
|
|
//flv body video tag |
|
|
|
//flv body tag header |
|
|
|
FlvTags flvTags = new FlvTags(); |
|
|
|
flvTags.Type = TagType.Video; |
|
|
|
flvTags.Timestamp = 0; |
|
|
|
flvTags.TimestampExt = 0; |
|
|
|
flvTags.StreamId = 0; |
|
|
|
//flv body tag body |
|
|
|
flvTags.VideoTagsData = new VideoTags(); |
|
|
|
flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; |
|
|
|
flvTags.VideoTagsData.VideoData = new AvcVideoPacke(); |
|
|
|
flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.SequenceHeader; |
|
|
|
flvTags.VideoTagsData.VideoData.CompositionTime = 0; |
|
|
|
AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(); |
|
|
|
aVCDecoderConfigurationRecord.AVCProfileIndication = spsInfo.profileIdc; |
|
|
|
aVCDecoderConfigurationRecord.ProfileCompatibility = (byte)spsInfo.profileCompat; |
|
|
|
aVCDecoderConfigurationRecord.AVCLevelIndication = spsInfo.levelIdc; |
|
|
|
aVCDecoderConfigurationRecord.NumOfPictureParameterSets = 1; |
|
|
|
aVCDecoderConfigurationRecord.PPSBuffer = ppsRawData; |
|
|
|
aVCDecoderConfigurationRecord.SPSBuffer = spsRawData; |
|
|
|
flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord; |
|
|
|
flvMessagePackWriter.WriteFlvTag(flvTags); |
|
|
|
return flvMessagePackWriter.FlushAndGetArray(); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
FlvArrayPool.Return(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
internal byte[] CreateSecondVideoTag0Frame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo) |
|
|
|
{ |
|
|
|
byte[] buffer = FlvArrayPool.Rent(2048); |
|
|
|
try |
|
|
|
{ |
|
|
|
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); |
|
|
|
//flv body video tag |
|
|
|
//flv body tag header |
|
|
|
FlvTags flvTags = new FlvTags(); |
|
|
|
flvTags.Type = TagType.Video; |
|
|
|
flvTags.Timestamp =0; |
|
|
|
flvTags.TimestampExt = 0; |
|
|
|
flvTags.StreamId = 0; |
|
|
|
//flv body tag body |
|
|
|
flvTags.VideoTagsData = new VideoTags(); |
|
|
|
flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; |
|
|
|
flvTags.VideoTagsData.VideoData = new AvcVideoPacke(); |
|
|
|
flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.SequenceHeader; |
|
|
|
flvTags.VideoTagsData.VideoData.CompositionTime = 0; |
|
|
|
AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(); |
|
|
|
aVCDecoderConfigurationRecord.AVCProfileIndication = spsInfo.profileIdc; |
|
|
|
aVCDecoderConfigurationRecord.ProfileCompatibility = (byte)spsInfo.profileCompat; |
|
|
|
aVCDecoderConfigurationRecord.AVCLevelIndication = spsInfo.levelIdc; |
|
|
|
aVCDecoderConfigurationRecord.NumOfPictureParameterSets = 1; |
|
|
|
aVCDecoderConfigurationRecord.PPSBuffer = ppsRawData; |
|
|
|
aVCDecoderConfigurationRecord.SPSBuffer = spsRawData; |
|
|
|
flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord; |
|
|
|
flvMessagePackWriter.WriteFlvTag(flvTags); |
|
|
|
return flvMessagePackWriter.FlushAndGetArray(); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
FlvArrayPool.Return(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
internal byte[] CreateVideoTagOtherFrame(FlvFrameInfo flvFrameInfo, H264NALU nALU, H264NALU sei) |
|
|
|
{ |
|
|
|
byte[] buffer = FlvArrayPool.Rent(65535); |
|
|
|
try |
|
|
|
{ |
|
|
|
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); |
|
|
|
//flv body video tag |
|
|
|
//flv body tag header |
|
|
|
FlvTags flvTags = new FlvTags(); |
|
|
|
flvTags.Type = TagType.Video; |
|
|
|
//pts |
|
|
|
flvTags.Timestamp = flvFrameInfo.Interval; |
|
|
|
flvTags.TimestampExt = 0; |
|
|
|
flvTags.StreamId = 0; |
|
|
|
//flv body tag body |
|
|
|
flvTags.VideoTagsData = new VideoTags(); |
|
|
|
flvTags.VideoTagsData.VideoData = new AvcVideoPacke(); |
|
|
|
flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw; |
|
|
|
if (nALU.NALUHeader.NalUnitType == 5 || nALU.DataType == JT1078DataType.视频I帧) |
|
|
|
{ |
|
|
|
flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
flvTags.VideoTagsData.FrameType = FrameType.InterFrame; |
|
|
|
} |
|
|
|
if(flvFrameInfo.DataType== JT1078DataType.视频I帧) |
|
|
|
{ |
|
|
|
//cts |
|
|
|
flvTags.VideoTagsData.VideoData.CompositionTime = nALU.LastIFrameInterval; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
//cts |
|
|
|
flvTags.VideoTagsData.VideoData.CompositionTime = nALU.LastFrameInterval; |
|
|
|
} |
|
|
|
flvTags.VideoTagsData.VideoData.MultiData = new List<byte[]>(); |
|
|
|
flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData); |
|
|
|
//忽略sei |
|
|
|
//if (sei != null && sei.RawData != null && sei.RawData.Length > 0) |
|
|
|
//{ |
|
|
|
// flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData); |
|
|
|
//} |
|
|
|
flvMessagePackWriter.WriteFlvTag(flvTags); |
|
|
|
return flvMessagePackWriter.FlushAndGetArray(); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
FlvArrayPool.Return(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// |
|
|
|
/// </summary> |
|
|
@@ -227,7 +59,7 @@ namespace JT1078.Flv |
|
|
|
try |
|
|
|
{ |
|
|
|
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); |
|
|
|
H264NALU sps=null, pps=null, sei=null; |
|
|
|
H264NALU sps = null, pps = null, sei = null; |
|
|
|
foreach (var naln in nALUs) |
|
|
|
{ |
|
|
|
key = key ?? naln.GetKey(); |
|
|
@@ -260,11 +92,11 @@ namespace JT1078.Flv |
|
|
|
logger.LogDebug($"Cache:{spsInfoCache.height}-{spsInfoCache.width},Current:{spsInfo.height}-{spsInfo.width}"); |
|
|
|
} |
|
|
|
} |
|
|
|
var secondFlvKeyFrame=CreateSecondFlvKeyFrame(sps.RawData, pps.RawData, spsInfo, flvFrameInfo); |
|
|
|
var secondFlvKeyFrame = CreateFirstFlvKeyFrame(sps.RawData, pps.RawData, spsInfo, flvFrameInfo.PreviousTagSize); |
|
|
|
flvMessagePackWriter.WriteArray(secondFlvKeyFrame.Buffer); |
|
|
|
flvFrameInfo.PreviousTagSize = secondFlvKeyFrame.PreviousTagSize; |
|
|
|
FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo); |
|
|
|
if(FirstFlvFrameCache.TryGetValue(key,out var firstFlvFrameCacche)) |
|
|
|
if (FirstFlvFrameCache.TryGetValue(key, out var firstFlvFrameCacche)) |
|
|
|
{ |
|
|
|
FirstFlvFrameCache.TryUpdate(key, (secondFlvKeyFrame.PreviousTagSize, secondFlvKeyFrame.Buffer, true), firstFlvFrameCacche); |
|
|
|
} |
|
|
@@ -283,13 +115,14 @@ namespace JT1078.Flv |
|
|
|
} |
|
|
|
} |
|
|
|
//cache PreviousTagSize |
|
|
|
FlvFrameInfoDict.TryAdd(key, new FlvFrameInfo{ |
|
|
|
PreviousTagSize = firstFlvKeyFrame.PreviousTagSize, |
|
|
|
Interval = (uint)(pps.Timestamp - sps.Timestamp), |
|
|
|
Timestamp= pps.Timestamp, |
|
|
|
} |
|
|
|
); |
|
|
|
FirstFlvFrameCache.TryAdd(key, (firstFlvKeyFrame.PreviousTagSize, firstFlvKeyFrame.Buffer, false)); |
|
|
|
FlvFrameInfoDict.TryAdd(key, new FlvFrameInfo |
|
|
|
{ |
|
|
|
PreviousTagSize = firstFlvKeyFrame.PreviousTagSize, |
|
|
|
Interval = (uint)(pps.Timestamp - sps.Timestamp), |
|
|
|
Timestamp = pps.Timestamp, |
|
|
|
} |
|
|
|
); |
|
|
|
FirstFlvFrameCache.TryAdd(key, (firstFlvKeyFrame.PreviousTagSize, firstFlvKeyFrame.Buffer, false)); |
|
|
|
VideoSPSDict.TryAdd(key, spsInfo); |
|
|
|
} |
|
|
|
sps = null; |
|
|
@@ -306,9 +139,26 @@ namespace JT1078.Flv |
|
|
|
//7 8 6 5 1 1 1 1 7 8 6 5 1 1 1 1 1 7 8 6 5 1 1 1 1 1 |
|
|
|
switch (naln.NALUHeader.NalUnitType) |
|
|
|
{ |
|
|
|
#warning 是否需要IDR帧? 每次发送IDR帧? 在测试的时候,是否由于图像变化不大所以不需要IDR帧? |
|
|
|
case 5:// IDR |
|
|
|
if (FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo idrInfo)) |
|
|
|
{ |
|
|
|
//当前的1078包与上一包1078的时间戳相减再进行累加 |
|
|
|
uint interval = (uint)(naln.Timestamp - idrInfo.Timestamp); |
|
|
|
idrInfo.Interval += interval; |
|
|
|
idrInfo.Timestamp = naln.Timestamp; |
|
|
|
// PreviousTagSize |
|
|
|
flvMessagePackWriter.WriteUInt32(idrInfo.PreviousTagSize); |
|
|
|
// Data Tag Frame |
|
|
|
var flvFrameBuffer = CreateVideoTagOtherFrame(idrInfo, naln, sei); |
|
|
|
flvMessagePackWriter.WriteArray(flvFrameBuffer); |
|
|
|
idrInfo.PreviousTagSize = (uint)flvFrameBuffer.Length; |
|
|
|
idrInfo.LastDataType = naln.DataType; |
|
|
|
FlvFrameInfoDict.TryUpdate(key, idrInfo, idrInfo); |
|
|
|
} |
|
|
|
break; |
|
|
|
case 1:// I/P/B |
|
|
|
if (FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo flvFrameInfo)) |
|
|
|
if (FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo flvFrameInfo)) |
|
|
|
{ |
|
|
|
//当前的1078包与上一包1078的时间戳相减再进行累加 |
|
|
|
uint interval = (uint)(naln.Timestamp - flvFrameInfo.Timestamp); |
|
|
@@ -320,7 +170,7 @@ namespace JT1078.Flv |
|
|
|
var flvFrameBuffer = CreateVideoTagOtherFrame(flvFrameInfo, naln, sei); |
|
|
|
flvMessagePackWriter.WriteArray(flvFrameBuffer); |
|
|
|
flvFrameInfo.PreviousTagSize = (uint)flvFrameBuffer.Length; |
|
|
|
flvFrameInfo.DataType = naln.DataType; |
|
|
|
flvFrameInfo.LastDataType = naln.DataType; |
|
|
|
FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo); |
|
|
|
} |
|
|
|
break; |
|
|
@@ -344,56 +194,7 @@ namespace JT1078.Flv |
|
|
|
FlvArrayPool.Return(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
internal (byte[] Buffer, uint PreviousTagSize) CreateFirstFlvKeyFrame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo) |
|
|
|
{ |
|
|
|
byte[] buffer = FlvArrayPool.Rent(65535); |
|
|
|
try |
|
|
|
{ |
|
|
|
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); |
|
|
|
//flv body PreviousTagSize awalys 0 |
|
|
|
flvMessagePackWriter.WriteUInt32(0); |
|
|
|
//flv body script tag |
|
|
|
var scriptTagFrameBuffer= CreateScriptTagFrame(spsInfo.width, spsInfo.height); |
|
|
|
flvMessagePackWriter.WriteArray(scriptTagFrameBuffer); |
|
|
|
//flv script tag PreviousTagSize |
|
|
|
flvMessagePackWriter.WriteUInt32((uint)scriptTagFrameBuffer.Length); |
|
|
|
//flv body video tag 0 |
|
|
|
var videoTagFrame0Buffer= CreateVideoTag0Frame(spsRawData, ppsRawData, spsInfo); |
|
|
|
flvMessagePackWriter.WriteArray(videoTagFrame0Buffer); |
|
|
|
uint videoTag0PreviousTagSize = (uint)videoTagFrame0Buffer.Length; |
|
|
|
return (flvMessagePackWriter.FlushAndGetArray(), videoTag0PreviousTagSize); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
FlvArrayPool.Return(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
internal (byte[] Buffer,uint PreviousTagSize) CreateSecondFlvKeyFrame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo, FlvFrameInfo flvFrameInfo) |
|
|
|
{ |
|
|
|
byte[] buffer = FlvArrayPool.Rent(65535); |
|
|
|
try |
|
|
|
{ |
|
|
|
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); |
|
|
|
//flv body PreviousTagSize awalys 0 |
|
|
|
//当首包的时候为 0 |
|
|
|
//当切花主次码流的时候需要根据上一包的大小 |
|
|
|
flvMessagePackWriter.WriteUInt32(flvFrameInfo.PreviousTagSize); |
|
|
|
//flv body script tag |
|
|
|
var scriptTagFrameBuffer = CreateScriptTagFrame(spsInfo.width, spsInfo.height); |
|
|
|
flvMessagePackWriter.WriteArray(scriptTagFrameBuffer); |
|
|
|
//flv script tag PreviousTagSize |
|
|
|
flvMessagePackWriter.WriteUInt32((uint)scriptTagFrameBuffer.Length); |
|
|
|
//flv body video tag 0 |
|
|
|
var videoTagFrame0Buffer = CreateSecondVideoTag0Frame(spsRawData, ppsRawData, spsInfo); |
|
|
|
flvMessagePackWriter.WriteArray(videoTagFrame0Buffer); |
|
|
|
uint videoTag0PreviousTagSize = (uint)videoTagFrame0Buffer.Length; |
|
|
|
return (flvMessagePackWriter.FlushAndGetArray(), videoTag0PreviousTagSize); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
FlvArrayPool.Return(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// |
|
|
|
/// </summary> |
|
|
@@ -449,6 +250,168 @@ namespace JT1078.Flv |
|
|
|
} |
|
|
|
return default; |
|
|
|
} |
|
|
|
|
|
|
|
internal byte[] CreateScriptTagFrame(int width, int height, double frameRate = 25d) |
|
|
|
{ |
|
|
|
byte[] buffer = FlvArrayPool.Rent(1024); |
|
|
|
try |
|
|
|
{ |
|
|
|
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); |
|
|
|
//flv body script tag |
|
|
|
//flv body tag header |
|
|
|
FlvTags flvTags = new FlvTags(); |
|
|
|
flvTags.Type = TagType.ScriptData; |
|
|
|
flvTags.Timestamp = 0; |
|
|
|
flvTags.TimestampExt = 0; |
|
|
|
flvTags.StreamId = 0; |
|
|
|
//flv body tag body |
|
|
|
flvTags.DataTagsData = new Amf3(); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas = new List<IAmf3Metadata>(); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Duration |
|
|
|
{ |
|
|
|
Value = 0d |
|
|
|
}); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_VideoDataRate |
|
|
|
{ |
|
|
|
Value = 0d |
|
|
|
}); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_VideoCodecId |
|
|
|
{ |
|
|
|
Value = 7d |
|
|
|
}); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_FrameRate |
|
|
|
{ |
|
|
|
Value = frameRate |
|
|
|
}); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Width |
|
|
|
{ |
|
|
|
Value = width |
|
|
|
}); |
|
|
|
flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_Height |
|
|
|
{ |
|
|
|
Value = height |
|
|
|
}); |
|
|
|
flvMessagePackWriter.WriteFlvTag(flvTags); |
|
|
|
return flvMessagePackWriter.FlushAndGetArray(); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
FlvArrayPool.Return(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
internal byte[] CreateVideoTag0Frame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo) |
|
|
|
{ |
|
|
|
byte[] buffer = FlvArrayPool.Rent(2048); |
|
|
|
try |
|
|
|
{ |
|
|
|
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); |
|
|
|
//flv body video tag |
|
|
|
//flv body tag header |
|
|
|
FlvTags flvTags = new FlvTags(); |
|
|
|
flvTags.Type = TagType.Video; |
|
|
|
flvTags.Timestamp = 0; |
|
|
|
flvTags.TimestampExt = 0; |
|
|
|
flvTags.StreamId = 0; |
|
|
|
//flv body tag body |
|
|
|
flvTags.VideoTagsData = new VideoTags(); |
|
|
|
flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; |
|
|
|
flvTags.VideoTagsData.VideoData = new AvcVideoPacke(); |
|
|
|
flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.SequenceHeader; |
|
|
|
flvTags.VideoTagsData.VideoData.CompositionTime = 0; |
|
|
|
AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(); |
|
|
|
aVCDecoderConfigurationRecord.AVCProfileIndication = spsInfo.profileIdc; |
|
|
|
aVCDecoderConfigurationRecord.ProfileCompatibility = (byte)spsInfo.profileCompat; |
|
|
|
aVCDecoderConfigurationRecord.AVCLevelIndication = spsInfo.levelIdc; |
|
|
|
aVCDecoderConfigurationRecord.NumOfPictureParameterSets = 1; |
|
|
|
aVCDecoderConfigurationRecord.PPSBuffer = ppsRawData; |
|
|
|
aVCDecoderConfigurationRecord.SPSBuffer = spsRawData; |
|
|
|
flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord; |
|
|
|
flvMessagePackWriter.WriteFlvTag(flvTags); |
|
|
|
return flvMessagePackWriter.FlushAndGetArray(); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
FlvArrayPool.Return(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
internal byte[] CreateVideoTagOtherFrame(FlvFrameInfo flvFrameInfo, H264NALU nALU, H264NALU sei) |
|
|
|
{ |
|
|
|
byte[] buffer = FlvArrayPool.Rent(65535); |
|
|
|
try |
|
|
|
{ |
|
|
|
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); |
|
|
|
//flv body video tag |
|
|
|
//flv body tag header |
|
|
|
FlvTags flvTags = new FlvTags(); |
|
|
|
flvTags.Type = TagType.Video; |
|
|
|
//pts |
|
|
|
flvTags.Timestamp = flvFrameInfo.Interval; |
|
|
|
flvTags.TimestampExt = 0; |
|
|
|
flvTags.StreamId = 0; |
|
|
|
//flv body tag body |
|
|
|
flvTags.VideoTagsData = new VideoTags(); |
|
|
|
flvTags.VideoTagsData.VideoData = new AvcVideoPacke(); |
|
|
|
flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw; |
|
|
|
//1: keyframe (for AVC, a seekable frame) —— 即H.264的IDR帧; |
|
|
|
//2: inter frame(for AVC, a non - seekable frame) —— H.264的普通I帧; |
|
|
|
//ref:https://www.cnblogs.com/chyingp/p/flv-getting-started.html |
|
|
|
if (nALU.NALUHeader.NalUnitType == 5) |
|
|
|
{ |
|
|
|
flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
flvTags.VideoTagsData.FrameType = FrameType.InterFrame; |
|
|
|
} |
|
|
|
if (flvFrameInfo.LastDataType == JT1078DataType.视频I帧) |
|
|
|
{ |
|
|
|
//cts |
|
|
|
flvTags.VideoTagsData.VideoData.CompositionTime = nALU.LastIFrameInterval; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
//cts |
|
|
|
flvTags.VideoTagsData.VideoData.CompositionTime = nALU.LastFrameInterval; |
|
|
|
} |
|
|
|
flvTags.VideoTagsData.VideoData.MultiData = new List<byte[]>(); |
|
|
|
flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData); |
|
|
|
//忽略sei |
|
|
|
//if (sei != null && sei.RawData != null && sei.RawData.Length > 0) |
|
|
|
//{ |
|
|
|
// flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData); |
|
|
|
//} |
|
|
|
flvMessagePackWriter.WriteFlvTag(flvTags); |
|
|
|
return flvMessagePackWriter.FlushAndGetArray(); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
FlvArrayPool.Return(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
internal (byte[] Buffer, uint PreviousTagSize) CreateFirstFlvKeyFrame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo, uint previousTagSize = 0) |
|
|
|
{ |
|
|
|
byte[] buffer = FlvArrayPool.Rent(65535); |
|
|
|
try |
|
|
|
{ |
|
|
|
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); |
|
|
|
//flv body PreviousTagSize awalys 0 |
|
|
|
flvMessagePackWriter.WriteUInt32(previousTagSize); |
|
|
|
//flv body script tag |
|
|
|
var scriptTagFrameBuffer = CreateScriptTagFrame(spsInfo.width, spsInfo.height); |
|
|
|
flvMessagePackWriter.WriteArray(scriptTagFrameBuffer); |
|
|
|
//flv script tag PreviousTagSize |
|
|
|
flvMessagePackWriter.WriteUInt32((uint)scriptTagFrameBuffer.Length); |
|
|
|
//flv body video tag 0 |
|
|
|
var videoTagFrame0Buffer = CreateVideoTag0Frame(spsRawData, ppsRawData, spsInfo); |
|
|
|
flvMessagePackWriter.WriteArray(videoTagFrame0Buffer); |
|
|
|
uint videoTag0PreviousTagSize = (uint)videoTagFrame0Buffer.Length; |
|
|
|
return (flvMessagePackWriter.FlushAndGetArray(), videoTag0PreviousTagSize); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
FlvArrayPool.Return(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
internal class FlvFrameInfo |
|
|
@@ -456,6 +419,6 @@ namespace JT1078.Flv |
|
|
|
public uint PreviousTagSize { get; set; } |
|
|
|
public ulong Timestamp { get; set; } |
|
|
|
public uint Interval { get; set; } |
|
|
|
public JT1078DataType DataType { get; set; } |
|
|
|
public JT1078DataType LastDataType { get; set; } |
|
|
|
} |
|
|
|
} |