diff --git a/src/JT1078.Flv.Test/FlvEncoderTest.cs b/src/JT1078.Flv.Test/FlvEncoderTest.cs index 8a8b707..42d085a 100644 --- a/src/JT1078.Flv.Test/FlvEncoderTest.cs +++ b/src/JT1078.Flv.Test/FlvEncoderTest.cs @@ -17,7 +17,7 @@ namespace JT1078.Flv.Test public class FlvEncoderTest { [Fact] - public void FlvEncoder_Test_1() + public void 测试第一帧的数据() { JT1078Package Package = null; var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.txt")); @@ -42,11 +42,14 @@ namespace JT1078.Flv.Test { File.Delete(filepath); } - File.WriteAllBytes(filepath, contents); + FileStream fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); + fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer); + fileStream.Write(contents); + fileStream.Close(); } [Fact] - public void FlvEncoder_Test_2() + public void 测试前几帧的数据() { FileStream fileStream=null; try @@ -78,6 +81,7 @@ namespace JT1078.Flv.Test 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++) { @@ -100,7 +104,7 @@ namespace JT1078.Flv.Test } [Fact] - public void FlvEncoder_Test_3() + public void 测试可以播放的Flv1() { FileStream fileStream = null; Flv.H264.H264Decoder decoder = new Flv.H264.H264Decoder(); @@ -134,6 +138,7 @@ namespace JT1078.Flv.Test } 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++) { @@ -156,7 +161,7 @@ namespace JT1078.Flv.Test } [Fact] - public void FlvEncoder_Test_4() + public void 测试可以播放的Flv2() { FileStream fileStream = null; Flv.H264.H264Decoder decoder = new Flv.H264.H264Decoder(); @@ -189,7 +194,8 @@ namespace JT1078.Flv.Test } } - fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); + 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++) { @@ -212,7 +218,7 @@ namespace JT1078.Flv.Test } [Fact] - public void FlvEncoder_Test_5() + public void 测试主次码流切换() { FileStream fileStream = null; Flv.H264.H264Decoder decoder = new Flv.H264.H264Decoder(); diff --git a/src/JT1078.Flv/FlvEncoder.cs b/src/JT1078.Flv/FlvEncoder.cs index b800dcb..1a9aa24 100644 --- a/src/JT1078.Flv/FlvEncoder.cs +++ b/src/JT1078.Flv/FlvEncoder.cs @@ -19,6 +19,9 @@ namespace JT1078.Flv { public class FlvEncoder { + /// + /// Flv固定头部数据 + /// public static readonly byte[] VideoFlvHeaderBuffer; private static readonly Flv.H264.H264Decoder H264Decoder; private static readonly ConcurrentDictionary VideoSPSDict; @@ -42,7 +45,7 @@ namespace JT1078.Flv { logger = loggerFactory.CreateLogger("FlvEncoder"); } - public byte[] CreateScriptTagFrame(int width, int height, double frameRate = 25d) + internal byte[] CreateScriptTagFrame(int width, int height, double frameRate = 25d) { byte[] buffer = FlvArrayPool.Rent(1024); try @@ -90,7 +93,7 @@ namespace JT1078.Flv FlvArrayPool.Return(buffer); } } - public byte[] CreateVideoTag0Frame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo) + internal byte[] CreateVideoTag0Frame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo) { byte[] buffer = FlvArrayPool.Rent(2048); try @@ -125,7 +128,7 @@ namespace JT1078.Flv FlvArrayPool.Return(buffer); } } - public byte[] CreateSecondVideoTag0Frame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo) + internal byte[] CreateSecondVideoTag0Frame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo) { byte[] buffer = FlvArrayPool.Rent(2048); try @@ -160,7 +163,7 @@ namespace JT1078.Flv FlvArrayPool.Return(buffer); } } - public byte[] CreateVideoTagOtherFrame(FlvFrameInfo flvFrameInfo, H264NALU nALU, H264NALU sei) + internal byte[] CreateVideoTagOtherFrame(FlvFrameInfo flvFrameInfo, H264NALU nALU, H264NALU sei) { byte[] buffer = FlvArrayPool.Rent(65535); try @@ -334,7 +337,7 @@ namespace JT1078.Flv FlvArrayPool.Return(buffer); } } - public (byte[] Buffer, uint PreviousTagSize) CreateFirstFlvKeyFrame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo) + internal (byte[] Buffer, uint PreviousTagSize) CreateFirstFlvKeyFrame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo) { byte[] buffer = FlvArrayPool.Rent(65535); try @@ -358,7 +361,7 @@ namespace JT1078.Flv FlvArrayPool.Return(buffer); } } - public (byte[] Buffer,uint PreviousTagSize) CreateSecondFlvKeyFrame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo, FlvFrameInfo flvFrameInfo) + internal (byte[] Buffer,uint PreviousTagSize) CreateSecondFlvKeyFrame(byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo, FlvFrameInfo flvFrameInfo) { byte[] buffer = FlvArrayPool.Rent(65535); try @@ -384,17 +387,29 @@ namespace JT1078.Flv FlvArrayPool.Return(buffer); } } + /// + /// + /// + /// 完整的1078包 + /// 默认65535 + /// public byte[] CreateFlvFrame(JT1078Package package,int minimumLength = 65535) { var nalus = H264Decoder.ParseNALU(package); if (nalus == null || nalus.Count <= 0) return default; return CreateFlvFrame(nalus, minimumLength); } - public byte[] GetFirstFlvFrame(string key,byte[] bufferFlvFrame) + /// + /// + /// + /// 设备号+通道号(1111111_1) + /// 当前接收到的flv数据 + /// + public byte[] GetFirstFlvFrame(string key,byte[] currentBufferFlvFrame) { if (FirstFlvFrameCache.TryGetValue(key, out var firstBuffer)) { - var length = firstBuffer.Buffer.Length + bufferFlvFrame.Length + VideoFlvHeaderBuffer.Length; + var length = firstBuffer.Buffer.Length + currentBufferFlvFrame.Length + VideoFlvHeaderBuffer.Length; byte[] buffer = FlvArrayPool.Rent(length); try { @@ -406,16 +421,16 @@ namespace JT1078.Flv BinaryPrimitives.WriteUInt32BigEndian(firstBuffer.Buffer, 0); firstBuffer.Buffer.CopyTo(tmp.Slice(VideoFlvHeaderBuffer.Length)); //新用户进来需要替换为上一包的PreviousTagSize - BinaryPrimitives.WriteUInt32BigEndian(bufferFlvFrame, firstBuffer.PreviousTagSize); - bufferFlvFrame.CopyTo(tmp.Slice(VideoFlvHeaderBuffer.Length + firstBuffer.Buffer.Length)); + BinaryPrimitives.WriteUInt32BigEndian(currentBufferFlvFrame, firstBuffer.PreviousTagSize); + currentBufferFlvFrame.CopyTo(tmp.Slice(VideoFlvHeaderBuffer.Length + firstBuffer.Buffer.Length)); return tmp.Slice(0, length).ToArray(); } else { firstBuffer.Buffer.CopyTo(tmp.Slice(VideoFlvHeaderBuffer.Length)); //新用户进来需要替换为首包的PreviousTagSize - BinaryPrimitives.WriteUInt32BigEndian(bufferFlvFrame, firstBuffer.PreviousTagSize); - bufferFlvFrame.CopyTo(tmp.Slice(VideoFlvHeaderBuffer.Length + firstBuffer.Buffer.Length)); + BinaryPrimitives.WriteUInt32BigEndian(currentBufferFlvFrame, firstBuffer.PreviousTagSize); + currentBufferFlvFrame.CopyTo(tmp.Slice(VideoFlvHeaderBuffer.Length + firstBuffer.Buffer.Length)); return tmp.Slice(0, length).ToArray(); } } @@ -428,7 +443,7 @@ namespace JT1078.Flv } } - public class FlvFrameInfo + internal class FlvFrameInfo { public uint PreviousTagSize { get; set; } public ulong Timestamp { get; set; } diff --git a/src/JT1078.Flv/JT1078.Flv.csproj b/src/JT1078.Flv/JT1078.Flv.csproj index bf6e962..0d3ae5f 100644 --- a/src/JT1078.Flv/JT1078.Flv.csproj +++ b/src/JT1078.Flv/JT1078.Flv.csproj @@ -14,12 +14,20 @@ https://github.com/SmallChi/JT1078/blob/master/LICENSE https://github.com/SmallChi/JT1078/blob/master/LICENSE false - 1.0.0-preview1 + 1.0.0-preview2 false true LICENSE + + JT1078.Flv.xml + + + + JT1078.Flv.xml + + diff --git a/src/JT1078.Flv/JT1078.Flv.xml b/src/JT1078.Flv/JT1078.Flv.xml new file mode 100644 index 0000000..5ebe50b --- /dev/null +++ b/src/JT1078.Flv/JT1078.Flv.xml @@ -0,0 +1,203 @@ + + + + JT1078.Flv + + + + + + + + + + ‭00010000‬ + + + + + ‭00100000‬ + + + + + ‭00110000‬ + + + + + 01000000 + + + + + 01010000 + + + + + + ref:"www.codeproject.com/tips/447938/high-performance-csharp-byte-array-to-hex-string-t" + + + + + 16进制字符串转16进制数组 + + + + + + + + Flv固定头部数据 + + + + + + + 完整的1078包 + 默认65535 + + + + + + + 设备号+通道号(1111111_1) + 当前接收到的flv数据 + + + + + Tag Data部分大小 + 3个字节 + + + + + Tag时间戳 + 3个字节 + + + + + stream id 总是0 + 3个字节 + + + + + 根据tag类型 + + + + + 根据tag类型 + + + + + + + + + + + + + Expunge any "Emulation Prevention" bytes from a "Raw Byte Sequence Payload" + + 防止竞争插入0x03 + + + + + + + 终端设备SIM卡号 + BCD[6] + + + + + 逻辑通道号 + + + + + 数据类型 + + + + + 该帧与上一个关键帧之间的时间间隔,单位毫秒(ms), + 当数据类型为非视频帧时,则没有该字段 + + + + + 该帧与上一个帧之间的时间间隔,单位毫秒(ms), + 当数据类型为非视频帧时,则没有该字段 + + + + + 时间戳 + 标识此RTP数据包当前帧的相对时间,单位毫秒(ms)。 + 当数据类型为01000时,则没有该字段 + + + + + 数据体 + + + + + Exp-Golomb指数哥伦布编码 + + + + + Advance the ExpGolomb decoder past a scaling list.The scaling + list is optionally transmitted as part of a sequence parameter + set and is not relevant to transmuxing. + @param count { number} + the number of entries in this scaling list + @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1 + + + + + + AMF3数据类型 + + + + + 元素个数 + + + + + + + + + + + 高4位 + 1: keyframe(for AVC, a seekable frame) —— 即H.264的IDR帧; + 2: inter frame(for AVC, a non- seekable frame) —— H.264的普通I帧; + + + + + 第四位 + 当 CodecID 为 7 时,VideoData 为 AVCVIDEOPACKE,也即 H.264媒体数据 + + + +