소스 검색

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

tags/v1.1.0
smallchi 5 년 전
부모
커밋
adf8a64898
10개의 변경된 파일116개의 추가작업 그리고 91개의 파일을 삭제
  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 파일 보기

@@ -330,3 +330,4 @@ ASALocalRun/
.mfractor/
/src/JT1078.Flv.Test/H264/JT1078_3.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 파일 보기

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

namespace JT1078.Flv.Test
{
@@ -208,5 +209,67 @@ namespace JT1078.Flv.Test
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 파일 보기


+ 5
- 11
src/JT1078.Flv.Test/H264/index.html 파일 보기

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

if (flvjs.isSupported()) {
var player = document.getElementById('player');
var flvPlayer = flvjs.createPlayer({
type: 'flv',
url: "jt1078_3.flv"
url: "jt1078_5.flv"
});
var width, height, flag;
flvPlayer.attachMediaElement(player);
flvPlayer.on(flvjs.Events.SCRIPTDATA_ARRIVED, (e) => {
console.log(e);
});
flvPlayer.load();
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>
</body>

+ 4
- 0
src/JT1078.Flv.Test/JT1078.Flv.Test.csproj 파일 보기

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

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

</Project>

+ 28
- 32
src/JT1078.Flv/FlvEncoder.cs 파일 보기

@@ -17,6 +17,7 @@ namespace JT1078.Flv
private const uint PreviousTagSizeFixedLength = 4;
private static readonly ConcurrentDictionary<string, uint> PreviousTagSizeDict;
private static readonly ConcurrentDictionary<string, bool> FrameInitDict;
private static readonly ConcurrentDictionary<string, SPSInfo> VideoSPSDict;
private static readonly Flv.H264.H264Decoder H264Decoder;
static FlvEncoder()
{
@@ -24,6 +25,7 @@ namespace JT1078.Flv
VideoFlvHeaderBuffer = VideoFlvHeader.ToArray().ToArray();
PreviousTagSizeDict = new ConcurrentDictionary<string, uint>();
FrameInitDict = new ConcurrentDictionary<string, bool>();
VideoSPSDict = new ConcurrentDictionary<string, SPSInfo>();
H264Decoder = new Flv.H264.H264Decoder();
}
/// <summary>
@@ -32,7 +34,7 @@ namespace JT1078.Flv
/// <param name="sps">NalUnitType->7</param>
/// <param name="pps">NalUnitType->8</param>
/// <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);
int currentMarkPosition = 0;
@@ -40,16 +42,18 @@ namespace JT1078.Flv
try
{
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
//flv header
flvMessagePackWriter.WriteArray(VideoFlvHeaderBuffer);

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

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

//flv body video tag
@@ -78,10 +82,10 @@ namespace JT1078.Flv
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
flvMessagePackWriter.WriteUInt32(0);
flvMessagePackWriter.WriteUInt32(previousTagSize);
//flv body script tag
//flv body tag header
FlvTags flvTags = new FlvTags();
@@ -141,7 +145,7 @@ namespace JT1078.Flv

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
flvMessagePackWriter.WriteUInt32(previousTagSize);
@@ -156,38 +160,18 @@ namespace JT1078.Flv
flvTags.VideoTagsData.VideoData = new AvcVideoPacke();
flvTags.VideoTagsData.VideoData.CompositionTime = 0;
flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw;
flvTags.VideoTagsData.VideoData.MultiData = new List<byte[]>();
LastFrameInterval += nALU.LastFrameInterval;
flvTags.Timestamp = LastFrameInterval;

if (nALU.NALUHeader.NalUnitType == 5)
{
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
{
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);
}
public byte[] CreateFlvFrame(List<H264NALU> nALUs, int minimumLength = 65535)
@@ -204,10 +188,22 @@ namespace JT1078.Flv
if (sps!=null && pps != null)
{
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));
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))
@@ -216,7 +212,7 @@ namespace JT1078.Flv
if (PreviousTagSizeDict.TryGetValue(key, out uint previousTagSize))
{
currentMarkPosition = flvMessagePackWriter.GetCurrentPosition();
CreateVideoTagOtherFrame(ref flvMessagePackWriter, previousTagSize, naln,sps, pps, sei);
CreateVideoTagOtherFrame(ref flvMessagePackWriter, previousTagSize, naln);
nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();
uint tmpPreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength);
PreviousTagSizeDict.TryUpdate(key, tmpPreviousTagSize, previousTagSize);


+ 11
- 2
src/JT1078.Flv/MessagePack/EXPGolombReader.cs 파일 보기

@@ -21,7 +21,7 @@ namespace JT1078.Flv.MessagePack
Word = 0;
BitsAvailable = 0;
}
public (byte profileIdc,byte levelIdc,uint profileCompat,int width, int height) ReadSPS()
public SPSInfo ReadSPS()
{
int sarScale = 1;
uint frameCropLeftOffset=0;
@@ -153,7 +153,7 @@ namespace JT1078.Flv.MessagePack
}
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)));
return (profileIdc, levelIdc, profileCompat,width, height);
return new SPSInfo { profileIdc= profileIdc, levelIdc= levelIdc, profileCompat= profileCompat, width= width, height= height };
}
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 파일 보기

@@ -85,19 +85,6 @@ namespace JT1078.Flv.MessagePack
else if(videoPacke.AvcPacketType == AvcPacketType.Raw)
{
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)
{
WriteInt32(videoPacke.Data.Length);
@@ -121,19 +108,10 @@ namespace JT1078.Flv.MessagePack
//reserved(6bits)+LengthSizeMinusOne(2bits)
WriteByte(0xFF);
WriteByte((byte)configurationRecord.NumOfSequenceParameterSets);

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

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

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

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

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


+ 0
- 2
src/JT1078.Flv/Metadata/AvcVideoPacke.cs 파일 보기

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

public byte[] Data { get; set; }

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

+ 4
- 22
src/JT1078.Protocol/MessagePack/JT1078MessagePackWriter.cs 파일 보기

@@ -24,40 +24,22 @@ namespace JT1078.Protocol.MessagePack
}
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);
}
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);
}
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);
}
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);
}
public void WriteArray(ReadOnlySpan<byte> src)


불러오는 중...
취소
저장