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