@@ -8,4 +8,6 @@ ffmpeg -i demo.264 -vcodec copy -f mp4 -movflags frag_keyframe+empty_moov fragme | |||||
ffmpeg -i ipc.264 -vcodec copy -f mp4 -movflags frag_keyframe+empty_moov ipc_fragmented_demo.mp4 | ffmpeg -i ipc.264 -vcodec copy -f mp4 -movflags frag_keyframe+empty_moov ipc_fragmented_demo.mp4 | ||||
ffmpeg -i jt1078_3.h264 -c:v copy -f mp4 -movflags empty_moov+default_base_moof+frag_keyframe fragmented_base_moof_demo.mp4 | |||||
chrome://media-internals/ | chrome://media-internals/ |
@@ -96,28 +96,28 @@ | |||||
// otherwise insert it to queue | // otherwise insert it to queue | ||||
var memview = new Uint8Array(arr); | var memview = new Uint8Array(arr); | ||||
if (verbose) { console.log("got", arr.byteLength, "bytes. Values=", memview[0], memview[1], memview[2], memview[3], memview[4]); } | if (verbose) { console.log("got", arr.byteLength, "bytes. Values=", memview[0], memview[1], memview[2], memview[3], memview[4]); } | ||||
res = getBox(memview, 0); | |||||
main_length = res[0]; name = res[1]; // this boxes length and name | |||||
if ((name == "ftyp") && (pass == 0)) { | |||||
pass = pass + 1; | |||||
console.log("got ftyp"); | |||||
} | |||||
else if ((name == "moov") && (pass == 1)) { | |||||
pass = pass + 1; | |||||
console.log("got moov"); | |||||
} | |||||
else if ((name == "moof") && (pass == 2)) { | |||||
if (hasFirstSampleFlag(memview)) { | |||||
pass = pass + 1; | |||||
console.log("got that special moof"); | |||||
} | |||||
else { | |||||
return; | |||||
} | |||||
} | |||||
else if (pass < 3) { | |||||
return; | |||||
} | |||||
//res = getBox(memview, 0); | |||||
//main_length = res[0]; name = res[1]; // this boxes length and name | |||||
//if ((name == "ftyp") && (pass == 0)) { | |||||
// pass = pass + 1; | |||||
// console.log("got ftyp"); | |||||
//} | |||||
//else if ((name == "moov") && (pass == 1)) { | |||||
// pass = pass + 1; | |||||
// console.log("got moov"); | |||||
//} | |||||
//else if ((name == "moof") && (pass == 2)) { | |||||
// if (hasFirstSampleFlag(memview)) { | |||||
// pass = pass + 1; | |||||
// console.log("got that special moof"); | |||||
// } | |||||
// else { | |||||
// return; | |||||
// } | |||||
//} | |||||
//else if (pass < 3) { | |||||
// return; | |||||
//} | |||||
// keep the latency to minimum | // keep the latency to minimum | ||||
let latest = stream_live.duration; | let latest = stream_live.duration; | ||||
if ((stream_live.duration >= buffering_sec) && | if ((stream_live.duration >= buffering_sec) && | ||||
@@ -132,8 +132,9 @@ | |||||
data = arr; | data = arr; | ||||
if (!stream_started) { | if (!stream_started) { | ||||
if (verbose) { console.log("Streaming started: ", memview[0], memview[1], memview[2], memview[3], memview[4]); } | if (verbose) { console.log("Streaming started: ", memview[0], memview[1], memview[2], memview[3], memview[4]); } | ||||
source_buffer.appendBuffer(data); | |||||
stream_started = true; | stream_started = true; | ||||
source_buffer.appendBuffer(data); | |||||
cc = cc + 1; | cc = cc + 1; | ||||
return; | return; | ||||
} | } | ||||
@@ -173,7 +174,7 @@ | |||||
.build(); | .build(); | ||||
ws.on("video", (message) => { | ws.on("video", (message) => { | ||||
var buff=base64ToArrayBuffer(message); | var buff=base64ToArrayBuffer(message); | ||||
console.log(buff); | |||||
//console.log(buff); | |||||
putPacket(buff); | putPacket(buff); | ||||
}); | }); | ||||
ws.start().catch(err => console.error(err)); | ws.start().catch(err => console.error(err)); | ||||
@@ -451,13 +451,13 @@ namespace JT1078.FMp4.Test | |||||
var nalus1 = h264Decoder.ParseNALU(package1); | var nalus1 = h264Decoder.ParseNALU(package1); | ||||
var moov = fMp4Encoder.EncoderMoovBox(nalus1, package1.Bodies.Length); | var moov = fMp4Encoder.EncoderMoovBox(nalus1, package1.Bodies.Length); | ||||
fileStream.Write(moov); | fileStream.Write(moov); | ||||
int moofOffset = ftyp.Length + moov.Length; | |||||
var flag = package1.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧 ? 1u : 0u; | |||||
var otherMoofBuffer = fMp4Encoder.EncoderMoofBox(nalus1, package1.Bodies.Length, package1.Timestamp, flag); | |||||
foreach (var package in packages) | foreach (var package in packages) | ||||
{ | { | ||||
var otherNalus = h264Decoder.ParseNALU(package); | var otherNalus = h264Decoder.ParseNALU(package); | ||||
var flag = package.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧 ? 1u : 0u; | |||||
var otherMoofBuffer = fMp4Encoder.EncoderMoofBox(otherNalus, package.Bodies.Length, package.Timestamp, package.LastIFrameInterval, flag); | |||||
var otherMdatBuffer = fMp4Encoder.EncoderMdatBox(otherNalus, package.Bodies.Length); | var otherMdatBuffer = fMp4Encoder.EncoderMdatBox(otherNalus, package.Bodies.Length); | ||||
fileStream.Write(otherMoofBuffer); | |||||
fileStream.Write(otherMdatBuffer); | fileStream.Write(otherMdatBuffer); | ||||
} | } | ||||
fileStream.Close(); | fileStream.Close(); | ||||
@@ -30,15 +30,13 @@ namespace JT1078.FMp4 | |||||
WriterFullBoxToBuffer(ref writer); | WriterFullBoxToBuffer(ref writer); | ||||
if(SampleDependencyTypes!=null && SampleDependencyTypes.Count > 0) | if(SampleDependencyTypes!=null && SampleDependencyTypes.Count > 0) | ||||
{ | { | ||||
foreach(var item in SampleDependencyTypes) | |||||
foreach (var item in SampleDependencyTypes) | |||||
{ | { | ||||
writer.WriteByte(item.IsLeading); | |||||
writer.WriteByte(item.SampleDependsOn); | |||||
writer.WriteByte(item.SampleIsDependedOn); | |||||
writer.WriteByte(item.SampleHasRedundancy); | |||||
writer.WriteByte(item.DegradPrio); | |||||
writer.WriteByte(item.IsNonSync); | |||||
writer.WriteByte(item.PaddingValue); | |||||
writer.WriteByte((byte)(item.IsLeading<<2 | | |||||
item.SampleDependsOn | | |||||
item.SampleIsDependedOn <<6| | |||||
item.SampleHasRedundancy << 4 | | |||||
item.IsNonSync)); | |||||
} | } | ||||
} | } | ||||
End(ref writer); | End(ref writer); | ||||
@@ -50,9 +48,9 @@ namespace JT1078.FMp4 | |||||
public byte SampleDependsOn { get; set; } | public byte SampleDependsOn { get; set; } | ||||
public byte SampleIsDependedOn { get; set; } | public byte SampleIsDependedOn { get; set; } | ||||
public byte SampleHasRedundancy { get; set; } | public byte SampleHasRedundancy { get; set; } | ||||
public byte DegradPrio { get; set; } | |||||
//public byte DegradPrio { get; set; } | |||||
public byte IsNonSync { get; set; } | public byte IsNonSync { get; set; } | ||||
public byte PaddingValue { get; set; } | |||||
//public byte PaddingValue { get; set; } | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -0,0 +1,51 @@ | |||||
using JT1078.FMp4.Interfaces; | |||||
using JT1078.FMp4.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.FMp4 | |||||
{ | |||||
/// <summary> | |||||
/// styp | |||||
/// </summary> | |||||
public class SegmentTypeBox : Mp4Box, IFMp4MessagePackFormatter | |||||
{ | |||||
/// <summary> | |||||
/// styp | |||||
/// </summary> | |||||
public SegmentTypeBox() : base("styp") | |||||
{ | |||||
} | |||||
/// <summary> | |||||
///因为兼容性一般可以分为推荐兼容性和默认兼容性。这里 major_brand 就相当于是推荐兼容性。通常,在 Web 中解码,一般而言都是使用 isom 这个万金油即可。如果是需要特定的格式,可以自行定义。 | |||||
/// 4位 | |||||
/// </summary> | |||||
public string MajorBrand { get; set; } | |||||
/// <summary> | |||||
/// 最低兼容版本 | |||||
/// 4位 | |||||
/// </summary> | |||||
public string MinorVersion { get; set; } = "\0\0\0\0"; | |||||
/// <summary> | |||||
/// 和MajorBrand类似,通常是针对 MP4 中包含的额外格式,比如,AVC,AAC 等相当于的音视频解码格式。 | |||||
/// 4位*n | |||||
/// </summary> | |||||
public List<string> CompatibleBrands { get; set; } = new List<string>(); | |||||
public void ToBuffer(ref FMp4MessagePackWriter writer) | |||||
{ | |||||
Start(ref writer); | |||||
writer.WriteASCII(MajorBrand); | |||||
writer.WriteASCII(MinorVersion); | |||||
if (CompatibleBrands != null && CompatibleBrands.Count > 0) | |||||
{ | |||||
foreach (var item in CompatibleBrands) | |||||
{ | |||||
writer.WriteASCII(item); | |||||
} | |||||
} | |||||
End(ref writer); | |||||
} | |||||
} | |||||
} |
@@ -37,6 +37,31 @@ namespace JT1078.FMp4 | |||||
h264Decoder = new H264Decoder(); | h264Decoder = new H264Decoder(); | ||||
} | } | ||||
public byte[] EncoderStypBox() | |||||
{ | |||||
byte[] buffer = FMp4ArrayPool.Rent(4096); | |||||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | |||||
try | |||||
{ | |||||
//styp | |||||
SegmentTypeBox stypTypeBox = new SegmentTypeBox(); | |||||
stypTypeBox.MajorBrand = "msdh"; | |||||
stypTypeBox.MinorVersion = "\0\0\0\0"; | |||||
stypTypeBox.CompatibleBrands.Add("isom"); | |||||
stypTypeBox.CompatibleBrands.Add("mp42"); | |||||
stypTypeBox.CompatibleBrands.Add("msdh"); | |||||
stypTypeBox.CompatibleBrands.Add("nsix"); | |||||
stypTypeBox.CompatibleBrands.Add("iso5"); | |||||
stypTypeBox.CompatibleBrands.Add("iso6"); | |||||
stypTypeBox.ToBuffer(ref writer); | |||||
var data = writer.FlushAndGetArray(); | |||||
return data; | |||||
} | |||||
finally | |||||
{ | |||||
FMp4ArrayPool.Return(buffer); | |||||
} | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// 编码ftyp盒子 | /// 编码ftyp盒子 | ||||
@@ -50,14 +75,22 @@ namespace JT1078.FMp4 | |||||
{ | { | ||||
//ftyp | //ftyp | ||||
FileTypeBox fileTypeBox = new FileTypeBox(); | FileTypeBox fileTypeBox = new FileTypeBox(); | ||||
fileTypeBox.MajorBrand = "msdh"; | |||||
fileTypeBox.MinorVersion = "\0\0\0\0"; | |||||
fileTypeBox.MajorBrand = "isom"; | |||||
fileTypeBox.MinorVersion = "\0\0\u0002\0"; | |||||
fileTypeBox.CompatibleBrands.Add("isom"); | fileTypeBox.CompatibleBrands.Add("isom"); | ||||
fileTypeBox.CompatibleBrands.Add("mp42"); | |||||
fileTypeBox.CompatibleBrands.Add("msdh"); | |||||
fileTypeBox.CompatibleBrands.Add("nsix"); | |||||
fileTypeBox.CompatibleBrands.Add("iso2"); | |||||
fileTypeBox.CompatibleBrands.Add("avc1"); | |||||
fileTypeBox.CompatibleBrands.Add("mp41"); | |||||
fileTypeBox.CompatibleBrands.Add("iso5"); | fileTypeBox.CompatibleBrands.Add("iso5"); | ||||
fileTypeBox.CompatibleBrands.Add("iso6"); | |||||
//FileTypeBox fileTypeBox = new FileTypeBox(); | |||||
//fileTypeBox.MajorBrand = "iso5"; | |||||
//fileTypeBox.MinorVersion = "\0\0\u0002\0"; | |||||
//fileTypeBox.CompatibleBrands.Add("iso5"); | |||||
//fileTypeBox.CompatibleBrands.Add("iso6"); | |||||
//fileTypeBox.CompatibleBrands.Add("mp41"); | |||||
//fileTypeBox.ToBuffer(ref writer); | |||||
fileTypeBox.ToBuffer(ref writer); | fileTypeBox.ToBuffer(ref writer); | ||||
var data = writer.FlushAndGetArray(); | var data = writer.FlushAndGetArray(); | ||||
return data; | return data; | ||||
@@ -132,35 +165,35 @@ namespace JT1078.FMp4 | |||||
avc1.AVCConfigurationBox.SPSs = new List<byte[]>() { spsNALU.RawData }; | avc1.AVCConfigurationBox.SPSs = new List<byte[]>() { spsNALU.RawData }; | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries.Add(avc1); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries.Add(avc1); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.TimeToSampleBox = new TimeToSampleBox() { | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.TimeToSampleBox = new TimeToSampleBox() { | ||||
TimeToSampleInfos=new List<TimeToSampleBox.TimeToSampleInfo> | |||||
{ | |||||
new TimeToSampleBox.TimeToSampleInfo | |||||
{ | |||||
SampleCount=0, | |||||
SampleDelta=0 | |||||
} | |||||
} | |||||
//TimeToSampleInfos=new List<TimeToSampleBox.TimeToSampleInfo> | |||||
//{ | |||||
// new TimeToSampleBox.TimeToSampleInfo | |||||
// { | |||||
// SampleCount=0, | |||||
// SampleDelta=0 | |||||
// } | |||||
//} | |||||
}; | }; | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleToChunkBox = new SampleToChunkBox() { | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleToChunkBox = new SampleToChunkBox() { | ||||
SampleToChunkInfos=new List<SampleToChunkBox.SampleToChunkInfo>() | |||||
{ | |||||
new SampleToChunkBox.SampleToChunkInfo | |||||
{ | |||||
//SampleToChunkInfos=new List<SampleToChunkBox.SampleToChunkInfo>() | |||||
//{ | |||||
// new SampleToChunkBox.SampleToChunkInfo | |||||
// { | |||||
} | |||||
} | |||||
// } | |||||
//} | |||||
}; | }; | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleSizeBox = new SampleSizeBox() { | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleSizeBox = new SampleSizeBox() { | ||||
EntrySize = new List<uint>() | |||||
{ | |||||
0 | |||||
} | |||||
//EntrySize = new List<uint>() | |||||
//{ | |||||
// 0 | |||||
//} | |||||
}; | }; | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.ChunkOffsetBox = new ChunkOffsetBox() { | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.ChunkOffsetBox = new ChunkOffsetBox() { | ||||
ChunkOffset=new List<uint>() | |||||
{ | |||||
0 | |||||
} | |||||
//ChunkOffset=new List<uint>() | |||||
//{ | |||||
// 0 | |||||
//} | |||||
}; | }; | ||||
movieBox.MovieExtendsBox = new MovieExtendsBox(); | movieBox.MovieExtendsBox = new MovieExtendsBox(); | ||||
movieBox.MovieExtendsBox.TrackExtendsBoxs = new List<TrackExtendsBox>(); | movieBox.MovieExtendsBox.TrackExtendsBoxs = new List<TrackExtendsBox>(); | ||||
@@ -186,7 +219,7 @@ namespace JT1078.FMp4 | |||||
/// 编码Moof盒子 | /// 编码Moof盒子 | ||||
/// </summary> | /// </summary> | ||||
/// <returns></returns> | /// <returns></returns> | ||||
public byte[] EncoderMoofBox(List<H264NALU> nalus, int naluLength,ulong timestamp, uint keyframeFlag,uint moofOffset=0) | |||||
public byte[] EncoderMoofBox(List<H264NALU> nalus, int naluLength,ulong timestamp,uint frameInterval, uint keyframeFlag,int moofOffset=0) | |||||
{ | { | ||||
byte[] buffer = FMp4ArrayPool.Rent(naluLength + 4096); | byte[] buffer = FMp4ArrayPool.Rent(naluLength + 4096); | ||||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | ||||
@@ -198,37 +231,60 @@ namespace JT1078.FMp4 | |||||
movieFragmentBox.TrackFragmentBox = new TrackFragmentBox(); | movieFragmentBox.TrackFragmentBox = new TrackFragmentBox(); | ||||
//0x39 写文件 | //0x39 写文件 | ||||
//0x02 分段 | //0x02 分段 | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(2); | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(0x20038); | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = 1; | movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = 1; | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = 48000; | movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = 48000; | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = (uint)naluLength; | movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = (uint)naluLength; | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = 0x1010000; | movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = 0x1010000; | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox(); | movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox(); | ||||
//movieFragmentBox.TrackFragmentBox.SampleDependencyTypeBox = new SampleDependencyTypeBox() | |||||
//{ | |||||
// SampleDependencyTypes = new List<SampleDependencyTypeBox.SampleDependencyType>() | |||||
//}; | |||||
//trun | //trun | ||||
//0x39 写文件 | //0x39 写文件 | ||||
//0x02 分段 | //0x02 分段 | ||||
uint flag = 0u; | |||||
//uint flag = 0x000200 | 0x000800 | 0x000400 | 0x000100; | |||||
uint flag = 4u; | |||||
//var sdtp = new SampleDependencyTypeBox.SampleDependencyType(); | |||||
//if (keyframeFlag==1) | |||||
//{ | |||||
// sdtp.SampleDependsOn = 2; | |||||
// sdtp.SampleIsDependedOn = 1; | |||||
//} | |||||
//else | |||||
//{ | |||||
// sdtp.SampleDependsOn = 1; | |||||
// sdtp.SampleIsDependedOn = 0; | |||||
//} | |||||
if (!first) | if (!first) | ||||
{ | { | ||||
flag = 4u; | |||||
//sdtp.IsLeading = 1; | |||||
//flag = 4u; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = 0; | movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = 0; | ||||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: flag); | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: 0x205); | |||||
first = true; | first = true; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
flag = 0x000400; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = timestamp * 1000; | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: flag); | |||||
//flag = 0x000400; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = BaseMediaDecodeTime; | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: 0x205); | |||||
BaseMediaDecodeTime += BaseMediaDecodeTime; | |||||
} | } | ||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = 0; | |||||
//movieFragmentBox.TrackFragmentBox.SampleDependencyTypeBox.SampleDependencyTypes.Add(sdtp); | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = 33554432; | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = new List<TrackRunBox.TrackRunInfo>(); | movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = new List<TrackRunBox.TrackRunInfo>(); | ||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo()); | |||||
//movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo()); | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo() | movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo() | ||||
{ | { | ||||
SampleDuration= frameInterval, | |||||
SampleSize = (uint)naluLength, | SampleSize = (uint)naluLength, | ||||
//SampleCompositionTimeOffset = package.Label3.DataType == JT1078DataType.视频I帧 ? package.LastIFrameInterval : package.LastFrameInterval, | |||||
SampleCompositionTimeOffset = frameInterval, | |||||
SampleFlags = flag | SampleFlags = flag | ||||
}); | }); | ||||
@@ -366,6 +422,8 @@ namespace JT1078.FMp4 | |||||
bool first = false; | bool first = false; | ||||
ulong BaseMediaDecodeTime = 2160000; | |||||
/// <summary> | /// <summary> | ||||
/// 编码其他视频数据盒子 | /// 编码其他视频数据盒子 | ||||
/// </summary> | /// </summary> | ||||
@@ -712,6 +712,34 @@ | |||||
4byte 32 - 28 | 4byte 32 - 28 | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="T:JT1078.FMp4.SegmentTypeBox"> | |||||
<summary> | |||||
styp | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT1078.FMp4.SegmentTypeBox.#ctor"> | |||||
<summary> | |||||
styp | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.FMp4.SegmentTypeBox.MajorBrand"> | |||||
<summary> | |||||
因为兼容性一般可以分为推荐兼容性和默认兼容性。这里 major_brand 就相当于是推荐兼容性。通常,在 Web 中解码,一般而言都是使用 isom 这个万金油即可。如果是需要特定的格式,可以自行定义。 | |||||
4位 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.FMp4.SegmentTypeBox.MinorVersion"> | |||||
<summary> | |||||
最低兼容版本 | |||||
4位 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.FMp4.SegmentTypeBox.CompatibleBrands"> | |||||
<summary> | |||||
和MajorBrand类似,通常是针对 MP4 中包含的额外格式,比如,AVC,AAC 等相当于的音视频解码格式。 | |||||
4位*n | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.FMp4.StereoVideoBox.Reserved"> | <member name="P:JT1078.FMp4.StereoVideoBox.Reserved"> | ||||
<summary> | <summary> | ||||
4btye 32 - 30 | 4btye 32 - 30 | ||||
@@ -1239,7 +1267,7 @@ | |||||
</summary> | </summary> | ||||
<returns></returns> | <returns></returns> | ||||
</member> | </member> | ||||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderMoofBox(System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU},System.Int32,System.UInt64,System.UInt32,System.UInt32)"> | |||||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderMoofBox(System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU},System.Int32,System.UInt64,System.UInt32,System.UInt32,System.Int32)"> | |||||
<summary> | <summary> | ||||
编码Moof盒子 | 编码Moof盒子 | ||||
</summary> | </summary> | ||||
@@ -46,7 +46,7 @@ namespace JT1078.SignalR.Test.Services | |||||
this.wsSession = wsSession; | this.wsSession = wsSession; | ||||
} | } | ||||
public Queue<byte[]> q = new Queue<byte[]>(); | |||||
public List<byte[]> q = new List<byte[]>(); | |||||
public void a() | public void a() | ||||
{ | { | ||||
@@ -65,23 +65,40 @@ namespace JT1078.SignalR.Test.Services | |||||
packages.Add(packageMerge); | packages.Add(packageMerge); | ||||
} | } | ||||
} | } | ||||
List<byte[]> first = new List<byte[]>(); | |||||
//var styp = fMp4Encoder.EncoderStypBox(); | |||||
//first.Add(styp); | |||||
//q.Enqueue(styp); | |||||
var ftyp = fMp4Encoder.EncoderFtypBox(); | var ftyp = fMp4Encoder.EncoderFtypBox(); | ||||
q.Enqueue(ftyp); | |||||
//q.Enqueue(ftyp); | |||||
first.Add(ftyp); | |||||
var package1 = packages[0]; | var package1 = packages[0]; | ||||
var nalus1 = h264Decoder.ParseNALU(package1); | var nalus1 = h264Decoder.ParseNALU(package1); | ||||
var moov = fMp4Encoder.EncoderMoovBox(nalus1, package1.Bodies.Length); | var moov = fMp4Encoder.EncoderMoovBox(nalus1, package1.Bodies.Length); | ||||
q.Enqueue(moov); | |||||
var flag = package1.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧 ? 1u : 0u; | |||||
var moofBuffer = fMp4Encoder.EncoderMoofBox(nalus1, package1.Bodies.Length, package1.Timestamp, flag); | |||||
q.Enqueue(moofBuffer); | |||||
//q.Enqueue(moov); | |||||
first.Add(moov); | |||||
q.Add(first.SelectMany(s=>s).ToArray()); | |||||
List<int> filter = new List<int>() { 6,7,8}; | |||||
foreach (var package in packages) | foreach (var package in packages) | ||||
{ | { | ||||
List<byte[]> other = new List<byte[]>(); | |||||
var otherNalus = h264Decoder.ParseNALU(package); | var otherNalus = h264Decoder.ParseNALU(package); | ||||
var otherMdatBuffer = fMp4Encoder.EncoderMdatBox(otherNalus, package.Bodies.Length); | |||||
q.Enqueue(otherMdatBuffer); | |||||
var filterNalus = otherNalus.Where(w => !filter.Contains(w.NALUHeader.NalUnitType)).ToList(); | |||||
var flag = package.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧 ? 1u : 0u; | |||||
var len = filterNalus.Sum(s => s.RawData.Length); | |||||
var len1 = otherNalus.Sum(s => s.RawData.Length); | |||||
var moofBuffer = fMp4Encoder.EncoderMoofBox(filterNalus, len, package.Timestamp, package.LastIFrameInterval, flag); | |||||
//q.Enqueue(moofBuffer); | |||||
other.Add(moofBuffer); | |||||
var otherMdatBuffer = fMp4Encoder.EncoderMdatBox(filterNalus, len); | |||||
//q.Enqueue(otherMdatBuffer); | |||||
other.Add(otherMdatBuffer); | |||||
q.Add(other.SelectMany(s => s).ToArray()); | |||||
} | } | ||||
} | } | ||||
public Dictionary<string,int> flag = new Dictionary<string, int>(); | |||||
protected async override Task ExecuteAsync(CancellationToken stoppingToken) | protected async override Task ExecuteAsync(CancellationToken stoppingToken) | ||||
{ | { | ||||
a(); | a(); | ||||
@@ -89,11 +106,22 @@ namespace JT1078.SignalR.Test.Services | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
if (wsSession.GetCount() > 0) | |||||
foreach(var session in wsSession.GetAll()) | |||||
{ | { | ||||
if (q.Count > 0) | |||||
if (flag.ContainsKey(session)) | |||||
{ | |||||
var len = flag[session]; | |||||
if (q.Count < len) | |||||
{ | |||||
break; | |||||
} | |||||
await _hubContext.Clients.Client(session).SendAsync("video", q[len], stoppingToken); | |||||
flag[session] = ++len; | |||||
} | |||||
else | |||||
{ | { | ||||
await _hubContext.Clients.All.SendAsync("video", q.Dequeue(), stoppingToken); | |||||
await _hubContext.Clients.Client(session).SendAsync("video", q[0], stoppingToken); | |||||
flag.Add(session, 1); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -29,5 +29,10 @@ namespace JT1078.SignalR.Test.Services | |||||
{ | { | ||||
sessions.TryRemove(connectionId,out _); | sessions.TryRemove(connectionId,out _); | ||||
} | } | ||||
public List<string> GetAll() | |||||
{ | |||||
return sessions.Keys.ToList(); | |||||
} | |||||
} | } | ||||
} | } |