瀏覽代碼

1.增加flv编码器支持多个NALU(SEI包)

2.在flv编码中将1078对应的参数给使用上
tags/v1.1.0
smallchi 5 年之前
父節點
當前提交
b82fe5bdac
共有 8 個文件被更改,包括 85 次插入55 次删除
  1. +11
    -18
      src/JT1078.Flv.Test/FlvEncoderTest.cs
  2. +10
    -3
      src/JT1078.Flv.Test/H264/index.html
  3. +3
    -0
      src/JT1078.Flv.Test/JT1078.Flv.Test.csproj
  4. +47
    -28
      src/JT1078.Flv/FlvEncoder.cs
  5. +1
    -0
      src/JT1078.Flv/H264/H264Decoder.cs
  6. +6
    -0
      src/JT1078.Flv/H264/H264NALU.cs
  7. +6
    -4
      src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs
  8. +1
    -2
      src/JT1078.Flv/Metadata/AvcVideoPacke.cs

+ 11
- 18
src/JT1078.Flv.Test/FlvEncoderTest.cs 查看文件

@@ -10,6 +10,7 @@ using JT1078.Protocol.Enums;
using JT1078.Flv.H264; using JT1078.Flv.H264;
using JT1078.Flv.MessagePack; using JT1078.Flv.MessagePack;
using JT1078.Flv.Metadata; using JT1078.Flv.Metadata;
using System.Diagnostics;


namespace JT1078.Flv.Test namespace JT1078.Flv.Test
{ {
@@ -35,15 +36,12 @@ namespace JT1078.Flv.Test


FlvEncoder encoder = new FlvEncoder(); FlvEncoder encoder = new FlvEncoder();
var contents = encoder.CreateFlvFrame(nalus); var contents = encoder.CreateFlvFrame(nalus);
byte[] tmp = new byte[FlvEncoder.VideoFlvHeaderBuffer.Length+ contents.Length];
Array.Copy(FlvEncoder.VideoFlvHeaderBuffer, tmp, FlvEncoder.VideoFlvHeaderBuffer.Length);
Array.Copy(contents,0,tmp, FlvEncoder.VideoFlvHeaderBuffer.Length, contents.Length);
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.flv"); var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.flv");
if (File.Exists(filepath)) if (File.Exists(filepath))
{ {
File.Delete(filepath); File.Delete(filepath);
} }
File.WriteAllBytes(filepath, tmp);
File.WriteAllBytes(filepath, contents);
} }


[Fact] [Fact]
@@ -79,7 +77,6 @@ namespace JT1078.Flv.Test
File.Delete(filepath); File.Delete(filepath);
} }
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; var totalPage = (h264NALULs.Count + 10 - 1) / 10;
for(var i=0;i< totalPage; i++) for(var i=0;i< totalPage; i++)
{ {
@@ -136,7 +133,6 @@ 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; var totalPage = (h264NALULs.Count + 10 - 1) / 10;
for (var i = 0; i < totalPage; i++) for (var i = 0; i < totalPage; i++)
{ {
@@ -192,9 +188,7 @@ namespace JT1078.Flv.Test
} }
} }


fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer);
fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
var totalPage = (h264NALULs.Count + 10 - 1) / 10; var totalPage = (h264NALULs.Count + 10 - 1) / 10;
for (var i = 0; i < totalPage; i++) for (var i = 0; i < totalPage; i++)
{ {
@@ -251,17 +245,16 @@ namespace JT1078.Flv.Test
} }
//var tmp1 = h264NALULs.Where(w => w.NALUHeader.NalUnitType == 7).ToList(); //var tmp1 = h264NALULs.Where(w => w.NALUHeader.NalUnitType == 7).ToList();
List<SPSInfo> tmpSpss = new List<SPSInfo>(); List<SPSInfo> tmpSpss = new List<SPSInfo>();
List<ushort> times = new List<ushort>();
List<ulong> times = new List<ulong>();
List<int> type = new List<int>(); List<int> type = new List<int>();
//foreach (var item in h264NALULs)
//{
// //ExpGolombReader expGolombReader = new ExpGolombReader(item.RawData);
// type.Add(item.NALUHeader.NalUnitType);
// times.Add(item.LastFrameInterval);
// //tmpSpss.Add(expGolombReader.ReadSPS());
//}
foreach (var item in h264NALULs)
{
//ExpGolombReader expGolombReader = new ExpGolombReader(item.RawData);
//type.Add(item.NALUHeader.NalUnitType);
times.Add(item.Timestamp);
//tmpSpss.Add(expGolombReader.ReadSPS());
}
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; var totalPage = (h264NALULs.Count + 10 - 1) / 10;
for (var i = 0; i < totalPage; i++) for (var i = 0; i < totalPage; i++)
{ {


+ 10
- 3
src/JT1078.Flv.Test/H264/index.html 查看文件

@@ -9,13 +9,20 @@
<body> <body>
<video muted="muted" webkit-playsinline="true" autoplay="true" id="player"></video> <video muted="muted" webkit-playsinline="true" autoplay="true" id="player"></video>
<script> <script>

//ref https://github.com/bilibili/flv.js/issues/427
//ref https://github.com/bilibili/flv.js/issues/498
if (flvjs.isSupported()) { if (flvjs.isSupported()) {
var player = document.getElementById('player'); var player = document.getElementById('player');
var flvPlayer = flvjs.createPlayer({ var flvPlayer = flvjs.createPlayer({
type: 'flv', type: 'flv',
url: "ws://localhost:1818/jt1078live?token=" + Math.floor((Math.random() * 1000000) + 1)
//url: "jt1078_5.flv"
lazyLoad: false,
lazyLoadMaxDuration: 0,
lazyLoadRecoverDuration: 0,
deferLoadAfterSourceOpen: false,
fixAudioTimestampGap: false,
//url: "ws://localhost:1818/jt1078live?token=" + Math.floor((Math.random() * 1000000) + 1)
//isLive: true,
url: "JT1078_3.flv"
}); });
var width, height, flag; var width, height, flag;
flvPlayer.attachMediaElement(player); flvPlayer.attachMediaElement(player);


+ 3
- 0
src/JT1078.Flv.Test/JT1078.Flv.Test.csproj 查看文件

@@ -40,6 +40,9 @@
<None Update="H264\JT1078_4.txt"> <None Update="H264\JT1078_4.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="H264\JT1078_5.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>


</Project> </Project>

+ 47
- 28
src/JT1078.Flv/FlvEncoder.cs 查看文件

@@ -22,6 +22,9 @@ namespace JT1078.Flv
public uint PreviousTagSize { get; set;} public uint PreviousTagSize { get; set;}
public uint LastFrameInterval { get; set; } public uint LastFrameInterval { get; set; }
public uint LastIFrameInterval { get; set; } public uint LastIFrameInterval { get; set; }
public ulong Timestamp { get; set; }
public uint Interval { get; set; }
public JT1078DataType DataType { get; set; }
} }


public static readonly byte[] VideoFlvHeaderBuffer; public static readonly byte[] VideoFlvHeaderBuffer;
@@ -122,7 +125,7 @@ namespace JT1078.Flv
FlvArrayPool.Return(buffer); FlvArrayPool.Return(buffer);
} }
} }
public byte[] CreateVideoTagOtherFrame(FlvFrameInfo flvFrameInfo, H264NALU nALU)
public byte[] CreateVideoTagOtherFrame(FlvFrameInfo flvFrameInfo, H264NALU nALU, H264NALU sei)
{ {
byte[] buffer = FlvArrayPool.Rent(65535); byte[] buffer = FlvArrayPool.Rent(65535);
try try
@@ -137,21 +140,30 @@ namespace JT1078.Flv
//flv body tag body //flv body tag body
flvTags.VideoTagsData = new VideoTags(); flvTags.VideoTagsData = new VideoTags();
flvTags.VideoTagsData.VideoData = new AvcVideoPacke(); flvTags.VideoTagsData.VideoData = new AvcVideoPacke();
flvTags.VideoTagsData.VideoData.CompositionTime = 0;
flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw; flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw;
if (nALU.NALUHeader.NalUnitType == 5 || nALU.DataType == JT1078DataType.视频I帧) if (nALU.NALUHeader.NalUnitType == 5 || nALU.DataType == JT1078DataType.视频I帧)
{ {
flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; flvTags.VideoTagsData.FrameType = FrameType.KeyFrame;
flvTags.Timestamp = flvFrameInfo.LastIFrameInterval;
} }
else else
{ {
flvTags.Timestamp = flvFrameInfo.LastFrameInterval;
flvTags.VideoTagsData.FrameType = FrameType.InterFrame; flvTags.VideoTagsData.FrameType = FrameType.InterFrame;
} }
#warning Timestamp时间戳没有控制好
flvTags.Timestamp = flvFrameInfo.LastIFrameInterval;
flvTags.VideoTagsData.VideoData.Data = nALU.RawData;
if(flvFrameInfo.DataType== JT1078DataType.视频I帧)
{
flvTags.VideoTagsData.VideoData.CompositionTime = flvFrameInfo.LastIFrameInterval;
}
else
{
flvTags.VideoTagsData.VideoData.CompositionTime = flvFrameInfo.LastFrameInterval;
}
flvTags.Timestamp = flvFrameInfo.Interval;
flvTags.VideoTagsData.VideoData.MultiData = new List<byte[]>();
flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData);
if(sei!=null && sei.RawData!=null && sei.RawData.Length > 0)
{
flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData);
}
flvMessagePackWriter.WriteFlvTag(flvTags); flvMessagePackWriter.WriteFlvTag(flvTags);
return flvMessagePackWriter.FlushAndGetArray(); return flvMessagePackWriter.FlushAndGetArray();
} }
@@ -160,14 +172,13 @@ namespace JT1078.Flv
FlvArrayPool.Return(buffer); FlvArrayPool.Return(buffer);
} }
} }

public byte[] CreateFlvFrame(List<H264NALU> nALUs,int minimumLength = 65535) public byte[] CreateFlvFrame(List<H264NALU> nALUs,int minimumLength = 65535)
{ {
byte[] buffer = FlvArrayPool.Rent(minimumLength); byte[] buffer = FlvArrayPool.Rent(minimumLength);
try try
{ {
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
H264NALU sps=null, pps=null;
H264NALU sps=null, pps=null, sei=null;
SPSInfo spsInfo = new SPSInfo(); SPSInfo spsInfo = new SPSInfo();
foreach (var naln in nALUs) foreach (var naln in nALUs)
{ {
@@ -177,19 +188,25 @@ namespace JT1078.Flv
if (VideoSPSDict.TryGetValue(key, out var spsInfoCache)) if (VideoSPSDict.TryGetValue(key, out var spsInfoCache))
{ {
//todo: 主次码流 //todo: 主次码流
if (spsInfoCache.height != spsInfo.height && spsInfoCache.width != spsInfo.width)
{
if (FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo flvFrameInfo))
{
CreateFlvKeyFrame(ref flvMessagePackWriter, key, sps.RawData, pps.RawData, spsInfo);
VideoSPSDict.TryUpdate(key, spsInfo, spsInfo);
flvFrameInfo.LastIFrameInterval = 0;
FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo);
}
}
//if (spsInfoCache.height != spsInfo.height && spsInfoCache.width != spsInfo.width)
//{
// if (FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo flvFrameInfo))
// {
// var rawData = H264Decoder.DiscardEmulationPreventionBytes(sps.RawData);
// ExpGolombReader h264GolombReader = new ExpGolombReader(rawData);
// spsInfo = h264GolombReader.ReadSPS();
// CreateFlvKeyFrame(ref flvMessagePackWriter, key, sps.RawData, pps.RawData, spsInfo);
// VideoSPSDict.TryUpdate(key, spsInfo, spsInfo);
// flvFrameInfo.LastIFrameInterval = 0;
// FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo);
// }
//}
} }
else else
{ {
var rawData = H264Decoder.DiscardEmulationPreventionBytes(sps.RawData);
ExpGolombReader h264GolombReader = new ExpGolombReader(rawData);
spsInfo = h264GolombReader.ReadSPS();
flvMessagePackWriter.WriteArray(VideoFlvHeaderBuffer); flvMessagePackWriter.WriteArray(VideoFlvHeaderBuffer);
CreateFlvKeyFrame(ref flvMessagePackWriter, key, sps.RawData, pps.RawData, spsInfo); CreateFlvKeyFrame(ref flvMessagePackWriter, key, sps.RawData, pps.RawData, spsInfo);
VideoSPSDict.TryAdd(key, spsInfo); VideoSPSDict.TryAdd(key, spsInfo);
@@ -205,27 +222,29 @@ namespace JT1078.Flv
case 1:// I/P/B case 1:// I/P/B
if (FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo flvFrameInfo)) if (FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo flvFrameInfo))
{ {
flvFrameInfo.LastIFrameInterval += naln.LastIFrameInterval;
flvFrameInfo.LastFrameInterval += naln.LastFrameInterval;
flvFrameInfo.LastIFrameInterval = naln.LastIFrameInterval;
flvFrameInfo.LastFrameInterval = naln.LastFrameInterval;
uint interval = (uint)(naln.Timestamp - flvFrameInfo.Timestamp);
flvFrameInfo.Interval += interval;
flvFrameInfo.Timestamp = naln.Timestamp;
// PreviousTagSize // PreviousTagSize
flvMessagePackWriter.WriteUInt32(flvFrameInfo.PreviousTagSize); flvMessagePackWriter.WriteUInt32(flvFrameInfo.PreviousTagSize);
// Data Tag Frame // Data Tag Frame
var flvFrameBuffer = CreateVideoTagOtherFrame(flvFrameInfo, naln);
var flvFrameBuffer = CreateVideoTagOtherFrame(flvFrameInfo, naln, sei);
flvMessagePackWriter.WriteArray(flvFrameBuffer); flvMessagePackWriter.WriteArray(flvFrameBuffer);
flvFrameInfo.PreviousTagSize = (uint)flvFrameBuffer.Length; flvFrameInfo.PreviousTagSize = (uint)flvFrameBuffer.Length;
flvFrameInfo.DataType = naln.DataType;
FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo); FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo);
} }
break; break;
case 7:// sps
case 7:// SPS
sps = naln; sps = naln;
var rawData = H264Decoder.DiscardEmulationPreventionBytes(naln.RawData);
ExpGolombReader h264GolombReader = new ExpGolombReader(rawData);
spsInfo = h264GolombReader.ReadSPS();
break; break;
case 8:// pps
case 8:// PPS
pps = naln; pps = naln;
break; break;
case 6://SEI case 6://SEI
sei = naln;
break; break;
default: default:
break; break;
@@ -247,7 +266,7 @@ namespace JT1078.Flv
flvMessagePackWriter.WriteArray(scriptTagFrameBuffer); flvMessagePackWriter.WriteArray(scriptTagFrameBuffer);
//flv script tag PreviousTagSize //flv script tag PreviousTagSize
flvMessagePackWriter.WriteUInt32((uint)scriptTagFrameBuffer.Length); flvMessagePackWriter.WriteUInt32((uint)scriptTagFrameBuffer.Length);
//flv body video tag
//flv body video tag 0
var videoTagFrame0Buffer= CreateVideoTag0Frame(spsRawData, ppsRawData, spsInfo); var videoTagFrame0Buffer= CreateVideoTag0Frame(spsRawData, ppsRawData, spsInfo);
flvMessagePackWriter.WriteArray(videoTagFrame0Buffer); flvMessagePackWriter.WriteArray(videoTagFrame0Buffer);
uint videoTag0PreviousTagSize = (uint)videoTagFrame0Buffer.Length; uint videoTag0PreviousTagSize = (uint)videoTagFrame0Buffer.Length;


+ 1
- 0
src/JT1078.Flv/H264/H264Decoder.cs 查看文件

@@ -75,6 +75,7 @@ namespace JT1078.Flv.H264
nALU.LogicChannelNumber = package.LogicChannelNumber; nALU.LogicChannelNumber = package.LogicChannelNumber;
nALU.LastFrameInterval = package.LastFrameInterval; nALU.LastFrameInterval = package.LastFrameInterval;
nALU.LastIFrameInterval = package.LastIFrameInterval; nALU.LastIFrameInterval = package.LastIFrameInterval;
nALU.Timestamp = package.Timestamp;
if (startCodePrefix == 3) if (startCodePrefix == 3)
{ {
nALU.StartCodePrefix = H264NALU.Start1; nALU.StartCodePrefix = H264NALU.Start1;


+ 6
- 0
src/JT1078.Flv/H264/H264NALU.cs 查看文件

@@ -35,6 +35,12 @@ namespace JT1078.Flv.H264
/// </summary> /// </summary>
public ushort LastFrameInterval { get; set; } public ushort LastFrameInterval { get; set; }
/// <summary> /// <summary>
/// 时间戳
/// 标识此RTP数据包当前帧的相对时间,单位毫秒(ms)。
/// 当数据类型为01000时,则没有该字段
/// </summary>
public ulong Timestamp { get; set; }
/// <summary>
/// 数据体 /// 数据体
/// </summary> /// </summary>
public byte[] RawData { get; set; } public byte[] RawData { get; set; }


+ 6
- 4
src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs 查看文件

@@ -65,7 +65,6 @@ namespace JT1078.Flv.MessagePack
public void WriteVideoTags(VideoTags videoTags) public void WriteVideoTags(VideoTags videoTags)
{ {
WriteByte((byte)((byte)videoTags.FrameType | (byte)videoTags.CodecId)); WriteByte((byte)((byte)videoTags.FrameType | (byte)videoTags.CodecId));
#warning 只处理H.264媒体数据
if (videoTags.CodecId== CodecId.AvcVideoPacke) if (videoTags.CodecId== CodecId.AvcVideoPacke)
{ {
WriteAvcVideoPacke(videoTags.VideoData); WriteAvcVideoPacke(videoTags.VideoData);
@@ -85,10 +84,13 @@ namespace JT1078.Flv.MessagePack
else if(videoPacke.AvcPacketType == AvcPacketType.Raw) else if(videoPacke.AvcPacketType == AvcPacketType.Raw)
{ {
WriteUInt24(videoPacke.CompositionTime); WriteUInt24(videoPacke.CompositionTime);
if (videoPacke.Data != null && videoPacke.Data.Length>0)
if (videoPacke.MultiData != null && videoPacke.MultiData.Count>0)
{ {
WriteInt32(videoPacke.Data.Length);
WriteArray(videoPacke.Data);
foreach(var item in videoPacke.MultiData)
{
WriteInt32(item.Length);
WriteArray(item);
}
} }
} }
else else


+ 1
- 2
src/JT1078.Flv/Metadata/AvcVideoPacke.cs 查看文件

@@ -12,7 +12,6 @@ namespace JT1078.Flv.Metadata
public uint CompositionTime { get; set; } public uint CompositionTime { get; set; }


public AVCDecoderConfigurationRecord AVCDecoderConfiguration { get; set; } public AVCDecoderConfigurationRecord AVCDecoderConfiguration { get; set; }

public byte[] Data { get; set; }
public List<byte[]> MultiData { get; set; }
} }
} }

Loading…
取消
儲存