From 9a4b368ad48ef1a774e463a0f8441098d7716908 Mon Sep 17 00:00:00 2001 From: "SmallChi(Koike)" <564952747@qq.com> Date: Tue, 25 May 2021 18:14:44 +0800 Subject: [PATCH] =?UTF-8?q?1.=E6=B7=BB=E5=8A=A0fmp4=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=EF=BC=88=E5=BE=85=E6=B5=8B=E8=AF=95=EF=BC=89=202.=E6=95=B4?= =?UTF-8?q?=E7=90=86=E5=89=8D=E7=AB=AFdemo=203.=E4=BF=AE=E6=94=B9=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 + .../IJT1078MsgProducer.cs | 4 +- .../JT1078.Gateway.Abstractions.csproj | 2 +- .../JT1078.Gateway.Abstractions.xml | 3 +- .../JT1078MsgProducer.cs | 5 +- .../JT1078.Gateway.TestNormalHosting.csproj | 6 +- .../Program.cs | 8 +- .../JT1078FMp4NormalMsgHostedService.cs | 71 +++++-- .../Services/MessageDispatchDataService.cs | 1 + .../Services/MessageDispatchHostedService.cs | 7 +- .../wwwroot/{ => flv_demo}/flv.min.js | 0 .../wwwroot/{ => flv_demo}/index.html | 0 .../wwwroot/fmp4_demo/index.html | 201 ++++++++++++++++++ .../wwwroot/{demo => hls_demo}/10.ts | Bin .../wwwroot/{demo => hls_demo}/11.ts | Bin .../wwwroot/{demo => hls_demo}/12.ts | Bin .../wwwroot/{demo => hls_demo}/13.ts | Bin .../wwwroot/{demo => hls_demo}/14.ts | Bin .../wwwroot/{demo => hls_demo}/15.ts | Bin .../wwwroot/{demo => hls_demo}/16.ts | Bin .../wwwroot/{demo => hls_demo}/17.ts | Bin .../wwwroot/{demo => hls_demo}/7.ts | Bin .../wwwroot/{demo => hls_demo}/8.ts | Bin .../wwwroot/{demo => hls_demo}/9.ts | Bin .../wwwroot/{demo => hls_demo}/demo.m3u8 | 0 .../wwwroot/{demo => hls_demo}/demo.ts | 0 .../wwwroot/{demo => hls_demo}/demo0.ts | Bin .../wwwroot/{demo => hls_demo}/index.html | 0 .../wwwroot/{demo => hls_demo}/live.m3u8 | 0 29 files changed, 282 insertions(+), 28 deletions(-) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{ => flv_demo}/flv.min.js (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{ => flv_demo}/index.html (100%) create mode 100644 src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/fmp4_demo/index.html rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/10.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/11.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/12.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/13.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/14.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/15.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/16.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/17.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/7.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/8.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/9.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/demo.m3u8 (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/demo.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/demo0.ts (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/index.html (100%) rename src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/{demo => hls_demo}/live.m3u8 (100%) diff --git a/README.md b/README.md index 5af94bd..c3a6453 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ 3. 了解WebSocket消息推送 4. [了解flv.js](https://github.com/bilibili/flv.js) 5. [了解hls.js](https://github.com/video-dev/hls.js) +6. 了解fmp4 > 注意:暂不支持音频 @@ -14,6 +15,7 @@ | --- | ---| --- |---|---| | flv | 😀| ☹ |😀|http-flv、ws-flv| | m3u8 | 😀| ☹ |😀|http| +| fmp4 | 😀| ☹ |☹|http-fmp4、ws-fmp4| ## NuGet安装 diff --git a/src/JT1078.Gateway.Abstractions/IJT1078MsgProducer.cs b/src/JT1078.Gateway.Abstractions/IJT1078MsgProducer.cs index 4abab68..07d889c 100644 --- a/src/JT1078.Gateway.Abstractions/IJT1078MsgProducer.cs +++ b/src/JT1078.Gateway.Abstractions/IJT1078MsgProducer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace JT1078.Gateway.Abstractions @@ -12,6 +13,7 @@ namespace JT1078.Gateway.Abstractions /// </summary> /// <param name="sim">设备sim终端号</param> /// <param name="data">jt1078 hex data</param> - ValueTask ProduceAsync(string sim, byte[] data); + /// <param name="cancellationToken">cts</param> + ValueTask ProduceAsync(string sim, byte[] data, CancellationToken cancellationToken = default); } } diff --git a/src/JT1078.Gateway.Abstractions/JT1078.Gateway.Abstractions.csproj b/src/JT1078.Gateway.Abstractions/JT1078.Gateway.Abstractions.csproj index 2da8e4e..de064f4 100644 --- a/src/JT1078.Gateway.Abstractions/JT1078.Gateway.Abstractions.csproj +++ b/src/JT1078.Gateway.Abstractions/JT1078.Gateway.Abstractions.csproj @@ -33,7 +33,7 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="JT1078" Version="1.1.0" /> + <PackageReference Include="JT1078" Version="1.1.1-preview1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" /> diff --git a/src/JT1078.Gateway.Abstractions/JT1078.Gateway.Abstractions.xml b/src/JT1078.Gateway.Abstractions/JT1078.Gateway.Abstractions.xml index d46ec53..23c27a9 100644 --- a/src/JT1078.Gateway.Abstractions/JT1078.Gateway.Abstractions.xml +++ b/src/JT1078.Gateway.Abstractions/JT1078.Gateway.Abstractions.xml @@ -9,12 +9,13 @@ 传输协议类型 </summary> </member> - <member name="M:JT1078.Gateway.Abstractions.IJT1078MsgProducer.ProduceAsync(System.String,System.Byte[])"> + <member name="M:JT1078.Gateway.Abstractions.IJT1078MsgProducer.ProduceAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> <summary> </summary> <param name="sim">设备sim终端号</param> <param name="data">jt1078 hex data</param> + <param name="cancellationToken">cts</param> </member> <member name="P:JT1078.Gateway.Abstractions.IJT1078Session.TerminalPhoneNo"> <summary> diff --git a/src/JT1078.Gateway.InMemoryMQ/JT1078MsgProducer.cs b/src/JT1078.Gateway.InMemoryMQ/JT1078MsgProducer.cs index 1b93f42..85b9d87 100644 --- a/src/JT1078.Gateway.InMemoryMQ/JT1078MsgProducer.cs +++ b/src/JT1078.Gateway.InMemoryMQ/JT1078MsgProducer.cs @@ -3,6 +3,7 @@ using JT1078.Protocol; using System; using System.Collections.Generic; using System.Text; +using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; @@ -24,9 +25,9 @@ namespace JT1078.Gateway.InMemoryMQ } - public async ValueTask ProduceAsync(string sim, byte[] data) + public async ValueTask ProduceAsync(string sim, byte[] data, CancellationToken cancellationToken = default) { - await Channel.Channel.Writer.WriteAsync((sim, data)); + await Channel.Channel.Writer.WriteAsync((sim, data), cancellationToken); } } } diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/JT1078.Gateway.TestNormalHosting.csproj b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/JT1078.Gateway.TestNormalHosting.csproj index f799921..1ff238f 100644 --- a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/JT1078.Gateway.TestNormalHosting.csproj +++ b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/JT1078.Gateway.TestNormalHosting.csproj @@ -31,11 +31,13 @@ <None Update="Configs\NLog.xsd"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> - <None Update="wwwroot\demo\demo.m3u8"> + <None Update="wwwroot\hls_demo\demo.m3u8"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> - <None Update="wwwroot\demo\demo.ts"> + <None Update="wwwroot\hls_demo\demo.ts"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> </ItemGroup> + + <ProjectExtensions><VisualStudio><UserProperties appsettings_1json__JsonSchema="" /></VisualStudio></ProjectExtensions> </Project> diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Program.cs b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Program.cs index 58172b1..f74782a 100644 --- a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Program.cs +++ b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Program.cs @@ -1,8 +1,10 @@ using JT1078.Flv; +using JT1078.FMp4; using JT1078.Gateway.InMemoryMQ; using JT1078.Gateway.TestNormalHosting.Services; using JT1078.Hls; using JT1078.Hls.Options; +using JT1078.Protocol.H264; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -35,20 +37,19 @@ namespace JT1078.Gateway.TestNormalHosting .ConfigureServices((hostContext, services) => { services.AddMemoryCache(); - services.AddSingleton<ILoggerFactory, LoggerFactory>(); - services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); //flv视频解码器 services.AddSingleton<FlvEncoder>(); //hls视频解码器 services.AddSingleton<TSEncoder>(); services.AddSingleton<M3U8FileManage>(); + services.AddSingleton<H264Decoder>(); + services.AddSingleton<FMp4Encoder>(); //添加hls依赖项 services.AddHlsGateway(hostContext.Configuration); services.Configure<M3U8Option>(hostContext.Configuration.GetSection("M3U8Option")); var m3u8Option = services.BuildServiceProvider().GetRequiredService<IOptions<M3U8Option>>().Value; services.AddSingleton(m3u8Option); - //使用内存队列实现会话通知 services.AddJT1078Gateway(hostContext.Configuration) .AddTcp() @@ -60,6 +61,7 @@ namespace JT1078.Gateway.TestNormalHosting //内存队列没有做分发,可以自己实现。 services.AddHostedService<JT1078FlvNormalMsgHostedService>(); services.AddHostedService<JT1078HlsNormalMsgHostedService>(); + services.AddHostedService<JT1078FMp4NormalMsgHostedService>(); services.AddSingleton<MessageDispatchDataService>(); services.AddHostedService<MessageDispatchHostedService>(); diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/JT1078FMp4NormalMsgHostedService.cs b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/JT1078FMp4NormalMsgHostedService.cs index b0461b1..a68f836 100644 --- a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/JT1078FMp4NormalMsgHostedService.cs +++ b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/JT1078FMp4NormalMsgHostedService.cs @@ -15,33 +15,37 @@ using JT1078.Protocol; using System.Text.Json; using System.Text.Json.Serialization; using JT1078.FMp4; +using JT1078.Protocol.H264; +using System.Collections.Concurrent; namespace JT1078.Gateway.TestNormalHosting.Services { public class JT1078FMp4NormalMsgHostedService : BackgroundService { - private IJT1078MsgConsumer JT1078MsgConsumer; private JT1078HttpSessionManager HttpSessionManager; private FMp4Encoder FM4Encoder; + private readonly H264Decoder H264Decoder; private ILogger Logger; private IMemoryCache memoryCache; - private const string ikey = "IKEY"; + private const string ikey = "IFMp4KEY"; private MessageDispatchDataService messageDispatchDataService; + private ConcurrentDictionary<string, List<H264NALU>> avFrameDict; public JT1078FMp4NormalMsgHostedService( MessageDispatchDataService messageDispatchDataService, IMemoryCache memoryCache, ILoggerFactory loggerFactory, + H264Decoder h264Decoder, FMp4Encoder fM4Encoder, - JT1078HttpSessionManager httpSessionManager, - IJT1078MsgConsumer msgConsumer) + JT1078HttpSessionManager httpSessionManager) { Logger = loggerFactory.CreateLogger<JT1078FMp4NormalMsgHostedService>(); - JT1078MsgConsumer = msgConsumer; HttpSessionManager = httpSessionManager; FM4Encoder = fM4Encoder; + H264Decoder = h264Decoder; this.memoryCache = memoryCache; this.messageDispatchDataService = messageDispatchDataService; + avFrameDict = new ConcurrentDictionary<string, List<H264NALU>>(); } protected async override Task ExecuteAsync(CancellationToken stoppingToken) { @@ -49,7 +53,8 @@ namespace JT1078.Gateway.TestNormalHosting.Services { var data = await messageDispatchDataService.FlvChannel.Reader.ReadAsync(); try - { + { + var nalus = H264Decoder.ParseNALU(data); if (Logger.IsEnabled(LogLevel.Debug)) { Logger.LogDebug(JsonSerializer.Serialize(HttpSessionManager.GetAll())); @@ -58,30 +63,66 @@ namespace JT1078.Gateway.TestNormalHosting.Services string key = $"{data.GetKey()}_{ikey}"; if (data.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧) { - memoryCache.Set(key, data); + var moov = FM4Encoder.EncoderMoovBox(nalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS), + nalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)); + memoryCache.Set(key, moov); } var httpSessions = HttpSessionManager.GetAllBySimAndChannelNo(data.SIM.TrimStart('0'), data.LogicChannelNumber); var firstHttpSessions = httpSessions.Where(w => !w.FirstSend).ToList(); if (firstHttpSessions.Count > 0) { - if (memoryCache.TryGetValue(key, out JT1078Package idata)) + try { - try - { - - } - catch (Exception ex) + var flvVideoBuffer = FM4Encoder.EncoderFtypBox(); + memoryCache.TryGetValue(key, out byte[] moovBuffer); + foreach (var session in firstHttpSessions) { - Logger.LogError(ex, $"{data.SIM},{true},{data.SN},{data.LogicChannelNumber},{data.Label3.DataType.ToString()},{data.Label3.SubpackageType.ToString()},{data.Bodies.ToHexString()}"); + HttpSessionManager.SendAVData(session, flvVideoBuffer, true); + if (moovBuffer != null) + { + HttpSessionManager.SendAVData(session, moovBuffer, false); + } } } + catch (Exception ex) + { + Logger.LogError(ex, $"{data.SIM},{true},{data.SN},{data.LogicChannelNumber},{data.Label3.DataType.ToString()},{data.Label3.SubpackageType.ToString()},{data.Bodies.ToHexString()}"); + } } var otherHttpSessions = httpSessions.Where(w => w.FirstSend).ToList(); if (otherHttpSessions.Count > 0) { try { - + if(!avFrameDict.TryGetValue(key, out List<H264NALU> frames)) + { + frames = new List<H264NALU>(); + avFrameDict.TryAdd(key, frames); + } + foreach (var nalu in nalus) + { + if (nalu.Slice) + { + //H264 NALU slice first_mb_in_slice + frames.Add(nalu); + } + else + { + if (nalus.Count > 0) + { + var otherBuffer = FM4Encoder.EncoderOtherVideoBox(frames); + foreach (var session in otherHttpSessions) + { + if (otherBuffer != null) + { + HttpSessionManager.SendAVData(session, otherBuffer, false); + } + } + frames.Clear(); + } + frames.Add(nalu); + } + } } catch (Exception ex) { diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/MessageDispatchDataService.cs b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/MessageDispatchDataService.cs index 07c81d4..5f37a7a 100644 --- a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/MessageDispatchDataService.cs +++ b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/MessageDispatchDataService.cs @@ -12,5 +12,6 @@ namespace JT1078.Gateway.TestNormalHosting.Services { public Channel<JT1078Package> HlsChannel = Channel.CreateUnbounded<JT1078Package>(); public Channel<JT1078Package> FlvChannel = Channel.CreateUnbounded<JT1078Package>(); + public Channel<JT1078Package> FMp4Channel = Channel.CreateUnbounded<JT1078Package>(); } } diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/MessageDispatchHostedService.cs b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/MessageDispatchHostedService.cs index 06624ef..96acf3c 100644 --- a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/MessageDispatchHostedService.cs +++ b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/Services/MessageDispatchHostedService.cs @@ -20,7 +20,7 @@ namespace JT1078.Gateway.TestNormalHosting.Services private readonly MessageDispatchDataService messageDispatchDataService; public MessageDispatchHostedService(IJT1078MsgConsumer JT1078MsgConsumer, - MessageDispatchDataService messageDispatchDataService) { + MessageDispatchDataService messageDispatchDataService) { this.JT1078MsgConsumer = JT1078MsgConsumer; this.messageDispatchDataService = messageDispatchDataService; } @@ -33,8 +33,9 @@ namespace JT1078.Gateway.TestNormalHosting.Services var merge = JT1078.Protocol.JT1078Serializer.Merge(package); if (merge != null) { - await messageDispatchDataService.HlsChannel.Writer.WriteAsync(merge); - await messageDispatchDataService.FlvChannel.Writer.WriteAsync(merge); + await messageDispatchDataService.HlsChannel.Writer.WriteAsync(merge, stoppingToken); + await messageDispatchDataService.FlvChannel.Writer.WriteAsync(merge, stoppingToken); + await messageDispatchDataService.FMp4Channel.Writer.WriteAsync(merge, stoppingToken); } }); return Task.CompletedTask; diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/flv.min.js b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/flv_demo/flv.min.js similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/flv.min.js rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/flv_demo/flv.min.js diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/index.html b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/flv_demo/index.html similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/index.html rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/flv_demo/index.html diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/fmp4_demo/index.html b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/fmp4_demo/index.html new file mode 100644 index 0000000..6d0c1e4 --- /dev/null +++ b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/fmp4_demo/index.html @@ -0,0 +1,201 @@ +<!DOCTYPE html> + +<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> +<head> + <meta charset="utf-8" /> + <title>WebSocket MSE Fmp4 demo</title> +</head> +<body> + <h1>MSE FMp4 Demo</h1> + <video id="stream_live" width="640" height="480" controls="false" autoplay="true" + muted="muted" + preload="auto"> + 浏览器不支持 + </video> + <ul id="messagesList"></ul> + <script> + //var mimeCodec = 'video/mp4;codecs="avc1.4D0014, mp4a.40.2"'; + // *** USER PARAMETERS *** + var verbose = true; + // var verbose = true; // enable for saturating the console .. + var buffering_sec = 1; // use some reasonable value + var buffering_sec_seek = buffering_sec * 0.9; + // ..seek the stream if it's this much away or + // from the last available timestamp + var buffering_sec_seek_distance = buffering_sec * 0.5; + // .. jump to this distance from the last avail. timestamp + // *** INTERNAL PARAMETERS *** + // set mimetype and codec + var mimeType = "video/mp4"; + var codecs = "avc1.4D0014"; // https://wiki.whatwg.org/wiki/Video_type_parameters + // if your stream has audio, remember to include it in these definitions.. otherwise your mse goes sour + var codecPars = mimeType + ';codecs="' + codecs + '"'; + var stream_started = false; // is the source_buffer updateend callback active nor not + // create media source instance + var ms = new MediaSource(); + // queue for incoming media packets + var queue = []; + var stream_live; // the HTMLMediaElement (i.e. <video> element) + var ws; // websocket + var seeked = false; // have have seeked manually once .. + var cc = 0; + var source_buffer; // source_buffer instance + var pass = 0; + // *** MP4 Box manipulation functions *** + // taken from here: https://stackoverflow.com/questions/54186634/sending-periodic-metadata-in-fragmented-live-mp4-stream/ + function toInt(arr, index) { // From bytes to big-endian 32-bit integer. Input: Uint8Array, index + var dv = new DataView(arr.buffer, 0); + return dv.getInt32(index, false); // big endian + } + function toString(arr, fr, to) { // From bytes to string. Input: Uint8Array, start index, stop index. + // https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String + return String.fromCharCode.apply(null, arr.slice(fr, to)); + } + function getBox(arr, i) { // input Uint8Array, start index + return [toInt(arr, i), toString(arr, i + 4, i + 8)] + } + function getSubBox(arr, box_name) { // input Uint8Array, box name + var i = 0; + res = getBox(arr, i); + main_length = res[0]; name = res[1]; // this boxes length and name + i = i + 8; + var sub_box = null; + while (i < main_length) { + res = getBox(arr, i); + l = res[0]; name = res[1]; + + if (box_name == name) { + sub_box = arr.slice(i, i + l) + } + i = i + l; + } + return sub_box; + } + function hasFirstSampleFlag(arr) { // input Uint8Array + // [moof [mfhd] [traf [tfhd] [tfdt] [trun]]] + var traf = getSubBox(arr, "traf"); + if (traf == null) { return false; } + var trun = getSubBox(traf, "trun"); + if (trun == null) { return false; } + // ISO/IEC 14496-12:2012(E) .. pages 5 and 57 + // bytes: (size 4), (name 4), (version 1 + tr_flags 3) + var flags = trun.slice(10, 13); // console.log(flags); + f = flags[1] & 4; // console.log(f); + return f == 4; + } + // consider these callbacks: + // - putPacket : called when websocket receives data + // - loadPacket : called when source_buffer is ready for more data + // Both operate on a common fifo + function putPacket(arr) { + // receives ArrayBuffer. Called when websocket gets more data + // first packet ever to arrive: write directly to source_buffer + // source_buffer ready to accept: write directly to source_buffer + // otherwise insert it to queue + var memview = new Uint8Array(arr); + 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; + // } + // keep the latency to minimum + let latest = stream_live.duration; + if ((stream_live.duration >= buffering_sec) && + ((latest - stream_live.currentTime) > buffering_sec_seek)) { + console.log("seek from ", stream_live.currentTime, " to ", latest); + df = (stream_live.duration - stream_live.currentTime); // this much away from the last available frame + if ((df > buffering_sec_seek)) { + seek_to = stream_live.duration - buffering_sec_seek_distance; + stream_live.currentTime = seek_to; + } + } + data = arr; + if (!stream_started) { + if (verbose) { console.log("Streaming started: ", memview[0], memview[1], memview[2], memview[3], memview[4]); } + stream_started = true; + source_buffer.appendBuffer(data); + + cc = cc + 1; + return; + } + queue.push(data); // add to the end + if (verbose) { console.log("queue push:", queue.length); } + } + + function loadPacket() { // called when source_buffer is ready for more + if (!source_buffer.updating) { // really, really ready + if (queue.length > 0) { + inp = queue.shift(); // pop from the beginning + if (verbose) { console.log("queue pop:", queue.length); } + var memview = new Uint8Array(inp); + if (verbose) { console.log(" ==> writing buffer with", memview[0], memview[1], memview[2], memview[3]); } + source_buffer.appendBuffer(inp); + cc = cc + 1; + } + else { // the queue runs empty, so the next packet is fed directly + stream_started = false; + } + } + else { // so it was not? + } + } + + function opened() { // MediaSource object is ready to go + // https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/duration + ms.duration = buffering_sec; + source_buffer = ms.addSourceBuffer(codecPars); + // https://developer.mozilla.org/en-US/docs/Web/API/source_buffer/mode + var myMode = source_buffer.mode; + source_buffer.mode = 'sequence'; + // source_buffer.mode = 'segments'; + source_buffer.addEventListener("updateend", loadPacket); + + ws = new WebSocket("ws://127.0.0.1:15555/live.mp4?sim=12345678901&channel=3&token=123456"); //创建WebSocket连接 + ws.onmessage = function (e) { + //当客户端收到服务端发来的消息时,触发onmessage事件,参数e.data包含server传递过来的数据 + console.log(e.data); + putPacket(e.data); + } + } + + function startup() { + ms.addEventListener('sourceopen', opened, false); + // get reference to video + stream_live = document.getElementById('stream_live'); + // set mediasource as source of video + stream_live.src = window.URL.createObjectURL(ms); + } + function base64ToArrayBuffer(base64) { + var binary_string = window.atob(base64); + var len = binary_string.length; + var bytes = new Uint8Array(len); + for (var i = 0; i < len; i++) { + bytes[i] = binary_string.charCodeAt(i); + } + return bytes.buffer; + } + window.onload = function () { + startup(); + } + + </script> +</body> +</html> \ No newline at end of file diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/10.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/10.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/10.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/10.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/11.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/11.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/11.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/11.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/12.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/12.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/12.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/12.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/13.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/13.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/13.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/13.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/14.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/14.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/14.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/14.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/15.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/15.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/15.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/15.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/16.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/16.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/16.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/16.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/17.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/17.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/17.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/17.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/7.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/7.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/7.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/7.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/8.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/8.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/8.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/8.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/9.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/9.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/9.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/9.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo.m3u8 b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/demo.m3u8 similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo.m3u8 rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/demo.m3u8 diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/demo.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/demo.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo0.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/demo0.ts similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo0.ts rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/demo0.ts diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/index.html b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/index.html similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/index.html rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/index.html diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/live.m3u8 b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/live.m3u8 similarity index 100% rename from src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/live.m3u8 rename to src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/hls_demo/live.m3u8