@@ -17,11 +17,3 @@ jobs: | |||||
run: dotnet --info | run: dotnet --info | ||||
- name: dotnet restore | - name: dotnet restore | ||||
run: dotnet restore ./src/JT1078.sln | run: dotnet restore ./src/JT1078.sln | ||||
- name: dotnet JT808.Protocol.Extensions.JT1078.Test build | |||||
run: dotnet build ./src/JT808.Protocol.Extensions.JT1078.Test/JT808.Protocol.Extensions.JT1078.Test.csproj | |||||
- name: dotnet JT808.Protocol.Extensions.JT1078.Test test | |||||
run: dotnet test ./src/JT808.Protocol.Extensions.JT1078.Test/JT808.Protocol.Extensions.JT1078.Test.csproj | |||||
- name: dotnet JT809.Protocol.Extensions.JT1078.Test build | |||||
run: dotnet build ./src/JT809.Protocol.Extensions.JT1078.Test/JT809.Protocol.Extensions.JT1078.Test.csproj | |||||
- name: dotnet JT809.Protocol.Extensions.JT1078.Test test | |||||
run: dotnet test ./src/JT809.Protocol.Extensions.JT1078.Test/JT809.Protocol.Extensions.JT1078.Test.csproj |
@@ -2,7 +2,7 @@ | |||||
道路运输车辆卫星定位系统-视频通讯协议主要分为三大部分。 | 道路运输车辆卫星定位系统-视频通讯协议主要分为三大部分。 | ||||
1. [设备终端到平台的通信也就是JT808](#808Ext) | |||||
1. [设备终端到平台的通信也就是JT808] | |||||
2. [企业平台到政府监管的通信也就是JT809](#809Ext) | 2. [企业平台到政府监管的通信也就是JT809](#809Ext) | ||||
3. [设备终端上传的实时音视频流数据也就是视频服务器](#1078) | 3. [设备终端上传的实时音视频流数据也就是视频服务器](#1078) | ||||
3.1 [将1078的数据(h264)编码成FLV](#1078flv) | 3.1 [将1078的数据(h264)编码成FLV](#1078flv) | ||||
@@ -20,8 +20,6 @@ | |||||
| Install-Package JT1078.Flv |  |  | | | Install-Package JT1078.Flv |  |  | | ||||
| Install-Package JT1078.Hls |  |  | | | Install-Package JT1078.Hls |  |  | | ||||
| Install-Package JT1078.FMp4 |  |  | | | Install-Package JT1078.FMp4 |  |  | | ||||
| Install-Package JT808.Protocol.Extensions.JT1078 |  |  | | |||||
| Install-Package JT809.Protocol.Extensions.JT1078 |  |  | | |||||
## <span id="1078">基于JT1078音视频流数据的RTP协议</span> | ## <span id="1078">基于JT1078音视频流数据的RTP协议</span> | ||||
@@ -214,102 +212,3 @@ Platform=AnyCpu Server=False Toolchain=.NET Core 3.1 | |||||
| **EXPGolombReader** | **100000** | **11,687.72 us** | **162.828 us** | **152.309 us** | **1515.6250** | **-** | **-** | **9375 KB** | | | **EXPGolombReader** | **100000** | **11,687.72 us** | **162.828 us** | **152.309 us** | **1515.6250** | **-** | **-** | **9375 KB** | | ||||
| H264Decoder | 100000 | 1,192,549.87 us | 7,656.632 us | 7,162.018 us | 128000.0000 | 3000.0000 | - | 786718.75 KB | | | H264Decoder | 100000 | 1,192,549.87 us | 7,656.632 us | 7,162.018 us | 128000.0000 | 3000.0000 | - | 786718.75 KB | | ||||
| FlvEncoder | 100000 | 216,951.31 us | 3,513.653 us | 2,934.059 us | 249333.3333 | - | - | 1528906.66 KB | | | FlvEncoder | 100000 | 216,951.31 us | 3,513.653 us | 2,934.059 us | 249333.3333 | - | - | 1528906.66 KB | | ||||
## <span id="808ext">基于JT808扩展的JT1078消息协议</span> | |||||
### JT808扩展协议消息对照表 | |||||
| 序号 | 消息ID | 完成情况 | 测试情况 | 消息体名称 | | |||||
| :---: | :-----------: | :------: | :------: | :----------------------------: | | |||||
| 1 | 0x0200_0x14 | √ | √ | 视频相关报警 | | |||||
| 2 | 0x0200_0x15 | √ | √ | 视频信号丢失报警状态 | | |||||
| 3 | 0x0200_0x16 | √ | √ | 视频信号遮挡报警状态 | | |||||
| 4 | 0x0200_0x17 | √ | √ | 存储器故障报警状态 | | |||||
| 5 | 0x0200_0x18 | √ | √ | 异常驾驶行为报警详细描述 | | |||||
| 6 | 0x8103_0x0075 | √ | √ | 音视频参数设置 | | |||||
| 7 | 0x8103_0x0076 | √ | √ | 音视频通道列表设置 | | |||||
| 8 | 0x8103_0x0077 | √ | √ | 单独视频通道参数设置 | | |||||
| 9 | 0x8103_0x0079 | √ | √ | 特殊报警录像参数设置 | | |||||
| 10 | 0x8103_0x007A | √ | √ | 视频相关报警屏蔽字 | | |||||
| 11 | 0x8103_0x007B | √ | √ | 图像分析报警参数设置 | | |||||
| 12 | 0x8103_0x007C | √ | √ | 终端休眠模式唤醒设置 | | |||||
| 13 | 0x1003 | √ | √ | 终端上传音视频属性 | | |||||
| 14 | 0x1005 | √ | √ | 终端上传乘客流量 | | |||||
| 15 | 0x1205 | √ | √ | 终端上传音视频资源列表 | | |||||
| 16 | 0x1206 | √ | √ | 文件上传完成通知 | | |||||
| 17 | 0x9003 | √ | √ | 查询终端音视频属性 | | |||||
| 18 | 0x9101 | √ | √ | 实时音视频传输请求 | | |||||
| 19 | 0x9102 | √ | √ | 音视频实时传输控制 | | |||||
| 20 | 0x9105 | √ | √ | 实时音视频传输状态通知 | | |||||
| 21 | 0x9201 | √ | √ | 平台下发远程录像回放请求 | | |||||
| 22 | 0x9202 | √ | √ | 平台下发远程录像回放控制 | | |||||
| 23 | 0x9205 | √ | √ | 查询资源列表 | | |||||
| 24 | 0x9206 | √ | √ | 文件上传指令 | | |||||
| 25 | 0x9207 | √ | √ | 文件上传控制 | | |||||
| 26 | 0x9301 | √ | √ | 云台旋转 | | |||||
| 27 | 0x9302 | √ | √ | 云台调整焦距控制 | | |||||
| 28 | 0x9303 | √ | √ | 云台调整光圈控制 | | |||||
| 29 | 0x9304 | √ | √ | 云台雨刷控制 | | |||||
| 30 | 0x9305 | √ | √ | 红外补光控制 | | |||||
| 31 | 0x9306 | √ | √ | 云台变倍控制 | | |||||
### 使用方法 | |||||
```dotnet | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
``` | |||||
## <span id="809ext">基于JT809扩展的JT1078消息协议</span> | |||||
### JT809扩展协议消息对照表 | |||||
#### 主链路动态信息交换消息 | |||||
| 序号 | 消息ID | 完成情况 | 测试情况 | 消息体名称 | | |||||
| :---: | :-----------: | :------: | :------: | :----------------------------: | | |||||
| 1 | 0x1700 | √ | √ | 主链路时效口令交互消息 | | |||||
| 2 | 0x1700_0x1701 | √ | √ | 时效口令上报消息(有疑问:数据体有问题) | | |||||
| 3 | 0x1700_0x1702 | √ | √ | 时效口令请求消息 | | |||||
| 4 | 0x1800 | √ | √ | 主链路实时音视频交互消息 | | |||||
| 5 | 0x1800_0x1801 | √ | √ | 实时音视频请求应答消息 | | |||||
| 6 | 0x1800_0x1802 | √ | √ | 主动请求停止实时音视频传输应答消息 | | |||||
| 7 | 0x1900 | √ | √ | 主链路远程录像检索 | | |||||
| 8 | 0x1900_0x1901 | √ | √ | 主动上传音视频资源目录信息消息 | | |||||
| 9 | 0x1900_0x1902 | √ | √ | 查询音视频资源目录应答消息 | | |||||
| 10 | 0x1A00 | √ | √ | 主链路远程录像回放交互消息 | | |||||
| 11 | 0x1A00_0x1A01 | √ | √ | 远程录像回放请求应答消息 | | |||||
| 12 | 0x1A00_0x1A02 | √ | √ | 远程录像回放控制应答消息 | | |||||
| 13 | 0x1B00 | √ | √ | 主链路远程录像下载交互消息 | | |||||
| 14 | 0x1B00_0x1B01 | √ | √ | 远程录像下载请求应答消息 | | |||||
| 15 | 0x1B00_0x1B02 | √ | √ | 远程录像下载通知消息 | | |||||
| 16 | 0x1B00_0x1B03 | √ | √ | 远程录像下载控制应答消息 | | |||||
#### 从链路动态信息交换消息 | |||||
| 序号 | 消息ID | 完成情况 | 测试情况 | 消息体名称 | | |||||
| :---: | :-----------: | :------: | :------: | :----------------------------: | | |||||
| 17 | 0x9700 | √ | √ | 从链路时效口令交互消息 | | |||||
| 18 | 0x9700_0x9702 | √ | √ | 时效口令请求应答消息(有疑问:应该有应答结果) | | |||||
| 19 | 0x9800 | √ | √ | 从链路实时音视频交互信息 | | |||||
| 20 | 0x9800_0x9801 | √ | √ | 实时音视频请求消息 | | |||||
| 21 | 0x9800_0x9802 | √ | √ | 主动请求停止实时音视频传输消息 | | |||||
| 22 | 0x9900 | √ | √ | 从链路远程录像检索交互消息 | | |||||
| 23 | 0x9900_0x9901 | √ | √ | 主动上传音视频资源目录信息应答消息 | | |||||
| 24 | 0x9900_0x9902 | √ | √ | 查询音视频资源目录请求消息 | | |||||
| 25 | 0x9A00 | √ | √ | 从链路远程录像回放交互消息 | | |||||
| 26 | 0x9A00_0x9A01 | √ | √ | 远程录像回放请求消息 | | |||||
| 27 | 0x9A00_0x9A02 | √ | √ | 远程录像回放控制消息 | | |||||
| 28 | 0x9B00 | √ | √ | 从链路远程录像下载交互消息 | | |||||
| 29 | 0x9B00_0x9B01 | √ | √ | 远程录像下载请求消息 | | |||||
| 30 | 0x9B00_0x9B02 | √ | √ | 远程录像下载完成通知应答消息 | | |||||
| 31 | 0x9B00_0x9B03 | √ | √ | 远程录像下载控制消息 | | |||||
### 使用方法 | |||||
```dotnet | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1.AddJT809Configure() | |||||
.AddJT1078Configure(); | |||||
``` |
@@ -1,6 +1,6 @@ | |||||
ffmpeg -i demo.mp4 -c copy -f flv -vcodec h264 -acodec aac demo_flv.flv | ffmpeg -i demo.mp4 -c copy -f flv -vcodec h264 -acodec aac demo_flv.flv | ||||
./ffmpeg -i JT1078_3.h264 -r 25 -c copy -f flv "D:\JT1078_3.flv" | |||||
ffmpeg -i JT1078_3.h264 -c copy -f flv JT1078_3.flv | |||||
ffmpeg -f dshow -i video="USB2.0 PC CAMERA" -t 60 -c copy -f h264 -vcodec h264 ipc.264 | ffmpeg -f dshow -i video="USB2.0 PC CAMERA" -t 60 -c copy -f h264 -vcodec h264 ipc.264 | ||||
@@ -8,4 +8,12 @@ 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 | |||||
ffprobe -of json -show_frames jt1078_3.h264 > jt1078_3.json | |||||
ffmpeg -re -i JT1078_7.flv -acodec copy -vcodec copy -f flv rtmp://127.0.0.1:1935/live/JT1078_7 | |||||
ffmpeg -re -i JT1078_7.h264 -acodec copy -vcodec copy -f flv rtmp://127.0.0.1:1935/live/JT1078_7 | |||||
chrome://media-internals/ | chrome://media-internals/ |
@@ -0,0 +1,2 @@ | |||||
[Settings] | |||||
language=Chinese |
@@ -12,10 +12,14 @@ | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<ProjectReference Include="..\JT1078.Flv\JT1078.Flv.csproj" /> | <ProjectReference Include="..\JT1078.Flv\JT1078.Flv.csproj" /> | ||||
<ProjectReference Include="..\JT1078.FMp4\JT1078.FMp4.csproj" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<None Include="..\..\doc\video\jt1078_1.txt" Link="jt1078_1.txt"> | <None Include="..\..\doc\video\jt1078_1.txt" Link="jt1078_1.txt"> | ||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
</None> | </None> | ||||
<None Include="..\..\doc\video\jt1078_3.txt" Link="jt1078_3.txt"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
</ItemGroup> | </ItemGroup> | ||||
</Project> | </Project> |
@@ -0,0 +1,132 @@ | |||||
using BenchmarkDotNet.Attributes; | |||||
using BenchmarkDotNet.Configs; | |||||
using BenchmarkDotNet.Environments; | |||||
using BenchmarkDotNet.Jobs; | |||||
using BenchmarkDotNet.Toolchains.CsProj; | |||||
using JT1078.Flv; | |||||
using JT1078.Flv.MessagePack; | |||||
using JT1078.FMp4; | |||||
using JT1078.Protocol; | |||||
using JT1078.Protocol.H264; | |||||
using JT1078.Protocol.MessagePack; | |||||
using JT808.Protocol.Extensions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
namespace JT1078.AV.Benchmark | |||||
{ | |||||
[Config(typeof(JT1078AVEncoderConfig))] | |||||
[MarkdownExporterAttribute.GitHub] | |||||
[MemoryDiagnoser] | |||||
public class JT1078AVEncoderContext | |||||
{ | |||||
JT1078Package Package; | |||||
List<H264NALU> H264NALUs; | |||||
List<H264NALU> FMp4H264NALUs; | |||||
H264NALU SPSNALu; | |||||
H264Decoder h264Decoder = new H264Decoder(); | |||||
FlvEncoder flvEncoder = new FlvEncoder(); | |||||
FMp4Encoder fmp4Encoder = new FMp4Encoder(); | |||||
[Params(100, 10000, 100000)] | |||||
public int N; | |||||
[GlobalSetup] | |||||
public void Setup() | |||||
{ | |||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "JT1078_1.txt")); | |||||
foreach (var line in lines) | |||||
{ | |||||
var data = line.Split(','); | |||||
var bytes = data[6].ToHexBytes(); | |||||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||||
Package = JT1078Serializer.Merge(package); | |||||
} | |||||
H264NALUs = h264Decoder.ParseNALU(Package); | |||||
SPSNALu = H264NALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS); | |||||
SPSNALu.RawData = h264Decoder.DiscardEmulationPreventionBytes(SPSNALu.RawData); | |||||
List<JT1078Package> packages = new List<JT1078Package>(); | |||||
var lines3 = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_3.txt")); | |||||
int mergeBodyLength = 0; | |||||
foreach (var line in lines3) | |||||
{ | |||||
var data = line.Split(','); | |||||
var bytes = data[6].ToHexBytes(); | |||||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||||
mergeBodyLength += package.DataBodyLength; | |||||
var packageMerge = JT1078Serializer.Merge(package); | |||||
if (packageMerge != null) | |||||
{ | |||||
packages.Add(packageMerge); | |||||
} | |||||
} | |||||
List<H264NALU> nalus = new List<H264NALU>(); | |||||
bool segmentFlag = false; | |||||
foreach (var package in packages) | |||||
{ | |||||
if (segmentFlag) | |||||
{ | |||||
break; | |||||
} | |||||
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package); | |||||
foreach (var nalu in h264NALUs) | |||||
{ | |||||
if (nalu.Slice) | |||||
{ | |||||
//H264 NALU slice first_mb_in_slice | |||||
nalus.Add(nalu); | |||||
} | |||||
else | |||||
{ | |||||
if (nalus.Count > 0) | |||||
{ | |||||
FMp4H264NALUs = new List<H264NALU>(nalus); | |||||
segmentFlag = true; | |||||
nalus.Clear(); | |||||
} | |||||
nalus.Add(nalu); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
[Benchmark(Description = "EXPGolombReader")] | |||||
public void EXPGolombReaderTest() | |||||
{ | |||||
for (var i = 0; i < N; i++) | |||||
{ | |||||
ExpGolombReader h264GolombReader = new ExpGolombReader(SPSNALu.RawData); | |||||
h264GolombReader.ReadSPS(); | |||||
} | |||||
} | |||||
[Benchmark(Description = "H264Decoder")] | |||||
public void H264Decoder() | |||||
{ | |||||
for (var i = 0; i < N; i++) | |||||
{ | |||||
var nalus = h264Decoder.ParseNALU(Package); | |||||
} | |||||
} | |||||
[Benchmark(Description = "FMp4Encoder")] | |||||
public void FMp4Encoder() | |||||
{ | |||||
for (var i = 0; i < N; i++) | |||||
{ | |||||
var buffer = fmp4Encoder.OtherVideoBox(FMp4H264NALUs); | |||||
} | |||||
} | |||||
} | |||||
public class JT1078AVEncoderConfig : ManualConfig | |||||
{ | |||||
public JT1078AVEncoderConfig() | |||||
{ | |||||
AddJob(Job.Default.WithGcServer(false).WithToolchain(CsProjCoreToolchain.NetCoreApp50).WithPlatform(Platform.AnyCpu)); | |||||
} | |||||
} | |||||
} |
@@ -1,76 +0,0 @@ | |||||
using BenchmarkDotNet.Attributes; | |||||
using BenchmarkDotNet.Configs; | |||||
using BenchmarkDotNet.Environments; | |||||
using BenchmarkDotNet.Jobs; | |||||
using BenchmarkDotNet.Toolchains.CsProj; | |||||
using JT1078.Flv; | |||||
using JT1078.Flv.MessagePack; | |||||
using JT1078.Protocol; | |||||
using JT1078.Protocol.H264; | |||||
using JT1078.Protocol.MessagePack; | |||||
using JT808.Protocol.Extensions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
namespace JT1078.AV.Benchmark | |||||
{ | |||||
[Config(typeof(JT1078FlvEncoderConfig))] | |||||
[MarkdownExporterAttribute.GitHub] | |||||
[MemoryDiagnoser] | |||||
public class JT1078FlvEncoderContext | |||||
{ | |||||
JT1078Package Package; | |||||
List<H264NALU> H264NALUs; | |||||
H264NALU SPSNALu; | |||||
H264Decoder h264Decoder = new H264Decoder(); | |||||
FlvEncoder flvEncoder = new FlvEncoder(); | |||||
[Params(100, 10000, 100000)] | |||||
public int N; | |||||
[GlobalSetup] | |||||
public void Setup() | |||||
{ | |||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "JT1078_1.txt")); | |||||
foreach (var line in lines) | |||||
{ | |||||
var data = line.Split(','); | |||||
var bytes = data[6].ToHexBytes(); | |||||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||||
Package = JT1078Serializer.Merge(package); | |||||
} | |||||
H264NALUs = h264Decoder.ParseNALU(Package); | |||||
SPSNALu = H264NALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == 7); | |||||
SPSNALu.RawData = h264Decoder.DiscardEmulationPreventionBytes(SPSNALu.RawData); | |||||
} | |||||
[Benchmark(Description = "EXPGolombReader")] | |||||
public void EXPGolombReaderTest() | |||||
{ | |||||
for (var i = 0; i < N; i++) | |||||
{ | |||||
ExpGolombReader h264GolombReader = new ExpGolombReader(SPSNALu.RawData); | |||||
h264GolombReader.ReadSPS(); | |||||
} | |||||
} | |||||
[Benchmark(Description = "H264Decoder")] | |||||
public void H264Decoder() | |||||
{ | |||||
for (var i = 0; i < N; i++) | |||||
{ | |||||
var nalus = h264Decoder.ParseNALU(Package); | |||||
} | |||||
} | |||||
} | |||||
public class JT1078FlvEncoderConfig : ManualConfig | |||||
{ | |||||
public JT1078FlvEncoderConfig() | |||||
{ | |||||
AddJob(Job.Default.WithGcServer(false).WithToolchain(CsProjCoreToolchain.NetCoreApp50).WithPlatform(Platform.AnyCpu)); | |||||
} | |||||
} | |||||
} |
@@ -8,7 +8,7 @@ namespace JT1078.AV.Benchmark | |||||
{ | { | ||||
static void Main(string[] args) | static void Main(string[] args) | ||||
{ | { | ||||
Summary summary = BenchmarkRunner.Run<JT1078FlvEncoderContext>(); | |||||
Summary summary = BenchmarkRunner.Run<JT1078AVEncoderContext>(); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -172,7 +172,7 @@ namespace JT1078.FMp4.Test | |||||
//stbl | //stbl | ||||
SampleTableBox sampleTableBox = new SampleTableBox(); | SampleTableBox sampleTableBox = new SampleTableBox(); | ||||
//stbl->stsd | //stbl->stsd | ||||
SampleDescriptionBox sampleDescriptionBox = new SampleDescriptionBox(HandlerType.none); | |||||
SampleDescriptionBox sampleDescriptionBox = new SampleDescriptionBox(); | |||||
//stbl->stsd->avc1 | //stbl->stsd->avc1 | ||||
AVC1SampleEntry aVC1SampleEntry = new AVC1SampleEntry(); | AVC1SampleEntry aVC1SampleEntry = new AVC1SampleEntry(); | ||||
aVC1SampleEntry.Width = 0x0220; | aVC1SampleEntry.Width = 0x0220; | ||||
@@ -0,0 +1,3 @@ | |||||
{ | |||||
"liveServer.settings.port": 5501 | |||||
} |
@@ -3,47 +3,204 @@ | |||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> | <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> | ||||
<head> | <head> | ||||
<meta charset="utf-8" /> | <meta charset="utf-8" /> | ||||
<title>fmp4 demo</title> | |||||
<title>WebSocket MSE Fmp4 demo</title> | |||||
<script type="text/javascript" src="signalr.min.js"></script> | |||||
</head> | </head> | ||||
<body> | <body> | ||||
<h1>MSE FMp4 Demo</h1> | <h1>MSE FMp4 Demo</h1> | ||||
<video autoplay controls></video> | |||||
<video id="stream_live" width="640" height="480" controls="false" autoplay="true" | |||||
muted="muted" | |||||
preload="auto"> | |||||
浏览器不支持 | |||||
</video> | |||||
<ul id="messagesList"></ul> | |||||
<!--<video src="/JT1078_4.mp4" autoplay controls></video>--> | <!--<video src="/JT1078_4.mp4" autoplay controls></video>--> | ||||
<script> | <script> | ||||
var video = document.querySelector('video'); | |||||
var mimeCodec = 'video/mp4;codecs="avc1.42E01E, mp4a.40.2"'; | |||||
if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) { | |||||
var mediaSource = new MediaSource(); | |||||
console.log(mediaSource.readyState); | |||||
video.src = URL.createObjectURL(mediaSource); | |||||
mediaSource.addEventListener('sourceopen', sourceOpen); | |||||
} else { | |||||
console.log('Unsupported MIME type pr cpdec:', mimeCodec); | |||||
} | |||||
function sourceOpen(_) { | |||||
URL.revokeObjectURL(video.src); | |||||
console.log(this.readyState); | |||||
var mediaSource = this; | |||||
var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec); | |||||
fetchMedia("/JT1078_4.mp4", function (buf) { | |||||
sourceBuffer.addEventListener("updateend", function (_) { | |||||
mediaSource.endOfStream(); | |||||
video.play(); | |||||
console.log(mediaSource.readyState); | |||||
}); | |||||
sourceBuffer.appendBuffer(buf); | |||||
//var mimeCodec = 'video/mp4;codecs="avc1.4D0014, mp4a.40.2"'; | |||||
// *** USER PARAMETERS *** | |||||
var verbose = true; | |||||
var buffering_sec = 3; // 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; | |||||
// } | |||||
// } | |||||
if (!source_buffer.updating) { | |||||
if (verbose) { console.log("Streaming started: ", memview[0], memview[1], memview[2], memview[3], memview[4]); } | |||||
stream_started = true; | |||||
source_buffer.appendBuffer(arr); | |||||
cc = cc + 1; | |||||
}else{ | |||||
queue.push(arr); // 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) { | |||||
var 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 signalR.HubConnectionBuilder() | |||||
.withUrl("http://127.0.0.1:5000/FMp4Hub") | |||||
.build(); | |||||
ws.on("video", (message) => { | |||||
var buff=base64ToArrayBuffer(message); | |||||
//console.log(buff); | |||||
putPacket(buff); | |||||
//先直接喂进去 | |||||
//mvhd.duration 谷歌浏览器不会缓存 | |||||
//source_buffer.appendBuffer(buff); | |||||
}); | }); | ||||
ws.start().catch(err => console.error(err)); | |||||
} | } | ||||
function fetchMedia(url, callback) { | |||||
console.log(url); | |||||
var xhr = new XMLHttpRequest; | |||||
xhr.open('get', url); | |||||
xhr.responseType = 'arraybuffer'; | |||||
xhr.onload = function () { | |||||
callback(xhr.response); | |||||
}; | |||||
xhr.send(); | |||||
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> | </script> | ||||
</body> | </body> | ||||
</html> | </html> |
@@ -58,6 +58,12 @@ | |||||
<None Include="..\..\doc\video\jt1078_1_fragmented.mp4" Link="H264\jt1078_1_fragmented.mp4"> | <None Include="..\..\doc\video\jt1078_1_fragmented.mp4" Link="H264\jt1078_1_fragmented.mp4"> | ||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
</None> | </None> | ||||
<None Include="..\..\doc\video\jt1078_5.txt" Link="H264\jt1078_5.txt"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Include="..\..\doc\video\jt1078_6.txt" Link="H264\jt1078_6.txt"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
</ItemGroup> | </ItemGroup> | ||||
</Project> | </Project> |
@@ -2,15 +2,18 @@ | |||||
using JT1078.FMp4.MessagePack; | using JT1078.FMp4.MessagePack; | ||||
using JT1078.FMp4.Samples; | using JT1078.FMp4.Samples; | ||||
using JT1078.Protocol; | using JT1078.Protocol; | ||||
using JT1078.Protocol.Enums; | |||||
using JT1078.Protocol.Extensions; | using JT1078.Protocol.Extensions; | ||||
using JT1078.Protocol.H264; | using JT1078.Protocol.H264; | ||||
using JT1078.Protocol.MessagePack; | using JT1078.Protocol.MessagePack; | ||||
using System; | using System; | ||||
using System.Buffers; | |||||
using System.Buffers.Binary; | using System.Buffers.Binary; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO; | using System.IO; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Text; | using System.Text; | ||||
using System.Threading; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Xunit; | using Xunit; | ||||
@@ -24,10 +27,10 @@ namespace JT1078.FMp4.Test | |||||
var jT1078Package = ParseNALUTest(); | var jT1078Package = ParseNALUTest(); | ||||
H264Decoder decoder = new H264Decoder(); | H264Decoder decoder = new H264Decoder(); | ||||
var nalus = decoder.ParseNALU(jT1078Package); | var nalus = decoder.ParseNALU(jT1078Package); | ||||
var spsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 7); | |||||
var spsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.SPS); | |||||
//SPS | //SPS | ||||
spsNALU.RawData = decoder.DiscardEmulationPreventionBytes(spsNALU.RawData); | spsNALU.RawData = decoder.DiscardEmulationPreventionBytes(spsNALU.RawData); | ||||
var ppsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 8); | |||||
var ppsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.PPS); | |||||
ppsNALU.RawData = decoder.DiscardEmulationPreventionBytes(ppsNALU.RawData); | ppsNALU.RawData = decoder.DiscardEmulationPreventionBytes(ppsNALU.RawData); | ||||
//ftyp | //ftyp | ||||
FileTypeBox fileTypeBox = new FileTypeBox(); | FileTypeBox fileTypeBox = new FileTypeBox(); | ||||
@@ -71,7 +74,7 @@ namespace JT1078.FMp4.Test | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes = new List<DataEntryBox>(); | movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes = new List<DataEntryBox>(); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes.Add(new DataEntryUrlBox(1)); | movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes.Add(new DataEntryUrlBox(1)); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox = new SampleTableBox(); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox = new SampleTableBox(); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox = new SampleDescriptionBox(movieBox.TrackBox.MediaBox.HandlerBox.HandlerType); | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox = new SampleDescriptionBox(); | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries = new List<SampleEntry>(); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries = new List<SampleEntry>(); | ||||
AVC1SampleEntry avc1 = new AVC1SampleEntry(); | AVC1SampleEntry avc1 = new AVC1SampleEntry(); | ||||
avc1.AVCConfigurationBox = new AVCConfigurationBox(); | avc1.AVCConfigurationBox = new AVCConfigurationBox(); | ||||
@@ -176,10 +179,10 @@ namespace JT1078.FMp4.Test | |||||
var jT1078Package = ParseNALUTest(); | var jT1078Package = ParseNALUTest(); | ||||
H264Decoder decoder = new H264Decoder(); | H264Decoder decoder = new H264Decoder(); | ||||
var nalus = decoder.ParseNALU(jT1078Package); | var nalus = decoder.ParseNALU(jT1078Package); | ||||
var spsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 7); | |||||
var spsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.SPS); | |||||
//SPS | //SPS | ||||
spsNALU.RawData = decoder.DiscardEmulationPreventionBytes(spsNALU.RawData); | spsNALU.RawData = decoder.DiscardEmulationPreventionBytes(spsNALU.RawData); | ||||
var ppsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 8); | |||||
var ppsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.PPS); | |||||
ppsNALU.RawData = decoder.DiscardEmulationPreventionBytes(ppsNALU.RawData); | ppsNALU.RawData = decoder.DiscardEmulationPreventionBytes(ppsNALU.RawData); | ||||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(new byte[65535]); | FMp4MessagePackWriter writer = new FMp4MessagePackWriter(new byte[65535]); | ||||
//ftyp | //ftyp | ||||
@@ -224,7 +227,7 @@ namespace JT1078.FMp4.Test | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes = new List<DataEntryBox>(); | movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes = new List<DataEntryBox>(); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes.Add(new DataEntryUrlBox(1)); | movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes.Add(new DataEntryUrlBox(1)); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox = new SampleTableBox(); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox = new SampleTableBox(); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox = new SampleDescriptionBox(movieBox.TrackBox.MediaBox.HandlerBox.HandlerType); | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox = new SampleDescriptionBox(); | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries = new List<SampleEntry>(); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries = new List<SampleEntry>(); | ||||
AVC1SampleEntry avc1 = new AVC1SampleEntry(); | AVC1SampleEntry avc1 = new AVC1SampleEntry(); | ||||
avc1.AVCConfigurationBox = new AVCConfigurationBox(); | avc1.AVCConfigurationBox = new AVCConfigurationBox(); | ||||
@@ -277,7 +280,7 @@ namespace JT1078.FMp4.Test | |||||
fragmentBox.MediaDataBox = new MediaDataBox(); | fragmentBox.MediaDataBox = new MediaDataBox(); | ||||
fragmentBox.MediaDataBox.Data = nalus.Select(s => s.RawData).ToList(); | fragmentBox.MediaDataBox.Data = nalus.Select(s => s.RawData).ToList(); | ||||
moofs.Add(fragmentBox); | moofs.Add(fragmentBox); | ||||
foreach(var moof in moofs) | |||||
foreach (var moof in moofs) | |||||
{ | { | ||||
moof.ToBuffer(ref writer); | moof.ToBuffer(ref writer); | ||||
} | } | ||||
@@ -324,10 +327,10 @@ namespace JT1078.FMp4.Test | |||||
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | ||||
var jT1078Package = packages.FirstOrDefault(); | var jT1078Package = packages.FirstOrDefault(); | ||||
var nalus = decoder.ParseNALU(jT1078Package); | var nalus = decoder.ParseNALU(jT1078Package); | ||||
var spsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 7); | |||||
var spsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.SPS); | |||||
//SPS | //SPS | ||||
spsNALU.RawData = decoder.DiscardEmulationPreventionBytes(spsNALU.RawData); | spsNALU.RawData = decoder.DiscardEmulationPreventionBytes(spsNALU.RawData); | ||||
var ppsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 8); | |||||
var ppsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.PPS); | |||||
ppsNALU.RawData = decoder.DiscardEmulationPreventionBytes(ppsNALU.RawData); | ppsNALU.RawData = decoder.DiscardEmulationPreventionBytes(ppsNALU.RawData); | ||||
ExpGolombReader h264GolombReader = new ExpGolombReader(spsNALU.RawData); | ExpGolombReader h264GolombReader = new ExpGolombReader(spsNALU.RawData); | ||||
var spsInfo = h264GolombReader.ReadSPS(); | var spsInfo = h264GolombReader.ReadSPS(); | ||||
@@ -373,7 +376,7 @@ namespace JT1078.FMp4.Test | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes = new List<DataEntryBox>(); | movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes = new List<DataEntryBox>(); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes.Add(new DataEntryUrlBox(1)); | movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes.Add(new DataEntryUrlBox(1)); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox = new SampleTableBox(); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox = new SampleTableBox(); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox = new SampleDescriptionBox(movieBox.TrackBox.MediaBox.HandlerBox.HandlerType); | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox = new SampleDescriptionBox(); | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries = new List<SampleEntry>(); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries = new List<SampleEntry>(); | ||||
AVC1SampleEntry avc1 = new AVC1SampleEntry(); | AVC1SampleEntry avc1 = new AVC1SampleEntry(); | ||||
avc1.AVCConfigurationBox = new AVCConfigurationBox(); | avc1.AVCConfigurationBox = new AVCConfigurationBox(); | ||||
@@ -437,26 +440,197 @@ namespace JT1078.FMp4.Test | |||||
public void Test4() | public void Test4() | ||||
{ | { | ||||
FMp4Encoder fMp4Encoder = new FMp4Encoder(); | FMp4Encoder fMp4Encoder = new FMp4Encoder(); | ||||
H264Decoder h264Decoder = new H264Decoder(); | |||||
var packages = ParseNALUTests(); | var packages = ParseNALUTests(); | ||||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_4.mp4"); | |||||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_5.mp4"); | |||||
if (File.Exists(filepath)) | if (File.Exists(filepath)) | ||||
{ | { | ||||
File.Delete(filepath); | File.Delete(filepath); | ||||
} | } | ||||
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | ||||
var package1 = packages[0]; | |||||
var buffer1 = fMp4Encoder.EncoderFirstVideoBox(package1); | |||||
fileStream.Write(buffer1); | |||||
int moofOffset = buffer1.Length; | |||||
foreach (var package in packages.Take(2)) | |||||
var ftyp = fMp4Encoder.FtypBox(); | |||||
fileStream.Write(ftyp); | |||||
var iNalus = h264Decoder.ParseNALU(packages[0]); | |||||
//判断第一帧是否关键帧 | |||||
var moov = fMp4Encoder.VideoMoovBox( | |||||
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS), | |||||
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)); | |||||
fileStream.Write(moov); | |||||
List<H264NALU> nalus = new List<H264NALU>(); | |||||
foreach (var package in packages) | |||||
{ | |||||
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package); | |||||
foreach (var nalu in h264NALUs) | |||||
{ | |||||
if (nalu.Slice) | |||||
{ | |||||
//H264 NALU slice first_mb_in_slice | |||||
nalus.Add(nalu); | |||||
} | |||||
else | |||||
{ | |||||
if (nalus.Count > 0) | |||||
{ | |||||
var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
fileStream.Write(otherBuffer); | |||||
nalus.Clear(); | |||||
} | |||||
nalus.Add(nalu); | |||||
} | |||||
} | |||||
} | |||||
fileStream.Close(); | |||||
} | |||||
[Fact] | |||||
public void Test5() | |||||
{ | |||||
FMp4Encoder fMp4Encoder = new FMp4Encoder(); | |||||
H264Decoder h264Decoder = new H264Decoder(); | |||||
var packages = ParseNALUTests(); | |||||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_6.mp4"); | |||||
if (File.Exists(filepath)) | |||||
{ | |||||
File.Delete(filepath); | |||||
} | |||||
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | |||||
var ftyp = fMp4Encoder.FtypBox(); | |||||
fileStream.Write(ftyp); | |||||
var iNalus = h264Decoder.ParseNALU(packages[0]); | |||||
//判断第一帧是否关键帧 | |||||
var moov = fMp4Encoder.VideoMoovBox( | |||||
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS), | |||||
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)); | |||||
fileStream.Write(moov); | |||||
List<H264NALU> nalus = new List<H264NALU>(); | |||||
foreach (var package in packages) | |||||
{ | |||||
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package); | |||||
if (package.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧) | |||||
{ | |||||
if (nalus.Count > 0) | |||||
{ | |||||
var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
fileStream.Write(otherBuffer); | |||||
nalus.Clear(); | |||||
} | |||||
} | |||||
nalus = nalus.Concat(h264NALUs).ToList(); | |||||
} | |||||
if (nalus.Count > 0) | |||||
{ | { | ||||
var otherBuffer = fMp4Encoder.EncoderOtherVideoBox(package, (ulong)moofOffset); | |||||
moofOffset += otherBuffer.Length; | |||||
var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
fileStream.Write(otherBuffer); | fileStream.Write(otherBuffer); | ||||
nalus.Clear(); | |||||
} | } | ||||
fileStream.Close(); | fileStream.Close(); | ||||
} | } | ||||
[Fact] | |||||
public void Test6() | |||||
{ | |||||
FMp4Encoder fMp4Encoder = new FMp4Encoder(); | |||||
H264Decoder h264Decoder = new H264Decoder(); | |||||
var packages = ParseNALUTests1(); | |||||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_7.mp4"); | |||||
if (File.Exists(filepath)) | |||||
{ | |||||
File.Delete(filepath); | |||||
} | |||||
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | |||||
var ftyp = fMp4Encoder.FtypBox(); | |||||
fileStream.Write(ftyp); | |||||
var iPackage = packages.FirstOrDefault(f => f.Label3.DataType == JT1078DataType.视频I帧); | |||||
var iNalus = h264Decoder.ParseNALU(iPackage); | |||||
//判断第一帧是否关键帧 | |||||
var moov = fMp4Encoder.VideoMoovBox( | |||||
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS), | |||||
iNalus.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)); | |||||
fileStream.Write(moov); | |||||
List<H264NALU> nalus = new List<H264NALU>(); | |||||
foreach (var package in packages) | |||||
{ | |||||
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package); | |||||
if (package.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧) | |||||
{ | |||||
if (nalus.Count > 0) | |||||
{ | |||||
fileStream.Write(fMp4Encoder.StypBox()); | |||||
var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
fileStream.Write(otherBuffer); | |||||
nalus.Clear(); | |||||
} | |||||
} | |||||
nalus = nalus.Concat(h264NALUs).ToList(); | |||||
} | |||||
if (nalus.Count > 0) | |||||
{ | |||||
var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
fileStream.Write(otherBuffer); | |||||
nalus.Clear(); | |||||
} | |||||
fileStream.Close(); | |||||
} | |||||
[Fact] | |||||
public void Test6_2() | |||||
{ | |||||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_7.h264"); | |||||
if (File.Exists(filepath)) | |||||
{ | |||||
File.Delete(filepath); | |||||
} | |||||
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | |||||
List<JT1078Package> packages = new List<JT1078Package>(); | |||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_6.txt")); | |||||
foreach (var line in lines) | |||||
{ | |||||
var bytes = line.ToHexBytes(); | |||||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||||
fileStream.Write(package.Bodies); | |||||
} | |||||
fileStream.Close(); | |||||
} | |||||
[Fact] | |||||
public void WebSocketMp4() | |||||
{ | |||||
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_8.mp4"); | |||||
if (File.Exists(filepath)) | |||||
{ | |||||
File.Delete(filepath); | |||||
} | |||||
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | |||||
System.Net.WebSockets.ClientWebSocket clientWebSocket = new System.Net.WebSockets.ClientWebSocket(); | |||||
clientWebSocket.ConnectAsync(new Uri("ws://127.0.0.1:81/live/JT1078_7.live.mp4"), CancellationToken.None).GetAwaiter().GetResult(); | |||||
Task.Run(async() => | |||||
{ | |||||
while (true) | |||||
{ | |||||
var buffer = new byte[4096]; | |||||
var result = await clientWebSocket.ReceiveAsync(buffer, CancellationToken.None); | |||||
if (result.EndOfMessage) | |||||
{ | |||||
await fileStream.WriteAsync(buffer.AsSpan(0, result.Count).ToArray()); | |||||
} | |||||
else | |||||
{ | |||||
await fileStream.WriteAsync(buffer); | |||||
} | |||||
} | |||||
}); | |||||
Thread.Sleep(100 * 1000); | |||||
} | |||||
[Fact] | [Fact] | ||||
public void tkhd_width_height_test() | public void tkhd_width_height_test() | ||||
{ | { | ||||
@@ -464,7 +638,7 @@ namespace JT1078.FMp4.Test | |||||
//01 20 00 00 | //01 20 00 00 | ||||
var a = BinaryPrimitives.ReadUInt32LittleEndian(new byte[] { 0x01, 0x60, 0, 0 }); | var a = BinaryPrimitives.ReadUInt32LittleEndian(new byte[] { 0x01, 0x60, 0, 0 }); | ||||
var b = BinaryPrimitives.ReadUInt32LittleEndian(new byte[] { 0x01, 0x20, 0, 0 }); | var b = BinaryPrimitives.ReadUInt32LittleEndian(new byte[] { 0x01, 0x20, 0, 0 }); | ||||
//00 00 01 60 | //00 00 01 60 | ||||
//00 00 01 20 | //00 00 01 20 | ||||
var c = BinaryPrimitives.ReadUInt32BigEndian(new byte[] { 0, 0, 0x01, 0x20 }); | var c = BinaryPrimitives.ReadUInt32BigEndian(new byte[] { 0, 0, 0x01, 0x20 }); | ||||
@@ -495,15 +669,15 @@ namespace JT1078.FMp4.Test | |||||
{ | { | ||||
var filepath1 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.mp4"); | var filepath1 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.mp4"); | ||||
var filepath2 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_1_fragmented.mp4"); | var filepath2 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_1_fragmented.mp4"); | ||||
var byte1=File.ReadAllBytes(filepath1); | |||||
var byte2=File.ReadAllBytes(filepath2); | |||||
if(byte1.Length== byte2.Length) | |||||
var byte1 = File.ReadAllBytes(filepath1); | |||||
var byte2 = File.ReadAllBytes(filepath2); | |||||
if (byte1.Length == byte2.Length) | |||||
{ | { | ||||
for(var i=0;i< byte1.Length; i++) | |||||
for (var i = 0; i < byte1.Length; i++) | |||||
{ | { | ||||
if (byte1[i] != byte2[i]) | if (byte1[i] != byte2[i]) | ||||
{ | { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -544,5 +718,24 @@ namespace JT1078.FMp4.Test | |||||
} | } | ||||
return packages; | return packages; | ||||
} | } | ||||
public List<JT1078Package> ParseNALUTests1() | |||||
{ | |||||
List<JT1078Package> packages = new List<JT1078Package>(); | |||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_6.txt")); | |||||
int mergeBodyLength = 0; | |||||
foreach (var line in lines) | |||||
{ | |||||
var bytes = line.ToHexBytes(); | |||||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||||
mergeBodyLength += package.DataBodyLength; | |||||
var packageMerge = JT1078Serializer.Merge(package); | |||||
if (packageMerge != null) | |||||
{ | |||||
packages.Add(packageMerge); | |||||
} | |||||
} | |||||
return packages; | |||||
} | |||||
} | } | ||||
} | } |
@@ -40,7 +40,10 @@ namespace JT1078.FMp4 | |||||
Start(ref writer); | Start(ref writer); | ||||
MovieHeaderBox.ToBuffer(ref writer); | MovieHeaderBox.ToBuffer(ref writer); | ||||
TrackBox.ToBuffer(ref writer); | TrackBox.ToBuffer(ref writer); | ||||
MovieExtendsBox.ToBuffer(ref writer); | |||||
if (MovieExtendsBox != null) | |||||
{ | |||||
MovieExtendsBox.ToBuffer(ref writer); | |||||
} | |||||
if (UserDataBox != null) | if (UserDataBox != null) | ||||
{ | { | ||||
UserDataBox.ToBuffer(ref writer); | UserDataBox.ToBuffer(ref writer); | ||||
@@ -38,6 +38,12 @@ namespace JT1078.FMp4 | |||||
TrackFragmentBox.ToBuffer(ref writer); | TrackFragmentBox.ToBuffer(ref writer); | ||||
} | } | ||||
End(ref writer); | End(ref writer); | ||||
var moofOffsetPosition = writer.GetMoofOffsetPosition(); | |||||
if (moofOffsetPosition > 0) | |||||
{ | |||||
writer.WriteUInt64Return((ulong)writer.GetCurrentPosition(), moofOffsetPosition); | |||||
} | |||||
writer.ClearMoofOffsetPosition(); | |||||
var trunOffsetPosition = writer.GetTrunOffsetPosition(); | var trunOffsetPosition = writer.GetTrunOffsetPosition(); | ||||
if (trunOffsetPosition > 0) | if (trunOffsetPosition > 0) | ||||
{ | { | ||||
@@ -38,7 +38,7 @@ namespace JT1078.FMp4 | |||||
MovieFragmentRandomAccessOffsetBox.ToBuffer(ref writer); | MovieFragmentRandomAccessOffsetBox.ToBuffer(ref writer); | ||||
} | } | ||||
End(ref writer); | End(ref writer); | ||||
var mfraSizePosition = writer.GetMfraSizePositionn(); | |||||
var mfraSizePosition = writer.GetMfraSizePosition(); | |||||
if (mfraSizePosition > 0) | if (mfraSizePosition > 0) | ||||
{ | { | ||||
writer.WriteInt32Return(writer.GetCurrentPosition() - SizePosition, mfraSizePosition); | writer.WriteInt32Return(writer.GetCurrentPosition() - SizePosition, mfraSizePosition); | ||||
@@ -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; } | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -16,15 +16,13 @@ namespace JT1078.FMp4 | |||||
/// <summary> | /// <summary> | ||||
/// stsd | /// stsd | ||||
/// </summary> | /// </summary> | ||||
/// <param name="handlerType"></param> | |||||
/// <param name="version"></param> | /// <param name="version"></param> | ||||
/// <param name="flags"></param> | /// <param name="flags"></param> | ||||
public SampleDescriptionBox(HandlerType handlerType,byte version=0, uint flags=0) : base("stsd", version, flags) | |||||
public SampleDescriptionBox(byte version=0, uint flags=0) : base("stsd", version, flags) | |||||
{ | { | ||||
HandlerType = handlerType; | |||||
} | } | ||||
public HandlerType HandlerType { get; set; } | |||||
public uint EntryCount { get; set; } | |||||
private uint EntryCount { get; set; } | |||||
public List<SampleEntry> SampleEntries { get; set; } | public List<SampleEntry> SampleEntries { get; set; } | ||||
public void ToBuffer(ref FMp4MessagePackWriter writer) | public void ToBuffer(ref FMp4MessagePackWriter writer) | ||||
{ | { | ||||
@@ -26,6 +26,10 @@ namespace JT1078.FMp4 | |||||
/// </summary> | /// </summary> | ||||
public TimeToSampleBox TimeToSampleBox { get; set; } | public TimeToSampleBox TimeToSampleBox { get; set; } | ||||
/// <summary> | /// <summary> | ||||
/// stss | |||||
/// </summary> | |||||
public SyncSampleBox SyncSampleBox { get; set; } | |||||
/// <summary> | |||||
/// ctts | /// ctts | ||||
/// </summary> | /// </summary> | ||||
public CompositionOffsetBox CompositionOffsetBox { get; set; } | public CompositionOffsetBox CompositionOffsetBox { get; set; } | ||||
@@ -44,7 +48,6 @@ namespace JT1078.FMp4 | |||||
/// </summary> | /// </summary> | ||||
public ChunkOffsetBox ChunkOffsetBox { get; set; } | public ChunkOffsetBox ChunkOffsetBox { get; set; } | ||||
//public ChunkLargeOffsetBox ChunkLargeOffsetBox { get; set; } | //public ChunkLargeOffsetBox ChunkLargeOffsetBox { get; set; } | ||||
//public SyncSampleBox SyncSampleBox { get; set; } | |||||
//public ShadowSyncSampleBox ShadowSyncSampleBox { get; set; } | //public ShadowSyncSampleBox ShadowSyncSampleBox { get; set; } | ||||
//public PaddingBitsBox PaddingBitsBox { get; set; } | //public PaddingBitsBox PaddingBitsBox { get; set; } | ||||
//public DegradationPriorityBox DegradationPriorityBox { get; set; } | //public DegradationPriorityBox DegradationPriorityBox { get; set; } | ||||
@@ -58,6 +61,10 @@ namespace JT1078.FMp4 | |||||
Start(ref writer); | Start(ref writer); | ||||
SampleDescriptionBox.ToBuffer(ref writer); | SampleDescriptionBox.ToBuffer(ref writer); | ||||
TimeToSampleBox.ToBuffer(ref writer); | TimeToSampleBox.ToBuffer(ref writer); | ||||
if (SyncSampleBox != null) | |||||
{ | |||||
SyncSampleBox.ToBuffer(ref writer); | |||||
} | |||||
if(CompositionOffsetBox!=null) | if(CompositionOffsetBox!=null) | ||||
CompositionOffsetBox.ToBuffer(ref writer); | CompositionOffsetBox.ToBuffer(ref writer); | ||||
SampleToChunkBox.ToBuffer(ref writer); | SampleToChunkBox.ToBuffer(ref writer); | ||||
@@ -1,58 +1,130 @@ | |||||
using System; | |||||
using JT1078.FMp4.Interfaces; | |||||
using JT1078.FMp4.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
namespace JT1078.FMp4 | namespace JT1078.FMp4 | ||||
{ | { | ||||
public class SegmentIndexBox : FullBox | |||||
/// <summary> | |||||
/// sidx | |||||
/// </summary> | |||||
public class SegmentIndexBox : FullBox,IFMp4MessagePackFormatter | |||||
{ | { | ||||
/// <summary> | |||||
/// sidx | |||||
/// </summary> | |||||
/// <param name="version"></param> | |||||
/// <param name="flags"></param> | |||||
public SegmentIndexBox(byte version, uint flags=0) : base("sidx", version, flags) | public SegmentIndexBox(byte version, uint flags=0) : base("sidx", version, flags) | ||||
{ | { | ||||
ReferencedSizePositions = new List<int>(); | |||||
} | } | ||||
public uint ReferenceID { get; set; } | |||||
public string Timescale { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// | /// | ||||
/// </summary> | /// </summary> | ||||
public ulong EarliestPresentationTimeLarge { get; set; } | |||||
public ulong FirstOffsetLarge { get; set; } | |||||
public uint ReferenceID { get; set; } = 1; | |||||
/// <summary> | /// <summary> | ||||
/// if(version==0) | |||||
/// | |||||
/// </summary> | /// </summary> | ||||
public uint EarliestPresentationTime { get; set; } | |||||
public uint Timescale { get; set; } = 1000; | |||||
/// <summary> | /// <summary> | ||||
/// if (version==0) | |||||
/// pts | |||||
/// if(version==0) | |||||
/// version==0 32 bit | |||||
/// version>0 64 bit | |||||
/// </summary> | |||||
public ulong EarliestPresentationTime { get; set; } | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
public ulong FirstOffset { get; set; } = 0; | |||||
private ushort Reserved { get; set; } | |||||
//private ushort ReferenceCount { get; set; } | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | /// </summary> | ||||
public uint FirstOffset { get; set; } | |||||
public ushort Reserved { get; set; } | |||||
public ushort ReferenceCount { get; set; } | |||||
public List<SegmentIndex> SegmentIndexs { get; set; } | public List<SegmentIndex> SegmentIndexs { get; set; } | ||||
public List<int> ReferencedSizePositions { get; set; } | |||||
public void ToBuffer(ref FMp4MessagePackWriter writer) | |||||
{ | |||||
Start(ref writer); | |||||
WriterFullBoxToBuffer(ref writer); | |||||
writer.WriteUInt32(ReferenceID); | |||||
writer.WriteUInt32(Timescale); | |||||
if (Version == 0) | |||||
{ | |||||
writer.WriteUInt32((uint)EarliestPresentationTime); | |||||
writer.WriteUInt32((uint)FirstOffset); | |||||
} | |||||
else | |||||
{ | |||||
writer.WriteUInt64(EarliestPresentationTime); | |||||
writer.WriteUInt64(FirstOffset); | |||||
} | |||||
writer.WriteUInt16(Reserved); | |||||
if(SegmentIndexs!=null && SegmentIndexs.Count > 0) | |||||
{ | |||||
writer.WriteUInt16((ushort)SegmentIndexs.Count); | |||||
foreach(var si in SegmentIndexs) | |||||
{ | |||||
if (si.ReferenceType) | |||||
{ | |||||
ReferencedSizePositions.Add(writer.GetCurrentPosition()); | |||||
writer.WriteUInt32(si.ReferencedSize | 0x80000000); | |||||
} | |||||
else | |||||
{ | |||||
ReferencedSizePositions.Add(writer.GetCurrentPosition()); | |||||
writer.WriteUInt32(si.ReferencedSize); | |||||
} | |||||
writer.WriteUInt32(si.SubsegmentDuration); | |||||
if (si.StartsWithSAP) | |||||
{ | |||||
writer.WriteUInt32((si.SAPDeltaTime | 0x80000000 | (uint)(si.SAPType << 28 & 0x70000000))); | |||||
} | |||||
else | |||||
{ | |||||
writer.WriteUInt32((si.SAPDeltaTime | (uint)((si.SAPType <<28) & 0x70000000))); | |||||
} | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
writer.WriteUInt16(0); | |||||
} | |||||
End(ref writer); | |||||
} | |||||
public class SegmentIndex | public class SegmentIndex | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// 4byte 32 - 1 | /// 4byte 32 - 1 | ||||
/// </summary> | /// </summary> | ||||
public bool ReferenceType { get; set; } | |||||
public bool ReferenceType { get; set; } = false; | |||||
/// <summary> | /// <summary> | ||||
/// 4byte 32 - 31 | /// 4byte 32 - 31 | ||||
/// ReferencedSize=(moof size) + (mdat size) | |||||
/// </summary> | |||||
public uint ReferencedSize { get; set; } = 0; | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | /// </summary> | ||||
public uint ReferencedSize { get; set; } | |||||
public uint SubsegmentDuration { get; set; } | public uint SubsegmentDuration { get; set; } | ||||
/// <summary> | /// <summary> | ||||
/// 4byte 32 - 1 | /// 4byte 32 - 1 | ||||
/// </summary> | /// </summary> | ||||
public bool StartsWithSAP { get; set; } | |||||
public bool StartsWithSAP { get; set; } = true; | |||||
/// <summary> | /// <summary> | ||||
/// 4byte 32 - 3 | /// 4byte 32 - 3 | ||||
/// </summary> | /// </summary> | ||||
public byte SAPType { get; set; } | |||||
public byte SAPType { get; set; } = 1; | |||||
/// <summary> | /// <summary> | ||||
/// 4byte 32 - 28 | /// 4byte 32 - 28 | ||||
/// </summary> | /// </summary> | ||||
public uint SAPDeltaTime { get; set; } | |||||
public uint SAPDeltaTime { get; set; } = 0; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -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); | |||||
} | |||||
} | |||||
} |
@@ -1,17 +1,46 @@ | |||||
using System; | |||||
using JT1078.FMp4.Interfaces; | |||||
using JT1078.FMp4.MessagePack; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
namespace JT1078.FMp4 | namespace JT1078.FMp4 | ||||
{ | { | ||||
public class SyncSampleBox : FullBox | |||||
/// <summary> | |||||
/// stss | |||||
/// </summary> | |||||
public class SyncSampleBox : FullBox, IFMp4MessagePackFormatter | |||||
{ | { | ||||
/// <summary> | |||||
/// stss | |||||
/// </summary> | |||||
/// <param name="version"></param> | |||||
/// <param name="flags"></param> | |||||
public SyncSampleBox(byte version=0, uint flags=0) : base("stss", version, flags) | public SyncSampleBox(byte version=0, uint flags=0) : base("stss", version, flags) | ||||
{ | { | ||||
} | } | ||||
public uint EntryCount { get; set; } | |||||
//private uint EntryCount { get; set; } | |||||
public List<uint> SampleNumber { get; set; } | |||||
public List<uint> SampleNumbers { get; set; } | |||||
public void ToBuffer(ref FMp4MessagePackWriter writer) | |||||
{ | |||||
Start(ref writer); | |||||
WriterFullBoxToBuffer(ref writer); | |||||
if(SampleNumbers!=null && SampleNumbers.Count > 0) | |||||
{ | |||||
writer.WriteUInt32((uint)SampleNumbers.Count); | |||||
foreach(var item in SampleNumbers) | |||||
{ | |||||
writer.WriteUInt32(item); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
writer.WriteUInt32(0); | |||||
} | |||||
End(ref writer); | |||||
} | |||||
} | } | ||||
} | } |
@@ -49,6 +49,7 @@ namespace JT1078.FMp4 | |||||
public uint DefaultSampleSize { get; set; } | public uint DefaultSampleSize { get; set; } | ||||
/// <summary> | /// <summary> | ||||
/// TFHD_FLAG_DEFAULT_FLAGS | /// TFHD_FLAG_DEFAULT_FLAGS | ||||
/// MOV_AUDIO == handler_type ? 0x02000000 : (0x00010000| 0x01000000); | |||||
/// </summary> | /// </summary> | ||||
public uint DefaultSampleFlags { get; set; } | public uint DefaultSampleFlags { get; set; } | ||||
#endregion | #endregion | ||||
@@ -59,7 +60,16 @@ namespace JT1078.FMp4 | |||||
writer.WriteUInt32(TrackID); | writer.WriteUInt32(TrackID); | ||||
if ((FMp4Constants.TFHD_FLAG_BASE_DATA_OFFSET & Flags) > 0) | if ((FMp4Constants.TFHD_FLAG_BASE_DATA_OFFSET & Flags) > 0) | ||||
{ | { | ||||
writer.WriteUInt64(BaseDataOffset); | |||||
if (BaseDataOffset > 0) | |||||
{ | |||||
writer.WriteUInt64(BaseDataOffset); | |||||
} | |||||
else | |||||
{ | |||||
//程序自动计算 | |||||
writer.CreateMoofOffsetPosition(); | |||||
writer.Skip(8, out _); | |||||
} | |||||
} | } | ||||
if ((FMp4Constants.TFHD_FLAG_SAMPLE_DESCRIPTION_INDEX & Flags) > 0) | if ((FMp4Constants.TFHD_FLAG_SAMPLE_DESCRIPTION_INDEX & Flags) > 0) | ||||
{ | { | ||||
@@ -13,13 +13,27 @@ namespace JT1078.FMp4 | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// trun | /// trun | ||||
/// 201 205 1 | |||||
/// 201 0010 0000 0001 | |||||
/// 205 0010 0000 0101 | |||||
/// 1 0000 0000 0001 | |||||
/// tr_flags 是用来表示下列 sample 相关的标识符是否应用到每个字段中: | |||||
/// 0x000001-0000 0000 0001: data-offset-present,只应用 data-offset | |||||
/// 0x000004-0000 0000 0100: 只对第一个 sample 应用对应的 flags。剩余 sample flags 就不管了。 | |||||
/// 0x000100-0001 0000 0000: 这个比较重要,表示每个 sample 都有自己的 duration,否则使用默认的 | |||||
/// 0x000200-0010 0000 0000: 每个 sample 有自己的 sample_size,否则使用默认的。 | |||||
/// 0x000400-0100 0000 0000: 对每个 sample 使用自己的 flags。否则,使用默认的。 | |||||
/// 0x000800-1000 0000 0000: 每个 sample 都有自己的 cts 值 | |||||
/// 0x000f01-1111 0000 0001 | |||||
/// </summary> | /// </summary> | ||||
/// <param name="version"></param> | /// <param name="version"></param> | ||||
/// <param name="flags"></param> | /// <param name="flags"></param> | ||||
public TrackRunBox(byte version=0, uint flags= 0x000f01) : base("trun", version, flags) | public TrackRunBox(byte version=0, uint flags= 0x000f01) : base("trun", version, flags) | ||||
{ | { | ||||
} | } | ||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
public uint SampleCount { get; set; } | public uint SampleCount { get; set; } | ||||
/// <summary> | /// <summary> | ||||
/// 可选的 | /// 可选的 | ||||
@@ -88,11 +102,11 @@ namespace JT1078.FMp4 | |||||
{ | { | ||||
if (Version == 0) | if (Version == 0) | ||||
{ | { | ||||
writer.WriteUInt32(trun.SampleCompositionTimeOffset); | |||||
writer.WriteUInt32((uint)trun.SampleCompositionTimeOffset); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
writer.WriteInt32(trun.SignedSampleCompositionTimeOffset); | |||||
writer.WriteInt32((int)trun.SampleCompositionTimeOffset); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -106,10 +120,12 @@ namespace JT1078.FMp4 | |||||
public uint SampleSize { get; set; } | public uint SampleSize { get; set; } | ||||
public uint SampleFlags { get; set; } | public uint SampleFlags { get; set; } | ||||
/// <summary> | /// <summary> | ||||
/// cts | |||||
/// version == 0 | /// version == 0 | ||||
/// 0:uint | |||||
/// >0:int | |||||
/// </summary> | /// </summary> | ||||
public uint SampleCompositionTimeOffset { get; set; } | |||||
public int SignedSampleCompositionTimeOffset { get; set; } | |||||
public long SampleCompositionTimeOffset { get; set; } | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -2,6 +2,7 @@ | |||||
using JT1078.FMp4.MessagePack; | using JT1078.FMp4.MessagePack; | ||||
using JT1078.FMp4.Samples; | using JT1078.FMp4.Samples; | ||||
using JT1078.Protocol; | using JT1078.Protocol; | ||||
using JT1078.Protocol.Enums; | |||||
using JT1078.Protocol.H264; | using JT1078.Protocol.H264; | ||||
using JT1078.Protocol.MessagePack; | using JT1078.Protocol.MessagePack; | ||||
using System; | using System; | ||||
@@ -17,65 +18,92 @@ namespace JT1078.FMp4 | |||||
/// stream data | /// stream data | ||||
/// ftyp | /// ftyp | ||||
/// moov | /// moov | ||||
/// styp 1 | |||||
/// moof 1 | /// moof 1 | ||||
/// mdat 1 | /// mdat 1 | ||||
/// ... | /// ... | ||||
/// styp n | |||||
/// moof n | /// moof n | ||||
/// mdat n | /// mdat n | ||||
/// mfra | /// mfra | ||||
/// ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing | |||||
/// </summary> | /// </summary> | ||||
public class FMp4Encoder | public class FMp4Encoder | ||||
{ | { | ||||
readonly H264Decoder h264Decoder; | |||||
Dictionary<string, TrackInfo> TrackInfos; | |||||
const uint DefaultSampleDuration = 40u; | |||||
const uint DefaultSampleFlags = 0x1010000; | |||||
const uint FirstSampleFlags = 33554432; | |||||
const uint TfhdFlags = 0x2003a; | |||||
//const uint TrunFlags = 0x205; | |||||
const uint TrunFlags = 0x205; | |||||
const uint SampleDescriptionIndex = 1; | |||||
const uint TrackID = 1; | |||||
/// <summary> | /// <summary> | ||||
/// | /// | ||||
/// </summary> | /// </summary> | ||||
public FMp4Encoder() | public FMp4Encoder() | ||||
{ | { | ||||
h264Decoder = new H264Decoder(); | |||||
TrackInfos = new Dictionary<string, TrackInfo>(StringComparer.OrdinalIgnoreCase); | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 编码首个视频盒子 | |||||
/// 编码ftyp盒子 | |||||
/// </summary> | /// </summary> | ||||
/// <param name="package">jt1078完整包</param> | |||||
/// <returns></returns> | /// <returns></returns> | ||||
public byte[] EncoderFirstVideoBox(JT1078Package package) | |||||
public byte[] FtypBox() | |||||
{ | { | ||||
byte[] buffer = FMp4ArrayPool.Rent(package.Bodies.Length + 4096); | |||||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(new byte[10 * 1024 * 1024]); | |||||
byte[] buffer = FMp4ArrayPool.Rent(1024); | |||||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | |||||
try | try | ||||
{ | { | ||||
var nalus = h264Decoder.ParseNALU(package); | |||||
var spsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 7); | |||||
//SPS | |||||
spsNALU.RawData = h264Decoder.DiscardEmulationPreventionBytes(spsNALU.RawData); | |||||
var ppsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 8); | |||||
ppsNALU.RawData = h264Decoder.DiscardEmulationPreventionBytes(ppsNALU.RawData); | |||||
ExpGolombReader h264GolombReader = new ExpGolombReader(spsNALU.RawData); | |||||
var spsInfo = h264GolombReader.ReadSPS(); | |||||
//ftyp | //ftyp | ||||
FileTypeBox fileTypeBox = new FileTypeBox(); | FileTypeBox fileTypeBox = new FileTypeBox(); | ||||
fileTypeBox.MajorBrand = "isom"; | fileTypeBox.MajorBrand = "isom"; | ||||
fileTypeBox.MinorVersion = "\0\0\u0002\0"; | fileTypeBox.MinorVersion = "\0\0\u0002\0"; | ||||
fileTypeBox.CompatibleBrands.Add("isom"); | fileTypeBox.CompatibleBrands.Add("isom"); | ||||
fileTypeBox.CompatibleBrands.Add("iso6"); | |||||
fileTypeBox.CompatibleBrands.Add("iso2"); | fileTypeBox.CompatibleBrands.Add("iso2"); | ||||
fileTypeBox.CompatibleBrands.Add("avc1"); | fileTypeBox.CompatibleBrands.Add("avc1"); | ||||
fileTypeBox.CompatibleBrands.Add("mp41"); | fileTypeBox.CompatibleBrands.Add("mp41"); | ||||
fileTypeBox.CompatibleBrands.Add("iso5"); | |||||
fileTypeBox.CompatibleBrands.Add("iso6"); | |||||
fileTypeBox.ToBuffer(ref writer); | |||||
var data = writer.FlushAndGetArray(); | |||||
return data; | |||||
} | |||||
finally | |||||
{ | |||||
FMp4ArrayPool.Return(buffer); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// 编码moov盒子 | |||||
/// </summary> | |||||
/// <returns></returns> | |||||
public byte[] VideoMoovBox(in H264NALU sps, in H264NALU pps) | |||||
{ | |||||
byte[] buffer = FMp4ArrayPool.Rent(sps.RawData.Length + pps.RawData.Length + 1024); | |||||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | |||||
try | |||||
{ | |||||
ExpGolombReader h264GolombReader = new ExpGolombReader(sps.RawData); | |||||
var spsInfo = h264GolombReader.ReadSPS(); | |||||
//moov | //moov | ||||
MovieBox movieBox = new MovieBox(); | MovieBox movieBox = new MovieBox(); | ||||
movieBox.MovieHeaderBox = new MovieHeaderBox(0, 0); | |||||
movieBox.MovieHeaderBox = new MovieHeaderBox(0, 2); | |||||
movieBox.MovieHeaderBox.CreationTime = 0; | movieBox.MovieHeaderBox.CreationTime = 0; | ||||
movieBox.MovieHeaderBox.ModificationTime = 0; | movieBox.MovieHeaderBox.ModificationTime = 0; | ||||
movieBox.MovieHeaderBox.Duration = 0; | movieBox.MovieHeaderBox.Duration = 0; | ||||
movieBox.MovieHeaderBox.Timescale = 1000; | movieBox.MovieHeaderBox.Timescale = 1000; | ||||
movieBox.MovieHeaderBox.NextTrackID = 2; | |||||
movieBox.MovieHeaderBox.NextTrackID = 99; | |||||
movieBox.TrackBox = new TrackBox(); | movieBox.TrackBox = new TrackBox(); | ||||
movieBox.TrackBox.TrackHeaderBox = new TrackHeaderBox(0, 3); | movieBox.TrackBox.TrackHeaderBox = new TrackHeaderBox(0, 3); | ||||
movieBox.TrackBox.TrackHeaderBox.CreationTime = 0; | movieBox.TrackBox.TrackHeaderBox.CreationTime = 0; | ||||
movieBox.TrackBox.TrackHeaderBox.ModificationTime = 0; | movieBox.TrackBox.TrackHeaderBox.ModificationTime = 0; | ||||
movieBox.TrackBox.TrackHeaderBox.TrackID = 1; | |||||
movieBox.TrackBox.TrackHeaderBox.TrackID = TrackID; | |||||
movieBox.TrackBox.TrackHeaderBox.Duration = 0; | movieBox.TrackBox.TrackHeaderBox.Duration = 0; | ||||
movieBox.TrackBox.TrackHeaderBox.TrackIsAudio = false; | movieBox.TrackBox.TrackHeaderBox.TrackIsAudio = false; | ||||
movieBox.TrackBox.TrackHeaderBox.Width = (uint)spsInfo.width; | movieBox.TrackBox.TrackHeaderBox.Width = (uint)spsInfo.width; | ||||
@@ -84,7 +112,8 @@ namespace JT1078.FMp4 | |||||
movieBox.TrackBox.MediaBox.MediaHeaderBox = new MediaHeaderBox(); | movieBox.TrackBox.MediaBox.MediaHeaderBox = new MediaHeaderBox(); | ||||
movieBox.TrackBox.MediaBox.MediaHeaderBox.CreationTime = 0; | movieBox.TrackBox.MediaBox.MediaHeaderBox.CreationTime = 0; | ||||
movieBox.TrackBox.MediaBox.MediaHeaderBox.ModificationTime = 0; | movieBox.TrackBox.MediaBox.MediaHeaderBox.ModificationTime = 0; | ||||
movieBox.TrackBox.MediaBox.MediaHeaderBox.Timescale = 1200000; | |||||
//movieBox.TrackBox.MediaBox.MediaHeaderBox.Timescale = 1200000; | |||||
movieBox.TrackBox.MediaBox.MediaHeaderBox.Timescale = 1000; | |||||
movieBox.TrackBox.MediaBox.MediaHeaderBox.Duration = 0; | movieBox.TrackBox.MediaBox.MediaHeaderBox.Duration = 0; | ||||
movieBox.TrackBox.MediaBox.HandlerBox = new HandlerBox(); | movieBox.TrackBox.MediaBox.HandlerBox = new HandlerBox(); | ||||
movieBox.TrackBox.MediaBox.HandlerBox.HandlerType = HandlerType.vide; | movieBox.TrackBox.MediaBox.HandlerBox.HandlerType = HandlerType.vide; | ||||
@@ -96,7 +125,7 @@ namespace JT1078.FMp4 | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes = new List<DataEntryBox>(); | movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes = new List<DataEntryBox>(); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes.Add(new DataEntryUrlBox(1)); | movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes.Add(new DataEntryUrlBox(1)); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox = new SampleTableBox(); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox = new SampleTableBox(); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox = new SampleDescriptionBox(movieBox.TrackBox.MediaBox.HandlerBox.HandlerType); | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox = new SampleDescriptionBox(); | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries = new List<SampleEntry>(); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries = new List<SampleEntry>(); | ||||
AVC1SampleEntry avc1 = new AVC1SampleEntry(); | AVC1SampleEntry avc1 = new AVC1SampleEntry(); | ||||
avc1.AVCConfigurationBox = new AVCConfigurationBox(); | avc1.AVCConfigurationBox = new AVCConfigurationBox(); | ||||
@@ -106,23 +135,23 @@ namespace JT1078.FMp4 | |||||
avc1.AVCConfigurationBox.AVCLevelIndication = spsInfo.levelIdc; | avc1.AVCConfigurationBox.AVCLevelIndication = spsInfo.levelIdc; | ||||
avc1.AVCConfigurationBox.AVCProfileIndication = spsInfo.profileIdc; | avc1.AVCConfigurationBox.AVCProfileIndication = spsInfo.profileIdc; | ||||
avc1.AVCConfigurationBox.ProfileCompatibility = (byte)spsInfo.profileCompat; | avc1.AVCConfigurationBox.ProfileCompatibility = (byte)spsInfo.profileCompat; | ||||
avc1.AVCConfigurationBox.PPSs = new List<byte[]>() { ppsNALU.RawData }; | |||||
avc1.AVCConfigurationBox.SPSs = new List<byte[]>() { spsNALU.RawData }; | |||||
avc1.AVCConfigurationBox.PPSs = new List<byte[]>() { pps.RawData }; | |||||
avc1.AVCConfigurationBox.SPSs = new List<byte[]>() { sps.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(); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SyncSampleBox = new SyncSampleBox(); | |||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleToChunkBox = new SampleToChunkBox(); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleToChunkBox = new SampleToChunkBox(); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleSizeBox = new SampleSizeBox(); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleSizeBox = new SampleSizeBox(); | ||||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.ChunkOffsetBox = new ChunkOffsetBox(); | movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.ChunkOffsetBox = new ChunkOffsetBox(); | ||||
movieBox.MovieExtendsBox = new MovieExtendsBox(); | movieBox.MovieExtendsBox = new MovieExtendsBox(); | ||||
movieBox.MovieExtendsBox.TrackExtendsBoxs = new List<TrackExtendsBox>(); | movieBox.MovieExtendsBox.TrackExtendsBoxs = new List<TrackExtendsBox>(); | ||||
TrackExtendsBox trex = new TrackExtendsBox(); | TrackExtendsBox trex = new TrackExtendsBox(); | ||||
trex.TrackID = 1; | |||||
trex.DefaultSampleDescriptionIndex = 1; | |||||
trex.TrackID = TrackID; | |||||
trex.DefaultSampleDescriptionIndex = SampleDescriptionIndex; | |||||
trex.DefaultSampleDuration = 0; | trex.DefaultSampleDuration = 0; | ||||
trex.DefaultSampleSize = 0; | trex.DefaultSampleSize = 0; | ||||
trex.DefaultSampleFlags = 0; | trex.DefaultSampleFlags = 0; | ||||
movieBox.MovieExtendsBox.TrackExtendsBoxs.Add(trex); | movieBox.MovieExtendsBox.TrackExtendsBoxs.Add(trex); | ||||
fileTypeBox.ToBuffer(ref writer); | |||||
movieBox.ToBuffer(ref writer); | movieBox.ToBuffer(ref writer); | ||||
var data = writer.FlushAndGetArray(); | var data = writer.FlushAndGetArray(); | ||||
return data; | return data; | ||||
@@ -133,40 +162,121 @@ namespace JT1078.FMp4 | |||||
} | } | ||||
} | } | ||||
/// <summary> | |||||
/// styp | |||||
/// </summary> | |||||
/// <returns></returns> | |||||
public byte[] StypBox() | |||||
{ | |||||
byte[] buffer = FMp4ArrayPool.Rent(1024); | |||||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | |||||
try | |||||
{ | |||||
SegmentTypeBox stypTypeBox = new SegmentTypeBox(); | |||||
stypTypeBox.MajorBrand = "isom"; | |||||
stypTypeBox.MinorVersion = "\0\0\0\0"; | |||||
stypTypeBox.CompatibleBrands.Add("isom"); | |||||
stypTypeBox.CompatibleBrands.Add("mp42"); | |||||
stypTypeBox.CompatibleBrands.Add("msdh"); | |||||
stypTypeBox.CompatibleBrands.Add("msix"); | |||||
stypTypeBox.CompatibleBrands.Add("iso5"); | |||||
stypTypeBox.CompatibleBrands.Add("iso6"); | |||||
stypTypeBox.ToBuffer(ref writer); | |||||
var data = writer.FlushAndGetArray(); | |||||
return data; | |||||
} | |||||
finally | |||||
{ | |||||
FMp4ArrayPool.Return(buffer); | |||||
} | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// 编码其他视频数据盒子 | /// 编码其他视频数据盒子 | ||||
/// </summary> | /// </summary> | ||||
/// <param name="package">jt1078完整包</param> | |||||
/// <param name="moofOffset">moof位置</param> | |||||
/// <returns></returns> | /// <returns></returns> | ||||
public byte[] EncoderOtherVideoBox(JT1078Package package, ulong moofOffset = 0) | |||||
public byte[] OtherVideoBox(in List<H264NALU> nalus) | |||||
{ | { | ||||
byte[] buffer = FMp4ArrayPool.Rent(package.Bodies.Length + 4096); | |||||
byte[] buffer = FMp4ArrayPool.Rent(nalus.Sum(s => s.RawData.Length + s.StartCodePrefix.Length) + 4096); | |||||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer); | ||||
try | try | ||||
{ | { | ||||
var nalus = h264Decoder.ParseNALU(package); | |||||
var truns = new List<TrackRunBox.TrackRunInfo>(); | |||||
List<byte[]> rawdatas = new List<byte[]>(); | |||||
uint iSize = 0; | |||||
ulong lastTimestamp = 0; | |||||
string key = string.Empty; | |||||
for (var i = 0; i < nalus.Count; i++) | |||||
{ | |||||
var nalu = nalus[i]; | |||||
if (string.IsNullOrEmpty(key)) | |||||
{ | |||||
key = nalu.GetKey(); | |||||
} | |||||
rawdatas.Add(nalu.RawData); | |||||
if (nalu.DataType == Protocol.Enums.JT1078DataType.视频I帧) | |||||
{ | |||||
iSize += (uint)(nalu.RawData.Length + nalu.StartCodePrefix.Length); | |||||
} | |||||
else | |||||
{ | |||||
if (iSize > 0) | |||||
{ | |||||
truns.Add(new TrackRunBox.TrackRunInfo() | |||||
{ | |||||
SampleDuration=40, | |||||
SampleSize = iSize, | |||||
}); | |||||
iSize = 0; | |||||
} | |||||
truns.Add(new TrackRunBox.TrackRunInfo() | |||||
{ | |||||
SampleDuration = 40, | |||||
SampleSize = (uint)(nalu.RawData.Length + nalu.StartCodePrefix.Length), | |||||
}); | |||||
} | |||||
if (i == (nalus.Count - 1)) | |||||
{ | |||||
lastTimestamp = nalu.Timestamp; | |||||
} | |||||
} | |||||
if (TrackInfos.TryGetValue(key, out TrackInfo trackInfo)) | |||||
{ | |||||
if (trackInfo.SN == uint.MaxValue) | |||||
{ | |||||
trackInfo.SN = 1; | |||||
} | |||||
trackInfo.SN++; | |||||
} | |||||
else | |||||
{ | |||||
trackInfo = new TrackInfo { SN = 1, DTS = 0 }; | |||||
TrackInfos.Add(key, trackInfo); | |||||
} | |||||
var movieFragmentBox = new MovieFragmentBox(); | var movieFragmentBox = new MovieFragmentBox(); | ||||
movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox(); | movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox(); | ||||
movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = package.SN; | |||||
movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = trackInfo.SN; | |||||
movieFragmentBox.TrackFragmentBox = new TrackFragmentBox(); | movieFragmentBox.TrackFragmentBox = new TrackFragmentBox(); | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(0x39); | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = 1; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.BaseDataOffset = moofOffset; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = 48000; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = (uint)package.Bodies.Length; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = 0x1010000; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(TfhdFlags); | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = TrackID; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.SampleDescriptionIndex = SampleDescriptionIndex; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = DefaultSampleDuration; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = truns[0].SampleSize; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = DefaultSampleFlags; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox(); | movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox(); | ||||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = package.Timestamp * 1000; | |||||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = trackInfo.DTS; | |||||
trackInfo.DTS += (ulong)(truns.Count * DefaultSampleDuration); | |||||
TrackInfos[key] = trackInfo; | |||||
//trun | //trun | ||||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: 0x5); | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = 0; | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = new List<TrackRunBox.TrackRunInfo>(); | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo()); | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: TrunFlags); | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = FirstSampleFlags; | |||||
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = truns; | |||||
movieFragmentBox.ToBuffer(ref writer); | movieFragmentBox.ToBuffer(ref writer); | ||||
var mediaDataBox = new MediaDataBox(); | var mediaDataBox = new MediaDataBox(); | ||||
mediaDataBox.Data = nalus.Select(s => s.RawData).ToList(); | |||||
mediaDataBox.Data = rawdatas; | |||||
mediaDataBox.ToBuffer(ref writer); | mediaDataBox.ToBuffer(ref writer); | ||||
var data = writer.FlushAndGetArray(); | var data = writer.FlushAndGetArray(); | ||||
return data; | return data; | ||||
} | } | ||||
@@ -175,5 +285,11 @@ namespace JT1078.FMp4 | |||||
FMp4ArrayPool.Return(buffer); | FMp4ArrayPool.Return(buffer); | ||||
} | } | ||||
} | } | ||||
struct TrackInfo | |||||
{ | |||||
public uint SN { get; set; } | |||||
public ulong DTS { get; set; } | |||||
} | |||||
} | } | ||||
} | } |
@@ -13,8 +13,8 @@ | |||||
<PackageProjectUrl>https://github.com/SmallChi/JT1078</PackageProjectUrl> | <PackageProjectUrl>https://github.com/SmallChi/JT1078</PackageProjectUrl> | ||||
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | <licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | ||||
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | <license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | ||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||||
<Version>1.0.0-preview1</Version> | |||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | |||||
<Version>1.0.0-preview5</Version> | |||||
<SignAssembly>false</SignAssembly> | <SignAssembly>false</SignAssembly> | ||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | ||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||||
@@ -27,9 +27,7 @@ | |||||
<PackagePath></PackagePath> | <PackagePath></PackagePath> | ||||
</None> | </None> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | <ItemGroup> | ||||
<ProjectReference Include="..\JT1078.Protocol\JT1078.Protocol.csproj" /> | <ProjectReference Include="..\JT1078.Protocol\JT1078.Protocol.csproj" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
@@ -571,11 +571,10 @@ | |||||
stsd | stsd | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="M:JT1078.FMp4.SampleDescriptionBox.#ctor(JT1078.FMp4.Enums.HandlerType,System.Byte,System.UInt32)"> | |||||
<member name="M:JT1078.FMp4.SampleDescriptionBox.#ctor(System.Byte,System.UInt32)"> | |||||
<summary> | <summary> | ||||
stsd | stsd | ||||
</summary> | </summary> | ||||
<param name="handlerType"></param> | |||||
<param name="version"></param> | <param name="version"></param> | ||||
<param name="flags"></param> | <param name="flags"></param> | ||||
</member> | </member> | ||||
@@ -622,6 +621,11 @@ | |||||
stts | stts | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="P:JT1078.FMp4.SampleTableBox.SyncSampleBox"> | |||||
<summary> | |||||
stss | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.FMp4.SampleTableBox.CompositionOffsetBox"> | <member name="P:JT1078.FMp4.SampleTableBox.CompositionOffsetBox"> | ||||
<summary> | <summary> | ||||
ctts | ctts | ||||
@@ -672,19 +676,44 @@ | |||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<!-- Badly formed XML comment ignored for member "P:JT1078.FMp4.SchemeTypeBox.SchemeUri" --> | <!-- Badly formed XML comment ignored for member "P:JT1078.FMp4.SchemeTypeBox.SchemeUri" --> | ||||
<member name="P:JT1078.FMp4.SegmentIndexBox.EarliestPresentationTimeLarge"> | |||||
<member name="T:JT1078.FMp4.SegmentIndexBox"> | |||||
<summary> | |||||
sidx | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT1078.FMp4.SegmentIndexBox.#ctor(System.Byte,System.UInt32)"> | |||||
<summary> | |||||
sidx | |||||
</summary> | |||||
<param name="version"></param> | |||||
<param name="flags"></param> | |||||
</member> | |||||
<member name="P:JT1078.FMp4.SegmentIndexBox.ReferenceID"> | |||||
<summary> | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.FMp4.SegmentIndexBox.Timescale"> | |||||
<summary> | <summary> | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="P:JT1078.FMp4.SegmentIndexBox.EarliestPresentationTime"> | <member name="P:JT1078.FMp4.SegmentIndexBox.EarliestPresentationTime"> | ||||
<summary> | <summary> | ||||
if(version==0) | |||||
pts | |||||
if(version==0) | |||||
version==0 32 bit | |||||
version>0 64 bit | |||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="P:JT1078.FMp4.SegmentIndexBox.FirstOffset"> | <member name="P:JT1078.FMp4.SegmentIndexBox.FirstOffset"> | ||||
<summary> | <summary> | ||||
if (version==0) | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndexs"> | |||||
<summary> | |||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndex.ReferenceType"> | <member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndex.ReferenceType"> | ||||
@@ -695,6 +724,12 @@ | |||||
<member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndex.ReferencedSize"> | <member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndex.ReferencedSize"> | ||||
<summary> | <summary> | ||||
4byte 32 - 31 | 4byte 32 - 31 | ||||
ReferencedSize=(moof size) + (mdat size) | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndex.SubsegmentDuration"> | |||||
<summary> | |||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndex.StartsWithSAP"> | <member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndex.StartsWithSAP"> | ||||
@@ -712,6 +747,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 | ||||
@@ -770,6 +833,18 @@ | |||||
length:ItemCount | length:ItemCount | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="T:JT1078.FMp4.SyncSampleBox"> | |||||
<summary> | |||||
stss | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT1078.FMp4.SyncSampleBox.#ctor(System.Byte,System.UInt32)"> | |||||
<summary> | |||||
stss | |||||
</summary> | |||||
<param name="version"></param> | |||||
<param name="flags"></param> | |||||
</member> | |||||
<member name="T:JT1078.FMp4.TimeToSampleBox"> | <member name="T:JT1078.FMp4.TimeToSampleBox"> | ||||
<summary> | <summary> | ||||
stts | stts | ||||
@@ -908,6 +983,7 @@ | |||||
<member name="P:JT1078.FMp4.TrackFragmentHeaderBox.DefaultSampleFlags"> | <member name="P:JT1078.FMp4.TrackFragmentHeaderBox.DefaultSampleFlags"> | ||||
<summary> | <summary> | ||||
TFHD_FLAG_DEFAULT_FLAGS | TFHD_FLAG_DEFAULT_FLAGS | ||||
MOV_AUDIO == handler_type ? 0x02000000 : (0x00010000| 0x01000000); | |||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="T:JT1078.FMp4.TrackFragmentRandomAccessBox"> | <member name="T:JT1078.FMp4.TrackFragmentRandomAccessBox"> | ||||
@@ -978,10 +1054,27 @@ | |||||
<member name="M:JT1078.FMp4.TrackRunBox.#ctor(System.Byte,System.UInt32)"> | <member name="M:JT1078.FMp4.TrackRunBox.#ctor(System.Byte,System.UInt32)"> | ||||
<summary> | <summary> | ||||
trun | trun | ||||
201 205 1 | |||||
201 0010 0000 0001 | |||||
205 0010 0000 0101 | |||||
1 0000 0000 0001 | |||||
tr_flags 是用来表示下列 sample 相关的标识符是否应用到每个字段中: | |||||
0x000001-0000 0000 0001: data-offset-present,只应用 data-offset | |||||
0x000004-0000 0000 0100: 只对第一个 sample 应用对应的 flags。剩余 sample flags 就不管了。 | |||||
0x000100-0001 0000 0000: 这个比较重要,表示每个 sample 都有自己的 duration,否则使用默认的 | |||||
0x000200-0010 0000 0000: 每个 sample 有自己的 sample_size,否则使用默认的。 | |||||
0x000400-0100 0000 0000: 对每个 sample 使用自己的 flags。否则,使用默认的。 | |||||
0x000800-1000 0000 0000: 每个 sample 都有自己的 cts 值 | |||||
0x000f01-1111 0000 0001 | |||||
</summary> | </summary> | ||||
<param name="version"></param> | <param name="version"></param> | ||||
<param name="flags"></param> | <param name="flags"></param> | ||||
</member> | </member> | ||||
<member name="P:JT1078.FMp4.TrackRunBox.SampleCount"> | |||||
<summary> | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.FMp4.TrackRunBox.DataOffset"> | <member name="P:JT1078.FMp4.TrackRunBox.DataOffset"> | ||||
<summary> | <summary> | ||||
可选的 | 可选的 | ||||
@@ -1002,7 +1095,10 @@ | |||||
</member> | </member> | ||||
<member name="P:JT1078.FMp4.TrackRunBox.TrackRunInfo.SampleCompositionTimeOffset"> | <member name="P:JT1078.FMp4.TrackRunBox.TrackRunInfo.SampleCompositionTimeOffset"> | ||||
<summary> | <summary> | ||||
cts | |||||
version == 0 | version == 0 | ||||
0:uint | |||||
>0:int | |||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="T:JT1078.FMp4.URIBox"> | <member name="T:JT1078.FMp4.URIBox"> | ||||
@@ -1213,12 +1309,15 @@ | |||||
stream data | stream data | ||||
ftyp | ftyp | ||||
moov | moov | ||||
styp 1 | |||||
moof 1 | moof 1 | ||||
mdat 1 | mdat 1 | ||||
... | ... | ||||
styp n | |||||
moof n | moof n | ||||
mdat n | mdat n | ||||
mfra | mfra | ||||
ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing | |||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="M:JT1078.FMp4.FMp4Encoder.#ctor"> | <member name="M:JT1078.FMp4.FMp4Encoder.#ctor"> | ||||
@@ -1226,19 +1325,28 @@ | |||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderFirstVideoBox(JT1078.Protocol.JT1078Package)"> | |||||
<member name="M:JT1078.FMp4.FMp4Encoder.FtypBox"> | |||||
<summary> | |||||
编码ftyp盒子 | |||||
</summary> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT1078.FMp4.FMp4Encoder.VideoMoovBox(JT1078.Protocol.H264.H264NALU@,JT1078.Protocol.H264.H264NALU@)"> | |||||
<summary> | |||||
编码moov盒子 | |||||
</summary> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT1078.FMp4.FMp4Encoder.StypBox"> | |||||
<summary> | <summary> | ||||
编码首个视频盒子 | |||||
styp | |||||
</summary> | </summary> | ||||
<param name="package">jt1078完整包</param> | |||||
<returns></returns> | <returns></returns> | ||||
</member> | </member> | ||||
<member name="M:JT1078.FMp4.FMp4Encoder.EncoderOtherVideoBox(JT1078.Protocol.JT1078Package,System.UInt64)"> | |||||
<member name="M:JT1078.FMp4.FMp4Encoder.OtherVideoBox(System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU}@)"> | |||||
<summary> | <summary> | ||||
编码其他视频数据盒子 | 编码其他视频数据盒子 | ||||
</summary> | </summary> | ||||
<param name="package">jt1078完整包</param> | |||||
<param name="moofOffset">moof位置</param> | |||||
<returns></returns> | <returns></returns> | ||||
</member> | </member> | ||||
<member name="P:JT1078.FMp4.FullBox.Version"> | <member name="P:JT1078.FMp4.FullBox.Version"> | ||||
@@ -13,12 +13,14 @@ namespace JT1078.FMp4.MessagePack | |||||
private int TrunDataOffsetPosition; | private int TrunDataOffsetPosition; | ||||
private int MfraSizePosition; | private int MfraSizePosition; | ||||
private int MoofOffsetPosition; | |||||
public FMp4MessagePackWriter(Span<byte> buffer) | public FMp4MessagePackWriter(Span<byte> buffer) | ||||
{ | { | ||||
this.writer = new FMp4BufferWriter(buffer); | this.writer = new FMp4BufferWriter(buffer); | ||||
TrunDataOffsetPosition = 0; | TrunDataOffsetPosition = 0; | ||||
MfraSizePosition = 0; | MfraSizePosition = 0; | ||||
MoofOffsetPosition = 0; | |||||
} | } | ||||
public byte[] FlushAndGetArray() | public byte[] FlushAndGetArray() | ||||
{ | { | ||||
@@ -109,6 +111,10 @@ namespace JT1078.FMp4.MessagePack | |||||
{ | { | ||||
BinaryPrimitives.WriteUInt32BigEndian(writer.Written.Slice(position, 4), value); | BinaryPrimitives.WriteUInt32BigEndian(writer.Written.Slice(position, 4), value); | ||||
} | } | ||||
public void WriteUInt64Return(ulong value, int position) | |||||
{ | |||||
BinaryPrimitives.WriteUInt64BigEndian(writer.Written.Slice(position, 8), value); | |||||
} | |||||
public void WriteByteReturn(byte value, int position) | public void WriteByteReturn(byte value, int position) | ||||
{ | { | ||||
writer.Written[position] = value; | writer.Written[position] = value; | ||||
@@ -139,7 +145,7 @@ namespace JT1078.FMp4.MessagePack | |||||
MfraSizePosition = writer.WrittenCount; | MfraSizePosition = writer.WrittenCount; | ||||
} | } | ||||
public int GetMfraSizePositionn() | |||||
public int GetMfraSizePosition() | |||||
{ | { | ||||
return MfraSizePosition; | return MfraSizePosition; | ||||
} | } | ||||
@@ -149,6 +155,22 @@ namespace JT1078.FMp4.MessagePack | |||||
MfraSizePosition = 0; | MfraSizePosition = 0; | ||||
} | } | ||||
public void CreateMoofOffsetPosition() | |||||
{ | |||||
MoofOffsetPosition = writer.WrittenCount; | |||||
} | |||||
public int GetMoofOffsetPosition() | |||||
{ | |||||
return MoofOffsetPosition; | |||||
} | |||||
public void ClearMoofOffsetPosition() | |||||
{ | |||||
MoofOffsetPosition = 0; | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// ref | /// ref | ||||
/// </summary> | /// </summary> | ||||
@@ -40,6 +40,9 @@ namespace JT1078.Flv | |||||
readonly H264Decoder h264Decoder; | readonly H264Decoder h264Decoder; | ||||
readonly AudioCodecFactory audioCodecFactory; | readonly AudioCodecFactory audioCodecFactory; | ||||
//public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false) | //public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false) | ||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
public FlvEncoder() | public FlvEncoder() | ||||
{ | { | ||||
audioCodecFactory = new AudioCodecFactory(); | audioCodecFactory = new AudioCodecFactory(); | ||||
@@ -207,9 +210,9 @@ namespace JT1078.Flv | |||||
var nalus = h264Decoder.ParseNALU(package); | var nalus = h264Decoder.ParseNALU(package); | ||||
if (nalus != null && nalus.Count > 0) | if (nalus != null && nalus.Count > 0) | ||||
{ | { | ||||
var sei = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 6); | |||||
var sps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 7); | |||||
var pps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == 8); | |||||
var sei = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == NalUnitType.SEI); | |||||
var sps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == NalUnitType.SPS); | |||||
var pps = nalus.FirstOrDefault(x => x.NALUHeader.NalUnitType == NalUnitType.PPS); | |||||
nalus.Remove(sps); | nalus.Remove(sps); | ||||
nalus.Remove(pps); | nalus.Remove(pps); | ||||
nalus.Remove(sei); | nalus.Remove(sei); | ||||
@@ -297,7 +300,7 @@ namespace JT1078.Flv | |||||
//1: keyframe (for AVC, a seekable frame) —— 即H.264的IDR帧; | //1: keyframe (for AVC, a seekable frame) —— 即H.264的IDR帧; | ||||
//2: inter frame(for AVC, a non - seekable frame) —— H.264的普通I帧; | //2: inter frame(for AVC, a non - seekable frame) —— H.264的普通I帧; | ||||
//ref:https://www.cnblogs.com/chyingp/p/flv-getting-started.html | //ref:https://www.cnblogs.com/chyingp/p/flv-getting-started.html | ||||
if (nALU.NALUHeader.NalUnitType == 5) | |||||
if (nALU.NALUHeader.NalUnitType == NalUnitType.IDR) | |||||
{ | { | ||||
flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; | flvTags.VideoTagsData.FrameType = FrameType.KeyFrame; | ||||
} | } | ||||
@@ -14,7 +14,7 @@ | |||||
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | <licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | ||||
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | <license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | ||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | <GeneratePackageOnBuild>false</GeneratePackageOnBuild> | ||||
<Version>1.1.0</Version> | |||||
<Version>1.1.1-preview1</Version> | |||||
<SignAssembly>false</SignAssembly> | <SignAssembly>false</SignAssembly> | ||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | ||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||||
@@ -181,6 +181,11 @@ | |||||
4、<see cref="M:JT1078.Flv.FlvEncoder.EncoderAudioTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传true | 4、<see cref="M:JT1078.Flv.FlvEncoder.EncoderAudioTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传true | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="M:JT1078.Flv.FlvEncoder.#ctor"> | |||||
<summary> | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"> | <member name="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"> | ||||
<summary> | <summary> | ||||
编码flv头 | 编码flv头 | ||||
@@ -42,5 +42,8 @@ | |||||
<None Include="..\..\doc\video\jt1078_2.txt" Link="H264\jt1078_2.txt"> | <None Include="..\..\doc\video\jt1078_2.txt" Link="H264\jt1078_2.txt"> | ||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
</None> | </None> | ||||
<None Include="..\..\doc\video\jt1078_5.txt" Link="H264\jt1078_5.txt"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
</ItemGroup> | </ItemGroup> | ||||
</Project> | </Project> |
@@ -5,11 +5,54 @@ using System.IO; | |||||
using System.Text; | using System.Text; | ||||
using Xunit; | using Xunit; | ||||
using JT1078.Protocol.Extensions; | using JT1078.Protocol.Extensions; | ||||
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; | |||||
using System.Net.Sockets; | |||||
using System.Threading; | |||||
namespace JT1078.Hls.Test | namespace JT1078.Hls.Test | ||||
{ | { | ||||
public class M3U8_Test | public class M3U8_Test | ||||
{ | { | ||||
/// <summary> | |||||
/// 模拟发送视频数据 | |||||
/// </summary> | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
try | |||||
{ | |||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_5.txt")); | |||||
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); | |||||
clientSocket.Connect("127.0.0.1", 10888); | |||||
long lasttime = 0; | |||||
foreach (var line in lines) | |||||
{ | |||||
var temp = line.Split(','); | |||||
if (lasttime == 0) | |||||
{ | |||||
lasttime = long.Parse(temp[0]); | |||||
} | |||||
else | |||||
{ | |||||
var ts = long.Parse(temp[0]) - lasttime; | |||||
if(ts>0) | |||||
{ | |||||
Thread.Sleep(TimeSpan.FromMilliseconds(ts)); | |||||
} | |||||
else if (ts == 0) | |||||
{ | |||||
} | |||||
lasttime = long.Parse(temp[0]); | |||||
} | |||||
var data= temp[1].ToHexBytes(); | |||||
clientSocket.Send(data); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
//Assert.Throws<Exception>(() => { }); | |||||
} | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// 生成m3u8索引文件 | /// 生成m3u8索引文件 | ||||
/// </summary> | /// </summary> | ||||
@@ -90,13 +90,13 @@ namespace JT1078.Hls.Test | |||||
JT1078Package fullpackage = JT1078Serializer.Merge(package); | JT1078Package fullpackage = JT1078Serializer.Merge(package); | ||||
if (fullpackage != null) | if (fullpackage != null) | ||||
{ | { | ||||
var sdt = tSEncoder.CreateSDT(fullpackage); | |||||
var sdt = tSEncoder.CreateSDT(); | |||||
string sdtHEX = sdt.ToHexString(); | string sdtHEX = sdt.ToHexString(); | ||||
fileStream.Write(sdt); | fileStream.Write(sdt); | ||||
var pat = tSEncoder.CreatePAT(fullpackage); | |||||
var pat = tSEncoder.CreatePAT(); | |||||
string patHEX = pat.ToHexString(); | string patHEX = pat.ToHexString(); | ||||
fileStream.Write(pat); | fileStream.Write(pat); | ||||
var pmt = tSEncoder.CreatePMT(fullpackage); | |||||
var pmt = tSEncoder.CreatePMT(); | |||||
fileStream.Write(pmt); | fileStream.Write(pmt); | ||||
var pes = tSEncoder.CreatePES(fullpackage); | var pes = tSEncoder.CreatePES(fullpackage); | ||||
fileStream.Write(pes); | fileStream.Write(pes); | ||||
@@ -139,13 +139,13 @@ namespace JT1078.Hls.Test | |||||
{ | { | ||||
if (isNeedFirstHeadler) | if (isNeedFirstHeadler) | ||||
{ | { | ||||
var sdt = tSEncoder.CreateSDT(fullpackage); | |||||
var sdt = tSEncoder.CreateSDT(); | |||||
string sdtHEX = sdt.ToHexString(); | string sdtHEX = sdt.ToHexString(); | ||||
fileStream.Write(sdt); | fileStream.Write(sdt); | ||||
var pat = tSEncoder.CreatePAT(fullpackage); | |||||
var pat = tSEncoder.CreatePAT(); | |||||
string patHEX = pat.ToHexString(); | string patHEX = pat.ToHexString(); | ||||
fileStream.Write(pat); | fileStream.Write(pat); | ||||
var pmt = tSEncoder.CreatePMT(fullpackage); | |||||
var pmt = tSEncoder.CreatePMT(); | |||||
fileStream.Write(pmt); | fileStream.Write(pmt); | ||||
var pes = tSEncoder.CreatePES(fullpackage, 18888); | var pes = tSEncoder.CreatePES(fullpackage, 18888); | ||||
fileStream.Write(pes); | fileStream.Write(pes); | ||||
@@ -7,14 +7,14 @@ | |||||
<Authors>SmallChi(Koike)</Authors> | <Authors>SmallChi(Koike)</Authors> | ||||
<PackageId>JT1078.Hls</PackageId> | <PackageId>JT1078.Hls</PackageId> | ||||
<Product>JT1078.Hls</Product> | <Product>JT1078.Hls</Product> | ||||
<Description>基于JT1078的Hls视频编码器</Description> | |||||
<Description>基于JT1078的Hls视频编码器、ts文件路径参数名改成可配置</Description> | |||||
<PackageReleaseNotes>基于JT1078的Hls视频编码器</PackageReleaseNotes> | <PackageReleaseNotes>基于JT1078的Hls视频编码器</PackageReleaseNotes> | ||||
<RepositoryUrl>https://github.com/SmallChi/JT1078</RepositoryUrl> | <RepositoryUrl>https://github.com/SmallChi/JT1078</RepositoryUrl> | ||||
<PackageProjectUrl>https://github.com/SmallChi/JT1078</PackageProjectUrl> | <PackageProjectUrl>https://github.com/SmallChi/JT1078</PackageProjectUrl> | ||||
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | <licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | ||||
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | <license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | ||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | <GeneratePackageOnBuild>false</GeneratePackageOnBuild> | ||||
<Version>1.1.0-preview1</Version> | |||||
<Version>1.1.0-preview4</Version> | |||||
<SignAssembly>false</SignAssembly> | <SignAssembly>false</SignAssembly> | ||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | ||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||||
@@ -24,10 +24,6 @@ | |||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> | <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> | ||||
<PackageReference Include="System.Memory" Version="4.5.4" /> | <PackageReference Include="System.Memory" Version="4.5.4" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT1078.Protocol\JT1078.Protocol.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | <ItemGroup> | ||||
<None Include="..\..\LICENSE"> | <None Include="..\..\LICENSE"> | ||||
<Pack>True</Pack> | <Pack>True</Pack> | ||||
@@ -37,4 +33,7 @@ | |||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" /> | <PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT1078.Protocol\JT1078.Protocol.csproj" /> | |||||
</ItemGroup> | |||||
</Project> | </Project> |
@@ -96,6 +96,19 @@ | |||||
m3u8文件管理 | m3u8文件管理 | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="M:JT1078.Hls.M3U8FileManage.#ctor(JT1078.Hls.Options.M3U8Option)"> | |||||
<summary> | |||||
</summary> | |||||
<param name="m3U8Option"></param> | |||||
</member> | |||||
<member name="M:JT1078.Hls.M3U8FileManage.#ctor(JT1078.Hls.Options.M3U8Option,JT1078.Hls.TSEncoder)"> | |||||
<summary> | |||||
</summary> | |||||
<param name="m3U8Option"></param> | |||||
<param name="tSEncoder"></param> | |||||
</member> | |||||
<member name="M:JT1078.Hls.M3U8FileManage.CreateTsData(JT1078.Protocol.JT1078Package)"> | <member name="M:JT1078.Hls.M3U8FileManage.CreateTsData(JT1078.Protocol.JT1078Package)"> | ||||
<summary> | <summary> | ||||
生成ts和m3u8文件 | 生成ts和m3u8文件 | ||||
@@ -132,6 +145,12 @@ | |||||
<param name="key">终端号_通道号(用作目录)</param> | <param name="key">终端号_通道号(用作目录)</param> | ||||
<param name="data">文件内容</param> | <param name="data">文件内容</param> | ||||
</member> | </member> | ||||
<member name="M:JT1078.Hls.M3U8FileManage.AppendM3U8End"> | |||||
<summary> | |||||
添加结束标识 | |||||
直播流用不到 | |||||
</summary> | |||||
</member> | |||||
<member name="M:JT1078.Hls.M3U8FileManage.Clear(System.String,System.Int32)"> | <member name="M:JT1078.Hls.M3U8FileManage.Clear(System.String,System.Int32)"> | ||||
<summary> | <summary> | ||||
停止观看直播时清零数据 | 停止观看直播时清零数据 | ||||
@@ -144,6 +163,16 @@ | |||||
TS文件信息 | TS文件信息 | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="P:JT1078.Hls.M3U8FileManage.TsFileInfo.Sim"> | |||||
<summary> | |||||
设备手机号 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.Hls.M3U8FileManage.TsFileInfo.ChannelNo"> | |||||
<summary> | |||||
设备逻辑通道号 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.Hls.M3U8FileManage.TsFileInfo.FileName"> | <member name="P:JT1078.Hls.M3U8FileManage.TsFileInfo.FileName"> | ||||
<summary> | <summary> | ||||
ts文件名 | ts文件名 | ||||
@@ -184,6 +213,16 @@ | |||||
每个ts文件的最大时长 | 每个ts文件的最大时长 | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="P:JT1078.Hls.Options.M3U8Option.TsPathSimParamName"> | |||||
<summary> | |||||
ts路径sim参数名称 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.Hls.Options.M3U8Option.TsPathChannelParamName"> | |||||
<summary> | |||||
ts路径通道参数名称 | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.Hls.Options.M3U8Option.M3U8FileName"> | <member name="P:JT1078.Hls.Options.M3U8Option.M3U8FileName"> | ||||
<summary> | <summary> | ||||
m3u8文件 | m3u8文件 | ||||
@@ -263,6 +302,11 @@ | |||||
4.PES | 4.PES | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="M:JT1078.Hls.TSEncoder.#ctor"> | |||||
<summary> | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.Hls.TS_AdaptationInfo.PCRIncluded"> | <member name="P:JT1078.Hls.TS_AdaptationInfo.PCRIncluded"> | ||||
<summary> | <summary> | ||||
取0x50表示包含PCR或0x40表示不包含PCR | 取0x50表示包含PCR或0x40表示不包含PCR | ||||
@@ -19,15 +19,28 @@ namespace JT1078.Hls | |||||
public class M3U8FileManage | public class M3U8FileManage | ||||
{ | { | ||||
private TSEncoder tSEncoder; | private TSEncoder tSEncoder; | ||||
public readonly M3U8Option m3U8Option; | |||||
private M3U8Option m3U8Option; | |||||
ConcurrentDictionary<string, TsFileInfo> curTsFileInfoDic = new ConcurrentDictionary<string, TsFileInfo>();//当前文件信息 | ConcurrentDictionary<string, TsFileInfo> curTsFileInfoDic = new ConcurrentDictionary<string, TsFileInfo>();//当前文件信息 | ||||
ConcurrentDictionary<string, Queue<TsFileInfo>> tsFileInfoQueueDic = new ConcurrentDictionary<string, Queue<TsFileInfo>>(); | ConcurrentDictionary<string, Queue<TsFileInfo>> tsFileInfoQueueDic = new ConcurrentDictionary<string, Queue<TsFileInfo>>(); | ||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
/// <param name="m3U8Option"></param> | |||||
public M3U8FileManage(M3U8Option m3U8Option):this(m3U8Option, new TSEncoder()) | |||||
{ | |||||
} | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
/// <param name="m3U8Option"></param> | |||||
/// <param name="tSEncoder"></param> | |||||
public M3U8FileManage(M3U8Option m3U8Option, TSEncoder tSEncoder) | public M3U8FileManage(M3U8Option m3U8Option, TSEncoder tSEncoder) | ||||
{ | { | ||||
this.tSEncoder = tSEncoder; | this.tSEncoder = tSEncoder; | ||||
this.m3U8Option = m3U8Option; | this.m3U8Option = m3U8Option; | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 生成ts和m3u8文件 | /// 生成ts和m3u8文件 | ||||
/// </summary> | /// </summary> | ||||
@@ -35,9 +48,8 @@ namespace JT1078.Hls | |||||
public void CreateTsData(JT1078Package jt1078Package) | public void CreateTsData(JT1078Package jt1078Package) | ||||
{ | { | ||||
string key = jt1078Package.GetKey(); | string key = jt1078Package.GetKey(); | ||||
string hlsFileDirectory = m3U8Option.HlsFileDirectory; | |||||
string m3u8FileName = Path.Combine(hlsFileDirectory, key, m3U8Option.M3U8FileName); | |||||
if (!File.Exists(m3u8FileName)) File.Create(m3u8FileName);//创建m3u8文件 | |||||
//string hlsFileDirectory = m3U8Option.HlsFileDirectory; | |||||
//string m3u8FileName = Path.Combine(hlsFileDirectory, key, m3U8Option.M3U8FileName); | |||||
var buff = TSArrayPool.Rent(jt1078Package.Bodies.Length + 1024); | var buff = TSArrayPool.Rent(jt1078Package.Bodies.Length + 1024); | ||||
TSMessagePackWriter tSMessagePackWriter = new TSMessagePackWriter(buff); | TSMessagePackWriter tSMessagePackWriter = new TSMessagePackWriter(buff); | ||||
try | try | ||||
@@ -50,7 +62,7 @@ namespace JT1078.Hls | |||||
CreateTsFile(curTsFileInfo.FileName,key, tSMessagePackWriter.FlushAndGetArray()); | CreateTsFile(curTsFileInfo.FileName,key, tSMessagePackWriter.FlushAndGetArray()); | ||||
curTsFileInfo.Duration = (jt1078Package.Timestamp - curTsFileInfo.TsFirst1078PackageTimeStamp) / 1000.0; | curTsFileInfo.Duration = (jt1078Package.Timestamp - curTsFileInfo.TsFirst1078PackageTimeStamp) / 1000.0; | ||||
//按设定的时间(默认为10秒)切分ts文件 | //按设定的时间(默认为10秒)切分ts文件 | ||||
if (curTsFileInfo.Duration > m3U8Option.TsFileMaxSecond) | |||||
if (curTsFileInfo.Duration > (m3U8Option.TsFileMaxSecond-1)) | |||||
{ | { | ||||
var tsFileInfoQueue = ManageTsFileInfo(key, curTsFileInfo); | var tsFileInfoQueue = ManageTsFileInfo(key, curTsFileInfo); | ||||
CreateM3U8File(curTsFileInfo, tsFileInfoQueue); | CreateM3U8File(curTsFileInfo, tsFileInfoQueue); | ||||
@@ -63,11 +75,11 @@ namespace JT1078.Hls | |||||
curTsFileInfo.IsCreateTsFile = false; | curTsFileInfo.IsCreateTsFile = false; | ||||
curTsFileInfo.TsFirst1078PackageTimeStamp = jt1078Package.Timestamp; | curTsFileInfo.TsFirst1078PackageTimeStamp = jt1078Package.Timestamp; | ||||
curTsFileInfo.FileName = $"{curTsFileInfo.TsFileSerialNo}.ts"; | curTsFileInfo.FileName = $"{curTsFileInfo.TsFileSerialNo}.ts"; | ||||
var sdt = tSEncoder.CreateSDT(jt1078Package); | |||||
var sdt = tSEncoder.CreateSDT(); | |||||
tSMessagePackWriter.WriteArray(sdt); | tSMessagePackWriter.WriteArray(sdt); | ||||
var pat = tSEncoder.CreatePAT(jt1078Package); | |||||
var pat = tSEncoder.CreatePAT(); | |||||
tSMessagePackWriter.WriteArray(pat); | tSMessagePackWriter.WriteArray(pat); | ||||
var pmt = tSEncoder.CreatePMT(jt1078Package); | |||||
var pmt = tSEncoder.CreatePMT(); | |||||
tSMessagePackWriter.WriteArray(pmt); | tSMessagePackWriter.WriteArray(pmt); | ||||
var pes = tSEncoder.CreatePES(jt1078Package); | var pes = tSEncoder.CreatePES(jt1078Package); | ||||
tSMessagePackWriter.WriteArray(pes); | tSMessagePackWriter.WriteArray(pes); | ||||
@@ -92,7 +104,7 @@ namespace JT1078.Hls | |||||
if (tsFileInfoQueue.Count >= m3U8Option.TsFileCapacity) | if (tsFileInfoQueue.Count >= m3U8Option.TsFileCapacity) | ||||
{ | { | ||||
var deleteTsFileInfo = tsFileInfoQueue.Dequeue(); | var deleteTsFileInfo = tsFileInfoQueue.Dequeue(); | ||||
var deleteTsFileName = Path.Combine(m3U8Option.HlsFileDirectory, deleteTsFileInfo.FileName); | |||||
var deleteTsFileName = Path.Combine(m3U8Option.HlsFileDirectory, key, deleteTsFileInfo.FileName); | |||||
if (File.Exists(deleteTsFileName)) File.Delete(deleteTsFileName); | if (File.Exists(deleteTsFileName)) File.Delete(deleteTsFileName); | ||||
} | } | ||||
tsFileInfoQueue.Enqueue(curTsFileInfo); | tsFileInfoQueue.Enqueue(curTsFileInfo); | ||||
@@ -124,9 +136,10 @@ namespace JT1078.Hls | |||||
{ | { | ||||
var tsFileInfo = tsFileInfoQueue.ElementAt(i); | var tsFileInfo = tsFileInfoQueue.ElementAt(i); | ||||
sb.AppendLine($"#EXTINF:{tsFileInfo.Duration},"); | sb.AppendLine($"#EXTINF:{tsFileInfo.Duration},"); | ||||
sb.AppendLine(tsFileInfo.FileName); | |||||
sb.AppendLine($"{tsFileInfo.FileName}?{m3U8Option.TsPathSimParamName}={tsFileInfo.Sim}&{m3U8Option.TsPathChannelParamName}={tsFileInfo.ChannelNo}"); | |||||
} | } | ||||
using (FileStream fs = new FileStream(m3U8Option.M3U8FileName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) | |||||
string m3u8FileName = Path.Combine(m3U8Option.HlsFileDirectory,$"{curTsFileInfo.Sim}_{curTsFileInfo.ChannelNo}", m3U8Option.M3U8FileName); | |||||
using (FileStream fs = new FileStream(m3u8FileName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) | |||||
{ | { | ||||
var buffer = Encoding.UTF8.GetBytes(sb.ToString()); | var buffer = Encoding.UTF8.GetBytes(sb.ToString()); | ||||
fs.Write(buffer,0, buffer.Length); | fs.Write(buffer,0, buffer.Length); | ||||
@@ -142,9 +155,17 @@ namespace JT1078.Hls | |||||
{ | { | ||||
if (!curTsFileInfoDic.TryGetValue(key, out var curTsFileInfo)) | if (!curTsFileInfoDic.TryGetValue(key, out var curTsFileInfo)) | ||||
{ | { | ||||
curTsFileInfo = new TsFileInfo(); | |||||
curTsFileInfo = new TsFileInfo() | |||||
{ | |||||
Sim = key.Split('_')[0], | |||||
ChannelNo = key.Split('_')[1] | |||||
}; | |||||
curTsFileInfoDic.TryAdd(key, curTsFileInfo); | curTsFileInfoDic.TryAdd(key, curTsFileInfo); | ||||
} | } | ||||
else { | |||||
curTsFileInfo.Sim = key.Split('_')[0]; | |||||
curTsFileInfo.ChannelNo = key.Split('_')[1]; | |||||
} | |||||
return curTsFileInfo; | return curTsFileInfo; | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
@@ -162,17 +183,17 @@ namespace JT1078.Hls | |||||
} | } | ||||
} | } | ||||
///// <summary> | |||||
///// 添加结束标识 | |||||
///// 直播流用不到 | |||||
///// </summary> | |||||
///// <param name="filepath"></param> | |||||
////public void AppendM3U8End() | |||||
////{ | |||||
//// StringBuilder sb = new StringBuilder(); | |||||
//// sb.AppendLine("#EXT-X-ENDLIST"); //m3u8文件结束符 表示视频已经结束 有这个标志同时也说明当前流是一个非直播流 | |||||
//// //#EXT-X-PLAYLIST-TYPE:VOD/Live //VOD表示当前视频流不是一个直播流,而是点播流(也就是视频的全部ts文件已经生成) | |||||
////} | |||||
/// <summary> | |||||
/// 添加结束标识 | |||||
/// 直播流用不到 | |||||
/// </summary> | |||||
public void AppendM3U8End() | |||||
{ | |||||
StringBuilder sb = new StringBuilder(); | |||||
sb.AppendLine("#EXT-X-ENDLIST"); | |||||
//m3u8文件结束符 表示视频已经结束 有这个标志同时也说明当前流是一个非直播流 | |||||
//#EXT-X-PLAYLIST-TYPE:VOD/Live //VOD表示当前视频流不是一个直播流,而是点播流(也就是视频的全部ts文件已经生成) | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// 停止观看直播时清零数据 | /// 停止观看直播时清零数据 | ||||
@@ -193,6 +214,14 @@ namespace JT1078.Hls | |||||
/// </summary> | /// </summary> | ||||
internal class TsFileInfo | internal class TsFileInfo | ||||
{ | { | ||||
/// <summary> | |||||
/// 设备手机号 | |||||
/// </summary> | |||||
public string Sim { get; set; } | |||||
/// <summary> | |||||
/// 设备逻辑通道号 | |||||
/// </summary> | |||||
public string ChannelNo { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// ts文件名 | /// ts文件名 | ||||
/// </summary> | /// </summary> | ||||
@@ -18,12 +18,20 @@ namespace JT1078.Hls.Options | |||||
/// </summary> | /// </summary> | ||||
public int TsFileMaxSecond { get; set; } = 10; | public int TsFileMaxSecond { get; set; } = 10; | ||||
/// <summary> | /// <summary> | ||||
/// ts路径sim参数名称 | |||||
/// </summary> | |||||
public string TsPathSimParamName { get; set; } = "sim"; | |||||
/// <summary> | |||||
/// ts路径通道参数名称 | |||||
/// </summary> | |||||
public string TsPathChannelParamName { get; set; } = "channel"; | |||||
/// <summary> | |||||
/// m3u8文件 | /// m3u8文件 | ||||
/// </summary> | /// </summary> | ||||
public string M3U8FileName { get; set; } = "live.m3u8"; | public string M3U8FileName { get; set; } = "live.m3u8"; | ||||
/// <summary> | /// <summary> | ||||
/// hls文件路径(包括m3u8路径,ts路径) | /// hls文件路径(包括m3u8路径,ts路径) | ||||
/// </summary> | /// </summary> | ||||
public string HlsFileDirectory { get; set; } | |||||
public string HlsFileDirectory { get; set; } = "wwwroot"; | |||||
} | } | ||||
} | } |
@@ -36,12 +36,15 @@ namespace JT1078.Hls | |||||
//todo:音频同步 | //todo:音频同步 | ||||
//private Dictionary<string, byte> AudioCounter = new Dictionary<string, byte>(); | //private Dictionary<string, byte> AudioCounter = new Dictionary<string, byte>(); | ||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
public TSEncoder() | public TSEncoder() | ||||
{ | { | ||||
VideoCounter = new Dictionary<string, byte>(StringComparer.OrdinalIgnoreCase); | VideoCounter = new Dictionary<string, byte>(StringComparer.OrdinalIgnoreCase); | ||||
} | } | ||||
public byte[] CreateSDT(JT1078Package jt1078Package, int minBufferSize = 188) | |||||
public byte[] CreateSDT(int minBufferSize = 188) | |||||
{ | { | ||||
byte[] buffer = TSArrayPool.Rent(minBufferSize); | byte[] buffer = TSArrayPool.Rent(minBufferSize); | ||||
try | try | ||||
@@ -85,7 +88,7 @@ namespace JT1078.Hls | |||||
TSArrayPool.Return(buffer); | TSArrayPool.Return(buffer); | ||||
} | } | ||||
} | } | ||||
public byte[] CreatePAT(JT1078Package jt1078Package, int minBufferSize = 188) | |||||
public byte[] CreatePAT(int minBufferSize = 188) | |||||
{ | { | ||||
byte[] buffer = TSArrayPool.Rent(minBufferSize); | byte[] buffer = TSArrayPool.Rent(minBufferSize); | ||||
try | try | ||||
@@ -111,7 +114,7 @@ namespace JT1078.Hls | |||||
TSArrayPool.Return(buffer); | TSArrayPool.Return(buffer); | ||||
} | } | ||||
} | } | ||||
public byte[] CreatePMT(JT1078Package jt1078Package, int minBufferSize = 188) | |||||
public byte[] CreatePMT(int minBufferSize = 188) | |||||
{ | { | ||||
byte[] buffer = TSArrayPool.Rent(minBufferSize); | byte[] buffer = TSArrayPool.Rent(minBufferSize); | ||||
try | try | ||||
@@ -139,7 +142,7 @@ namespace JT1078.Hls | |||||
TSArrayPool.Return(buffer); | TSArrayPool.Return(buffer); | ||||
} | } | ||||
} | } | ||||
public byte[] CreatePES(JT1078Package jt1078Package, int minBufferSize = 1024) | |||||
public byte[] CreatePES(in JT1078Package jt1078Package, int minBufferSize = 1024) | |||||
{ | { | ||||
//将1078一帧的数据拆分成一小段一小段的PES包 | //将1078一帧的数据拆分成一小段一小段的PES包 | ||||
byte[] buffer = TSArrayPool.Rent(jt1078Package.Bodies.Length + minBufferSize); | byte[] buffer = TSArrayPool.Rent(jt1078Package.Bodies.Length + minBufferSize); | ||||
@@ -1,84 +0,0 @@ | |||||
using BenchmarkDotNet.Attributes; | |||||
using BenchmarkDotNet.Configs; | |||||
using BenchmarkDotNet.Environments; | |||||
using BenchmarkDotNet.Jobs; | |||||
using BenchmarkDotNet.Toolchains.CsProj; | |||||
using JT1078.Flv.MessagePack; | |||||
using JT1078.Protocol; | |||||
using JT1078.Protocol.H264; | |||||
using JT1078.Protocol.MessagePack; | |||||
using JT1078.Protocol.Extensions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
namespace JT1078.Flv.Benchmark | |||||
{ | |||||
[Config(typeof(JT1078FlvEncoderConfig))] | |||||
[MarkdownExporterAttribute.GitHub] | |||||
[MemoryDiagnoser] | |||||
public class JT1078FlvEncoderContext | |||||
{ | |||||
JT1078Package Package; | |||||
List<H264NALU> H264NALUs; | |||||
H264NALU SPSNALu; | |||||
H264Decoder h264Decoder = new H264Decoder(); | |||||
FlvEncoder flvEncoder = new FlvEncoder(); | |||||
[Params(100, 10000, 100000)] | |||||
public int N; | |||||
[GlobalSetup] | |||||
public void Setup() | |||||
{ | |||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "JT1078_1.txt")); | |||||
foreach (var line in lines) | |||||
{ | |||||
var data = line.Split(','); | |||||
var bytes = data[6].ToHexBytes(); | |||||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||||
Package = JT1078Serializer.Merge(package); | |||||
} | |||||
H264NALUs = h264Decoder.ParseNALU(Package); | |||||
SPSNALu = H264NALUs.FirstOrDefault(f => f.NALUHeader.NalUnitType == 7); | |||||
SPSNALu.RawData = h264Decoder.DiscardEmulationPreventionBytes(SPSNALu.RawData); | |||||
} | |||||
[Benchmark(Description = "EXPGolombReader")] | |||||
public void EXPGolombReaderTest() | |||||
{ | |||||
for (var i = 0; i < N; i++) | |||||
{ | |||||
ExpGolombReader h264GolombReader = new ExpGolombReader(SPSNALu.RawData); | |||||
h264GolombReader.ReadSPS(); | |||||
} | |||||
} | |||||
[Benchmark(Description = "H264Decoder")] | |||||
public void H264Decoder() | |||||
{ | |||||
for (var i = 0; i < N; i++) | |||||
{ | |||||
var nalus = h264Decoder.ParseNALU(Package); | |||||
} | |||||
} | |||||
//[Benchmark(Description = "FlvEncoder")] | |||||
//public void FlvEncoder() | |||||
//{ | |||||
// for(var i=0;i< N;i++) | |||||
// { | |||||
// var contents = flvEncoder.CreateFlvFrame(H264NALUs); | |||||
// } | |||||
//} | |||||
} | |||||
public class JT1078FlvEncoderConfig : ManualConfig | |||||
{ | |||||
public JT1078FlvEncoderConfig() | |||||
{ | |||||
AddJob(Job.Default.WithGcServer(false).WithToolchain(CsProjCoreToolchain.NetCoreApp50).WithPlatform(Platform.AnyCpu)); | |||||
} | |||||
} | |||||
} |
@@ -26,7 +26,7 @@ namespace JT1078.Protocol.Test.H264 | |||||
var nalu = nalus[0]; | var nalu = nalus[0]; | ||||
Assert.Equal(0, nalu.NALUHeader.ForbiddenZeroBit); | Assert.Equal(0, nalu.NALUHeader.ForbiddenZeroBit); | ||||
Assert.Equal(3, nalu.NALUHeader.NalRefIdc); | Assert.Equal(3, nalu.NALUHeader.NalRefIdc); | ||||
Assert.Equal(1, nalu.NALUHeader.NalUnitType); | |||||
Assert.Equal(NalUnitType.SLICE, nalu.NALUHeader.NalUnitType); | |||||
} | } | ||||
[Fact] | [Fact] | ||||
@@ -48,7 +48,7 @@ namespace JT1078.Protocol.Test.H264 | |||||
Assert.Equal(4, nalus.Count); | Assert.Equal(4, nalus.Count); | ||||
//SPS -> 7 | //SPS -> 7 | ||||
var spsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 7); | |||||
var spsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.SPS); | |||||
Assert.NotNull(spsNALU); | Assert.NotNull(spsNALU); | ||||
spsNALU.RawData = decoder.DiscardEmulationPreventionBytes(spsNALU.RawData); | spsNALU.RawData = decoder.DiscardEmulationPreventionBytes(spsNALU.RawData); | ||||
//"Z00AFJWoWCWQ" | //"Z00AFJWoWCWQ" | ||||
@@ -63,15 +63,15 @@ namespace JT1078.Protocol.Test.H264 | |||||
Assert.Equal(352, spsInfo.width); | Assert.Equal(352, spsInfo.width); | ||||
//PPS -> 8 | //PPS -> 8 | ||||
var ppsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 8); | |||||
var ppsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.PPS); | |||||
Assert.NotNull(ppsNALU); | Assert.NotNull(ppsNALU); | ||||
//IDR -> 5 关键帧 | //IDR -> 5 关键帧 | ||||
var idrNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 5); | |||||
var idrNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.IDR); | |||||
Assert.NotNull(idrNALU); | Assert.NotNull(idrNALU); | ||||
//SEI -> 6 | //SEI -> 6 | ||||
var seiNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 6); | |||||
var seiNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.SEI); | |||||
Assert.NotNull(seiNALU); | Assert.NotNull(seiNALU); | ||||
} | } | ||||
@@ -95,5 +95,47 @@ namespace JT1078.Protocol.Test.H264 | |||||
} | } | ||||
fileStream.Close(); | fileStream.Close(); | ||||
} | } | ||||
[Fact] | |||||
public void ParseNALUTest4() | |||||
{ | |||||
string file = "jt1078_5"; | |||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", $"{file}.txt")); | |||||
string filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", $"{file}.h264"); | |||||
if (File.Exists(filepath)) | |||||
{ | |||||
File.Delete(filepath); | |||||
} | |||||
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | |||||
foreach (var line in lines) | |||||
{ | |||||
var data = line.Split(','); | |||||
var bytes = data[1].ToHexBytes(); | |||||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||||
fileStream.Write(package.Bodies); | |||||
} | |||||
fileStream.Close(); | |||||
} | |||||
[Fact] | |||||
public void ParseNALUTest5() | |||||
{ | |||||
string file = "jt1078_6"; | |||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", $"{file}.txt")); | |||||
List<H264NALU> nALUs = new List<H264NALU>(); | |||||
H264Decoder decoder = new H264Decoder(); | |||||
foreach (var line in lines) | |||||
{ | |||||
var bytes = line.ToHexBytes(); | |||||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||||
var packageMerge = JT1078Serializer.Merge(package); | |||||
if (packageMerge != null) | |||||
{ | |||||
var nalus = decoder.ParseNALU(packageMerge); | |||||
nALUs = nALUs.Concat(nalus).ToList(); | |||||
} | |||||
} | |||||
var a = nALUs.Count(c => !c.Slice); | |||||
} | |||||
} | } | ||||
} | } |
@@ -14,7 +14,7 @@ namespace JT1078.Flv.Test.H264 | |||||
NALUHeader header = new NALUHeader(0xc0); | NALUHeader header = new NALUHeader(0xc0); | ||||
Assert.Equal(1, header.ForbiddenZeroBit); | Assert.Equal(1, header.ForbiddenZeroBit); | ||||
Assert.Equal(2, header.NalRefIdc); | Assert.Equal(2, header.NalRefIdc); | ||||
Assert.Equal(0, header.NalUnitType); | |||||
Assert.Equal(NalUnitType.None, header.NalUnitType); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -29,5 +29,11 @@ | |||||
<None Include="..\..\doc\video\jt1078_2.txt" Link="H264\jt1078_2.txt"> | <None Include="..\..\doc\video\jt1078_2.txt" Link="H264\jt1078_2.txt"> | ||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
</None> | </None> | ||||
<None Include="..\..\doc\video\jt1078_5.txt" Link="H264\jt1078_5.txt"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Include="..\..\doc\video\jt1078_6.txt" Link="H264\jt1078_6.txt"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
</ItemGroup> | </ItemGroup> | ||||
</Project> | </Project> |
@@ -336,13 +336,13 @@ namespace JT1078.Protocol.Test | |||||
[Fact] | [Fact] | ||||
public void MergeTest() | public void MergeTest() | ||||
{ | { | ||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "JT1078.txt")); | |||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "h264","JT1078_1.txt")); | |||||
JT1078Package merge=null; | JT1078Package merge=null; | ||||
int mergeBodyLength=0; | int mergeBodyLength=0; | ||||
foreach (var line in lines) | foreach (var line in lines) | ||||
{ | { | ||||
var data = line.Split(','); | var data = line.Split(','); | ||||
var bytes = data[5].ToHexBytes(); | |||||
var bytes = data[6].ToHexBytes(); | |||||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | JT1078Package package = JT1078Serializer.Deserialize(bytes); | ||||
mergeBodyLength += package.DataBodyLength; | mergeBodyLength += package.DataBodyLength; | ||||
merge = JT1078Serializer.Merge(package); | merge = JT1078Serializer.Merge(package); | ||||
@@ -18,7 +18,7 @@ namespace JT1078.Protocol.H264 | |||||
public List<H264NALU> ParseNALU(JT1078Package package, string key = null) | public List<H264NALU> ParseNALU(JT1078Package package, string key = null) | ||||
{ | { | ||||
List<H264NALU> h264NALUs = new List<H264NALU>(); | List<H264NALU> h264NALUs = new List<H264NALU>(); | ||||
int i=0,state=0; | |||||
int i=0,state=0,laststate=0; | |||||
int? lastIndex=null; | int? lastIndex=null; | ||||
int length = package.Bodies.Length; | int length = package.Bodies.Length; | ||||
byte value; | byte value; | ||||
@@ -45,9 +45,10 @@ namespace JT1078.Protocol.H264 | |||||
if (lastIndex.HasValue) | if (lastIndex.HasValue) | ||||
{ | { | ||||
var tmp = buffer.Slice(lastIndex.Value, i - state - 1 - lastIndex.Value); | var tmp = buffer.Slice(lastIndex.Value, i - state - 1 - lastIndex.Value); | ||||
h264NALUs.Add(Create(package, tmp, state)); | |||||
h264NALUs.Add(Create(package, tmp, state+1)); | |||||
} | } | ||||
lastIndex = i; | lastIndex = i; | ||||
laststate = state+1; | |||||
state = 0; | state = 0; | ||||
} | } | ||||
else | else | ||||
@@ -61,11 +62,69 @@ namespace JT1078.Protocol.H264 | |||||
} | } | ||||
if (lastIndex.HasValue) | if (lastIndex.HasValue) | ||||
{ | { | ||||
h264NALUs.Add(Create(package, buffer.Slice(lastIndex.Value), 4)); | |||||
h264NALUs.Add(Create(package, buffer.Slice(lastIndex.Value), laststate)); | |||||
} | } | ||||
return h264NALUs; | return h264NALUs; | ||||
} | } | ||||
/// <summary> | |||||
/// | |||||
/// <see cref="https://github.com/samirkumardas/jmuxer/blob/master/src/parsers/h264.js"/> | |||||
/// </summary> | |||||
/// <param name="package"></param> | |||||
/// <param name="h264NALUs"></param> | |||||
/// <param name="key"></param> | |||||
/// <returns></returns> | |||||
public void ParseNALU(JT1078Package package, List<H264NALU> h264NALUs,string key = null) | |||||
{ | |||||
int i = 0, state = 0, laststate = 0; | |||||
int? lastIndex = null; | |||||
int length = package.Bodies.Length; | |||||
byte value; | |||||
ReadOnlySpan<byte> buffer = package.Bodies; | |||||
while (i < length) | |||||
{ | |||||
value = buffer[i++]; | |||||
switch (state) | |||||
{ | |||||
case 0: | |||||
if (value == 0) state = 1; | |||||
break; | |||||
case 1: | |||||
state = value == 0 ? 2 : 0; | |||||
break; | |||||
case 2: | |||||
case 3: | |||||
if (value == 0) | |||||
{ | |||||
state = 3; | |||||
} | |||||
else if (value == 1 && i < length) | |||||
{ | |||||
if (lastIndex.HasValue) | |||||
{ | |||||
var tmp = buffer.Slice(lastIndex.Value, i - state - 1 - lastIndex.Value); | |||||
h264NALUs.Add(Create(package, tmp, lastIndex.Value)); | |||||
} | |||||
lastIndex = i; | |||||
laststate = state + 1; | |||||
state = 0; | |||||
} | |||||
else | |||||
{ | |||||
state = 0; | |||||
} | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
if (lastIndex.HasValue) | |||||
{ | |||||
h264NALUs.Add(Create(package, buffer.Slice(lastIndex.Value), laststate)); | |||||
} | |||||
} | |||||
private H264NALU Create(JT1078Package package,ReadOnlySpan<byte> nalu, int startCodePrefix) | private H264NALU Create(JT1078Package package,ReadOnlySpan<byte> nalu, int startCodePrefix) | ||||
{ | { | ||||
H264NALU nALU = new H264NALU(); | H264NALU nALU = new H264NALU(); | ||||
@@ -75,6 +134,7 @@ namespace JT1078.Protocol.H264 | |||||
nALU.LastFrameInterval = package.LastFrameInterval; | nALU.LastFrameInterval = package.LastFrameInterval; | ||||
nALU.LastIFrameInterval = package.LastIFrameInterval; | nALU.LastIFrameInterval = package.LastIFrameInterval; | ||||
nALU.Timestamp = package.Timestamp; | nALU.Timestamp = package.Timestamp; | ||||
nALU.Slice = (nalu[1] & 0x80)== 0x80; | |||||
nALU.RawData = nalu.ToArray(); | nALU.RawData = nalu.ToArray(); | ||||
if (startCodePrefix == 3) | if (startCodePrefix == 3) | ||||
{ | { | ||||
@@ -40,6 +40,12 @@ namespace JT1078.Protocol.H264 | |||||
/// 当数据类型为01000时,则没有该字段 | /// 当数据类型为01000时,则没有该字段 | ||||
/// </summary> | /// </summary> | ||||
public ulong Timestamp { get; set; } | public ulong Timestamp { get; set; } | ||||
/// <summary> | |||||
/// 是否切片 0x80 | |||||
/// H264 NALU slice first_mb_in_slice | |||||
/// </summary> | |||||
public bool Slice { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// 数据体 | /// 数据体 | ||||
/// </summary> | /// </summary> | ||||
@@ -10,16 +10,33 @@ namespace JT1078.Protocol.H264 | |||||
{ | { | ||||
ForbiddenZeroBit = (value & 0x80) >> 7; | ForbiddenZeroBit = (value & 0x80) >> 7; | ||||
NalRefIdc = (value & 0x60) >> 5; | NalRefIdc = (value & 0x60) >> 5; | ||||
NalUnitType = value & 0x1f; | |||||
NalUnitType = (NalUnitType)(value & 0x1f); | |||||
} | } | ||||
public NALUHeader(ReadOnlySpan<byte> value) | public NALUHeader(ReadOnlySpan<byte> value) | ||||
{ | { | ||||
ForbiddenZeroBit = (value[0] & 0x80) >> 7; | ForbiddenZeroBit = (value[0] & 0x80) >> 7; | ||||
NalRefIdc = (value[0] & 0x60) >> 5; | NalRefIdc = (value[0] & 0x60) >> 5; | ||||
NalUnitType = value[0] & 0x1f; | |||||
NalUnitType = (NalUnitType)(value[0] & 0x1f); | |||||
} | } | ||||
public int ForbiddenZeroBit { get; set; } | public int ForbiddenZeroBit { get; set; } | ||||
public int NalRefIdc { get; set; } | public int NalRefIdc { get; set; } | ||||
public int NalUnitType { get; set; } | |||||
public NalUnitType NalUnitType { get; set; } | |||||
} | |||||
public enum NalUnitType : int | |||||
{ | |||||
None=0, | |||||
SLICE = 1, | |||||
DPA = 2, | |||||
DPB = 3, | |||||
DPC = 4, | |||||
IDR = 5, | |||||
SEI = 6, | |||||
SPS = 7, | |||||
PPS = 8, | |||||
AUD = 9, | |||||
EOSEQ = 10, | |||||
EOSTREAM = 11, | |||||
FILL = 12, | |||||
} | } | ||||
} | } |
@@ -14,7 +14,7 @@ | |||||
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | <licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | ||||
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | <license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | ||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | <GeneratePackageOnBuild>false</GeneratePackageOnBuild> | ||||
<Version>1.1.0</Version> | |||||
<Version>1.1.1-preview1</Version> | |||||
<SignAssembly>false</SignAssembly> | <SignAssembly>false</SignAssembly> | ||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | ||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||||
@@ -122,6 +122,16 @@ | |||||
<param name="key"></param> | <param name="key"></param> | ||||
<returns></returns> | <returns></returns> | ||||
</member> | </member> | ||||
<member name="M:JT1078.Protocol.H264.H264Decoder.ParseNALU(JT1078.Protocol.JT1078Package,System.Collections.Generic.List{JT1078.Protocol.H264.H264NALU},System.String)"> | |||||
<summary> | |||||
<see cref="!:https://github.com/samirkumardas/jmuxer/blob/master/src/parsers/h264.js"/> | |||||
</summary> | |||||
<param name="package"></param> | |||||
<param name="h264NALUs"></param> | |||||
<param name="key"></param> | |||||
<returns></returns> | |||||
</member> | |||||
<member name="M:JT1078.Protocol.H264.H264Decoder.DiscardEmulationPreventionBytes(System.ReadOnlySpan{System.Byte})"> | <member name="M:JT1078.Protocol.H264.H264Decoder.DiscardEmulationPreventionBytes(System.ReadOnlySpan{System.Byte})"> | ||||
<summary> | <summary> | ||||
Expunge any "Emulation Prevention" bytes from a "Raw Byte Sequence Payload" | Expunge any "Emulation Prevention" bytes from a "Raw Byte Sequence Payload" | ||||
@@ -166,6 +176,12 @@ | |||||
当数据类型为01000时,则没有该字段 | 当数据类型为01000时,则没有该字段 | ||||
</summary> | </summary> | ||||
</member> | </member> | ||||
<member name="P:JT1078.Protocol.H264.H264NALU.Slice"> | |||||
<summary> | |||||
是否切片 0x80 | |||||
H264 NALU slice first_mb_in_slice | |||||
</summary> | |||||
</member> | |||||
<member name="P:JT1078.Protocol.H264.H264NALU.RawData"> | <member name="P:JT1078.Protocol.H264.H264NALU.RawData"> | ||||
<summary> | <summary> | ||||
数据体 | 数据体 | ||||
@@ -0,0 +1,48 @@ | |||||
using JT1078.SignalR.Test.Services; | |||||
using Microsoft.AspNetCore.SignalR; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Security.Claims; | |||||
using System.Text.Json; | |||||
using System.Threading.Tasks; | |||||
namespace JT1078.SignalR.Test.Hubs | |||||
{ | |||||
public class FMp4Hub : Hub | |||||
{ | |||||
private readonly ILogger logger; | |||||
private readonly WsSession wsSession; | |||||
public FMp4Hub( | |||||
WsSession wsSession, | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
this.wsSession = wsSession; | |||||
logger = loggerFactory.CreateLogger<FMp4Hub>(); | |||||
} | |||||
public override Task OnConnectedAsync() | |||||
{ | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug($"链接上:{Context.ConnectionId}"); | |||||
} | |||||
wsSession.TryAdd(Context.ConnectionId); | |||||
return base.OnConnectedAsync(); | |||||
} | |||||
public override Task OnDisconnectedAsync(Exception exception) | |||||
{ | |||||
if (logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
logger.LogDebug($"断开链接:{Context.ConnectionId}"); | |||||
} | |||||
wsSession.TryRemove(Context.ConnectionId); | |||||
return base.OnDisconnectedAsync(exception); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,30 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||||
<PropertyGroup> | |||||
<TargetFramework>net5.0</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT1078.FMp4\JT1078.FMp4.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Include="..\..\doc\video\jt1078_3.txt" Link="H264\jt1078_3.txt"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Include="..\..\doc\video\jt1078_1.txt" Link="H264\jt1078_1.txt"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Include="..\..\doc\video\jt1078_2.txt" Link="H264\jt1078_2.txt"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Include="..\..\doc\video\jt1078_1_fragmented.mp4" Link="H264\jt1078_1_fragmented.mp4"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Include="..\..\doc\video\jt1078_5.txt" Link="H264\jt1078_5.txt"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
<None Include="..\..\doc\video\jt1078_6.txt" Link="H264\jt1078_6.txt"> | |||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||||
</None> | |||||
</ItemGroup> | |||||
</Project> |
@@ -0,0 +1,26 @@ | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace JT1078.SignalR.Test | |||||
{ | |||||
public class Program | |||||
{ | |||||
public static void Main(string[] args) | |||||
{ | |||||
CreateHostBuilder(args).Build().Run(); | |||||
} | |||||
public static IHostBuilder CreateHostBuilder(string[] args) => | |||||
Host.CreateDefaultBuilder(args) | |||||
.ConfigureWebHostDefaults(webBuilder => | |||||
{ | |||||
webBuilder.UseStartup<Startup>(); | |||||
}); | |||||
} | |||||
} |
@@ -0,0 +1,174 @@ | |||||
using Microsoft.AspNetCore.SignalR; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Text; | |||||
using System.Text.Json; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.Extensions.Options; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Http; | |||||
using JT1078.SignalR.Test.Hubs; | |||||
using JT1078.FMp4; | |||||
using JT1078.Protocol; | |||||
using System.IO; | |||||
using JT1078.Protocol.Extensions; | |||||
using JT1078.Protocol.H264; | |||||
using System.Net.WebSockets; | |||||
namespace JT1078.SignalR.Test.Services | |||||
{ | |||||
public class ToWebSocketService: BackgroundService | |||||
{ | |||||
private readonly ILogger<ToWebSocketService> logger; | |||||
private readonly IHubContext<FMp4Hub> _hubContext; | |||||
private readonly FMp4Encoder fMp4Encoder; | |||||
private readonly WsSession wsSession; | |||||
private readonly H264Decoder h264Decoder; | |||||
public ToWebSocketService( | |||||
H264Decoder h264Decoder, | |||||
WsSession wsSession, | |||||
FMp4Encoder fMp4Encoder, | |||||
ILoggerFactory loggerFactory, | |||||
IHubContext<FMp4Hub> hubContext) | |||||
{ | |||||
this.h264Decoder = h264Decoder; | |||||
logger = loggerFactory.CreateLogger<ToWebSocketService>(); | |||||
this.fMp4Encoder = fMp4Encoder; | |||||
_hubContext = hubContext; | |||||
this.wsSession = wsSession; | |||||
} | |||||
public List<byte[]> q = new List<byte[]>(); | |||||
public void a() | |||||
{ | |||||
List<JT1078Package> packages = new List<JT1078Package>(); | |||||
//var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_3.txt")); | |||||
//var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_5.txt")); | |||||
var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_6.txt")); | |||||
int mergeBodyLength = 0; | |||||
foreach (var line in lines) | |||||
{ | |||||
//var data = line.Split(','); | |||||
//jt1078_5 | |||||
//var bytes = data[1].ToHexBytes(); | |||||
//jt1078_3 | |||||
//var bytes = data[6].ToHexBytes(); | |||||
//jt1078_6 | |||||
var bytes = line.ToHexBytes(); | |||||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||||
mergeBodyLength += package.DataBodyLength; | |||||
var packageMerge = JT1078Serializer.Merge(package); | |||||
if (packageMerge != null) | |||||
{ | |||||
packages.Add(packageMerge); | |||||
} | |||||
} | |||||
List<byte[]> first = new List<byte[]>(); | |||||
//var styp = fMp4Encoder.EncoderStypBox(); | |||||
//first.Add(styp); | |||||
//q.Enqueue(styp); | |||||
var ftyp = fMp4Encoder.FtypBox(); | |||||
//q.Enqueue(ftyp); | |||||
first.Add(ftyp); | |||||
var package1 = packages[0]; | |||||
var nalus1 = h264Decoder.ParseNALU(package1); | |||||
var moov = fMp4Encoder.VideoMoovBox( | |||||
nalus1.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.SPS), | |||||
nalus1.FirstOrDefault(f => f.NALUHeader.NalUnitType == NalUnitType.PPS)); | |||||
//q.Enqueue(moov); | |||||
first.Add(moov); | |||||
q.Add(first.SelectMany(s=>s).ToArray()); | |||||
List<NalUnitType> filter = new List<NalUnitType>() { NalUnitType.SEI,NalUnitType.SPS,NalUnitType.PPS,NalUnitType.AUD}; | |||||
List<H264NALU> nalus = new List<H264NALU>(); | |||||
foreach (var package in packages) | |||||
{ | |||||
List<H264NALU> h264NALUs = h264Decoder.ParseNALU(package); | |||||
//if(package.Label3.DataType== Protocol.Enums.JT1078DataType.视频I帧) | |||||
//{ | |||||
// if (nalus.Count > 0) | |||||
// { | |||||
// var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
// q.Add(fMp4Encoder.StypBox().Concat(otherBuffer).ToArray()); | |||||
// nalus.Clear(); | |||||
// } | |||||
// else | |||||
// { | |||||
// nalus = nalus.Concat(h264NALUs).ToList(); | |||||
// } | |||||
//} | |||||
//else | |||||
//{ | |||||
// nalus = nalus.Concat(h264NALUs).ToList(); | |||||
//} | |||||
foreach (var nalu in h264NALUs) | |||||
{ | |||||
if (nalu.Slice) | |||||
{ | |||||
//H264 NALU slice first_mb_in_slice | |||||
nalus.Add(nalu); | |||||
} | |||||
else | |||||
{ | |||||
if (nalus.Count > 0) | |||||
{ | |||||
q.Add(fMp4Encoder.StypBox()); | |||||
var otherBuffer = fMp4Encoder.OtherVideoBox(nalus); | |||||
q.Add(otherBuffer); | |||||
nalus.Clear(); | |||||
} | |||||
nalus.Add(nalu); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
public Dictionary<string,int> flag = new Dictionary<string, int>(); | |||||
protected async override Task ExecuteAsync(CancellationToken stoppingToken) | |||||
{ | |||||
a(); | |||||
while (!stoppingToken.IsCancellationRequested) | |||||
{ | |||||
try | |||||
{ | |||||
foreach(var session in wsSession.GetAll()) | |||||
{ | |||||
if (flag.ContainsKey(session)) | |||||
{ | |||||
var len = flag[session]; | |||||
if (q.Count <= len) | |||||
{ | |||||
break; | |||||
} | |||||
await _hubContext.Clients.Client(session).SendAsync("video", q[len], stoppingToken); | |||||
len++; | |||||
flag[session] = len; | |||||
} | |||||
else | |||||
{ | |||||
await _hubContext.Clients.Client(session).SendAsync("video", q[0], stoppingToken); | |||||
flag.Add(session, 1); | |||||
} | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogError(ex,""); | |||||
} | |||||
await Task.Delay(1000); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,38 @@ | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace JT1078.SignalR.Test.Services | |||||
{ | |||||
public class WsSession | |||||
{ | |||||
private ConcurrentDictionary<string, string> sessions; | |||||
public WsSession() | |||||
{ | |||||
sessions = new ConcurrentDictionary<string, string>(); | |||||
} | |||||
public void TryAdd(string connectionId) | |||||
{ | |||||
sessions.TryAdd(connectionId, connectionId); | |||||
} | |||||
public int GetCount() | |||||
{ | |||||
return sessions.Count; | |||||
} | |||||
public void TryRemove(string connectionId) | |||||
{ | |||||
sessions.TryRemove(connectionId,out _); | |||||
} | |||||
public List<string> GetAll() | |||||
{ | |||||
return sessions.Keys.ToList(); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,66 @@ | |||||
using JT1078.FMp4; | |||||
using JT1078.Protocol.H264; | |||||
using JT1078.SignalR.Test.Hubs; | |||||
using JT1078.SignalR.Test.Services; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace JT1078.SignalR.Test | |||||
{ | |||||
public class Startup | |||||
{ | |||||
public Startup(IConfiguration configuration) | |||||
{ | |||||
Configuration = configuration; | |||||
} | |||||
public IConfiguration Configuration { get; } | |||||
// This method gets called by the runtime. Use this method to add services to the container. | |||||
public void ConfigureServices(IServiceCollection services) | |||||
{ | |||||
services.AddControllers(); | |||||
services.AddSignalR(); | |||||
services.AddSingleton<FMp4Encoder>(); | |||||
services.AddSingleton<H264Decoder>(); | |||||
services.AddSingleton<WsSession>(); | |||||
services.AddHostedService<ToWebSocketService>(); | |||||
services.AddCors(options => options.AddPolicy("CorsPolicy", builder => | |||||
{ | |||||
builder.AllowAnyMethod() | |||||
.AllowAnyHeader() | |||||
.AllowCredentials() | |||||
.SetIsOriginAllowed(o => true); | |||||
})); | |||||
} | |||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | |||||
{ | |||||
if (env.IsDevelopment()) | |||||
{ | |||||
app.UseDeveloperExceptionPage(); | |||||
} | |||||
app.UseRouting(); | |||||
app.UseCors("CorsPolicy"); | |||||
app.UseAuthorization(); | |||||
app.UseEndpoints(endpoints => | |||||
{ | |||||
endpoints.MapHub<FMp4Hub>("/FMp4Hub"); | |||||
}); | |||||
app.UseEndpoints(endpoints => | |||||
{ | |||||
endpoints.MapControllers(); | |||||
}); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
{ | |||||
"Logging": { | |||||
"LogLevel": { | |||||
"Default": "Information", | |||||
"Microsoft": "Warning", | |||||
"Microsoft.Hosting.Lifetime": "Information" | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,10 @@ | |||||
{ | |||||
"Logging": { | |||||
"LogLevel": { | |||||
"Default": "Information", | |||||
"Microsoft": "Warning", | |||||
"Microsoft.Hosting.Lifetime": "Information" | |||||
} | |||||
}, | |||||
"AllowedHosts": "*" | |||||
} |
@@ -3,16 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 | |||||
# Visual Studio Version 16 | # Visual Studio Version 16 | ||||
VisualStudioVersion = 16.0.29020.237 | VisualStudioVersion = 16.0.29020.237 | ||||
MinimumVisualStudioVersion = 10.0.40219.1 | MinimumVisualStudioVersion = 10.0.40219.1 | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Protocol.Extensions.JT1078", "JT808.Protocol.Extensions.JT1078\JT808.Protocol.Extensions.JT1078.csproj", "{F060F379-C8E4-4CA6-A54C-938A9780ACD2}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT809.Protocol.Extensions.JT1078", "JT809.Protocol.Extensions.JT1078\JT809.Protocol.Extensions.JT1078.csproj", "{2F987285-EB7A-4934-909E-50E42A2D1140}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Protocol", "JT1078.Protocol\JT1078.Protocol.csproj", "{60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Protocol", "JT1078.Protocol\JT1078.Protocol.csproj", "{60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}" | ||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Protocol.Extensions.JT1078.Test", "JT808.Protocol.Extensions.JT1078.Test\JT808.Protocol.Extensions.JT1078.Test.csproj", "{C6A43FDF-C609-40BB-B598-87F0BF7B944B}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT809.Protocol.Extensions.JT1078.Test", "JT809.Protocol.Extensions.JT1078.Test\JT809.Protocol.Extensions.JT1078.Test.csproj", "{E9FF2716-EF30-4180-879B-E8AB979ACFF3}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Protocol.Test", "JT1078.Protocol.Test\JT1078.Protocol.Test.csproj", "{9ADD82F9-E0B2-4263-8573-151F673BB33F}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Protocol.Test", "JT1078.Protocol.Test\JT1078.Protocol.Test.csproj", "{9ADD82F9-E0B2-4263-8573-151F673BB33F}" | ||||
EndProject | EndProject | ||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0655AF84-E578-409F-AB0E-B47E0D2F6814}" | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0655AF84-E578-409F-AB0E-B47E0D2F6814}" | ||||
@@ -40,6 +32,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", | |||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.AV.Benchmark", "JT1078.AV.Benchmark\JT1078.AV.Benchmark.csproj", "{93D6C094-5A3A-4DFA-B52B-605FDFFB6094}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.AV.Benchmark", "JT1078.AV.Benchmark\JT1078.AV.Benchmark.csproj", "{93D6C094-5A3A-4DFA-B52B-605FDFFB6094}" | ||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.SignalR.Test", "JT1078.SignalR.Test\JT1078.SignalR.Test.csproj", "{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}" | |||||
EndProject | |||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
Debug|Any CPU = Debug|Any CPU | Debug|Any CPU = Debug|Any CPU | ||||
@@ -50,30 +44,6 @@ Global | |||||
Release|x86 = Release|x86 | Release|x86 = Release|x86 | ||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Debug|x64.ActiveCfg = Debug|Any CPU | |||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Debug|x64.Build.0 = Debug|Any CPU | |||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Debug|x86.ActiveCfg = Debug|Any CPU | |||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Debug|x86.Build.0 = Debug|Any CPU | |||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Release|x64.ActiveCfg = Release|Any CPU | |||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Release|x64.Build.0 = Release|Any CPU | |||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Release|x86.ActiveCfg = Release|Any CPU | |||||
{F060F379-C8E4-4CA6-A54C-938A9780ACD2}.Release|x86.Build.0 = Release|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Debug|x64.ActiveCfg = Debug|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Debug|x64.Build.0 = Debug|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Debug|x86.ActiveCfg = Debug|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Debug|x86.Build.0 = Debug|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Release|x64.ActiveCfg = Release|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Release|x64.Build.0 = Release|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Release|x86.ActiveCfg = Release|Any CPU | |||||
{2F987285-EB7A-4934-909E-50E42A2D1140}.Release|x86.Build.0 = Release|Any CPU | |||||
{60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | {60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
{60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Debug|Any CPU.Build.0 = Debug|Any CPU | {60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Debug|x64.ActiveCfg = Debug|Any CPU | {60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Debug|x64.ActiveCfg = Debug|Any CPU | ||||
@@ -86,30 +56,6 @@ Global | |||||
{60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Release|x64.Build.0 = Release|Any CPU | {60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Release|x64.Build.0 = Release|Any CPU | ||||
{60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Release|x86.ActiveCfg = Release|Any CPU | {60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Release|x86.ActiveCfg = Release|Any CPU | ||||
{60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Release|x86.Build.0 = Release|Any CPU | {60CAC24B-7317-48BF-9DBF-7F3ECA3689A4}.Release|x86.Build.0 = Release|Any CPU | ||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Debug|x64.ActiveCfg = Debug|Any CPU | |||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Debug|x64.Build.0 = Debug|Any CPU | |||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Debug|x86.ActiveCfg = Debug|Any CPU | |||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Debug|x86.Build.0 = Debug|Any CPU | |||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Release|x64.ActiveCfg = Release|Any CPU | |||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Release|x64.Build.0 = Release|Any CPU | |||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Release|x86.ActiveCfg = Release|Any CPU | |||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B}.Release|x86.Build.0 = Release|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Debug|x64.ActiveCfg = Debug|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Debug|x64.Build.0 = Debug|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Debug|x86.ActiveCfg = Debug|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Debug|x86.Build.0 = Debug|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Release|x64.ActiveCfg = Release|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Release|x64.Build.0 = Release|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Release|x86.ActiveCfg = Release|Any CPU | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3}.Release|x86.Build.0 = Release|Any CPU | |||||
{9ADD82F9-E0B2-4263-8573-151F673BB33F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | {9ADD82F9-E0B2-4263-8573-151F673BB33F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
{9ADD82F9-E0B2-4263-8573-151F673BB33F}.Debug|Any CPU.Build.0 = Debug|Any CPU | {9ADD82F9-E0B2-4263-8573-151F673BB33F}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{9ADD82F9-E0B2-4263-8573-151F673BB33F}.Debug|x64.ActiveCfg = Debug|Any CPU | {9ADD82F9-E0B2-4263-8573-151F673BB33F}.Debug|x64.ActiveCfg = Debug|Any CPU | ||||
@@ -218,19 +164,30 @@ Global | |||||
{93D6C094-5A3A-4DFA-B52B-605FDFFB6094}.Release|x64.Build.0 = Release|Any CPU | {93D6C094-5A3A-4DFA-B52B-605FDFFB6094}.Release|x64.Build.0 = Release|Any CPU | ||||
{93D6C094-5A3A-4DFA-B52B-605FDFFB6094}.Release|x86.ActiveCfg = Release|Any CPU | {93D6C094-5A3A-4DFA-B52B-605FDFFB6094}.Release|x86.ActiveCfg = Release|Any CPU | ||||
{93D6C094-5A3A-4DFA-B52B-605FDFFB6094}.Release|x86.Build.0 = Release|Any CPU | {93D6C094-5A3A-4DFA-B52B-605FDFFB6094}.Release|x86.Build.0 = Release|Any CPU | ||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Debug|x64.ActiveCfg = Debug|Any CPU | |||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Debug|x64.Build.0 = Debug|Any CPU | |||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Debug|x86.ActiveCfg = Debug|Any CPU | |||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Debug|x86.Build.0 = Debug|Any CPU | |||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Release|x64.ActiveCfg = Release|Any CPU | |||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Release|x64.Build.0 = Release|Any CPU | |||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Release|x86.ActiveCfg = Release|Any CPU | |||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}.Release|x86.Build.0 = Release|Any CPU | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(NestedProjects) = preSolution | GlobalSection(NestedProjects) = preSolution | ||||
{C6A43FDF-C609-40BB-B598-87F0BF7B944B} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | |||||
{9ADD82F9-E0B2-4263-8573-151F673BB33F} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | {9ADD82F9-E0B2-4263-8573-151F673BB33F} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | ||||
{77402142-54E5-4E64-8F9E-BCAAC2CD0E8D} = {807ADB1F-FED4-4A56-82D2-F08F1FB7C886} | {77402142-54E5-4E64-8F9E-BCAAC2CD0E8D} = {807ADB1F-FED4-4A56-82D2-F08F1FB7C886} | ||||
{D13FE092-1D11-4545-A322-9F06BCDAC0FD} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | {D13FE092-1D11-4545-A322-9F06BCDAC0FD} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | ||||
{5564C20B-BFF4-4A2A-BDF2-C7427E93E993} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | {5564C20B-BFF4-4A2A-BDF2-C7427E93E993} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | ||||
{56E76D56-4CCC-401F-B25D-9AB41D58A10A} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | {56E76D56-4CCC-401F-B25D-9AB41D58A10A} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | ||||
{93D6C094-5A3A-4DFA-B52B-605FDFFB6094} = {807ADB1F-FED4-4A56-82D2-F08F1FB7C886} | {93D6C094-5A3A-4DFA-B52B-605FDFFB6094} = {807ADB1F-FED4-4A56-82D2-F08F1FB7C886} | ||||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(ExtensibilityGlobals) = postSolution | GlobalSection(ExtensibilityGlobals) = postSolution | ||||
SolutionGuid = {FAE1656D-226F-4B4B-8C33-615D7E632B26} | SolutionGuid = {FAE1656D-226F-4B4B-8C33-615D7E632B26} | ||||
@@ -1,22 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFramework>net5.0</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" /> | |||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" /> | |||||
<PackageReference Include="xunit" Version="2.4.1" /> | |||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | |||||
<PrivateAssets>all</PrivateAssets> | |||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | |||||
</PackageReference> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT808.Protocol.Extensions.JT1078\JT808.Protocol.Extensions.JT1078.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,91 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using JT808.Protocol.MessageBody; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Reflection; | |||||
using Xunit; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808LocationAttach | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808LocationAttach() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1.AddJT808Configure(new DefaultGlobalConfig()) | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<DefaultGlobalConfig>(); | |||||
JT808Serializer = new JT808Serializer(defaultConfig); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x0200 jT808UploadLocationRequest = new JT808_0x0200 | |||||
{ | |||||
AlarmFlag = 1, | |||||
Altitude = 40, | |||||
GPSTime = DateTime.Parse("2018-07-15 10:10:10"), | |||||
Lat = 12222222, | |||||
Lng = 132444444, | |||||
Speed = 60, | |||||
Direction = 0, | |||||
StatusFlag = 2, | |||||
JT808LocationAttachData = new Dictionary<byte, JT808_0x0200_BodyBase>() | |||||
}; | |||||
jT808UploadLocationRequest.JT808LocationAttachData.Add(0x14, new JT808_0x0200_0x14 | |||||
{ | |||||
VideoRelateAlarm = 100 | |||||
}); | |||||
jT808UploadLocationRequest.JT808LocationAttachData.Add(0x15, new JT808_0x0200_0x15 | |||||
{ | |||||
VideoSignalLoseAlarmStatus = 100 | |||||
}); | |||||
jT808UploadLocationRequest.JT808LocationAttachData.Add(0x16, new JT808_0x0200_0x16 | |||||
{ | |||||
VideoSignalOcclusionAlarmStatus = 100 | |||||
}); | |||||
jT808UploadLocationRequest.JT808LocationAttachData.Add(0x17, new JT808_0x0200_0x17 | |||||
{ | |||||
StorageFaultAlarmStatus = 100 | |||||
}); | |||||
jT808UploadLocationRequest.JT808LocationAttachData.Add(0x18, new JT808_0x0200_0x18 | |||||
{ | |||||
AbnormalDrivingBehaviorAlarmType = 100, | |||||
FatigueLevel = 88 | |||||
}); | |||||
var hex = JT808Serializer.Serialize(jT808UploadLocationRequest).ToHexString(); | |||||
Assert.Equal("000000010000000200BA7F0E07E4F11C0028003C0000180715101010140400000064150400000064160400000064170200641803006458", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
byte[] bodys = "000000010000000200BA7F0E07E4F11C0028003C0000180715101010140400000064150400000064160400000064170200641803006458".ToHexBytes(); | |||||
JT808_0x0200 jT808UploadLocationRequest = JT808Serializer.Deserialize<JT808_0x0200>(bodys); | |||||
Assert.Equal(1u, jT808UploadLocationRequest.AlarmFlag); | |||||
Assert.Equal(DateTime.Parse("2018-07-15 10:10:10"), jT808UploadLocationRequest.GPSTime); | |||||
Assert.Equal(12222222, jT808UploadLocationRequest.Lat); | |||||
Assert.Equal(132444444, jT808UploadLocationRequest.Lng); | |||||
Assert.Equal(60, jT808UploadLocationRequest.Speed); | |||||
Assert.Equal(2u, jT808UploadLocationRequest.StatusFlag); | |||||
Assert.Equal(100u, ((JT808_0x0200_0x14)jT808UploadLocationRequest.JT808LocationAttachData[0x14]).VideoRelateAlarm); | |||||
Assert.Equal(100u, ((JT808_0x0200_0x15)jT808UploadLocationRequest.JT808LocationAttachData[0x15]).VideoSignalLoseAlarmStatus); | |||||
Assert.Equal(100u, ((JT808_0x0200_0x16)jT808UploadLocationRequest.JT808LocationAttachData[0x16]).VideoSignalOcclusionAlarmStatus); | |||||
Assert.Equal(100u, ((JT808_0x0200_0x17)jT808UploadLocationRequest.JT808LocationAttachData[0x17]).StorageFaultAlarmStatus); | |||||
Assert.Equal(100u, ((JT808_0x0200_0x18)jT808UploadLocationRequest.JT808LocationAttachData[0x18]).AbnormalDrivingBehaviorAlarmType); | |||||
Assert.Equal(88, ((JT808_0x0200_0x18)jT808UploadLocationRequest.JT808LocationAttachData[0x18]).FatigueLevel); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
byte[] bodys = "000000010000000200BA7F0E07E4F11C0028003C0000180715101010140400000064150400000064160400000064170200641803006458".ToHexBytes(); | |||||
string json = JT808Serializer.Analyze<JT808_0x0200>(bodys); | |||||
} | |||||
} | |||||
} |
@@ -1,114 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Extensions.JT1078.Enums; | |||||
using JT808.Protocol.MessageBody; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x0200Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x0200Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
} | |||||
[Fact] | |||||
public void Test_0x14_1() | |||||
{ | |||||
JT808_0x0200 jT808UploadLocationRequest = new JT808_0x0200 | |||||
{ | |||||
AlarmFlag = 1, | |||||
Altitude = 40, | |||||
GPSTime = DateTime.Parse("2020-01-31 20:20:20"), | |||||
Lat = 12222222, | |||||
Lng = 132444444, | |||||
Speed = 60, | |||||
Direction = 0, | |||||
StatusFlag = 2, | |||||
JT808LocationAttachData = new Dictionary<byte, JT808_0x0200_BodyBase>() | |||||
}; | |||||
jT808UploadLocationRequest.JT808LocationAttachData.Add(JT808_JT1078_Constants.JT808_0X0200_0x14, new JT808_0x0200_0x14 | |||||
{ | |||||
VideoRelateAlarm = (uint)(VideoRelateAlarmType.视频信号遮挡报警 | VideoRelateAlarmType.其他视频设备故障报警) | |||||
}); | |||||
var hex = JT808Serializer.Serialize(jT808UploadLocationRequest).ToHexString(); | |||||
Assert.Equal("000000010000000200BA7F0E07E4F11C0028003C000020013120202014040000000A", hex); | |||||
} | |||||
[Fact] | |||||
public void Test_0x14_2() | |||||
{ | |||||
byte[] bodys = "000000010000000200BA7F0E07E4F11C0028003C000020013120202014040000000A".ToHexBytes(); | |||||
JT808_0x0200 jT808UploadLocationRequest = JT808Serializer.Deserialize<JT808_0x0200>(bodys); | |||||
Assert.Equal((uint)1, jT808UploadLocationRequest.AlarmFlag); | |||||
Assert.Equal(DateTime.Parse("2020-01-31 20:20:20"), jT808UploadLocationRequest.GPSTime); | |||||
Assert.Equal(12222222, jT808UploadLocationRequest.Lat); | |||||
Assert.Equal(132444444, jT808UploadLocationRequest.Lng); | |||||
Assert.Equal(60, jT808UploadLocationRequest.Speed); | |||||
Assert.Equal((uint)2, jT808UploadLocationRequest.StatusFlag); | |||||
Assert.Equal((uint)(VideoRelateAlarmType.视频信号遮挡报警 | VideoRelateAlarmType.其他视频设备故障报警), ((JT808_0x0200_0x14)jT808UploadLocationRequest.JT808LocationAttachData[JT808_JT1078_Constants.JT808_0X0200_0x14]).VideoRelateAlarm); | |||||
} | |||||
[Fact] | |||||
public void Test_0x14_3() | |||||
{ | |||||
byte[] bodys = "000000010000000200BA7F0E07E4F11C0028003C000020013120202014040000000A".ToHexBytes(); | |||||
string json = JT808Serializer.Analyze<JT808_0x0200>(bodys); | |||||
} | |||||
[Fact] | |||||
public void Test_0x15_1() | |||||
{ | |||||
JT808_0x0200 jT808UploadLocationRequest = new JT808_0x0200 | |||||
{ | |||||
AlarmFlag = 1, | |||||
Altitude = 40, | |||||
GPSTime = DateTime.Parse("2020-01-31 20:20:20"), | |||||
Lat = 12222222, | |||||
Lng = 132444444, | |||||
Speed = 60, | |||||
Direction = 0, | |||||
StatusFlag = 2, | |||||
JT808LocationAttachData = new Dictionary<byte, JT808_0x0200_BodyBase>() | |||||
}; | |||||
jT808UploadLocationRequest.JT808LocationAttachData.Add(JT808_JT1078_Constants.JT808_0X0200_0x15, new JT808_0x0200_0x15 | |||||
{ | |||||
VideoSignalLoseAlarmStatus=3 | |||||
}); | |||||
var hex = JT808Serializer.Serialize(jT808UploadLocationRequest).ToHexString(); | |||||
Assert.Equal("000000010000000200BA7F0E07E4F11C0028003C0000200131202020150400000003", hex); | |||||
} | |||||
[Fact] | |||||
public void Test_0x15_2() | |||||
{ | |||||
byte[] bodys = "000000010000000200BA7F0E07E4F11C0028003C0000200131202020150400000003".ToHexBytes(); | |||||
JT808_0x0200 jT808UploadLocationRequest = JT808Serializer.Deserialize<JT808_0x0200>(bodys); | |||||
Assert.Equal((uint)1, jT808UploadLocationRequest.AlarmFlag); | |||||
Assert.Equal(DateTime.Parse("2020-01-31 20:20:20"), jT808UploadLocationRequest.GPSTime); | |||||
Assert.Equal(12222222, jT808UploadLocationRequest.Lat); | |||||
Assert.Equal(132444444, jT808UploadLocationRequest.Lng); | |||||
Assert.Equal(60, jT808UploadLocationRequest.Speed); | |||||
Assert.Equal((uint)2, jT808UploadLocationRequest.StatusFlag); | |||||
Assert.Equal(3u, ((JT808_0x0200_0x15)jT808UploadLocationRequest.JT808LocationAttachData[JT808_JT1078_Constants.JT808_0X0200_0x15]).VideoSignalLoseAlarmStatus); | |||||
} | |||||
[Fact] | |||||
public void Test_0x15_3() | |||||
{ | |||||
byte[] bodys = "000000010000000200BA7F0E07E4F11C0028003C0000200131202020150400000003".ToHexBytes(); | |||||
string json = JT808Serializer.Analyze<JT808_0x0200>(bodys); | |||||
} | |||||
} | |||||
} |
@@ -1,65 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x1003Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x1003Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x1003 jT808_0x1003 = new JT808_0x1003() | |||||
{ | |||||
AudioFrameLength = 1, | |||||
EnterAudioChannelsNumber = 2, | |||||
EnterAudioEncoding = 3, | |||||
EnterAudioSampleDigits = 4, | |||||
EnterAudioSampleRate = 5, | |||||
IsSupportedAudioOutput = 1, | |||||
VideoEncoding = 6, | |||||
TerminalSupportedMaxNumberOfAudioPhysicalChannels = 7, | |||||
TerminalSupportedMaxNumberOfVideoPhysicalChannels = 8 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x1003).ToHexString(); | |||||
Assert.Equal("03020504000101060708", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
JT808_0x1003 jT808_0x1003 = JT808Serializer.Deserialize<JT808_0x1003>("03020504000101060708".ToHexBytes()); | |||||
Assert.Equal(1,jT808_0x1003.AudioFrameLength); | |||||
Assert.Equal(2, jT808_0x1003.EnterAudioChannelsNumber); | |||||
Assert.Equal(3, jT808_0x1003.EnterAudioEncoding); | |||||
Assert.Equal(4, jT808_0x1003.EnterAudioSampleDigits); | |||||
Assert.Equal(5, jT808_0x1003.EnterAudioSampleRate); | |||||
Assert.Equal(1, jT808_0x1003.IsSupportedAudioOutput); | |||||
Assert.Equal(6, jT808_0x1003.VideoEncoding); | |||||
Assert.Equal(7, jT808_0x1003.TerminalSupportedMaxNumberOfAudioPhysicalChannels); | |||||
Assert.Equal(8, jT808_0x1003.TerminalSupportedMaxNumberOfVideoPhysicalChannels); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var json = JT808Serializer.Analyze<JT808_0x1003>("03020504000101060708".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,66 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x1005Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x1005Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x1005 jT808_0x1005 = new JT808_0x1005() | |||||
{ | |||||
BeginTime=Convert.ToDateTime("2019-07-16 10:20:01"), | |||||
EndTime= Convert.ToDateTime("2019-07-16 10:25:02"), | |||||
GettingOffNumber=1, | |||||
GettingOnNumber=1 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x1005).ToHexString(); | |||||
Assert.Equal("19071610200119071610250200010001", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x1005 = JT808Serializer.Deserialize<JT808_0x1005>("19071610200119071610250200010001".ToHexBytes()); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 10:20:01"),jT808_0x1005.BeginTime); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 10:25:02"),jT808_0x1005.EndTime); | |||||
Assert.Equal(1, jT808_0x1005.GettingOffNumber); | |||||
Assert.Equal(1, jT808_0x1005.GettingOnNumber); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var json = JT808Serializer.Analyze<JT808_0x1005>("19071610200119071610250200010001".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,141 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using System.Linq; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x1205Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x1205Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x1205 jT808_0x1205 = new JT808_0x1205() | |||||
{ | |||||
MsgNum = 1, | |||||
AVResouceTotal = 2, | |||||
AVResouces = new List<JT808_0x1205_AVResouce> { | |||||
new JT808_0x1205_AVResouce{ | |||||
AlarmFlag=1, | |||||
AVResourceType=2, | |||||
BeginTime=Convert.ToDateTime("2019-07-16 10:20:01"), | |||||
EndTime=Convert.ToDateTime("2019-07-16 10:25:01"), | |||||
FileSize=3, | |||||
LogicChannelNo=4, | |||||
MemoryType=5, | |||||
StreamType=6 | |||||
}, | |||||
new JT808_0x1205_AVResouce{ | |||||
AlarmFlag=11, | |||||
AVResourceType=21, | |||||
BeginTime=Convert.ToDateTime("2019-07-16 11:20:01"), | |||||
EndTime=Convert.ToDateTime("2019-07-16 11:25:02"), | |||||
FileSize=31, | |||||
LogicChannelNo=41, | |||||
MemoryType=51, | |||||
StreamType=61 | |||||
} | |||||
} | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x1205).ToHexString(); | |||||
Assert.Equal("0001000000020419071610200119071610250100000000000000010206050000000329190716112001190716112502000000000000000B153D330000001F", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x1205 = JT808Serializer.Deserialize<JT808_0x1205>("0001000000020419071610200119071610250100000000000000010206050000000329190716112001190716112502000000000000000B153D330000001F".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0x1205.MsgNum); | |||||
Assert.Equal(2u, jT808_0x1205.AVResouceTotal); | |||||
Assert.Equal(1u, jT808_0x1205.AVResouces[0].AlarmFlag); | |||||
Assert.Equal(2, jT808_0x1205.AVResouces[0].AVResourceType); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 10:20:01"),jT808_0x1205.AVResouces[0].BeginTime); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 10:25:01"),jT808_0x1205.AVResouces[0].EndTime); | |||||
Assert.Equal(3u, jT808_0x1205.AVResouces[0].FileSize); | |||||
Assert.Equal(4, jT808_0x1205.AVResouces[0].LogicChannelNo); | |||||
Assert.Equal(5, jT808_0x1205.AVResouces[0].MemoryType); | |||||
Assert.Equal(6, jT808_0x1205.AVResouces[0].StreamType); | |||||
Assert.Equal(11u, jT808_0x1205.AVResouces[1].AlarmFlag); | |||||
Assert.Equal(21, jT808_0x1205.AVResouces[1].AVResourceType); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 11:20:01"),jT808_0x1205.AVResouces[1].BeginTime); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 11:25:02"),jT808_0x1205.AVResouces[1].EndTime); | |||||
Assert.Equal(31u, jT808_0x1205.AVResouces[1].FileSize); | |||||
Assert.Equal(41, jT808_0x1205.AVResouces[1].LogicChannelNo); | |||||
Assert.Equal(51, jT808_0x1205.AVResouces[1].MemoryType); | |||||
Assert.Equal(61, jT808_0x1205.AVResouces[1].StreamType); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var json = JT808Serializer.Analyze<JT808_0x1205>("000100000002041907161020011907161025010000000102060500000003291907161120011907161125020000000B153D330000001F".ToHexBytes()); | |||||
} | |||||
[Fact] | |||||
public void Test4() | |||||
{ | |||||
var data1 = JT808Serializer.HeaderDeserialize("7E120523A204066657506200EB00020001015A00000023012012191042052012191050190000000000000000000101064446D10120121910221720121910420500000000000000000001010F1FE8EB0120121910023420121910221700000000000000000001010F182D5C0120121909471120121910015500000000000000000001010B38F2430120121909274020121909471100000000000000000001010F056DB40120121909080920121909274000000000000000000001010F0724380120121908483820121909080900000000000000000001010F0530AB0120121908290720121908483800000000000000000001010F05896C0120121908093720121908290700000000000000000001010F02CD3B0120121907500520121908093700000000000000000001010F056FEF0120121907303420121907500500000000000000000001010F043C3401201219072541201219073034000000000000000000010103C26C5F0120121907061120121907254100000000000000000001010F03F0C10120121906464220121907061100000000000000000001010F02F6330120121906271220121906464200000000000000000001010F02E43B0120121906074220121906271200000000000000000001010F033D670120121905481120121906074200000000000000000001010F088BF20120121905284120121905481100000000000000000001010F03F9FE0120121905091020121905284100000000000000000001010F05B1040120121904494020121905091000000000000000000001010F02B3540120121904301020121904494000000000000000000001010F0417B00120121904103920121904301000000000000000000001010F0538970120121903510820121904103900000000000000000001010F054E9E0120121903313820121903510800000000000000000001010F016ECB0120121903120820121903313800000000000000000001010F0333C00120121902523820121903120700000000000000000001010F029D230120121902330720121902523700000000000000000001010F0354E40120121902133720121902330700000000000000000001010F03303D0120121901540720121902133700000000000000000001010F04981E0120121901343720121901540700000000000000000001010F02AD940120121901150820121901343700000000000000000001010EFFD7CF0120121900553720121901150800000000000000000001010F07D9330120121900360720121900553700000000000000000001010F040E740C7E".ToHexBytes()); | |||||
var data2 = JT808Serializer.HeaderDeserialize("7E1205203804066657506200EC000200020120121900163320121900360700000000000000000001010F0CE4CD0120121900002220121900163300000000000000000001010C6F9E7B5D7E".ToHexBytes()); | |||||
var realBody = data1.Bodies.Concat(data2.Bodies).ToArray(); | |||||
var result = JT808Serializer.Deserialize<JT808_0x1205>(realBody); | |||||
} | |||||
[Fact] | |||||
public void Test5() | |||||
{ | |||||
var data1 = JT808Serializer.Deserialize("7E120523A204066657506200EB00020001015A00000023012012191042052012191050190000000000000000000101064446D10120121910221720121910420500000000000000000001010F1FE8EB0120121910023420121910221700000000000000000001010F182D5C0120121909471120121910015500000000000000000001010B38F2430120121909274020121909471100000000000000000001010F056DB40120121909080920121909274000000000000000000001010F0724380120121908483820121909080900000000000000000001010F0530AB0120121908290720121908483800000000000000000001010F05896C0120121908093720121908290700000000000000000001010F02CD3B0120121907500520121908093700000000000000000001010F056FEF0120121907303420121907500500000000000000000001010F043C3401201219072541201219073034000000000000000000010103C26C5F0120121907061120121907254100000000000000000001010F03F0C10120121906464220121907061100000000000000000001010F02F6330120121906271220121906464200000000000000000001010F02E43B0120121906074220121906271200000000000000000001010F033D670120121905481120121906074200000000000000000001010F088BF20120121905284120121905481100000000000000000001010F03F9FE0120121905091020121905284100000000000000000001010F05B1040120121904494020121905091000000000000000000001010F02B3540120121904301020121904494000000000000000000001010F0417B00120121904103920121904301000000000000000000001010F0538970120121903510820121904103900000000000000000001010F054E9E0120121903313820121903510800000000000000000001010F016ECB0120121903120820121903313800000000000000000001010F0333C00120121902523820121903120700000000000000000001010F029D230120121902330720121902523700000000000000000001010F0354E40120121902133720121902330700000000000000000001010F03303D0120121901540720121902133700000000000000000001010F04981E0120121901343720121901540700000000000000000001010F02AD940120121901150820121901343700000000000000000001010EFFD7CF0120121900553720121901150800000000000000000001010F07D9330120121900360720121900553700000000000000000001010F040E740C7E".ToHexBytes()); | |||||
var data2 = JT808Serializer.Deserialize("7E1205203804066657506200EC000200020120121900163320121900360700000000000000000001010F0CE4CD0120121900002220121900163300000000000000000001010C6F9E7B5D7E".ToHexBytes()); | |||||
var realBody = data1.SubDataBodies.Concat(data2.SubDataBodies).ToArray(); | |||||
var result = JT808Serializer.Deserialize<JT808_0x1205>(realBody); | |||||
} | |||||
[Fact] | |||||
public void Test6() | |||||
{ | |||||
var pack1 = JT808Serializer.Deserialize("7E120523A204066657506200EB00020001015A00000023012012191042052012191050190000000000000000000101064446D10120121910221720121910420500000000000000000001010F1FE8EB0120121910023420121910221700000000000000000001010F182D5C0120121909471120121910015500000000000000000001010B38F2430120121909274020121909471100000000000000000001010F056DB40120121909080920121909274000000000000000000001010F0724380120121908483820121909080900000000000000000001010F0530AB0120121908290720121908483800000000000000000001010F05896C0120121908093720121908290700000000000000000001010F02CD3B0120121907500520121908093700000000000000000001010F056FEF0120121907303420121907500500000000000000000001010F043C3401201219072541201219073034000000000000000000010103C26C5F0120121907061120121907254100000000000000000001010F03F0C10120121906464220121907061100000000000000000001010F02F6330120121906271220121906464200000000000000000001010F02E43B0120121906074220121906271200000000000000000001010F033D670120121905481120121906074200000000000000000001010F088BF20120121905284120121905481100000000000000000001010F03F9FE0120121905091020121905284100000000000000000001010F05B1040120121904494020121905091000000000000000000001010F02B3540120121904301020121904494000000000000000000001010F0417B00120121904103920121904301000000000000000000001010F0538970120121903510820121904103900000000000000000001010F054E9E0120121903313820121903510800000000000000000001010F016ECB0120121903120820121903313800000000000000000001010F0333C00120121902523820121903120700000000000000000001010F029D230120121902330720121902523700000000000000000001010F0354E40120121902133720121902330700000000000000000001010F03303D0120121901540720121902133700000000000000000001010F04981E0120121901343720121901540700000000000000000001010F02AD940120121901150820121901343700000000000000000001010EFFD7CF0120121900553720121901150800000000000000000001010F07D9330120121900360720121900553700000000000000000001010F040E740C7E".ToHexBytes()); | |||||
var pack2 = JT808Serializer.Deserialize("7E1205203804066657506200EC000200020120121900163320121900360700000000000000000001010F0CE4CD0120121900002220121900163300000000000000000001010C6F9E7B5D7E".ToHexBytes()); | |||||
var realBody = pack1.SubDataBodies.Concat(pack2.SubDataBodies).ToArray(); | |||||
var result = JT808Serializer.Analyze(pack1.Header.MsgId,realBody); | |||||
} | |||||
[Fact] | |||||
public void Test7() | |||||
{ | |||||
var pack1 = JT808Serializer.Deserialize("7E120523A204066657506200EB00020001015A00000023012012191042052012191050190000000000000000000101064446D10120121910221720121910420500000000000000000001010F1FE8EB0120121910023420121910221700000000000000000001010F182D5C0120121909471120121910015500000000000000000001010B38F2430120121909274020121909471100000000000000000001010F056DB40120121909080920121909274000000000000000000001010F0724380120121908483820121909080900000000000000000001010F0530AB0120121908290720121908483800000000000000000001010F05896C0120121908093720121908290700000000000000000001010F02CD3B0120121907500520121908093700000000000000000001010F056FEF0120121907303420121907500500000000000000000001010F043C3401201219072541201219073034000000000000000000010103C26C5F0120121907061120121907254100000000000000000001010F03F0C10120121906464220121907061100000000000000000001010F02F6330120121906271220121906464200000000000000000001010F02E43B0120121906074220121906271200000000000000000001010F033D670120121905481120121906074200000000000000000001010F088BF20120121905284120121905481100000000000000000001010F03F9FE0120121905091020121905284100000000000000000001010F05B1040120121904494020121905091000000000000000000001010F02B3540120121904301020121904494000000000000000000001010F0417B00120121904103920121904301000000000000000000001010F0538970120121903510820121904103900000000000000000001010F054E9E0120121903313820121903510800000000000000000001010F016ECB0120121903120820121903313800000000000000000001010F0333C00120121902523820121903120700000000000000000001010F029D230120121902330720121902523700000000000000000001010F0354E40120121902133720121902330700000000000000000001010F03303D0120121901540720121902133700000000000000000001010F04981E0120121901343720121901540700000000000000000001010F02AD940120121901150820121901343700000000000000000001010EFFD7CF0120121900553720121901150800000000000000000001010F07D9330120121900360720121900553700000000000000000001010F040E740C7E".ToHexBytes()); | |||||
var pack2 = JT808Serializer.Deserialize("7E1205203804066657506200EC000200020120121900163320121900360700000000000000000001010F0CE4CD0120121900002220121900163300000000000000000001010C6F9E7B5D7E".ToHexBytes()); | |||||
var realBody = pack1.SubDataBodies.Concat(pack2.SubDataBodies).ToArray(); | |||||
var result = JT808Serializer.AnalyzeJsonBuffer(pack1.Header.MsgId, realBody); | |||||
string json = Encoding.UTF8.GetString(result); | |||||
} | |||||
} | |||||
} |
@@ -1,62 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x1206Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x1206Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x1206 jT808_0x1206 = new JT808_0x1206() | |||||
{ | |||||
MsgNum=1, | |||||
Result=1 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x1206).ToHexString(); | |||||
Assert.Equal("000101", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x1206 = JT808Serializer.Deserialize<JT808_0x1206>("000101".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0x1206.MsgNum); | |||||
Assert.Equal(1, jT808_0x1206.Result); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x1206 = JT808Serializer.Analyze<JT808_0x1206>("000101".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,300 +0,0 @@ | |||||
using JT808.Protocol.Enums; | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using JT808.Protocol.MessageBody; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Reflection; | |||||
using System.Text; | |||||
using Xunit; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x8103CustomId | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x8103CustomId() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1.AddJT808Configure(new DefaultGlobalConfig()) | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<DefaultGlobalConfig>(); | |||||
JT808Serializer = new JT808Serializer(defaultConfig); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808Package jT808Package = new JT808Package | |||||
{ | |||||
Header = new JT808Header | |||||
{ | |||||
MsgId = JT808MsgId.设置终端参数.ToUInt16Value(), | |||||
ManualMsgNum = 10, | |||||
TerminalPhoneNo = "123456789", | |||||
}, | |||||
Bodies = new JT808_0x8103 | |||||
{ | |||||
ParamList = new List<JT808_0x8103_BodyBase> { | |||||
new JT808_0x8103_0x0075 { | |||||
AudioOutputEnabled=1, | |||||
OSD=2, | |||||
RTS_EncodeMode=3, | |||||
RTS_KF_Interval=4, | |||||
RTS_Resolution=5, | |||||
RTS_Target_CodeRate=6, | |||||
RTS_Target_FPS=7, | |||||
StreamStore_EncodeMode=8, | |||||
StreamStore_KF_Interval=9, | |||||
StreamStore_Resolution=10, | |||||
StreamStore_Target_CodeRate=11, | |||||
StreamStore_Target_FPS=12 | |||||
}, | |||||
new JT808_0x8103_0x0076 { | |||||
AudioChannelTotal=1, | |||||
AVChannelTotal=2, | |||||
VudioChannelTotal=3, | |||||
ParamLength=27, | |||||
AVChannelRefTables=new List<JT808_0x8103_0x0076_AVChannelRefTable>{ | |||||
new JT808_0x8103_0x0076_AVChannelRefTable{ | |||||
ChannelType =0, | |||||
IsConnectCloudPlat =1, | |||||
LogicChannelNo =2, | |||||
PhysicalChannelNo =3 }, | |||||
new JT808_0x8103_0x0076_AVChannelRefTable{ | |||||
ChannelType =4, | |||||
IsConnectCloudPlat =5, | |||||
LogicChannelNo =6, | |||||
PhysicalChannelNo =7 }, | |||||
new JT808_0x8103_0x0076_AVChannelRefTable{ | |||||
ChannelType =8, | |||||
IsConnectCloudPlat =9, | |||||
LogicChannelNo =10, | |||||
PhysicalChannelNo =11 }, | |||||
new JT808_0x8103_0x0076_AVChannelRefTable{ | |||||
ChannelType =12, | |||||
IsConnectCloudPlat =13, | |||||
LogicChannelNo =14, | |||||
PhysicalChannelNo =15 }, | |||||
new JT808_0x8103_0x0076_AVChannelRefTable{ | |||||
ChannelType =16, | |||||
IsConnectCloudPlat =17, | |||||
LogicChannelNo =18, | |||||
PhysicalChannelNo =19 }, | |||||
new JT808_0x8103_0x0076_AVChannelRefTable{ | |||||
ChannelType =20, | |||||
IsConnectCloudPlat =21, | |||||
LogicChannelNo =22, | |||||
PhysicalChannelNo =23 } | |||||
} | |||||
}, | |||||
new JT808_0x8103_0x0077{ | |||||
NeedSetChannelTotal=2, | |||||
ParamLength=43, | |||||
SignalChannels=new List<JT808_0x8103_0x0077_SignalChannel>{ | |||||
new JT808_0x8103_0x0077_SignalChannel{ | |||||
LogicChannelNo =1, | |||||
OSD =2, | |||||
RTS_EncodeMode =3, | |||||
RTS_KF_Interval =4, | |||||
RTS_Resolution =5, | |||||
RTS_Target_CodeRate =6, | |||||
RTS_Target_FPS =7, | |||||
StreamStore_EncodeMode =8, | |||||
StreamStore_KF_Interval =9, | |||||
StreamStore_Resolution =10, | |||||
StreamStore_Target_CodeRate=11, | |||||
StreamStore_Target_FPS =12 | |||||
}, | |||||
new JT808_0x8103_0x0077_SignalChannel{ | |||||
LogicChannelNo=1, | |||||
OSD =2, | |||||
RTS_EncodeMode =3, | |||||
RTS_KF_Interval =4, | |||||
RTS_Resolution =5, | |||||
RTS_Target_CodeRate =6, | |||||
RTS_Target_FPS =7, | |||||
StreamStore_EncodeMode =8, | |||||
StreamStore_KF_Interval =9, | |||||
StreamStore_Resolution =10, | |||||
StreamStore_Target_CodeRate=11, | |||||
StreamStore_Target_FPS =12 | |||||
} | |||||
} | |||||
}, | |||||
new JT808_0x8103_0x0079{ | |||||
BeginMinute=1, | |||||
Duration =2, | |||||
StorageThresholds =3 | |||||
}, | |||||
new JT808_0x8103_0x007A{ | |||||
AlarmShielding=1 | |||||
}, | |||||
new JT808_0x8103_0x007B{ | |||||
NuclearLoadNumber=1, | |||||
FatigueThreshold =2 | |||||
}, | |||||
new JT808_0x8103_0x007C{ | |||||
SleepWakeMode=1, | |||||
TimerWakeDaySet =2, | |||||
WakeConditionType =3, | |||||
TimerWakeDayParamter=new JT808_0x8103_0x007C_TimerWakeDayParamter{ | |||||
TimePeriod1CloseTime="12", | |||||
TimePeriod1WakeTime="23", | |||||
TimePeriod2CloseTime="34", | |||||
TimePeriod2WakeTime="45", | |||||
TimePeriod3CloseTime="56", | |||||
TimePeriod3WakeTime="67", | |||||
TimePeriod4CloseTime="78", | |||||
TimePeriod4WakeTime="89", | |||||
TimerWakeEnableFlag=10 | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808Package).ToHexString(); | |||||
Assert.Equal("7E8103009C000123456789000A070000007515030500040700000006080A00090C0000000B000201000000761B02010303020001070604050B0A08090F0E0C0D1312101117161415000000772B0201030500040700000006080A00090C0000000B000201030500040700000006080A00090C0000000B000200000079030302010000007A04000000010000007B0201020000007C140103020A00230012004500340067005600890078587E", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
byte[] bytes = "7E8103009C000123456789000A070000007515030500040700000006080A00090C0000000B000201000000761B02010303020001070604050B0A08090F0E0C0D1312101117161415000000772B0201030500040700000006080A00090C0000000B000201030500040700000006080A00090C0000000B000200000079030302010000007A04000000010000007B0201020000007C140103020A00230012004500340067005600890078587E".ToHexBytes(); | |||||
JT808Package jT808_0X8103 = JT808Serializer.Deserialize(bytes); | |||||
Assert.Equal(JT808MsgId.设置终端参数.ToUInt16Value(), jT808_0X8103.Header.MsgId); | |||||
Assert.Equal(10, jT808_0X8103.Header.MsgNum); | |||||
Assert.Equal("123456789", jT808_0X8103.Header.TerminalPhoneNo); | |||||
JT808_0x8103 jT808_0x8103 = (JT808_0x8103)jT808_0X8103.Bodies; | |||||
foreach (var item in jT808_0x8103.ParamList) | |||||
{ | |||||
switch (item) | |||||
{ | |||||
case JT808_0x8103_0x0075 jT808_0x8103_0x0075: | |||||
Assert.Equal(1,jT808_0x8103_0x0075.AudioOutputEnabled); | |||||
Assert.Equal(2,jT808_0x8103_0x0075.OSD); | |||||
Assert.Equal(3,jT808_0x8103_0x0075.RTS_EncodeMode); | |||||
Assert.Equal(4,jT808_0x8103_0x0075.RTS_KF_Interval); | |||||
Assert.Equal(5,jT808_0x8103_0x0075.RTS_Resolution); | |||||
Assert.Equal(6u,jT808_0x8103_0x0075.RTS_Target_CodeRate); | |||||
Assert.Equal(7,jT808_0x8103_0x0075.RTS_Target_FPS); | |||||
Assert.Equal(8,jT808_0x8103_0x0075.StreamStore_EncodeMode); | |||||
Assert.Equal(9,jT808_0x8103_0x0075.StreamStore_KF_Interval); | |||||
Assert.Equal(10,jT808_0x8103_0x0075.StreamStore_Resolution); | |||||
Assert.Equal(11u,jT808_0x8103_0x0075.StreamStore_Target_CodeRate); | |||||
Assert.Equal(12u,jT808_0x8103_0x0075.StreamStore_Target_FPS); | |||||
break; | |||||
case JT808_0x8103_0x0076 jT808_0x8103_0x0076: | |||||
Assert.Equal(1,jT808_0x8103_0x0076.AudioChannelTotal); | |||||
Assert.Equal(2, jT808_0x8103_0x0076.AVChannelTotal); | |||||
Assert.Equal(3,jT808_0x8103_0x0076.VudioChannelTotal); | |||||
Assert.Equal(27,jT808_0x8103_0x0076.ParamLength); | |||||
Assert.Equal(0,jT808_0x8103_0x0076.AVChannelRefTables[0].ChannelType); | |||||
Assert.Equal(1,jT808_0x8103_0x0076.AVChannelRefTables[0].IsConnectCloudPlat); | |||||
Assert.Equal(2,jT808_0x8103_0x0076.AVChannelRefTables[0].LogicChannelNo); | |||||
Assert.Equal(3,jT808_0x8103_0x0076.AVChannelRefTables[0].PhysicalChannelNo); | |||||
Assert.Equal(4,jT808_0x8103_0x0076.AVChannelRefTables[1].ChannelType); | |||||
Assert.Equal(5,jT808_0x8103_0x0076.AVChannelRefTables[1].IsConnectCloudPlat); | |||||
Assert.Equal(6,jT808_0x8103_0x0076.AVChannelRefTables[1].LogicChannelNo); | |||||
Assert.Equal(7,jT808_0x8103_0x0076.AVChannelRefTables[1].PhysicalChannelNo); | |||||
Assert.Equal(8,jT808_0x8103_0x0076.AVChannelRefTables[2].ChannelType); | |||||
Assert.Equal(9,jT808_0x8103_0x0076.AVChannelRefTables[2].IsConnectCloudPlat); | |||||
Assert.Equal(10,jT808_0x8103_0x0076.AVChannelRefTables[2].LogicChannelNo); | |||||
Assert.Equal(11,jT808_0x8103_0x0076.AVChannelRefTables[2].PhysicalChannelNo); | |||||
Assert.Equal(12,jT808_0x8103_0x0076.AVChannelRefTables[3].ChannelType); | |||||
Assert.Equal(13,jT808_0x8103_0x0076.AVChannelRefTables[3].IsConnectCloudPlat); | |||||
Assert.Equal(14,jT808_0x8103_0x0076.AVChannelRefTables[3].LogicChannelNo); | |||||
Assert.Equal(15,jT808_0x8103_0x0076.AVChannelRefTables[3].PhysicalChannelNo); | |||||
Assert.Equal(16,jT808_0x8103_0x0076.AVChannelRefTables[4].ChannelType); | |||||
Assert.Equal(17,jT808_0x8103_0x0076.AVChannelRefTables[4].IsConnectCloudPlat); | |||||
Assert.Equal(18,jT808_0x8103_0x0076.AVChannelRefTables[4].LogicChannelNo); | |||||
Assert.Equal(19,jT808_0x8103_0x0076.AVChannelRefTables[4].PhysicalChannelNo); | |||||
Assert.Equal(20,jT808_0x8103_0x0076.AVChannelRefTables[5].ChannelType); | |||||
Assert.Equal(21,jT808_0x8103_0x0076.AVChannelRefTables[5].IsConnectCloudPlat); | |||||
Assert.Equal(22,jT808_0x8103_0x0076.AVChannelRefTables[5].LogicChannelNo); | |||||
Assert.Equal(23,jT808_0x8103_0x0076.AVChannelRefTables[5].PhysicalChannelNo); | |||||
break; | |||||
case JT808_0x8103_0x0077 jT808_0x8103_0x0077: | |||||
Assert.Equal(2,jT808_0x8103_0x0077.NeedSetChannelTotal); | |||||
Assert.Equal(43,jT808_0x8103_0x0077.ParamLength); | |||||
Assert.Equal(1,jT808_0x8103_0x0077.SignalChannels[0].LogicChannelNo); | |||||
Assert.Equal(2,jT808_0x8103_0x0077.SignalChannels[0].OSD); | |||||
Assert.Equal(3,jT808_0x8103_0x0077.SignalChannels[0].RTS_EncodeMode); | |||||
Assert.Equal(4,jT808_0x8103_0x0077.SignalChannels[0].RTS_KF_Interval); | |||||
Assert.Equal(5,jT808_0x8103_0x0077.SignalChannels[0].RTS_Resolution); | |||||
Assert.Equal(6u,jT808_0x8103_0x0077.SignalChannels[0].RTS_Target_CodeRate); | |||||
Assert.Equal(7,jT808_0x8103_0x0077.SignalChannels[0].RTS_Target_FPS); | |||||
Assert.Equal(8,jT808_0x8103_0x0077.SignalChannels[0].StreamStore_EncodeMode); | |||||
Assert.Equal(9,jT808_0x8103_0x0077.SignalChannels[0].StreamStore_KF_Interval); | |||||
Assert.Equal(10,jT808_0x8103_0x0077.SignalChannels[0].StreamStore_Resolution); | |||||
Assert.Equal(11u,jT808_0x8103_0x0077.SignalChannels[0].StreamStore_Target_CodeRate); | |||||
Assert.Equal(12,jT808_0x8103_0x0077.SignalChannels[0].StreamStore_Target_FPS); | |||||
Assert.Equal(1,jT808_0x8103_0x0077.SignalChannels[1].LogicChannelNo); | |||||
Assert.Equal(2,jT808_0x8103_0x0077.SignalChannels[1].OSD); | |||||
Assert.Equal(3,jT808_0x8103_0x0077.SignalChannels[1].RTS_EncodeMode); | |||||
Assert.Equal(4,jT808_0x8103_0x0077.SignalChannels[1].RTS_KF_Interval); | |||||
Assert.Equal(5,jT808_0x8103_0x0077.SignalChannels[1].RTS_Resolution); | |||||
Assert.Equal(6u,jT808_0x8103_0x0077.SignalChannels[1].RTS_Target_CodeRate); | |||||
Assert.Equal(7,jT808_0x8103_0x0077.SignalChannels[1].RTS_Target_FPS); | |||||
Assert.Equal(8,jT808_0x8103_0x0077.SignalChannels[1].StreamStore_EncodeMode); | |||||
Assert.Equal(9,jT808_0x8103_0x0077.SignalChannels[1].StreamStore_KF_Interval); | |||||
Assert.Equal(10,jT808_0x8103_0x0077.SignalChannels[1].StreamStore_Resolution); | |||||
Assert.Equal(11u,jT808_0x8103_0x0077.SignalChannels[1].StreamStore_Target_CodeRate); | |||||
Assert.Equal(12,jT808_0x8103_0x0077.SignalChannels[1].StreamStore_Target_FPS); | |||||
break; | |||||
case JT808_0x8103_0x0079 jT808_0x8103_0x0079: | |||||
Assert.Equal(1,jT808_0x8103_0x0079.BeginMinute); | |||||
Assert.Equal(2,jT808_0x8103_0x0079.Duration); | |||||
Assert.Equal(3,jT808_0x8103_0x0079.StorageThresholds); | |||||
break; | |||||
case JT808_0x8103_0x007A jT808_0x8103_0x007A: | |||||
Assert.Equal(1u,jT808_0x8103_0x007A.AlarmShielding); | |||||
break; | |||||
case JT808_0x8103_0x007B jT808_0x8103_0x007B: | |||||
Assert.Equal(1,jT808_0x8103_0x007B.NuclearLoadNumber); | |||||
Assert.Equal(2,jT808_0x8103_0x007B.FatigueThreshold); | |||||
break; | |||||
case JT808_0x8103_0x007C jT808_0x8103_0x007C: | |||||
Assert.Equal(1,jT808_0x8103_0x007C.SleepWakeMode); | |||||
Assert.Equal(2,jT808_0x8103_0x007C.TimerWakeDaySet); | |||||
Assert.Equal(3,jT808_0x8103_0x007C.WakeConditionType); | |||||
Assert.Equal("12", jT808_0x8103_0x007C.TimerWakeDayParamter.TimePeriod1CloseTime); | |||||
Assert.Equal("23",jT808_0x8103_0x007C.TimerWakeDayParamter.TimePeriod1WakeTime); | |||||
Assert.Equal("34",jT808_0x8103_0x007C.TimerWakeDayParamter.TimePeriod2CloseTime); | |||||
Assert.Equal("45",jT808_0x8103_0x007C.TimerWakeDayParamter.TimePeriod2WakeTime); | |||||
Assert.Equal("56",jT808_0x8103_0x007C.TimerWakeDayParamter.TimePeriod3CloseTime); | |||||
Assert.Equal("67",jT808_0x8103_0x007C.TimerWakeDayParamter.TimePeriod3WakeTime); | |||||
Assert.Equal("78",jT808_0x8103_0x007C.TimerWakeDayParamter.TimePeriod4CloseTime); | |||||
Assert.Equal("89", jT808_0x8103_0x007C.TimerWakeDayParamter.TimePeriod4WakeTime); | |||||
Assert.Equal(10,jT808_0x8103_0x007C.TimerWakeDayParamter.TimerWakeEnableFlag); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
byte[] bytes = "7E8103009C000123456789000A070000007515030500040700000006080A00090C0000000B000201000000761B02010303020001070604050B0A08090F0E0C0D1312101117161415000000772B0201030500040700000006080A00090C0000000B000201030500040700000006080A00090C0000000B000200000079030302010000007A04000000010000007B0201020000007C140103020A00230012004500340067005600890078587E".ToHexBytes(); | |||||
var jT808_0X8103 = JT808Serializer.Analyze(bytes); | |||||
} | |||||
} | |||||
class DefaultGlobalConfig : GlobalConfigBase | |||||
{ | |||||
public override string ConfigId { protected set; get; } = "Default"; | |||||
} | |||||
} |
@@ -1,101 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9101Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9101Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9101 jT808_0X9101 = new JT808_0x9101(); | |||||
jT808_0X9101.ServerIp = "127.0.0.1"; | |||||
jT808_0X9101.TcpPort = 1888; | |||||
jT808_0X9101.UdpPort = 0; | |||||
jT808_0X9101.ChannelNo= 1; | |||||
jT808_0X9101.DataType= 1; | |||||
jT808_0X9101.StreamType= 1; | |||||
var hex = JT808Serializer.Serialize(jT808_0X9101).ToHexString(); | |||||
Assert.Equal("093132372E302E302E3107600000010101", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
JT808_0x9101 jT808_0X9101= JT808Serializer.Deserialize<JT808_0x9101>("093132372E302E302E3107600000010101".ToHexBytes()); | |||||
Assert.Equal("127.0.0.1", jT808_0X9101.ServerIp); | |||||
Assert.Equal(9, jT808_0X9101.ServerIpLength); | |||||
Assert.Equal(1888, jT808_0X9101.TcpPort); | |||||
Assert.Equal(0, jT808_0X9101.UdpPort); | |||||
Assert.Equal(1, jT808_0X9101.ChannelNo); | |||||
Assert.Equal(1, jT808_0X9101.DataType); | |||||
Assert.Equal(1, jT808_0X9101.StreamType); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
JT808Package jT808Package = new JT808Package(); | |||||
JT808Header header = new JT808Header(); | |||||
header.MsgId = 0x9101; | |||||
header.MsgNum = 1; | |||||
header.TerminalPhoneNo = "12345679810"; | |||||
jT808Package.Header = header; | |||||
JT808_0x9101 jT808_0X9101 = new JT808_0x9101(); | |||||
jT808_0X9101.ServerIp = "127.0.0.1"; | |||||
jT808_0X9101.TcpPort = 1888; | |||||
jT808_0X9101.UdpPort = 0; | |||||
jT808_0X9101.ChannelNo = 1; | |||||
jT808_0X9101.DataType = 1; | |||||
jT808_0X9101.StreamType = 1; | |||||
jT808Package.Bodies = jT808_0X9101; | |||||
var hex = JT808Serializer.Serialize(jT808Package).ToHexString(); | |||||
Assert.Equal("7E910100110123456798100001093132372E302E302E31076000000101014C7E", hex); | |||||
//7E910100110123456798100001093132372E302E302E31076000000101014C7E | |||||
} | |||||
[Fact] | |||||
public void Test4() | |||||
{ | |||||
var jT808_0X9101 = JT808Serializer.Analyze<JT808_0x9101>("093132372E302E302E3107600000010101".ToHexBytes()); | |||||
} | |||||
[Fact] | |||||
public void Test5() | |||||
{ | |||||
JT808Package jT808Package = new JT808Package(); | |||||
JT808Header header = new JT808Header(); | |||||
header.MsgId = 0x9101; | |||||
header.MsgNum = 1; | |||||
header.TerminalPhoneNo = ""; | |||||
jT808Package.Header = header; | |||||
JT808_0x9101 jT808_0X9101 = new JT808_0x9101(); | |||||
jT808_0X9101.ServerIp = ""; | |||||
jT808_0X9101.TcpPort = 1078; | |||||
jT808_0X9101.UdpPort = 0; | |||||
jT808_0X9101.ChannelNo = 3; | |||||
jT808_0X9101.DataType = 1; | |||||
jT808_0X9101.StreamType = 1; | |||||
jT808Package.Bodies = jT808_0X9101; | |||||
var hex = JT808Serializer.Serialize(jT808Package).ToHexString(); | |||||
} | |||||
} | |||||
} |
@@ -1,73 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9102Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9102Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9102 jT808_0X9102 = new JT808_0x9102(); | |||||
jT808_0X9102.ChannelNo = 1; | |||||
jT808_0X9102.ControlCmd = 1; | |||||
jT808_0X9102.CloseAVData = 0; | |||||
jT808_0X9102.StreamType = 0; | |||||
var hex = JT808Serializer.Serialize(jT808_0X9102).ToHexString(); | |||||
Assert.Equal("01010000", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
JT808_0x9102 jT808_0X9102 = JT808Serializer.Deserialize<JT808_0x9102>("01010000".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0X9102.ChannelNo); | |||||
Assert.Equal(1, jT808_0X9102.ControlCmd); | |||||
Assert.Equal(0, jT808_0X9102.CloseAVData); | |||||
Assert.Equal(0, jT808_0X9102.StreamType); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
JT808Package jT808Package = new JT808Package(); | |||||
JT808Header header = new JT808Header(); | |||||
header.MsgId = 0x9102; | |||||
header.ManualMsgNum = 1; | |||||
header.TerminalPhoneNo = "12345679810"; | |||||
jT808Package.Header = header; | |||||
JT808_0x9102 jT808_0X9102 = new JT808_0x9102(); | |||||
jT808_0X9102.ChannelNo = 1; | |||||
jT808_0X9102.ControlCmd = 1; | |||||
jT808_0X9102.CloseAVData = 0; | |||||
jT808_0X9102.StreamType = 0; | |||||
jT808Package.Bodies = jT808_0X9102; | |||||
var hex = JT808Serializer.Serialize(jT808Package).ToHexString(); | |||||
//7E910200040123456798100001010100001E7E | |||||
Assert.Equal("7E910200040123456798100001010100001E7E", hex); | |||||
} | |||||
[Fact] | |||||
public void Test4() | |||||
{ | |||||
var jT808_0rX9102 = JT808Serializer.Analyze<JT808_0x9102>("01010000".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,62 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9105Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9105Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9105 jT808_0x9105 = new JT808_0x9105() | |||||
{ | |||||
ChannelNo=1, | |||||
DropRate=2 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9105).ToHexString(); | |||||
Assert.Equal("0102", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9105 = JT808Serializer.Deserialize<JT808_0x9105>("0102".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0x9105.ChannelNo); | |||||
Assert.Equal(2, jT808_0x9105.DropRate); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x9105 = JT808Serializer.Analyze<JT808_0x9105>("0102".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,82 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9201Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9201Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9201 jT808_0x9201 = new JT808_0x9201() | |||||
{ | |||||
ChannelNo = 1, | |||||
MediaType = 2, | |||||
BeginTime = Convert.ToDateTime("2019-07-16 10:10:10"), | |||||
EndTime = Convert.ToDateTime("2019-07-16 10:10:10"), | |||||
PlaySpeed=3, | |||||
MemoryType=5, | |||||
PlaybackWay=6, | |||||
ServerIp="127.0.0.1", | |||||
ServerIpLength=9, | |||||
StreamType=7, | |||||
TcpPort=80, | |||||
UdpPort=8080 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9201).ToHexString(); | |||||
Assert.Equal("093132372E302E302E3100501F90010207050603190716101010190716101010", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9201 = JT808Serializer.Deserialize<JT808_0x9201>("093132372E302E302E3100501F90010207050603190716101010190716101010".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0x9201.ChannelNo); | |||||
Assert.Equal(2, jT808_0x9201.MediaType); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 10:10:10"), jT808_0x9201.BeginTime); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 10:10:10"), jT808_0x9201.EndTime); | |||||
Assert.Equal(3, jT808_0x9201.PlaySpeed); | |||||
Assert.Equal(5, jT808_0x9201.MemoryType); | |||||
Assert.Equal(6, jT808_0x9201.PlaybackWay); | |||||
Assert.Equal("127.0.0.1", jT808_0x9201.ServerIp); | |||||
Assert.Equal(9, jT808_0x9201.ServerIpLength); | |||||
Assert.Equal(7, jT808_0x9201.StreamType); | |||||
Assert.Equal(80, jT808_0x9201.TcpPort); | |||||
Assert.Equal(8080, jT808_0x9201.UdpPort); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x9201 = JT808Serializer.Analyze<JT808_0x9201>("093132372E302E302E3100501F90010207050603190716101010190716101010".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,66 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9202Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9202Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9202 jT808_0x9202 = new JT808_0x9202() | |||||
{ | |||||
ChannelNo=1, | |||||
DragPlayPosition=Convert.ToDateTime("2019-07-16 10:10:10"), | |||||
PlaySpeed=2, | |||||
PlayControl=3 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9202).ToHexString(); | |||||
Assert.Equal("010302190716101010", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9202 = JT808Serializer.Deserialize<JT808_0x9202>("010302190716101010".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0x9202.ChannelNo); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 10:10:10"), jT808_0x9202.DragPlayPosition); | |||||
Assert.Equal(2, jT808_0x9202.PlaySpeed); | |||||
Assert.Equal(3, jT808_0x9202.PlayControl); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x9202 = JT808Serializer.Analyze<JT808_0x9202>("010302190716101010".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,72 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9205Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9205Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9205 jT808_0x9205 = new JT808_0x9205() | |||||
{ | |||||
AlarmFlag=1, | |||||
MediaType=2, | |||||
BeginTime= Convert.ToDateTime("2019-07-16 10:10:10"), | |||||
EndTime= Convert.ToDateTime("2019-07-16 10:10:11"), | |||||
ChannelNo=3, | |||||
MemoryType=4, | |||||
StreamType =5 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9205).ToHexString(); | |||||
Assert.Equal("031907161010101907161010110000000000000001020504", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9205 = JT808Serializer.Deserialize<JT808_0x9205>("031907161010101907161010110000000000000001020504".ToHexBytes()); | |||||
Assert.Equal(1u, jT808_0x9205.AlarmFlag); | |||||
Assert.Equal(2, jT808_0x9205.MediaType); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 10:10:10"), jT808_0x9205.BeginTime); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 10:10:11"), jT808_0x9205.EndTime); | |||||
Assert.Equal(3, jT808_0x9205.ChannelNo); | |||||
Assert.Equal(4, jT808_0x9205.MemoryType); | |||||
Assert.Equal(5, jT808_0x9205.StreamType); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x9205 = JT808Serializer.Analyze<JT808_0x9205>("031907161010101907161010110000000000000001020504".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,92 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9206Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9206Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9206 jT808_0x9206 = new JT808_0x9206() | |||||
{ | |||||
AlarmFlag=1, | |||||
MediaType=2, | |||||
BeginTime= Convert.ToDateTime("2019-07-16 10:10:10"), | |||||
EndTime= Convert.ToDateTime("2019-07-16 10:10:11"), | |||||
ChannelNo=3, | |||||
StreamType =5, | |||||
FileUploadPath ="D://1112", | |||||
FileUploadPathLength=8, | |||||
MemoryPositon=4, | |||||
Password="123456", | |||||
PasswordLength=6, | |||||
Port=808, | |||||
ServerIp="127.0.0.1", | |||||
ServerIpLength=9, | |||||
TaskExcuteCondition=7, | |||||
UserName="tk", | |||||
UserNameLength=2 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9206).ToHexString(); | |||||
Assert.Equal("093132372E302E302E31032802746B0631323334353608443A2F2F31313132031907161010101907161010110000000102050407", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9206 = JT808Serializer.Deserialize<JT808_0x9206>("093132372E302E302E31032802746B0631323334353608443A2F2F31313132031907161010101907161010110000000102050407".ToHexBytes()); | |||||
Assert.Equal(1u, jT808_0x9206.AlarmFlag); | |||||
Assert.Equal(2, jT808_0x9206.MediaType); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 10:10:10"), jT808_0x9206.BeginTime); | |||||
Assert.Equal(Convert.ToDateTime("2019-07-16 10:10:11"), jT808_0x9206.EndTime); | |||||
Assert.Equal(3, jT808_0x9206.ChannelNo); | |||||
Assert.Equal(5, jT808_0x9206.StreamType); | |||||
Assert.Equal("D://1112", jT808_0x9206.FileUploadPath); | |||||
Assert.Equal(8, jT808_0x9206.FileUploadPathLength); | |||||
Assert.Equal(4, jT808_0x9206.MemoryPositon); | |||||
Assert.Equal("123456", jT808_0x9206.Password); | |||||
Assert.Equal(6, jT808_0x9206.PasswordLength); | |||||
Assert.Equal(808, jT808_0x9206.Port); | |||||
Assert.Equal("127.0.0.1", jT808_0x9206.ServerIp); | |||||
Assert.Equal(9, jT808_0x9206.ServerIpLength); | |||||
Assert.Equal(7, jT808_0x9206.TaskExcuteCondition); | |||||
Assert.Equal("tk", jT808_0x9206.UserName); | |||||
Assert.Equal(2, jT808_0x9206.UserNameLength); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x9206 = JT808Serializer.Analyze<JT808_0x9206>("093132372E302E302E31032802746B0631323334353608443A2F2F31313132031907161010101907161010110000000102050407".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,62 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9207Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9207Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9207 jT808_0x9207 = new JT808_0x9207() | |||||
{ | |||||
MgsNum=1, | |||||
UploadControl=2 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9207).ToHexString(); | |||||
Assert.Equal("000102", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9207 = JT808Serializer.Deserialize<JT808_0x9207>("000102".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0x9207.MgsNum); | |||||
Assert.Equal(2, jT808_0x9207.UploadControl); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x9207 = JT808Serializer.Analyze<JT808_0x9207>("000102".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,65 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9301Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9301Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9301 jT808_0x9301 = new JT808_0x9301() | |||||
{ | |||||
ChannelNo=1, | |||||
Speed=2, | |||||
Direction=3 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9301).ToHexString(); | |||||
Assert.Equal("010302", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9301 = JT808Serializer.Deserialize<JT808_0x9301>("010302".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0x9301.ChannelNo); | |||||
Assert.Equal(2, jT808_0x9301.Speed); | |||||
Assert.Equal(3, jT808_0x9301.Direction); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x9301 = JT808Serializer.Analyze<JT808_0x9301>("010302".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,63 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9302Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9302Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9302 jT808_0x9302 = new JT808_0x9302() | |||||
{ | |||||
LogicChannelNo=1, | |||||
FocusAdjustmentDirection=2 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9302).ToHexString(); | |||||
Assert.Equal("0102", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9302 = JT808Serializer.Deserialize<JT808_0x9302>("0102".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0x9302.LogicChannelNo); | |||||
Assert.Equal(2, jT808_0x9302.FocusAdjustmentDirection); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x9302 = JT808Serializer.Analyze<JT808_0x9302>("0102".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,63 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9303Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9303Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9303 jT808_0x9303 = new JT808_0x9303() | |||||
{ | |||||
ChannelNo=1, | |||||
IrisAdjustment=2 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9303).ToHexString(); | |||||
Assert.Equal("0102", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9303 = JT808Serializer.Deserialize<JT808_0x9303>("0102".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0x9303.ChannelNo); | |||||
Assert.Equal(2, jT808_0x9303.IrisAdjustment); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x9303 = JT808Serializer.Analyze<JT808_0x9303>("0102".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,63 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9304Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9304Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9304 jT808_0x9304 = new JT808_0x9304() | |||||
{ | |||||
ChannelNo=1, | |||||
StartOrStop=2 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9304).ToHexString(); | |||||
Assert.Equal("0102", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9304 = JT808Serializer.Deserialize<JT808_0x9304>("0102".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0x9304.ChannelNo); | |||||
Assert.Equal(2, jT808_0x9304.StartOrStop); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x9304 = JT808Serializer.Analyze<JT808_0x9304>("0102".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,56 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9305Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9305Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9305 jT808_0x9305 = new JT808_0x9305() | |||||
{ | |||||
ChannelNo=1, | |||||
StartOrStop=2 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9305).ToHexString(); | |||||
Assert.Equal("0102", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9305 = JT808Serializer.Analyze<JT808_0x9305>("0102".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,63 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.Test | |||||
{ | |||||
public class JT808_0x9306Test | |||||
{ | |||||
JT808Serializer JT808Serializer; | |||||
public JT808_0x9306Test() | |||||
{ | |||||
IServiceCollection serviceDescriptors1 = new ServiceCollection(); | |||||
serviceDescriptors1 | |||||
.AddJT808Configure() | |||||
.AddJT1078Configure(); | |||||
var ServiceProvider1 = serviceDescriptors1.BuildServiceProvider(); | |||||
var defaultConfig = ServiceProvider1.GetRequiredService<IJT808Config>(); | |||||
JT808Serializer = defaultConfig.GetSerializer(); | |||||
Newtonsoft.Json.JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() => | |||||
{ | |||||
//日期类型默认格式化处理 | |||||
return new Newtonsoft.Json.JsonSerializerSettings | |||||
{ | |||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat, | |||||
DateFormatString = "yyyy-MM-dd HH:mm:ss" | |||||
}; | |||||
}); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
JT808_0x9306 jT808_0x9306 = new JT808_0x9306() | |||||
{ | |||||
ChannelNo=1, | |||||
ChangeMultipleControl=2 | |||||
}; | |||||
var hex = JT808Serializer.Serialize(jT808_0x9306).ToHexString(); | |||||
Assert.Equal("0102", hex); | |||||
} | |||||
[Fact] | |||||
public void Test2() | |||||
{ | |||||
var jT808_0x9306 = JT808Serializer.Deserialize<JT808_0x9306>("0102".ToHexBytes()); | |||||
Assert.Equal(1, jT808_0x9306.ChannelNo); | |||||
Assert.Equal(2, jT808_0x9306.ChangeMultipleControl); | |||||
} | |||||
[Fact] | |||||
public void Test3() | |||||
{ | |||||
var jT808_0x9306 = JT808Serializer.Analyze<JT808_0x9306>("0102".ToHexBytes()); | |||||
} | |||||
} | |||||
} |
@@ -1,20 +0,0 @@ | |||||
using JT808.Protocol.Extensions.JT1078.Enums; | |||||
using JT808.Protocol.Extensions.JT1078.MessageBody; | |||||
using JT808.Protocol.Interfaces; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Reflection; | |||||
using System.Text; | |||||
namespace JT808.Protocol.Extensions.JT1078 | |||||
{ | |||||
public static class DependencyInjectionExtensions | |||||
{ | |||||
public static IJT808Builder AddJT1078Configure(this IJT808Builder jT808Builder) | |||||
{ | |||||
jT808Builder.Config.Register(Assembly.GetExecutingAssembly()); | |||||
return jT808Builder; | |||||
} | |||||
} | |||||
} |
@@ -1,28 +0,0 @@ | |||||
namespace JT808.Protocol.Extensions.JT1078.Enums | |||||
{ | |||||
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 | |||||
public enum JT808_JT1078_MsgId:ushort | |||||
{ | |||||
终端上传音视频属性 = 0x1003, | |||||
终端上传乘客流量 = 0x1005, | |||||
终端上传音视频资源列表 = 0x1205, | |||||
文件上传完成通知 = 0x1206, | |||||
查询终端音视频属性 = 0x9003, | |||||
实时音视频传输请求 = 0x9101, | |||||
音视频实时传输控制 = 0x9102, | |||||
实时音视频传输状态通知 = 0x9105, | |||||
平台下发远程录像回放请求 = 0x9201, | |||||
平台下发远程录像回放控制 = 0x9202, | |||||
查询资源列表 = 0x9205, | |||||
文件上传指令 = 0x9206, | |||||
文件上传控制 = 0x9207, | |||||
云台旋转 = 0x9301, | |||||
云台调整焦距控制 = 0x9302, | |||||
云台调整光圈控制 = 0x9303, | |||||
云台雨刷控制 = 0x9304, | |||||
红外补光控制 = 0x9305, | |||||
云台变倍控制 = 0x9306 | |||||
} | |||||
#pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 | |||||
} |
@@ -1,18 +0,0 @@ | |||||
using System; | |||||
namespace JT808.Protocol.Extensions.JT1078.Enums | |||||
{ | |||||
[Flags] | |||||
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 | |||||
public enum VideoRelateAlarmType:uint | |||||
{ | |||||
视频信号丢失报警=0, | |||||
视频信号遮挡报警=2, | |||||
存储单元故障报警=4, | |||||
其他视频设备故障报警=8, | |||||
客车超员报警=16, | |||||
异常驾驶行为报警=32, | |||||
特殊报警录像达到存储阈值报警=64, | |||||
} | |||||
#pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 | |||||
} |
@@ -1,35 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFrameworks>netstandard2.0;netstandard2.1;net5.0;</TargetFrameworks> | |||||
<LangVersion>8.0</LangVersion> | |||||
<Copyright>Copyright 2019.</Copyright> | |||||
<Authors>SmallChi(Koike)</Authors> | |||||
<PackageId>JT808.Protocol.Extensions.JT1078</PackageId> | |||||
<Product>JT808.Protocol.Extensions.JT1078</Product> | |||||
<Description>基于JT808协议、GB808协议扩展的视频消息协议</Description> | |||||
<PackageReleaseNotes>基于JT808协议、GB808协议扩展的视频消息协议</PackageReleaseNotes> | |||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||||
<RepositoryUrl>https://github.com/SmallChi/JT1078</RepositoryUrl> | |||||
<PackageProjectUrl>https://github.com/SmallChi/JT1078</PackageProjectUrl> | |||||
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | |||||
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | |||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||||
<Version>2.3.5</Version> | |||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | |||||
<DocumentationFile>JT808.Protocol.Extensions.JT1078.xml</DocumentationFile> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<None Include="..\..\LICENSE"> | |||||
<Pack>True</Pack> | |||||
<PackagePath></PackagePath> | |||||
</None> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="JT808" Version="2.3.6" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,59 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT808.Protocol.Extensions.JT1078 | |||||
{ | |||||
public static class JT808_JT1078_Constants | |||||
{ | |||||
/// <summary> | |||||
/// 视频相关报警 | |||||
/// </summary> | |||||
public const byte JT808_0X0200_0x14 = 0x14; | |||||
/// <summary> | |||||
/// 视频信号丢失报警状态 | |||||
/// </summary> | |||||
public const byte JT808_0X0200_0x15 = 0x15; | |||||
/// <summary> | |||||
/// 视频信号遮挡报警状态 | |||||
/// </summary> | |||||
public const byte JT808_0X0200_0x16 = 0x16; | |||||
/// <summary> | |||||
/// 存储器故障报警状态 | |||||
/// </summary> | |||||
public const byte JT808_0X0200_0x17 = 0x17; | |||||
/// <summary> | |||||
/// 异常驾驶行为报警详细描述 | |||||
/// </summary> | |||||
public const byte JT808_0X0200_0x18 = 0x18; | |||||
/// <summary> | |||||
/// 音视频参数设置 | |||||
/// </summary> | |||||
public const uint JT808_0X8103_0x0075 = 0x0075; | |||||
/// <summary> | |||||
/// 音视频通道列表设置 | |||||
/// </summary> | |||||
public const uint JT808_0X8103_0x0076 = 0x0076; | |||||
/// <summary> | |||||
/// 单独视频通道参数设置 | |||||
/// </summary> | |||||
public const uint JT808_0X8103_0x0077 = 0x0077; | |||||
/// <summary> | |||||
/// 特殊报警录像参数设置 | |||||
/// </summary> | |||||
public const uint JT808_0X8103_0x0079 = 0x0079; | |||||
/// <summary> | |||||
/// 视频相关报警屏蔽字 | |||||
/// </summary> | |||||
public const uint JT808_0X8103_0x007A = 0x007A; | |||||
/// <summary> | |||||
/// 图像分析报警参数设置 | |||||
/// </summary> | |||||
public const uint JT808_0X8103_0x007B = 0x007B; | |||||
/// <summary> | |||||
/// 终端休眠模式唤醒设置 | |||||
/// </summary> | |||||
public const uint JT808_0X8103_0x007C = 0x007C; | |||||
} | |||||
} |
@@ -1,65 +0,0 @@ | |||||
using JT808.Protocol.Formatters; | |||||
using JT808.Protocol.MessageBody; | |||||
using JT808.Protocol.MessagePack; | |||||
using JT808.Protocol.Interfaces; | |||||
using System.Text.Json; | |||||
using JT808.Protocol.Extensions.JT1078.Enums; | |||||
using System.Linq; | |||||
namespace JT808.Protocol.Extensions.JT1078.MessageBody | |||||
{ | |||||
/// <summary> | |||||
/// 视频相关报警 | |||||
/// 0x0200_0x14 | |||||
/// </summary> | |||||
public class JT808_0x0200_0x14 : JT808_0x0200_BodyBase, IJT808MessagePackFormatter<JT808_0x0200_0x14>,IJT808Analyze | |||||
{ | |||||
public override byte AttachInfoId { get; set; } = 0x14; | |||||
/// <summary> | |||||
/// 数据 长度 | |||||
/// </summary> | |||||
public override byte AttachInfoLength { get; set; } = 4; | |||||
/// <summary> | |||||
/// 视频相关报警 | |||||
/// <see cref="JT808.Protocol.Extensions.JT1078.Enums.VideoRelateAlarmType"/> | |||||
/// </summary> | |||||
public uint VideoRelateAlarm { get; set; } | |||||
public void Analyze(ref JT808MessagePackReader reader, Utf8JsonWriter writer, IJT808Config config) | |||||
{ | |||||
JT808_0x0200_0x14 value = new JT808_0x0200_0x14(); | |||||
value.AttachInfoId = reader.ReadByte(); | |||||
writer.WriteNumber($"[{value.AttachInfoId.ReadNumber()}]附加信息Id", value.AttachInfoId); | |||||
value.AttachInfoLength = reader.ReadByte(); | |||||
writer.WriteNumber($"[{value.AttachInfoLength.ReadNumber()}]附加信息长度", value.AttachInfoLength); | |||||
value.VideoRelateAlarm = reader.ReadUInt32(); | |||||
writer.WriteNumber($"[{value.VideoRelateAlarm.ReadNumber()}]视频相关报警", value.VideoRelateAlarm); | |||||
var videoRelateAlarmFlags = JT808EnumExtensions.GetEnumTypes<VideoRelateAlarmType>(value.VideoRelateAlarm, 32); | |||||
if (videoRelateAlarmFlags.Any()) | |||||
{ | |||||
writer.WriteStartArray("视频报警集合"); | |||||
foreach (var item in videoRelateAlarmFlags) | |||||
{ | |||||
writer.WriteStringValue(item.ToString()); | |||||
} | |||||
writer.WriteEndArray(); | |||||
} | |||||
} | |||||
public JT808_0x0200_0x14 Deserialize(ref JT808MessagePackReader reader, IJT808Config config) | |||||
{ | |||||
JT808_0x0200_0x14 value = new JT808_0x0200_0x14(); | |||||
value.AttachInfoId = reader.ReadByte(); | |||||
value.AttachInfoLength = reader.ReadByte(); | |||||
value.VideoRelateAlarm = reader.ReadUInt32(); | |||||
return value; | |||||
} | |||||
public void Serialize(ref JT808MessagePackWriter writer, JT808_0x0200_0x14 value, IJT808Config config) | |||||
{ | |||||
writer.WriteByte(value.AttachInfoId); | |||||
writer.WriteByte(value.AttachInfoLength); | |||||
writer.WriteUInt32(value.VideoRelateAlarm); | |||||
} | |||||
} | |||||
} |
@@ -1,69 +0,0 @@ | |||||
using JT808.Protocol.Formatters; | |||||
using JT808.Protocol.Interfaces; | |||||
using JT808.Protocol.MessageBody; | |||||
using JT808.Protocol.MessagePack; | |||||
using System; | |||||
using System.Text.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.MessageBody | |||||
{ | |||||
/// <summary> | |||||
/// 视频信号丢失报警状态 | |||||
/// 0x0200_0x15 | |||||
/// </summary> | |||||
public class JT808_0x0200_0x15 : JT808_0x0200_BodyBase, IJT808MessagePackFormatter<JT808_0x0200_0x15>, IJT808Analyze | |||||
{ | |||||
public override byte AttachInfoId { get; set; } = 0x15; | |||||
/// <summary> | |||||
/// 数据 长度 | |||||
/// </summary> | |||||
public override byte AttachInfoLength { get; set; } = 4; | |||||
/// <summary> | |||||
/// 视频信号丢失报警状态 | |||||
/// </summary> | |||||
public uint VideoSignalLoseAlarmStatus { get; set; } | |||||
public void Analyze(ref JT808MessagePackReader reader, Utf8JsonWriter writer, IJT808Config config) | |||||
{ | |||||
JT808_0x0200_0x15 value = new JT808_0x0200_0x15(); | |||||
value.AttachInfoId = reader.ReadByte(); | |||||
writer.WriteNumber($"[{value.AttachInfoId.ReadNumber()}]附加信息Id", value.AttachInfoId); | |||||
value.AttachInfoLength = reader.ReadByte(); | |||||
writer.WriteNumber($"[{value.AttachInfoLength.ReadNumber()}]附加信息长度", value.AttachInfoLength); | |||||
value.VideoSignalLoseAlarmStatus = reader.ReadUInt32(); | |||||
writer.WriteNumber($"[{value.VideoSignalLoseAlarmStatus.ReadNumber()}]视频信号丢失报警状态", value.VideoSignalLoseAlarmStatus); | |||||
var videoSignalLoseAlarmStatusSpan = Convert.ToString(value.VideoSignalLoseAlarmStatus, 2).PadLeft(32, '0').AsSpan(); | |||||
writer.WriteStartArray("视频信号丢失报警状态集合"); | |||||
int index = 0; | |||||
foreach (var item in videoSignalLoseAlarmStatusSpan) | |||||
{ | |||||
if (item == '1') | |||||
{ | |||||
writer.WriteStringValue($"{index}通道视频信号丢失"); | |||||
} | |||||
else | |||||
{ | |||||
writer.WriteStringValue($"{index}通道视频正常"); | |||||
} | |||||
index++; | |||||
} | |||||
writer.WriteEndArray(); | |||||
} | |||||
public JT808_0x0200_0x15 Deserialize(ref JT808MessagePackReader reader, IJT808Config config) | |||||
{ | |||||
JT808_0x0200_0x15 value = new JT808_0x0200_0x15(); | |||||
value.AttachInfoId = reader.ReadByte(); | |||||
value.AttachInfoLength = reader.ReadByte(); | |||||
value.VideoSignalLoseAlarmStatus = reader.ReadUInt32(); | |||||
return value; | |||||
} | |||||
public void Serialize(ref JT808MessagePackWriter writer, JT808_0x0200_0x15 value, IJT808Config config) | |||||
{ | |||||
writer.WriteByte(value.AttachInfoId); | |||||
writer.WriteByte(value.AttachInfoLength); | |||||
writer.WriteUInt32(value.VideoSignalLoseAlarmStatus); | |||||
} | |||||
} | |||||
} |
@@ -1,69 +0,0 @@ | |||||
using JT808.Protocol.Formatters; | |||||
using JT808.Protocol.Interfaces; | |||||
using JT808.Protocol.MessageBody; | |||||
using JT808.Protocol.MessagePack; | |||||
using System; | |||||
using System.Text.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.MessageBody | |||||
{ | |||||
/// <summary> | |||||
/// 视频信号遮挡报警状态 | |||||
/// 0x0200_0x16 | |||||
/// </summary> | |||||
public class JT808_0x0200_0x16 : JT808_0x0200_BodyBase, IJT808MessagePackFormatter<JT808_0x0200_0x16>, IJT808Analyze | |||||
{ | |||||
public override byte AttachInfoId { get; set; } = 0x16; | |||||
/// <summary> | |||||
/// 数据 长度 | |||||
/// </summary> | |||||
public override byte AttachInfoLength { get; set; } = 4; | |||||
/// <summary> | |||||
/// 视频信号遮挡报警状态 | |||||
/// </summary> | |||||
public uint VideoSignalOcclusionAlarmStatus { get; set; } | |||||
public void Analyze(ref JT808MessagePackReader reader, Utf8JsonWriter writer, IJT808Config config) | |||||
{ | |||||
JT808_0x0200_0x16 value = new JT808_0x0200_0x16(); | |||||
value.AttachInfoId = reader.ReadByte(); | |||||
writer.WriteNumber($"[{value.AttachInfoId.ReadNumber()}]附加信息Id", value.AttachInfoId); | |||||
value.AttachInfoLength = reader.ReadByte(); | |||||
writer.WriteNumber($"[{value.AttachInfoLength.ReadNumber()}]附加信息长度", value.AttachInfoLength); | |||||
value.VideoSignalOcclusionAlarmStatus = reader.ReadUInt32(); | |||||
writer.WriteNumber($"[{value.VideoSignalOcclusionAlarmStatus.ReadNumber()}]视频信号遮挡报警状态", value.VideoSignalOcclusionAlarmStatus); | |||||
var videoSignalOcclusionAlarmStatusSpan = Convert.ToString(value.VideoSignalOcclusionAlarmStatus, 2).PadLeft(32, '0').AsSpan(); | |||||
writer.WriteStartArray("视频信号遮挡报警状态集合"); | |||||
int index = 0; | |||||
foreach (var item in videoSignalOcclusionAlarmStatusSpan) | |||||
{ | |||||
if (item == '1') | |||||
{ | |||||
writer.WriteStringValue($"{index}通道视频信号遮挡"); | |||||
} | |||||
else | |||||
{ | |||||
writer.WriteStringValue($"{index}通道视频正常"); | |||||
} | |||||
index++; | |||||
} | |||||
writer.WriteEndArray(); | |||||
} | |||||
public JT808_0x0200_0x16 Deserialize(ref JT808MessagePackReader reader, IJT808Config config) | |||||
{ | |||||
JT808_0x0200_0x16 value = new JT808_0x0200_0x16(); | |||||
value.AttachInfoId = reader.ReadByte(); | |||||
value.AttachInfoLength = reader.ReadByte(); | |||||
value.VideoSignalOcclusionAlarmStatus = reader.ReadUInt32(); | |||||
return value; | |||||
} | |||||
public void Serialize(ref JT808MessagePackWriter writer, JT808_0x0200_0x16 value, IJT808Config config) | |||||
{ | |||||
writer.WriteByte(value.AttachInfoId); | |||||
writer.WriteByte(value.AttachInfoLength); | |||||
writer.WriteUInt32(value.VideoSignalOcclusionAlarmStatus); | |||||
} | |||||
} | |||||
} |
@@ -1,83 +0,0 @@ | |||||
using JT808.Protocol.Formatters; | |||||
using JT808.Protocol.Interfaces; | |||||
using JT808.Protocol.MessageBody; | |||||
using JT808.Protocol.MessagePack; | |||||
using System; | |||||
using System.Text.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.MessageBody | |||||
{ | |||||
/// <summary> | |||||
/// 存储器故障报警状态 | |||||
/// 0x0200_0x17 | |||||
/// </summary> | |||||
public class JT808_0x0200_0x17 : JT808_0x0200_BodyBase, IJT808MessagePackFormatter<JT808_0x0200_0x17>, IJT808Analyze | |||||
{ | |||||
public override byte AttachInfoId { get; set; } = 0x17; | |||||
/// <summary> | |||||
/// 数据 长度 | |||||
/// </summary> | |||||
public override byte AttachInfoLength { get; set; } = 2; | |||||
/// <summary> | |||||
/// 存储器故障报警状态 | |||||
/// </summary> | |||||
public ushort StorageFaultAlarmStatus{ get; set; } | |||||
public void Analyze(ref JT808MessagePackReader reader, Utf8JsonWriter writer, IJT808Config config) | |||||
{ | |||||
JT808_0x0200_0x17 value = new JT808_0x0200_0x17(); | |||||
value.AttachInfoId = reader.ReadByte(); | |||||
writer.WriteNumber($"[{value.AttachInfoId.ReadNumber()}]附加信息Id", value.AttachInfoId); | |||||
value.AttachInfoLength = reader.ReadByte(); | |||||
writer.WriteNumber($"[{value.AttachInfoLength.ReadNumber()}]附加信息长度", value.AttachInfoLength); | |||||
value.StorageFaultAlarmStatus = reader.ReadUInt16(); | |||||
writer.WriteNumber($"[{value.StorageFaultAlarmStatus.ReadNumber()}]存储器故障报警状态", value.StorageFaultAlarmStatus); | |||||
var storageFaultAlarmStatusSpan = Convert.ToString(value.StorageFaultAlarmStatus, 2).PadLeft(16, '0').AsSpan(); | |||||
writer.WriteStartArray("存储器故障报警状态集合"); | |||||
int index = 0; | |||||
foreach (var item in storageFaultAlarmStatusSpan) | |||||
{ | |||||
if (index < 4) | |||||
{ | |||||
if (item == '1') | |||||
{ | |||||
writer.WriteStringValue($"{index}灾备存储装置故障"); | |||||
} | |||||
else | |||||
{ | |||||
writer.WriteStringValue($"{index}灾备存储装置正常"); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
if (item == '1') | |||||
{ | |||||
writer.WriteStringValue($"{index}主存储器故障"); | |||||
} | |||||
else | |||||
{ | |||||
writer.WriteStringValue($"{index}主存储器正常"); | |||||
} | |||||
} | |||||
index++; | |||||
} | |||||
writer.WriteEndArray(); | |||||
} | |||||
public JT808_0x0200_0x17 Deserialize(ref JT808MessagePackReader reader, IJT808Config config) | |||||
{ | |||||
JT808_0x0200_0x17 value = new JT808_0x0200_0x17(); | |||||
value.AttachInfoId = reader.ReadByte(); | |||||
value.AttachInfoLength = reader.ReadByte(); | |||||
value.StorageFaultAlarmStatus = reader.ReadUInt16(); | |||||
return value; | |||||
} | |||||
public void Serialize(ref JT808MessagePackWriter writer, JT808_0x0200_0x17 value, IJT808Config config) | |||||
{ | |||||
writer.WriteByte(value.AttachInfoId); | |||||
writer.WriteByte(value.AttachInfoLength); | |||||
writer.WriteUInt16(value.StorageFaultAlarmStatus); | |||||
} | |||||
} | |||||
} |
@@ -1,90 +0,0 @@ | |||||
using JT808.Protocol.Formatters; | |||||
using JT808.Protocol.Interfaces; | |||||
using JT808.Protocol.MessageBody; | |||||
using JT808.Protocol.MessagePack; | |||||
using System; | |||||
using System.Text.Json; | |||||
namespace JT808.Protocol.Extensions.JT1078.MessageBody | |||||
{ | |||||
/// <summary> | |||||
/// 异常驾驶行为报警详细描述 | |||||
/// 0x0200_0x18 | |||||
/// </summary> | |||||
public class JT808_0x0200_0x18 : JT808_0x0200_BodyBase, IJT808MessagePackFormatter<JT808_0x0200_0x18>, IJT808Analyze | |||||
{ | |||||
public override byte AttachInfoId { get; set; } = 0x18; | |||||
/// <summary> | |||||
/// 数据 长度 | |||||
/// </summary> | |||||
public override byte AttachInfoLength { get; set; } = 3; | |||||
/// <summary> | |||||
/// 异常驾驶行为报警类型 | |||||
/// </summary> | |||||
public ushort AbnormalDrivingBehaviorAlarmType{ get; set; } | |||||
/// <summary> | |||||
/// 疲劳程度 | |||||
/// </summary> | |||||
public byte FatigueLevel { get; set; } | |||||
public void Analyze(ref JT808MessagePackReader reader, Utf8JsonWriter writer, IJT808Config config) | |||||
{ | |||||
JT808_0x0200_0x18 value = new JT808_0x0200_0x18(); | |||||
value.AttachInfoId = reader.ReadByte(); | |||||
writer.WriteNumber($"[{value.AttachInfoId.ReadNumber()}]附加信息Id", value.AttachInfoId); | |||||
value.AttachInfoLength = reader.ReadByte(); | |||||
writer.WriteNumber($"[{value.AttachInfoLength.ReadNumber()}]附加信息长度", value.AttachInfoLength); | |||||
value.AbnormalDrivingBehaviorAlarmType = reader.ReadUInt16(); | |||||
writer.WriteNumber($"[{value.AbnormalDrivingBehaviorAlarmType.ReadNumber()}]异常驾驶行为报警类型", value.AbnormalDrivingBehaviorAlarmType); | |||||
writer.WriteStartArray("视频信号遮挡报警状态集合"); | |||||
var abnormalDrivingBehaviorAlarmTypeSpan = Convert.ToString(value.AbnormalDrivingBehaviorAlarmType, 2).PadLeft(16, '0').AsSpan(); | |||||
int index = 0; | |||||
foreach (var item in abnormalDrivingBehaviorAlarmTypeSpan) | |||||
{ | |||||
string tmpResult = item == '1' ? "有" : "无"; | |||||
if (index == 0) | |||||
{ | |||||
writer.WriteStringValue($"[bit{index}疲劳]_{tmpResult}"); | |||||
} | |||||
else if (index == 1) | |||||
{ | |||||
writer.WriteStringValue($"[bit{index}打电话]_{tmpResult}"); | |||||
} | |||||
else if (index == 2) | |||||
{ | |||||
writer.WriteStringValue($"[bit{index}抽烟]_{tmpResult}"); | |||||
} | |||||
else if (index>=3 && index<=10) | |||||
{ | |||||
writer.WriteStringValue($"[bit{index}保留]_{tmpResult}"); | |||||
} | |||||
else | |||||
{ | |||||
writer.WriteStringValue($"[bit{index}自定义]_{tmpResult}"); | |||||
} | |||||
index++; | |||||
} | |||||
writer.WriteEndArray(); | |||||
value.FatigueLevel = reader.ReadByte(); | |||||
writer.WriteNumber($"[{value.FatigueLevel.ReadNumber()}]疲劳程度", value.FatigueLevel); | |||||
} | |||||
public JT808_0x0200_0x18 Deserialize(ref JT808MessagePackReader reader, IJT808Config config) | |||||
{ | |||||
JT808_0x0200_0x18 value = new JT808_0x0200_0x18(); | |||||
value.AttachInfoId = reader.ReadByte(); | |||||
value.AttachInfoLength = reader.ReadByte(); | |||||
value.AbnormalDrivingBehaviorAlarmType = reader.ReadUInt16(); | |||||
value.FatigueLevel = reader.ReadByte(); | |||||
return value; | |||||
} | |||||
public void Serialize(ref JT808MessagePackWriter writer, JT808_0x0200_0x18 value, IJT808Config config) | |||||
{ | |||||
writer.WriteByte(value.AttachInfoId); | |||||
writer.WriteByte(value.AttachInfoLength); | |||||
writer.WriteUInt16(value.AbnormalDrivingBehaviorAlarmType); | |||||
writer.WriteByte(value.FatigueLevel); | |||||
} | |||||
} | |||||
} |