Browse Source

修改flv编码方式

tags/v1.1.0
smallchi 5 years ago
parent
commit
6950d241be
3 changed files with 133 additions and 102 deletions
  1. +26
    -15
      src/JT1078.Flv.Test/FlvEncoderTest.cs
  2. +86
    -86
      src/JT1078.Flv/FlvEncoder.cs
  3. +21
    -1
      src/JT1078.Flv/JT1078.Flv.csproj

+ 26
- 15
src/JT1078.Flv.Test/FlvEncoderTest.cs View File

@@ -34,12 +34,15 @@ namespace JT1078.Flv.Test

FlvEncoder encoder = new FlvEncoder();
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");
if (File.Exists(filepath))
{
File.Delete(filepath);
}
File.WriteAllBytes(filepath, contents);
File.WriteAllBytes(filepath, tmp);
}

[Fact]
@@ -75,7 +78,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++)
{
@@ -133,6 +136,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++)
{
@@ -189,6 +193,8 @@ 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++)
{
@@ -245,21 +251,26 @@ namespace JT1078.Flv.Test
}
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++)
List<ushort> times = new List<ushort>();
List<int> type = new List<int>();
foreach (var item in h264NALULs)
{
var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList());
if (flv2.Length != 0)
{
fileStream.Write(flv2);
}
//ExpGolombReader expGolombReader = new ExpGolombReader(item.RawData);
type.Add(item.NALUHeader.NalUnitType);
times.Add(item.LastFrameInterval);
//tmpSpss.Add(expGolombReader.ReadSPS());
}
//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++)
//{
// var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList());
// if (flv2.Length != 0)
// {
// fileStream.Write(flv2);
// }
//}
}
catch (Exception ex)
{


+ 86
- 86
src/JT1078.Flv/FlvEncoder.cs View File

@@ -13,74 +13,58 @@ namespace JT1078.Flv
{
public class FlvEncoder
{
private static readonly byte[] VideoFlvHeaderBuffer;
struct FlvFrameInfo
{
public uint PreviousTagSize { get; set;}
public uint LastFrameInterval { get; set; }
}
public static readonly byte[] VideoFlvHeaderBuffer;
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;
private static readonly ConcurrentDictionary<string, FlvFrameInfo> FlvFrameInfoDict;
static FlvEncoder()
{
FlvHeader VideoFlvHeader = new FlvHeader(true, false);
VideoFlvHeaderBuffer = VideoFlvHeader.ToArray().ToArray();
PreviousTagSizeDict = new ConcurrentDictionary<string, uint>();
FrameInitDict = new ConcurrentDictionary<string, bool>();
VideoSPSDict = new ConcurrentDictionary<string, SPSInfo>();
FlvFrameInfoDict = new ConcurrentDictionary<string, FlvFrameInfo>();
H264Decoder = new Flv.H264.H264Decoder();
}

/// <summary>
///
/// </summary>
/// <param name="sps">NalUnitType->7</param>
/// <param name="pps">NalUnitType->8</param>
/// <returns></returns>
public byte[] CreateFlvFirstFrame(H264NALU sps, H264NALU pps,bool isSendFlvHeader=true,uint previousTagSize=0)
private void CreateFlvKeyFrame(ref FlvMessagePackWriter flvMessagePackWriter, string key,byte[] spsRawData, byte[] ppsRawData, SPSInfo spsInfo, uint previousTagSize = 0)
{
byte[] buffer = FlvArrayPool.Rent(65535);
int currentMarkPosition = 0;
int nextMarkPosition = 0;
try
{
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
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, previousTagSize);
nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();

//flv body video tag
uint scriptTagFramePreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength);
AVCDecoderConfigurationRecord aVCDecoderConfigurationRecord = new AVCDecoderConfigurationRecord();
aVCDecoderConfigurationRecord.AVCProfileIndication = spsInfo.profileIdc;
aVCDecoderConfigurationRecord.ProfileCompatibility = (byte)spsInfo.profileCompat;
aVCDecoderConfigurationRecord.AVCLevelIndication = spsInfo.levelIdc;
aVCDecoderConfigurationRecord.NumOfPictureParameterSets = 1;
aVCDecoderConfigurationRecord.PPSBuffer = pps.RawData;
aVCDecoderConfigurationRecord.SPSBuffer = sps.RawData;
currentMarkPosition = flvMessagePackWriter.GetCurrentPosition();
//flv body script tag
CreateScriptTagFrame(ref flvMessagePackWriter, spsInfo.width, spsInfo.height, previousTagSize);
nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();

currentMarkPosition = flvMessagePackWriter.GetCurrentPosition();
CreateVideoTag0Frame(ref flvMessagePackWriter, scriptTagFramePreviousTagSize, aVCDecoderConfigurationRecord);
nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();
//flv body video tag
uint scriptTagFramePreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength);
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;

//flv body video tag 0
uint videoTag0PreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength);
//cache PreviousTagSize
PreviousTagSizeDict.AddOrUpdate(sps.GetKey(), videoTag0PreviousTagSize, (a, b) => videoTag0PreviousTagSize);
currentMarkPosition = flvMessagePackWriter.GetCurrentPosition();
CreateVideoTag0Frame(ref flvMessagePackWriter, scriptTagFramePreviousTagSize, aVCDecoderConfigurationRecord);
nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();

return flvMessagePackWriter.FlushAndGetArray();
}
finally
{
FlvArrayPool.Return(buffer);
}
//flv body video tag 0
uint videoTag0PreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength);
//cache PreviousTagSize
FlvFrameInfoDict.AddOrUpdate(key, new FlvFrameInfo { PreviousTagSize= videoTag0PreviousTagSize }, (a, b) => {
b.PreviousTagSize = videoTag0PreviousTagSize;
return b;
});
}
private void CreateScriptTagFrame(ref FlvMessagePackWriter flvMessagePackWriter, int width, int height,uint previousTagSize, double frameRate = 25d)
{
@@ -142,13 +126,10 @@ namespace JT1078.Flv
flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord;
flvMessagePackWriter.WriteFlvTag(flvTags);
}

public static uint LastFrameInterval = 0;

private void CreateVideoTagOtherFrame(ref FlvMessagePackWriter flvMessagePackWriter, uint previousTagSize, H264NALU nALU)
private void CreateVideoTagOtherFrame(ref FlvMessagePackWriter flvMessagePackWriter, FlvFrameInfo flvFrameInfo, H264NALU nALU)
{
//flv body PreviousTagSize
flvMessagePackWriter.WriteUInt32(previousTagSize);
flvMessagePackWriter.WriteUInt32(flvFrameInfo.PreviousTagSize);
//flv body video tag
//flv body tag header
FlvTags flvTags = new FlvTags();
@@ -160,10 +141,8 @@ namespace JT1078.Flv
flvTags.VideoTagsData.VideoData = new AvcVideoPacke();
flvTags.VideoTagsData.VideoData.CompositionTime = 0;
flvTags.VideoTagsData.VideoData.AvcPacketType = AvcPacketType.Raw;
LastFrameInterval += nALU.LastFrameInterval;
flvTags.Timestamp = LastFrameInterval;
if (nALU.NALUHeader.NalUnitType == 5)
flvTags.Timestamp = flvFrameInfo.LastFrameInterval;
if (nALU.NALUHeader.NalUnitType == 5 || nALU.DataType== JT1078DataType.视频I帧)
{
flvTags.VideoTagsData.FrameType = FrameType.KeyFrame;
}
@@ -182,42 +161,63 @@ namespace JT1078.Flv
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
int currentMarkPosition = 0;
int nextMarkPosition = 0;
H264NALU sps = nALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == 7);
H264NALU pps = nALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == 8);
H264NALU sei = nALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == 6);
if (sps!=null && pps != null)
H264NALU sps=null, pps=null;
SPSInfo spsInfo = new SPSInfo();
foreach (var naln in nALUs)
{
string key = sps.GetKey();
ExpGolombReader h264GolombReader = new ExpGolombReader(sps.RawData);
var spsInfo = h264GolombReader.ReadSPS();
if(VideoSPSDict.TryGetValue(key,out var spsInfoCache))
string key = naln.GetKey();
if (sps != null && pps != null)
{
if(spsInfoCache.height!= spsInfo.height && spsInfoCache.width!= spsInfo.width)
if (VideoSPSDict.TryGetValue(key, out var spsInfoCache))
{
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, flvFrameInfo.PreviousTagSize);
VideoSPSDict.TryUpdate(key, spsInfo, spsInfo);
flvFrameInfo.LastFrameInterval = 0;
FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo);
}
}
}
else
{
uint previousTagSize = 0;
PreviousTagSizeDict.TryGetValue(key, out previousTagSize);
flvMessagePackWriter.WriteArray(CreateFlvFirstFrame(sps, pps,false, previousTagSize));
VideoSPSDict.TryUpdate(key, spsInfo, spsInfo);
CreateFlvKeyFrame(ref flvMessagePackWriter, key, sps.RawData, pps.RawData, spsInfo, 0);
VideoSPSDict.TryAdd(key, spsInfo);
}
}
else
//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)
{
flvMessagePackWriter.WriteArray(CreateFlvFirstFrame(sps, pps));
VideoSPSDict.TryAdd(key, spsInfo);
case 5:// IDR
case 1:// I/P/B
if (FlvFrameInfoDict.TryGetValue(key, out FlvFrameInfo flvFrameInfo))
{
currentMarkPosition = flvMessagePackWriter.GetCurrentPosition();
CreateVideoTagOtherFrame(ref flvMessagePackWriter, flvFrameInfo, naln);
nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();
uint tmpPreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength);
flvFrameInfo.PreviousTagSize = tmpPreviousTagSize;
flvFrameInfo.LastFrameInterval += naln.LastFrameInterval;
FlvFrameInfoDict.TryUpdate(key, flvFrameInfo, flvFrameInfo);
}
break;
case 7:// sps
sps = naln;
var rawData = H264Decoder.DiscardEmulationPreventionBytes(naln.RawData);
ExpGolombReader h264GolombReader = new ExpGolombReader(rawData);
spsInfo = h264GolombReader.ReadSPS();
break;
case 8:// pps
pps = naln;
break;
case 6://SEI
break;
default:
break;
}
}
foreach (var naln in nALUs.Where(w=> w.NALUHeader.NalUnitType != 7 && w.NALUHeader.NalUnitType != 8 && w.NALUHeader.NalUnitType != 6))
{
string key = naln.GetKey();
if (PreviousTagSizeDict.TryGetValue(key, out uint previousTagSize))
{
currentMarkPosition = flvMessagePackWriter.GetCurrentPosition();
CreateVideoTagOtherFrame(ref flvMessagePackWriter, previousTagSize, naln);
nextMarkPosition = flvMessagePackWriter.GetCurrentPosition();
uint tmpPreviousTagSize = (uint)(nextMarkPosition - currentMarkPosition - PreviousTagSizeFixedLength);
PreviousTagSizeDict.TryUpdate(key, tmpPreviousTagSize, previousTagSize);
}
}
return flvMessagePackWriter.FlushAndGetArray();
}
finally


+ 21
- 1
src/JT1078.Flv/JT1078.Flv.csproj View File

@@ -3,6 +3,21 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;</TargetFrameworks>
<LangVersion>8.0</LangVersion>
<Copyright>Copyright 2019.</Copyright>
<Authors>SmallChi(Koike)</Authors>
<PackageId>JT1078.Flv</PackageId>
<Product>JT1078.Flv</Product>
<Description>基于JT1078的Flv视频包</Description>
<PackageReleaseNotes>基于JT1078的Flv视频包</PackageReleaseNotes>
<RepositoryUrl>https://github.com/SmallChi/JT1078</RepositoryUrl>
<PackageProjectUrl>https://github.com/SmallChi/JT1078</PackageProjectUrl>
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl>
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>1.0.0-preview1</Version>
<SignAssembly>false</SignAssembly>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
@@ -12,5 +27,10 @@
<ItemGroup>
<ProjectReference Include="..\JT1078.Protocol\JT1078.Protocol.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
</Project>

Loading…
Cancel
Save