Przeglądaj źródła

主次码流之间的切换有待研究(一般用子码流看实时视频就够了,物联卡流量很贵消耗不起)

tags/v1.1.0
smallchi 5 lat temu
rodzic
commit
adf8a64898
10 zmienionych plików z 116 dodań i 91 usunięć
  1. +1
    -0
      .gitignore
  2. +63
    -0
      src/JT1078.Flv.Test/FlvEncoderTest.cs
  3. BIN
      src/JT1078.Flv.Test/H264/JT1078_5.rar
  4. +5
    -11
      src/JT1078.Flv.Test/H264/index.html
  5. +4
    -0
      src/JT1078.Flv.Test/JT1078.Flv.Test.csproj
  6. +28
    -32
      src/JT1078.Flv/FlvEncoder.cs
  7. +11
    -2
      src/JT1078.Flv/MessagePack/EXPGolombReader.cs
  8. +0
    -22
      src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs
  9. +0
    -2
      src/JT1078.Flv/Metadata/AvcVideoPacke.cs
  10. +4
    -22
      src/JT1078.Protocol/MessagePack/JT1078MessagePackWriter.cs

+ 1
- 0
.gitignore Wyświetl plik

@@ -330,3 +330,4 @@ ASALocalRun/
.mfractor/ .mfractor/
/src/JT1078.Flv.Test/H264/JT1078_3.txt /src/JT1078.Flv.Test/H264/JT1078_3.txt
/src/JT1078.Flv.Test/H264/JT1078_4.txt /src/JT1078.Flv.Test/H264/JT1078_4.txt
/src/JT1078.Flv.Test/H264/JT1078_5.txt

+ 63
- 0
src/JT1078.Flv.Test/FlvEncoderTest.cs Wyświetl plik

@@ -8,6 +8,7 @@ using System.IO;
using System.Linq; using System.Linq;
using JT1078.Protocol.Enums; using JT1078.Protocol.Enums;
using JT1078.Flv.H264; using JT1078.Flv.H264;
using JT1078.Flv.MessagePack;


namespace JT1078.Flv.Test namespace JT1078.Flv.Test
{ {
@@ -208,5 +209,67 @@ namespace JT1078.Flv.Test
fileStream?.Dispose(); fileStream?.Dispose();
} }
} }

[Fact]
public void FlvEncoder_Test_5()
{
FileStream fileStream = null;
Flv.H264.H264Decoder decoder = new Flv.H264.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;

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>();
foreach (var item in tmp1)
{
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();
}
}
} }
} }

BIN
src/JT1078.Flv.Test/H264/JT1078_5.rar Wyświetl plik


+ 5
- 11
src/JT1078.Flv.Test/H264/index.html Wyświetl plik

@@ -8,27 +8,21 @@
</head> </head>
<body> <body>
<video muted="muted" webkit-playsinline="true" autoplay="true" id="player"></video> <video muted="muted" webkit-playsinline="true" autoplay="true" id="player"></video>
<video muted="muted" webkit-playsinline="true" autoplay="true" id="player2"></video>
<script> <script>


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: "jt1078_3.flv"
url: "jt1078_5.flv"
}); });
var width, height, flag;
flvPlayer.attachMediaElement(player); flvPlayer.attachMediaElement(player);
flvPlayer.on(flvjs.Events.SCRIPTDATA_ARRIVED, (e) => {
console.log(e);
});
flvPlayer.load(); flvPlayer.load();
flvPlayer.play(); flvPlayer.play();

var player2 = document.getElementById('player2');
var flvPlayer2 = flvjs.createPlayer({
type: 'flv',
url: "jt1078_4.flv"
});
flvPlayer2.attachMediaElement(player2);
flvPlayer2.load();
flvPlayer2.play();
} }
</script> </script>
</body> </body>

+ 4
- 0
src/JT1078.Flv.Test/JT1078.Flv.Test.csproj Wyświetl plik

@@ -7,6 +7,7 @@
<ItemGroup> <ItemGroup>
<None Remove="H264\JT1078_3.rar" /> <None Remove="H264\JT1078_3.rar" />
<None Remove="H264\JT1078_4.rar" /> <None Remove="H264\JT1078_4.rar" />
<None Remove="H264\JT1078_5.rar" />
</ItemGroup> </ItemGroup>


<ItemGroup> <ItemGroup>
@@ -39,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>

+ 28
- 32
src/JT1078.Flv/FlvEncoder.cs Wyświetl plik

@@ -17,6 +17,7 @@ namespace JT1078.Flv
private const uint PreviousTagSizeFixedLength = 4; private const uint PreviousTagSizeFixedLength = 4;
private static readonly ConcurrentDictionary<string, uint> PreviousTagSizeDict; private static readonly ConcurrentDictionary<string, uint> PreviousTagSizeDict;
private static readonly ConcurrentDictionary<string, bool> FrameInitDict; private static readonly ConcurrentDictionary<string, bool> FrameInitDict;
private static readonly ConcurrentDictionary<string, SPSInfo> VideoSPSDict;
private static readonly Flv.H264.H264Decoder H264Decoder; private static readonly Flv.H264.H264Decoder H264Decoder;
static FlvEncoder() static FlvEncoder()
{ {
@@ -24,6 +25,7 @@ namespace JT1078.Flv
VideoFlvHeaderBuffer = VideoFlvHeader.ToArray().ToArray(); VideoFlvHeaderBuffer = VideoFlvHeader.ToArray().ToArray();
PreviousTagSizeDict = new ConcurrentDictionary<string, uint>(); PreviousTagSizeDict = new ConcurrentDictionary<string, uint>();
FrameInitDict = new ConcurrentDictionary<string, bool>(); FrameInitDict = new ConcurrentDictionary<string, bool>();
VideoSPSDict = new ConcurrentDictionary<string, SPSInfo>();
H264Decoder = new Flv.H264.H264Decoder(); H264Decoder = new Flv.H264.H264Decoder();
} }
/// <summary> /// <summary>
@@ -32,7 +34,7 @@ namespace JT1078.Flv
/// <param name="sps">NalUnitType->7</param> /// <param name="sps">NalUnitType->7</param>
/// <param name="pps">NalUnitType->8</param> /// <param name="pps">NalUnitType->8</param>
/// <returns></returns> /// <returns></returns>
public byte[] CreateFlvFirstFrame(H264NALU sps, H264NALU pps)
public byte[] CreateFlvFirstFrame(H264NALU sps, H264NALU pps,bool isSendFlvHeader=true,uint previousTagSize=0)
{ {
byte[] buffer = FlvArrayPool.Rent(65535); byte[] buffer = FlvArrayPool.Rent(65535);
int currentMarkPosition = 0; int currentMarkPosition = 0;
@@ -40,16 +42,18 @@ namespace JT1078.Flv
try try
{ {
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
//flv header
flvMessagePackWriter.WriteArray(VideoFlvHeaderBuffer);

if (isSendFlvHeader)
{
//flv header
flvMessagePackWriter.WriteArray(VideoFlvHeaderBuffer);
}
//SPS -> 7 //SPS -> 7
ExpGolombReader h264GolombReader = new ExpGolombReader(sps.RawData); ExpGolombReader h264GolombReader = new ExpGolombReader(sps.RawData);
var spsInfo = h264GolombReader.ReadSPS(); var spsInfo = h264GolombReader.ReadSPS();


currentMarkPosition = flvMessagePackWriter.GetCurrentPosition(); currentMarkPosition = flvMessagePackWriter.GetCurrentPosition();
//flv body script tag //flv body script tag
CreateScriptTagFrame(ref flvMessagePackWriter, spsInfo.width, spsInfo.height);
CreateScriptTagFrame(ref flvMessagePackWriter, spsInfo.width, spsInfo.height, previousTagSize);
nextMarkPosition = flvMessagePackWriter.GetCurrentPosition(); nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();


//flv body video tag //flv body video tag
@@ -78,10 +82,10 @@ namespace JT1078.Flv
FlvArrayPool.Return(buffer); FlvArrayPool.Return(buffer);
} }
} }
private void CreateScriptTagFrame(ref FlvMessagePackWriter flvMessagePackWriter, int width, int height,double frameRate = 25d)
private void CreateScriptTagFrame(ref FlvMessagePackWriter flvMessagePackWriter, int width, int height,uint previousTagSize, double frameRate = 25d)
{ {
//flv body PreviousTagSize awalys 0 //flv body PreviousTagSize awalys 0
flvMessagePackWriter.WriteUInt32(0);
flvMessagePackWriter.WriteUInt32(previousTagSize);
//flv body script tag //flv body script tag
//flv body tag header //flv body tag header
FlvTags flvTags = new FlvTags(); FlvTags flvTags = new FlvTags();
@@ -141,7 +145,7 @@ namespace JT1078.Flv


public static uint LastFrameInterval = 0; public static uint LastFrameInterval = 0;


private void CreateVideoTagOtherFrame(ref FlvMessagePackWriter flvMessagePackWriter, uint previousTagSize, H264NALU nALU, H264NALU sps, H264NALU pps,H264NALU sei)
private void CreateVideoTagOtherFrame(ref FlvMessagePackWriter flvMessagePackWriter, uint previousTagSize, H264NALU nALU)
{ {
//flv body PreviousTagSize //flv body PreviousTagSize
flvMessagePackWriter.WriteUInt32(previousTagSize); flvMessagePackWriter.WriteUInt32(previousTagSize);
@@ -156,38 +160,18 @@ namespace JT1078.Flv
flvTags.VideoTagsData.VideoData = new AvcVideoPacke(); flvTags.VideoTagsData.VideoData = new AvcVideoPacke();
flvTags.VideoTagsData.VideoData.CompositionTime = 0; flvTags.VideoTagsData.VideoData.CompositionTime = 0;
flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw; flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw;
flvTags.VideoTagsData.VideoData.MultiData = new List<byte[]>();
LastFrameInterval += nALU.LastFrameInterval; LastFrameInterval += nALU.LastFrameInterval;
flvTags.Timestamp = LastFrameInterval; flvTags.Timestamp = LastFrameInterval;

if (nALU.NALUHeader.NalUnitType == 5) if (nALU.NALUHeader.NalUnitType == 5)
{ {
flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; flvTags.VideoTagsData.FrameType = FrameType.KeyFrame;
if (sps!=null && pps != null)
{
//flvTags.VideoTagsData.VideoData.MultiData.Add(sps.RawData);
//flvTags.VideoTagsData.VideoData.MultiData.Add(pps.RawData);
flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData);
}
else
{
//if (sei != null)
//{
// flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData);
//}
flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData);
}
} }
else else
{ {
flvTags.VideoTagsData.FrameType = FrameType.InterFrame; flvTags.VideoTagsData.FrameType = FrameType.InterFrame;
//if (sei != null)
//{
// flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData);
//}
flvTags.VideoTagsData.VideoData.MultiData.Add(nALU.RawData);
} }
flvTags.VideoTagsData.VideoData.Data = nALU.RawData;
flvMessagePackWriter.WriteFlvTag(flvTags); flvMessagePackWriter.WriteFlvTag(flvTags);
} }
public byte[] CreateFlvFrame(List<H264NALU> nALUs, int minimumLength = 65535) public byte[] CreateFlvFrame(List<H264NALU> nALUs, int minimumLength = 65535)
@@ -204,10 +188,22 @@ namespace JT1078.Flv
if (sps!=null && pps != null) if (sps!=null && pps != null)
{ {
string key = sps.GetKey(); string key = sps.GetKey();
if (!FrameInitDict.ContainsKey(key))
ExpGolombReader h264GolombReader = new ExpGolombReader(sps.RawData);
var spsInfo = h264GolombReader.ReadSPS();
if(VideoSPSDict.TryGetValue(key,out var spsInfoCache))
{
if(spsInfoCache.height!= spsInfo.height && spsInfoCache.width!= spsInfo.width)
{
uint previousTagSize = 0;
PreviousTagSizeDict.TryGetValue(key, out previousTagSize);
flvMessagePackWriter.WriteArray(CreateFlvFirstFrame(sps, pps,false, previousTagSize));
VideoSPSDict.TryUpdate(key, spsInfo, spsInfo);
}
}
else
{ {
flvMessagePackWriter.WriteArray(CreateFlvFirstFrame(sps, pps)); flvMessagePackWriter.WriteArray(CreateFlvFirstFrame(sps, pps));
FrameInitDict.TryAdd(key, true);
VideoSPSDict.TryAdd(key, spsInfo);
} }
} }
foreach (var naln in nALUs.Where(w=> w.NALUHeader.NalUnitType != 7 && w.NALUHeader.NalUnitType != 8 && w.NALUHeader.NalUnitType != 6)) foreach (var naln in nALUs.Where(w=> w.NALUHeader.NalUnitType != 7 && w.NALUHeader.NalUnitType != 8 && w.NALUHeader.NalUnitType != 6))
@@ -216,7 +212,7 @@ namespace JT1078.Flv
if (PreviousTagSizeDict.TryGetValue(key, out uint previousTagSize)) if (PreviousTagSizeDict.TryGetValue(key, out uint previousTagSize))
{ {
currentMarkPosition = flvMessagePackWriter.GetCurrentPosition(); currentMarkPosition = flvMessagePackWriter.GetCurrentPosition();
CreateVideoTagOtherFrame(ref flvMessagePackWriter, previousTagSize, naln,sps, pps, sei);
CreateVideoTagOtherFrame(ref flvMessagePackWriter, previousTagSize, naln);
nextMarkPosition = flvMessagePackWriter.GetCurrentPosition(); nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();
uint tmpPreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength); uint tmpPreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength);
PreviousTagSizeDict.TryUpdate(key, tmpPreviousTagSize, previousTagSize); PreviousTagSizeDict.TryUpdate(key, tmpPreviousTagSize, previousTagSize);


+ 11
- 2
src/JT1078.Flv/MessagePack/EXPGolombReader.cs Wyświetl plik

@@ -21,7 +21,7 @@ namespace JT1078.Flv.MessagePack
Word = 0; Word = 0;
BitsAvailable = 0; BitsAvailable = 0;
} }
public (byte profileIdc,byte levelIdc,uint profileCompat,int width, int height) ReadSPS()
public SPSInfo ReadSPS()
{ {
int sarScale = 1; int sarScale = 1;
uint frameCropLeftOffset=0; uint frameCropLeftOffset=0;
@@ -153,7 +153,7 @@ namespace JT1078.Flv.MessagePack
} }
int width= (int)((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale); int width= (int)((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale);
int height = (int)(((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - ((frameMbsOnlyFlag == 1U ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset))); int height = (int)(((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - ((frameMbsOnlyFlag == 1U ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset)));
return (profileIdc, levelIdc, profileCompat,width, height);
return new SPSInfo { profileIdc= profileIdc, levelIdc= levelIdc, profileCompat= profileCompat, width= width, height= height };
} }
public void LoadWord() public void LoadWord()
{ {
@@ -310,4 +310,13 @@ namespace JT1078.Flv.MessagePack
} }
} }
} }

public struct SPSInfo
{
public byte profileIdc { get; set; }
public byte levelIdc { get; set; }
public uint profileCompat { get; set; }
public int width { get; set; }
public int height { get; set; }
}
} }

+ 0
- 22
src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs Wyświetl plik

@@ -85,19 +85,6 @@ namespace JT1078.Flv.MessagePack
else if(videoPacke.AvcPacketType == AvcPacketType.Raw) else if(videoPacke.AvcPacketType == AvcPacketType.Raw)
{ {
WriteUInt24(videoPacke.CompositionTime); WriteUInt24(videoPacke.CompositionTime);
//One or more NALUs
//WriteArray(new byte[] {0,0,0,1});
if (videoPacke.MultiData != null)
{
foreach(var item in videoPacke.MultiData)
{
if (item != null && item.Length > 0)
{
WriteInt32(item.Length);
WriteArray(item);
}
}
}
if (videoPacke.Data != null && videoPacke.Data.Length>0) if (videoPacke.Data != null && videoPacke.Data.Length>0)
{ {
WriteInt32(videoPacke.Data.Length); WriteInt32(videoPacke.Data.Length);
@@ -121,19 +108,10 @@ namespace JT1078.Flv.MessagePack
//reserved(6bits)+LengthSizeMinusOne(2bits) //reserved(6bits)+LengthSizeMinusOne(2bits)
WriteByte(0xFF); WriteByte(0xFF);
WriteByte((byte)configurationRecord.NumOfSequenceParameterSets); WriteByte((byte)configurationRecord.NumOfSequenceParameterSets);

WriteUInt16((ushort)(configurationRecord.SPSBuffer.Length)); WriteUInt16((ushort)(configurationRecord.SPSBuffer.Length));

//WriteUInt16((ushort)(configurationRecord.SPSBuffer.Length + 4));
//WriteArray(new byte[] { 0, 0, 0, 1 });

WriteArray(configurationRecord.SPSBuffer); WriteArray(configurationRecord.SPSBuffer);
WriteByte(configurationRecord.NumOfPictureParameterSets); WriteByte(configurationRecord.NumOfPictureParameterSets);

WriteUInt16((ushort)(configurationRecord.PPSBuffer.Length)); WriteUInt16((ushort)(configurationRecord.PPSBuffer.Length));

//WriteUInt16((ushort)(configurationRecord.PPSBuffer.Length+4));
//WriteArray(new byte[] { 0, 0, 0, 1 });
WriteArray(configurationRecord.PPSBuffer); WriteArray(configurationRecord.PPSBuffer);
} }
} }


+ 0
- 2
src/JT1078.Flv/Metadata/AvcVideoPacke.cs Wyświetl plik

@@ -14,7 +14,5 @@ namespace JT1078.Flv.Metadata
public AVCDecoderConfigurationRecord AVCDecoderConfiguration { get; set; } public AVCDecoderConfigurationRecord AVCDecoderConfiguration { get; set; }


public byte[] Data { get; set; } public byte[] Data { get; set; }

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

+ 4
- 22
src/JT1078.Protocol/MessagePack/JT1078MessagePackWriter.cs Wyświetl plik

@@ -24,40 +24,22 @@ namespace JT1078.Protocol.MessagePack
} }
public void WriteUInt16(ushort value) public void WriteUInt16(ushort value)
{ {
var span = writer.Free;
span[0] = (byte)(value >> 8);
span[1] = (byte)value;
BinaryPrimitives.WriteUInt16BigEndian(writer.Free, value);
writer.Advance(2); writer.Advance(2);
} }
public void WriteInt32(int value) public void WriteInt32(int value)
{ {
var span = writer.Free;
span[0] = (byte)(value >> 24);
span[1] = (byte)(value >> 16);
span[2] = (byte)(value >> 8);
span[3] = (byte)value;
BinaryPrimitives.WriteInt32BigEndian(writer.Free, value);
writer.Advance(4); writer.Advance(4);
} }
public void WriteUInt64(ulong value) public void WriteUInt64(ulong value)
{ {
var span = writer.Free;
span[0] = (byte)(value >> 56);
span[1] = (byte)(value >> 48);
span[2] = (byte)(value >> 40);
span[3] = (byte)(value >> 32);
span[4] = (byte)(value >> 24);
span[5] = (byte)(value >> 16);
span[6] = (byte)(value >> 8);
span[7] = (byte)value;
BinaryPrimitives.WriteUInt64BigEndian(writer.Free, value);
writer.Advance(8); writer.Advance(8);
} }
public void WriteUInt32(uint value) public void WriteUInt32(uint value)
{ {
var span = writer.Free;
span[0] = (byte)(value >> 24);
span[1] = (byte)(value >> 16);
span[2] = (byte)(value >> 8);
span[3] = (byte)value;
BinaryPrimitives.WriteUInt32BigEndian(writer.Free, value);
writer.Advance(4); writer.Advance(4);
} }
public void WriteArray(ReadOnlySpan<byte> src) public void WriteArray(ReadOnlySpan<byte> src)


Ładowanie…
Anuluj
Zapisz