@@ -17,11 +17,3 @@ jobs: | |||
run: dotnet --info | |||
- name: dotnet restore | |||
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) | |||
3. [设备终端上传的实时音视频流数据也就是视频服务器](#1078) | |||
3.1 [将1078的数据(h264)编码成FLV](#1078flv) | |||
@@ -20,8 +20,6 @@ | |||
| Install-Package JT1078.Flv |  |  | | |||
| Install-Package JT1078.Hls |  |  | | |||
| Install-Package JT1078.FMp4 |  |  | | |||
| Install-Package JT808.Protocol.Extensions.JT1078 |  |  | | |||
| Install-Package JT809.Protocol.Extensions.JT1078 |  |  | | |||
## <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** | | |||
| 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 | | |||
## <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 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 | |||
@@ -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 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/ |
@@ -0,0 +1,2 @@ | |||
[Settings] | |||
language=Chinese |
@@ -12,10 +12,14 @@ | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\JT1078.Flv\JT1078.Flv.csproj" /> | |||
<ProjectReference Include="..\JT1078.FMp4\JT1078.FMp4.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="..\..\doc\video\jt1078_1.txt" Link="jt1078_1.txt"> | |||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
</None> | |||
<None Include="..\..\doc\video\jt1078_3.txt" Link="jt1078_3.txt"> | |||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
</None> | |||
</ItemGroup> | |||
</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) | |||
{ | |||
Summary summary = BenchmarkRunner.Run<JT1078FlvEncoderContext>(); | |||
Summary summary = BenchmarkRunner.Run<JT1078AVEncoderContext>(); | |||
} | |||
} | |||
} |
@@ -172,7 +172,7 @@ namespace JT1078.FMp4.Test | |||
//stbl | |||
SampleTableBox sampleTableBox = new SampleTableBox(); | |||
//stbl->stsd | |||
SampleDescriptionBox sampleDescriptionBox = new SampleDescriptionBox(HandlerType.none); | |||
SampleDescriptionBox sampleDescriptionBox = new SampleDescriptionBox(); | |||
//stbl->stsd->avc1 | |||
AVC1SampleEntry aVC1SampleEntry = new AVC1SampleEntry(); | |||
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"> | |||
<head> | |||
<meta charset="utf-8" /> | |||
<title>fmp4 demo</title> | |||
<title>WebSocket MSE Fmp4 demo</title> | |||
<script type="text/javascript" src="signalr.min.js"></script> | |||
</head> | |||
<body> | |||
<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>--> | |||
<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> | |||
</body> | |||
</html> |
@@ -58,6 +58,12 @@ | |||
<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> |
@@ -2,15 +2,18 @@ | |||
using JT1078.FMp4.MessagePack; | |||
using JT1078.FMp4.Samples; | |||
using JT1078.Protocol; | |||
using JT1078.Protocol.Enums; | |||
using JT1078.Protocol.Extensions; | |||
using JT1078.Protocol.H264; | |||
using JT1078.Protocol.MessagePack; | |||
using System; | |||
using System.Buffers; | |||
using System.Buffers.Binary; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using Xunit; | |||
@@ -24,10 +27,10 @@ namespace JT1078.FMp4.Test | |||
var jT1078Package = ParseNALUTest(); | |||
H264Decoder decoder = new H264Decoder(); | |||
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 | |||
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); | |||
//ftyp | |||
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.Add(new DataEntryUrlBox(1)); | |||
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>(); | |||
AVC1SampleEntry avc1 = new AVC1SampleEntry(); | |||
avc1.AVCConfigurationBox = new AVCConfigurationBox(); | |||
@@ -176,10 +179,10 @@ namespace JT1078.FMp4.Test | |||
var jT1078Package = ParseNALUTest(); | |||
H264Decoder decoder = new H264Decoder(); | |||
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 | |||
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); | |||
FMp4MessagePackWriter writer = new FMp4MessagePackWriter(new byte[65535]); | |||
//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.Add(new DataEntryUrlBox(1)); | |||
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>(); | |||
AVC1SampleEntry avc1 = new AVC1SampleEntry(); | |||
avc1.AVCConfigurationBox = new AVCConfigurationBox(); | |||
@@ -277,7 +280,7 @@ namespace JT1078.FMp4.Test | |||
fragmentBox.MediaDataBox = new MediaDataBox(); | |||
fragmentBox.MediaDataBox.Data = nalus.Select(s => s.RawData).ToList(); | |||
moofs.Add(fragmentBox); | |||
foreach(var moof in moofs) | |||
foreach (var moof in moofs) | |||
{ | |||
moof.ToBuffer(ref writer); | |||
} | |||
@@ -324,10 +327,10 @@ namespace JT1078.FMp4.Test | |||
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write); | |||
var jT1078Package = packages.FirstOrDefault(); | |||
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 | |||
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); | |||
ExpGolombReader h264GolombReader = new ExpGolombReader(spsNALU.RawData); | |||
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.Add(new DataEntryUrlBox(1)); | |||
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>(); | |||
AVC1SampleEntry avc1 = new AVC1SampleEntry(); | |||
avc1.AVCConfigurationBox = new AVCConfigurationBox(); | |||
@@ -437,26 +440,197 @@ namespace JT1078.FMp4.Test | |||
public void Test4() | |||
{ | |||
FMp4Encoder fMp4Encoder = new FMp4Encoder(); | |||
H264Decoder h264Decoder = new H264Decoder(); | |||
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)) | |||
{ | |||
File.Delete(filepath); | |||
} | |||
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); | |||
nalus.Clear(); | |||
} | |||
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] | |||
public void tkhd_width_height_test() | |||
{ | |||
@@ -464,7 +638,7 @@ namespace JT1078.FMp4.Test | |||
//01 20 00 00 | |||
var a = BinaryPrimitives.ReadUInt32LittleEndian(new byte[] { 0x01, 0x60, 0, 0 }); | |||
var b = BinaryPrimitives.ReadUInt32LittleEndian(new byte[] { 0x01, 0x20, 0, 0 }); | |||
//00 00 01 60 | |||
//00 00 01 20 | |||
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 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]) | |||
{ | |||
} | |||
} | |||
} | |||
@@ -544,5 +718,24 @@ namespace JT1078.FMp4.Test | |||
} | |||
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); | |||
MovieHeaderBox.ToBuffer(ref writer); | |||
TrackBox.ToBuffer(ref writer); | |||
MovieExtendsBox.ToBuffer(ref writer); | |||
if (MovieExtendsBox != null) | |||
{ | |||
MovieExtendsBox.ToBuffer(ref writer); | |||
} | |||
if (UserDataBox != null) | |||
{ | |||
UserDataBox.ToBuffer(ref writer); | |||
@@ -38,6 +38,12 @@ namespace JT1078.FMp4 | |||
TrackFragmentBox.ToBuffer(ref writer); | |||
} | |||
End(ref writer); | |||
var moofOffsetPosition = writer.GetMoofOffsetPosition(); | |||
if (moofOffsetPosition > 0) | |||
{ | |||
writer.WriteUInt64Return((ulong)writer.GetCurrentPosition(), moofOffsetPosition); | |||
} | |||
writer.ClearMoofOffsetPosition(); | |||
var trunOffsetPosition = writer.GetTrunOffsetPosition(); | |||
if (trunOffsetPosition > 0) | |||
{ | |||
@@ -38,7 +38,7 @@ namespace JT1078.FMp4 | |||
MovieFragmentRandomAccessOffsetBox.ToBuffer(ref writer); | |||
} | |||
End(ref writer); | |||
var mfraSizePosition = writer.GetMfraSizePositionn(); | |||
var mfraSizePosition = writer.GetMfraSizePosition(); | |||
if (mfraSizePosition > 0) | |||
{ | |||
writer.WriteInt32Return(writer.GetCurrentPosition() - SizePosition, mfraSizePosition); | |||
@@ -30,15 +30,13 @@ namespace JT1078.FMp4 | |||
WriterFullBoxToBuffer(ref writer); | |||
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); | |||
@@ -50,9 +48,9 @@ namespace JT1078.FMp4 | |||
public byte SampleDependsOn { get; set; } | |||
public byte SampleIsDependedOn { get; set; } | |||
public byte SampleHasRedundancy { get; set; } | |||
public byte DegradPrio { get; set; } | |||
//public byte DegradPrio { 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> | |||
/// stsd | |||
/// </summary> | |||
/// <param name="handlerType"></param> | |||
/// <param name="version"></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 void ToBuffer(ref FMp4MessagePackWriter writer) | |||
{ | |||
@@ -26,6 +26,10 @@ namespace JT1078.FMp4 | |||
/// </summary> | |||
public TimeToSampleBox TimeToSampleBox { get; set; } | |||
/// <summary> | |||
/// stss | |||
/// </summary> | |||
public SyncSampleBox SyncSampleBox { get; set; } | |||
/// <summary> | |||
/// ctts | |||
/// </summary> | |||
public CompositionOffsetBox CompositionOffsetBox { get; set; } | |||
@@ -44,7 +48,6 @@ namespace JT1078.FMp4 | |||
/// </summary> | |||
public ChunkOffsetBox ChunkOffsetBox { get; set; } | |||
//public ChunkLargeOffsetBox ChunkLargeOffsetBox { get; set; } | |||
//public SyncSampleBox SyncSampleBox { get; set; } | |||
//public ShadowSyncSampleBox ShadowSyncSampleBox { get; set; } | |||
//public PaddingBitsBox PaddingBitsBox { get; set; } | |||
//public DegradationPriorityBox DegradationPriorityBox { get; set; } | |||
@@ -58,6 +61,10 @@ namespace JT1078.FMp4 | |||
Start(ref writer); | |||
SampleDescriptionBox.ToBuffer(ref writer); | |||
TimeToSampleBox.ToBuffer(ref writer); | |||
if (SyncSampleBox != null) | |||
{ | |||
SyncSampleBox.ToBuffer(ref writer); | |||
} | |||
if(CompositionOffsetBox!=null) | |||
CompositionOffsetBox.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.Text; | |||
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) | |||
{ | |||
ReferencedSizePositions = new List<int>(); | |||
} | |||
public uint ReferenceID { get; set; } | |||
public string Timescale { get; set; } | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
public ulong EarliestPresentationTimeLarge { get; set; } | |||
public ulong FirstOffsetLarge { get; set; } | |||
public uint ReferenceID { get; set; } = 1; | |||
/// <summary> | |||
/// if(version==0) | |||
/// | |||
/// </summary> | |||
public uint EarliestPresentationTime { get; set; } | |||
public uint Timescale { get; set; } = 1000; | |||
/// <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> | |||
public uint FirstOffset { get; set; } | |||
public ushort Reserved { get; set; } | |||
public ushort ReferenceCount { 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 | |||
{ | |||
/// <summary> | |||
/// 4byte 32 - 1 | |||
/// </summary> | |||
public bool ReferenceType { get; set; } | |||
public bool ReferenceType { get; set; } = false; | |||
/// <summary> | |||
/// 4byte 32 - 31 | |||
/// ReferencedSize=(moof size) + (mdat size) | |||
/// </summary> | |||
public uint ReferencedSize { get; set; } = 0; | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
public uint ReferencedSize { get; set; } | |||
public uint SubsegmentDuration { get; set; } | |||
/// <summary> | |||
/// 4byte 32 - 1 | |||
/// </summary> | |||
public bool StartsWithSAP { get; set; } | |||
public bool StartsWithSAP { get; set; } = true; | |||
/// <summary> | |||
/// 4byte 32 - 3 | |||
/// </summary> | |||
public byte SAPType { get; set; } | |||
public byte SAPType { get; set; } = 1; | |||
/// <summary> | |||
/// 4byte 32 - 28 | |||
/// </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.Text; | |||
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 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; } | |||
/// <summary> | |||
/// TFHD_FLAG_DEFAULT_FLAGS | |||
/// MOV_AUDIO == handler_type ? 0x02000000 : (0x00010000| 0x01000000); | |||
/// </summary> | |||
public uint DefaultSampleFlags { get; set; } | |||
#endregion | |||
@@ -59,7 +60,16 @@ namespace JT1078.FMp4 | |||
writer.WriteUInt32(TrackID); | |||
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) | |||
{ | |||
@@ -13,13 +13,27 @@ namespace JT1078.FMp4 | |||
{ | |||
/// <summary> | |||
/// 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> | |||
/// <param name="version"></param> | |||
/// <param name="flags"></param> | |||
public TrackRunBox(byte version=0, uint flags= 0x000f01) : base("trun", version, flags) | |||
{ | |||
} | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
public uint SampleCount { get; set; } | |||
/// <summary> | |||
/// 可选的 | |||
@@ -88,11 +102,11 @@ namespace JT1078.FMp4 | |||
{ | |||
if (Version == 0) | |||
{ | |||
writer.WriteUInt32(trun.SampleCompositionTimeOffset); | |||
writer.WriteUInt32((uint)trun.SampleCompositionTimeOffset); | |||
} | |||
else | |||
{ | |||
writer.WriteInt32(trun.SignedSampleCompositionTimeOffset); | |||
writer.WriteInt32((int)trun.SampleCompositionTimeOffset); | |||
} | |||
} | |||
} | |||
@@ -106,10 +120,12 @@ namespace JT1078.FMp4 | |||
public uint SampleSize { get; set; } | |||
public uint SampleFlags { get; set; } | |||
/// <summary> | |||
/// cts | |||
/// version == 0 | |||
/// 0:uint | |||
/// >0:int | |||
/// </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.Samples; | |||
using JT1078.Protocol; | |||
using JT1078.Protocol.Enums; | |||
using JT1078.Protocol.H264; | |||
using JT1078.Protocol.MessagePack; | |||
using System; | |||
@@ -17,65 +18,92 @@ namespace JT1078.FMp4 | |||
/// stream data | |||
/// ftyp | |||
/// moov | |||
/// styp 1 | |||
/// moof 1 | |||
/// mdat 1 | |||
/// ... | |||
/// styp n | |||
/// moof n | |||
/// mdat n | |||
/// mfra | |||
/// ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing | |||
/// </summary> | |||
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> | |||
public FMp4Encoder() | |||
{ | |||
h264Decoder = new H264Decoder(); | |||
TrackInfos = new Dictionary<string, TrackInfo>(StringComparer.OrdinalIgnoreCase); | |||
} | |||
/// <summary> | |||
/// 编码首个视频盒子 | |||
/// 编码ftyp盒子 | |||
/// </summary> | |||
/// <param name="package">jt1078完整包</param> | |||
/// <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 | |||
{ | |||
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 | |||
FileTypeBox fileTypeBox = new FileTypeBox(); | |||
fileTypeBox.MajorBrand = "isom"; | |||
fileTypeBox.MinorVersion = "\0\0\u0002\0"; | |||
fileTypeBox.CompatibleBrands.Add("isom"); | |||
fileTypeBox.CompatibleBrands.Add("iso6"); | |||
fileTypeBox.CompatibleBrands.Add("iso2"); | |||
fileTypeBox.CompatibleBrands.Add("avc1"); | |||
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 | |||
MovieBox movieBox = new MovieBox(); | |||
movieBox.MovieHeaderBox = new MovieHeaderBox(0, 0); | |||
movieBox.MovieHeaderBox = new MovieHeaderBox(0, 2); | |||
movieBox.MovieHeaderBox.CreationTime = 0; | |||
movieBox.MovieHeaderBox.ModificationTime = 0; | |||
movieBox.MovieHeaderBox.Duration = 0; | |||
movieBox.MovieHeaderBox.Timescale = 1000; | |||
movieBox.MovieHeaderBox.NextTrackID = 2; | |||
movieBox.MovieHeaderBox.NextTrackID = 99; | |||
movieBox.TrackBox = new TrackBox(); | |||
movieBox.TrackBox.TrackHeaderBox = new TrackHeaderBox(0, 3); | |||
movieBox.TrackBox.TrackHeaderBox.CreationTime = 0; | |||
movieBox.TrackBox.TrackHeaderBox.ModificationTime = 0; | |||
movieBox.TrackBox.TrackHeaderBox.TrackID = 1; | |||
movieBox.TrackBox.TrackHeaderBox.TrackID = TrackID; | |||
movieBox.TrackBox.TrackHeaderBox.Duration = 0; | |||
movieBox.TrackBox.TrackHeaderBox.TrackIsAudio = false; | |||
movieBox.TrackBox.TrackHeaderBox.Width = (uint)spsInfo.width; | |||
@@ -84,7 +112,8 @@ namespace JT1078.FMp4 | |||
movieBox.TrackBox.MediaBox.MediaHeaderBox = new MediaHeaderBox(); | |||
movieBox.TrackBox.MediaBox.MediaHeaderBox.CreationTime = 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.HandlerBox = new HandlerBox(); | |||
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.Add(new DataEntryUrlBox(1)); | |||
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>(); | |||
AVC1SampleEntry avc1 = new AVC1SampleEntry(); | |||
avc1.AVCConfigurationBox = new AVCConfigurationBox(); | |||
@@ -106,23 +135,23 @@ namespace JT1078.FMp4 | |||
avc1.AVCConfigurationBox.AVCLevelIndication = spsInfo.levelIdc; | |||
avc1.AVCConfigurationBox.AVCProfileIndication = spsInfo.profileIdc; | |||
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.TimeToSampleBox = new TimeToSampleBox(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SyncSampleBox = new SyncSampleBox(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleToChunkBox = new SampleToChunkBox(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleSizeBox = new SampleSizeBox(); | |||
movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.ChunkOffsetBox = new ChunkOffsetBox(); | |||
movieBox.MovieExtendsBox = new MovieExtendsBox(); | |||
movieBox.MovieExtendsBox.TrackExtendsBoxs = new List<TrackExtendsBox>(); | |||
TrackExtendsBox trex = new TrackExtendsBox(); | |||
trex.TrackID = 1; | |||
trex.DefaultSampleDescriptionIndex = 1; | |||
trex.TrackID = TrackID; | |||
trex.DefaultSampleDescriptionIndex = SampleDescriptionIndex; | |||
trex.DefaultSampleDuration = 0; | |||
trex.DefaultSampleSize = 0; | |||
trex.DefaultSampleFlags = 0; | |||
movieBox.MovieExtendsBox.TrackExtendsBoxs.Add(trex); | |||
fileTypeBox.ToBuffer(ref writer); | |||
movieBox.ToBuffer(ref writer); | |||
var data = writer.FlushAndGetArray(); | |||
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> | |||
/// <param name="package">jt1078完整包</param> | |||
/// <param name="moofOffset">moof位置</param> | |||
/// <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); | |||
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(); | |||
movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox(); | |||
movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = package.SN; | |||
movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = trackInfo.SN; | |||
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.BaseMediaDecodeTime = package.Timestamp * 1000; | |||
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = trackInfo.DTS; | |||
trackInfo.DTS += (ulong)(truns.Count * DefaultSampleDuration); | |||
TrackInfos[key] = trackInfo; | |||
//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); | |||
var mediaDataBox = new MediaDataBox(); | |||
mediaDataBox.Data = nalus.Select(s => s.RawData).ToList(); | |||
mediaDataBox.Data = rawdatas; | |||
mediaDataBox.ToBuffer(ref writer); | |||
var data = writer.FlushAndGetArray(); | |||
return data; | |||
} | |||
@@ -175,5 +285,11 @@ namespace JT1078.FMp4 | |||
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> | |||
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | |||
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | |||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||
<Version>1.0.0-preview1</Version> | |||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | |||
<Version>1.0.0-preview5</Version> | |||
<SignAssembly>false</SignAssembly> | |||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | |||
@@ -27,9 +27,7 @@ | |||
<PackagePath></PackagePath> | |||
</None> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\JT1078.Protocol\JT1078.Protocol.csproj" /> | |||
</ItemGroup> | |||
@@ -571,11 +571,10 @@ | |||
stsd | |||
</summary> | |||
</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> | |||
stsd | |||
</summary> | |||
<param name="handlerType"></param> | |||
<param name="version"></param> | |||
<param name="flags"></param> | |||
</member> | |||
@@ -622,6 +621,11 @@ | |||
stts | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.FMp4.SampleTableBox.SyncSampleBox"> | |||
<summary> | |||
stss | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.FMp4.SampleTableBox.CompositionOffsetBox"> | |||
<summary> | |||
ctts | |||
@@ -672,19 +676,44 @@ | |||
</summary> | |||
</member> | |||
<!-- 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> | |||
</member> | |||
<member name="P:JT1078.FMp4.SegmentIndexBox.EarliestPresentationTime"> | |||
<summary> | |||
if(version==0) | |||
pts | |||
if(version==0) | |||
version==0 32 bit | |||
version>0 64 bit | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.FMp4.SegmentIndexBox.FirstOffset"> | |||
<summary> | |||
if (version==0) | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndexs"> | |||
<summary> | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndex.ReferenceType"> | |||
@@ -695,6 +724,12 @@ | |||
<member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndex.ReferencedSize"> | |||
<summary> | |||
4byte 32 - 31 | |||
ReferencedSize=(moof size) + (mdat size) | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndex.SubsegmentDuration"> | |||
<summary> | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.FMp4.SegmentIndexBox.SegmentIndex.StartsWithSAP"> | |||
@@ -712,6 +747,34 @@ | |||
4byte 32 - 28 | |||
</summary> | |||
</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"> | |||
<summary> | |||
4btye 32 - 30 | |||
@@ -770,6 +833,18 @@ | |||
length:ItemCount | |||
</summary> | |||
</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"> | |||
<summary> | |||
stts | |||
@@ -908,6 +983,7 @@ | |||
<member name="P:JT1078.FMp4.TrackFragmentHeaderBox.DefaultSampleFlags"> | |||
<summary> | |||
TFHD_FLAG_DEFAULT_FLAGS | |||
MOV_AUDIO == handler_type ? 0x02000000 : (0x00010000| 0x01000000); | |||
</summary> | |||
</member> | |||
<member name="T:JT1078.FMp4.TrackFragmentRandomAccessBox"> | |||
@@ -978,10 +1054,27 @@ | |||
<member name="M:JT1078.FMp4.TrackRunBox.#ctor(System.Byte,System.UInt32)"> | |||
<summary> | |||
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> | |||
<param name="version"></param> | |||
<param name="flags"></param> | |||
</member> | |||
<member name="P:JT1078.FMp4.TrackRunBox.SampleCount"> | |||
<summary> | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.FMp4.TrackRunBox.DataOffset"> | |||
<summary> | |||
可选的 | |||
@@ -1002,7 +1095,10 @@ | |||
</member> | |||
<member name="P:JT1078.FMp4.TrackRunBox.TrackRunInfo.SampleCompositionTimeOffset"> | |||
<summary> | |||
cts | |||
version == 0 | |||
0:uint | |||
>0:int | |||
</summary> | |||
</member> | |||
<member name="T:JT1078.FMp4.URIBox"> | |||
@@ -1213,12 +1309,15 @@ | |||
stream data | |||
ftyp | |||
moov | |||
styp 1 | |||
moof 1 | |||
mdat 1 | |||
... | |||
styp n | |||
moof n | |||
mdat n | |||
mfra | |||
ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.FMp4.FMp4Encoder.#ctor"> | |||
@@ -1226,19 +1325,28 @@ | |||
</summary> | |||
</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> | |||
编码首个视频盒子 | |||
styp | |||
</summary> | |||
<param name="package">jt1078完整包</param> | |||
<returns></returns> | |||
</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> | |||
<param name="package">jt1078完整包</param> | |||
<param name="moofOffset">moof位置</param> | |||
<returns></returns> | |||
</member> | |||
<member name="P:JT1078.FMp4.FullBox.Version"> | |||
@@ -13,12 +13,14 @@ namespace JT1078.FMp4.MessagePack | |||
private int TrunDataOffsetPosition; | |||
private int MfraSizePosition; | |||
private int MoofOffsetPosition; | |||
public FMp4MessagePackWriter(Span<byte> buffer) | |||
{ | |||
this.writer = new FMp4BufferWriter(buffer); | |||
TrunDataOffsetPosition = 0; | |||
MfraSizePosition = 0; | |||
MoofOffsetPosition = 0; | |||
} | |||
public byte[] FlushAndGetArray() | |||
{ | |||
@@ -109,6 +111,10 @@ namespace JT1078.FMp4.MessagePack | |||
{ | |||
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) | |||
{ | |||
writer.Written[position] = value; | |||
@@ -139,7 +145,7 @@ namespace JT1078.FMp4.MessagePack | |||
MfraSizePosition = writer.WrittenCount; | |||
} | |||
public int GetMfraSizePositionn() | |||
public int GetMfraSizePosition() | |||
{ | |||
return MfraSizePosition; | |||
} | |||
@@ -149,6 +155,22 @@ namespace JT1078.FMp4.MessagePack | |||
MfraSizePosition = 0; | |||
} | |||
public void CreateMoofOffsetPosition() | |||
{ | |||
MoofOffsetPosition = writer.WrittenCount; | |||
} | |||
public int GetMoofOffsetPosition() | |||
{ | |||
return MoofOffsetPosition; | |||
} | |||
public void ClearMoofOffsetPosition() | |||
{ | |||
MoofOffsetPosition = 0; | |||
} | |||
/// <summary> | |||
/// ref | |||
/// </summary> | |||
@@ -40,6 +40,9 @@ namespace JT1078.Flv | |||
readonly H264Decoder h264Decoder; | |||
readonly AudioCodecFactory audioCodecFactory; | |||
//public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false) | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
public FlvEncoder() | |||
{ | |||
audioCodecFactory = new AudioCodecFactory(); | |||
@@ -207,9 +210,9 @@ namespace JT1078.Flv | |||
var nalus = h264Decoder.ParseNALU(package); | |||
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(pps); | |||
nalus.Remove(sei); | |||
@@ -297,7 +300,7 @@ namespace JT1078.Flv | |||
//1: keyframe (for AVC, a seekable frame) —— 即H.264的IDR帧; | |||
//2: inter frame(for AVC, a non - seekable frame) —— H.264的普通I帧; | |||
//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; | |||
} | |||
@@ -14,7 +14,7 @@ | |||
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | |||
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | |||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||
<Version>1.1.0</Version> | |||
<Version>1.1.1-preview1</Version> | |||
<SignAssembly>false</SignAssembly> | |||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | |||
@@ -181,6 +181,11 @@ | |||
4、<see cref="M:JT1078.Flv.FlvEncoder.EncoderAudioTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传true | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.Flv.FlvEncoder.#ctor"> | |||
<summary> | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"> | |||
<summary> | |||
编码flv头 | |||
@@ -42,5 +42,8 @@ | |||
<None Include="..\..\doc\video\jt1078_2.txt" Link="H264\jt1078_2.txt"> | |||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
</None> | |||
<None Include="..\..\doc\video\jt1078_5.txt" Link="H264\jt1078_5.txt"> | |||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | |||
</None> | |||
</ItemGroup> | |||
</Project> |
@@ -5,11 +5,54 @@ using System.IO; | |||
using System.Text; | |||
using Xunit; | |||
using JT1078.Protocol.Extensions; | |||
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; | |||
using System.Net.Sockets; | |||
using System.Threading; | |||
namespace JT1078.Hls.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> | |||
/// 生成m3u8索引文件 | |||
/// </summary> | |||
@@ -90,13 +90,13 @@ namespace JT1078.Hls.Test | |||
JT1078Package fullpackage = JT1078Serializer.Merge(package); | |||
if (fullpackage != null) | |||
{ | |||
var sdt = tSEncoder.CreateSDT(fullpackage); | |||
var sdt = tSEncoder.CreateSDT(); | |||
string sdtHEX = sdt.ToHexString(); | |||
fileStream.Write(sdt); | |||
var pat = tSEncoder.CreatePAT(fullpackage); | |||
var pat = tSEncoder.CreatePAT(); | |||
string patHEX = pat.ToHexString(); | |||
fileStream.Write(pat); | |||
var pmt = tSEncoder.CreatePMT(fullpackage); | |||
var pmt = tSEncoder.CreatePMT(); | |||
fileStream.Write(pmt); | |||
var pes = tSEncoder.CreatePES(fullpackage); | |||
fileStream.Write(pes); | |||
@@ -139,13 +139,13 @@ namespace JT1078.Hls.Test | |||
{ | |||
if (isNeedFirstHeadler) | |||
{ | |||
var sdt = tSEncoder.CreateSDT(fullpackage); | |||
var sdt = tSEncoder.CreateSDT(); | |||
string sdtHEX = sdt.ToHexString(); | |||
fileStream.Write(sdt); | |||
var pat = tSEncoder.CreatePAT(fullpackage); | |||
var pat = tSEncoder.CreatePAT(); | |||
string patHEX = pat.ToHexString(); | |||
fileStream.Write(pat); | |||
var pmt = tSEncoder.CreatePMT(fullpackage); | |||
var pmt = tSEncoder.CreatePMT(); | |||
fileStream.Write(pmt); | |||
var pes = tSEncoder.CreatePES(fullpackage, 18888); | |||
fileStream.Write(pes); | |||
@@ -7,14 +7,14 @@ | |||
<Authors>SmallChi(Koike)</Authors> | |||
<PackageId>JT1078.Hls</PackageId> | |||
<Product>JT1078.Hls</Product> | |||
<Description>基于JT1078的Hls视频编码器</Description> | |||
<Description>基于JT1078的Hls视频编码器、ts文件路径参数名改成可配置</Description> | |||
<PackageReleaseNotes>基于JT1078的Hls视频编码器</PackageReleaseNotes> | |||
<RepositoryUrl>https://github.com/SmallChi/JT1078</RepositoryUrl> | |||
<PackageProjectUrl>https://github.com/SmallChi/JT1078</PackageProjectUrl> | |||
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | |||
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | |||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||
<Version>1.1.0-preview1</Version> | |||
<Version>1.1.0-preview4</Version> | |||
<SignAssembly>false</SignAssembly> | |||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | |||
@@ -24,10 +24,6 @@ | |||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> | |||
<PackageReference Include="System.Memory" Version="4.5.4" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\JT1078.Protocol\JT1078.Protocol.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="..\..\LICENSE"> | |||
<Pack>True</Pack> | |||
@@ -37,4 +33,7 @@ | |||
<ItemGroup> | |||
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\JT1078.Protocol\JT1078.Protocol.csproj" /> | |||
</ItemGroup> | |||
</Project> |
@@ -96,6 +96,19 @@ | |||
m3u8文件管理 | |||
</summary> | |||
</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)"> | |||
<summary> | |||
生成ts和m3u8文件 | |||
@@ -132,6 +145,12 @@ | |||
<param name="key">终端号_通道号(用作目录)</param> | |||
<param name="data">文件内容</param> | |||
</member> | |||
<member name="M:JT1078.Hls.M3U8FileManage.AppendM3U8End"> | |||
<summary> | |||
添加结束标识 | |||
直播流用不到 | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.Hls.M3U8FileManage.Clear(System.String,System.Int32)"> | |||
<summary> | |||
停止观看直播时清零数据 | |||
@@ -144,6 +163,16 @@ | |||
TS文件信息 | |||
</summary> | |||
</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"> | |||
<summary> | |||
ts文件名 | |||
@@ -184,6 +213,16 @@ | |||
每个ts文件的最大时长 | |||
</summary> | |||
</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"> | |||
<summary> | |||
m3u8文件 | |||
@@ -263,6 +302,11 @@ | |||
4.PES | |||
</summary> | |||
</member> | |||
<member name="M:JT1078.Hls.TSEncoder.#ctor"> | |||
<summary> | |||
</summary> | |||
</member> | |||
<member name="P:JT1078.Hls.TS_AdaptationInfo.PCRIncluded"> | |||
<summary> | |||
取0x50表示包含PCR或0x40表示不包含PCR | |||
@@ -19,15 +19,28 @@ namespace JT1078.Hls | |||
public class M3U8FileManage | |||
{ | |||
private TSEncoder tSEncoder; | |||
public readonly M3U8Option m3U8Option; | |||
private M3U8Option m3U8Option; | |||
ConcurrentDictionary<string, TsFileInfo> curTsFileInfoDic = new ConcurrentDictionary<string, 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) | |||
{ | |||
this.tSEncoder = tSEncoder; | |||
this.m3U8Option = m3U8Option; | |||
} | |||
/// <summary> | |||
/// 生成ts和m3u8文件 | |||
/// </summary> | |||
@@ -35,9 +48,8 @@ namespace JT1078.Hls | |||
public void CreateTsData(JT1078Package jt1078Package) | |||
{ | |||
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); | |||
TSMessagePackWriter tSMessagePackWriter = new TSMessagePackWriter(buff); | |||
try | |||
@@ -50,7 +62,7 @@ namespace JT1078.Hls | |||
CreateTsFile(curTsFileInfo.FileName,key, tSMessagePackWriter.FlushAndGetArray()); | |||
curTsFileInfo.Duration = (jt1078Package.Timestamp - curTsFileInfo.TsFirst1078PackageTimeStamp) / 1000.0; | |||
//按设定的时间(默认为10秒)切分ts文件 | |||
if (curTsFileInfo.Duration > m3U8Option.TsFileMaxSecond) | |||
if (curTsFileInfo.Duration > (m3U8Option.TsFileMaxSecond-1)) | |||
{ | |||
var tsFileInfoQueue = ManageTsFileInfo(key, curTsFileInfo); | |||
CreateM3U8File(curTsFileInfo, tsFileInfoQueue); | |||
@@ -63,11 +75,11 @@ namespace JT1078.Hls | |||
curTsFileInfo.IsCreateTsFile = false; | |||
curTsFileInfo.TsFirst1078PackageTimeStamp = jt1078Package.Timestamp; | |||
curTsFileInfo.FileName = $"{curTsFileInfo.TsFileSerialNo}.ts"; | |||
var sdt = tSEncoder.CreateSDT(jt1078Package); | |||
var sdt = tSEncoder.CreateSDT(); | |||
tSMessagePackWriter.WriteArray(sdt); | |||
var pat = tSEncoder.CreatePAT(jt1078Package); | |||
var pat = tSEncoder.CreatePAT(); | |||
tSMessagePackWriter.WriteArray(pat); | |||
var pmt = tSEncoder.CreatePMT(jt1078Package); | |||
var pmt = tSEncoder.CreatePMT(); | |||
tSMessagePackWriter.WriteArray(pmt); | |||
var pes = tSEncoder.CreatePES(jt1078Package); | |||
tSMessagePackWriter.WriteArray(pes); | |||
@@ -92,7 +104,7 @@ namespace JT1078.Hls | |||
if (tsFileInfoQueue.Count >= m3U8Option.TsFileCapacity) | |||
{ | |||
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); | |||
} | |||
tsFileInfoQueue.Enqueue(curTsFileInfo); | |||
@@ -124,9 +136,10 @@ namespace JT1078.Hls | |||
{ | |||
var tsFileInfo = tsFileInfoQueue.ElementAt(i); | |||
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()); | |||
fs.Write(buffer,0, buffer.Length); | |||
@@ -142,9 +155,17 @@ namespace JT1078.Hls | |||
{ | |||
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); | |||
} | |||
else { | |||
curTsFileInfo.Sim = key.Split('_')[0]; | |||
curTsFileInfo.ChannelNo = key.Split('_')[1]; | |||
} | |||
return curTsFileInfo; | |||
} | |||
/// <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> | |||
/// 停止观看直播时清零数据 | |||
@@ -193,6 +214,14 @@ namespace JT1078.Hls | |||
/// </summary> | |||
internal class TsFileInfo | |||
{ | |||
/// <summary> | |||
/// 设备手机号 | |||
/// </summary> | |||
public string Sim { get; set; } | |||
/// <summary> | |||
/// 设备逻辑通道号 | |||
/// </summary> | |||
public string ChannelNo { get; set; } | |||
/// <summary> | |||
/// ts文件名 | |||
/// </summary> | |||
@@ -18,12 +18,20 @@ namespace JT1078.Hls.Options | |||
/// </summary> | |||
public int TsFileMaxSecond { get; set; } = 10; | |||
/// <summary> | |||
/// ts路径sim参数名称 | |||
/// </summary> | |||
public string TsPathSimParamName { get; set; } = "sim"; | |||
/// <summary> | |||
/// ts路径通道参数名称 | |||
/// </summary> | |||
public string TsPathChannelParamName { get; set; } = "channel"; | |||
/// <summary> | |||
/// m3u8文件 | |||
/// </summary> | |||
public string M3U8FileName { get; set; } = "live.m3u8"; | |||
/// <summary> | |||
/// hls文件路径(包括m3u8路径,ts路径) | |||
/// </summary> | |||
public string HlsFileDirectory { get; set; } | |||
public string HlsFileDirectory { get; set; } = "wwwroot"; | |||
} | |||
} |
@@ -36,12 +36,15 @@ namespace JT1078.Hls | |||
//todo:音频同步 | |||
//private Dictionary<string, byte> AudioCounter = new Dictionary<string, byte>(); | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
public TSEncoder() | |||
{ | |||
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); | |||
try | |||
@@ -85,7 +88,7 @@ namespace JT1078.Hls | |||
TSArrayPool.Return(buffer); | |||
} | |||
} | |||
public byte[] CreatePAT(JT1078Package jt1078Package, int minBufferSize = 188) | |||
public byte[] CreatePAT(int minBufferSize = 188) | |||
{ | |||
byte[] buffer = TSArrayPool.Rent(minBufferSize); | |||
try | |||
@@ -111,7 +114,7 @@ namespace JT1078.Hls | |||
TSArrayPool.Return(buffer); | |||
} | |||
} | |||
public byte[] CreatePMT(JT1078Package jt1078Package, int minBufferSize = 188) | |||
public byte[] CreatePMT(int minBufferSize = 188) | |||
{ | |||
byte[] buffer = TSArrayPool.Rent(minBufferSize); | |||
try | |||
@@ -139,7 +142,7 @@ namespace JT1078.Hls | |||
TSArrayPool.Return(buffer); | |||
} | |||
} | |||
public byte[] CreatePES(JT1078Package jt1078Package, int minBufferSize = 1024) | |||
public byte[] CreatePES(in JT1078Package jt1078Package, int minBufferSize = 1024) | |||
{ | |||
//将1078一帧的数据拆分成一小段一小段的PES包 | |||
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]; | |||
Assert.Equal(0, nalu.NALUHeader.ForbiddenZeroBit); | |||
Assert.Equal(3, nalu.NALUHeader.NalRefIdc); | |||
Assert.Equal(1, nalu.NALUHeader.NalUnitType); | |||
Assert.Equal(NalUnitType.SLICE, nalu.NALUHeader.NalUnitType); | |||
} | |||
[Fact] | |||
@@ -48,7 +48,7 @@ namespace JT1078.Protocol.Test.H264 | |||
Assert.Equal(4, nalus.Count); | |||
//SPS -> 7 | |||
var spsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 7); | |||
var spsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.SPS); | |||
Assert.NotNull(spsNALU); | |||
spsNALU.RawData = decoder.DiscardEmulationPreventionBytes(spsNALU.RawData); | |||
//"Z00AFJWoWCWQ" | |||
@@ -63,15 +63,15 @@ namespace JT1078.Protocol.Test.H264 | |||
Assert.Equal(352, spsInfo.width); | |||
//PPS -> 8 | |||
var ppsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 8); | |||
var ppsNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.PPS); | |||
Assert.NotNull(ppsNALU); | |||
//IDR -> 5 关键帧 | |||
var idrNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 5); | |||
var idrNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.IDR); | |||
Assert.NotNull(idrNALU); | |||
//SEI -> 6 | |||
var seiNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == 6); | |||
var seiNALU = nalus.FirstOrDefault(n => n.NALUHeader.NalUnitType == NalUnitType.SEI); | |||
Assert.NotNull(seiNALU); | |||
} | |||
@@ -95,5 +95,47 @@ namespace JT1078.Protocol.Test.H264 | |||
} | |||
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); | |||
Assert.Equal(1, header.ForbiddenZeroBit); | |||
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"> | |||
<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> |
@@ -336,13 +336,13 @@ namespace JT1078.Protocol.Test | |||
[Fact] | |||
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; | |||
int mergeBodyLength=0; | |||
foreach (var line in lines) | |||
{ | |||
var data = line.Split(','); | |||
var bytes = data[5].ToHexBytes(); | |||
var bytes = data[6].ToHexBytes(); | |||
JT1078Package package = JT1078Serializer.Deserialize(bytes); | |||
mergeBodyLength += package.DataBodyLength; | |||
merge = JT1078Serializer.Merge(package); | |||
@@ -18,7 +18,7 @@ namespace JT1078.Protocol.H264 | |||
public List<H264NALU> ParseNALU(JT1078Package package, string key = null) | |||
{ | |||
List<H264NALU> h264NALUs = new List<H264NALU>(); | |||
int i=0,state=0; | |||
int i=0,state=0,laststate=0; | |||
int? lastIndex=null; | |||
int length = package.Bodies.Length; | |||
byte value; | |||
@@ -45,9 +45,10 @@ namespace JT1078.Protocol.H264 | |||
if (lastIndex.HasValue) | |||
{ | |||
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; | |||
laststate = state+1; | |||
state = 0; | |||
} | |||
else | |||
@@ -61,11 +62,69 @@ namespace JT1078.Protocol.H264 | |||
} | |||
if (lastIndex.HasValue) | |||
{ | |||
h264NALUs.Add(Create(package, buffer.Slice(lastIndex.Value), 4)); | |||
h264NALUs.Add(Create(package, buffer.Slice(lastIndex.Value), laststate)); | |||
} | |||
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) | |||
{ | |||
H264NALU nALU = new H264NALU(); | |||
@@ -75,6 +134,7 @@ namespace JT1078.Protocol.H264 | |||
nALU.LastFrameInterval = package.LastFrameInterval; | |||
nALU.LastIFrameInterval = package.LastIFrameInterval; | |||
nALU.Timestamp = package.Timestamp; | |||
nALU.Slice = (nalu[1] & 0x80)== 0x80; | |||
nALU.RawData = nalu.ToArray(); | |||
if (startCodePrefix == 3) | |||
{ | |||
@@ -40,6 +40,12 @@ namespace JT1078.Protocol.H264 | |||
/// 当数据类型为01000时,则没有该字段 | |||
/// </summary> | |||
public ulong Timestamp { get; set; } | |||
/// <summary> | |||
/// 是否切片 0x80 | |||
/// H264 NALU slice first_mb_in_slice | |||
/// </summary> | |||
public bool Slice { get; set; } | |||
/// <summary> | |||
/// 数据体 | |||
/// </summary> | |||
@@ -10,16 +10,33 @@ namespace JT1078.Protocol.H264 | |||
{ | |||
ForbiddenZeroBit = (value & 0x80) >> 7; | |||
NalRefIdc = (value & 0x60) >> 5; | |||
NalUnitType = value & 0x1f; | |||
NalUnitType = (NalUnitType)(value & 0x1f); | |||
} | |||
public NALUHeader(ReadOnlySpan<byte> value) | |||
{ | |||
ForbiddenZeroBit = (value[0] & 0x80) >> 7; | |||
NalRefIdc = (value[0] & 0x60) >> 5; | |||
NalUnitType = value[0] & 0x1f; | |||
NalUnitType = (NalUnitType)(value[0] & 0x1f); | |||
} | |||
public int ForbiddenZeroBit { 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> | |||
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | |||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | |||
<Version>1.1.0</Version> | |||
<Version>1.1.1-preview1</Version> | |||
<SignAssembly>false</SignAssembly> | |||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | |||
@@ -122,6 +122,16 @@ | |||
<param name="key"></param> | |||
<returns></returns> | |||
</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})"> | |||
<summary> | |||
Expunge any "Emulation Prevention" bytes from a "Raw Byte Sequence Payload" | |||
@@ -166,6 +176,12 @@ | |||
当数据类型为01000时,则没有该字段 | |||
</summary> | |||
</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"> | |||
<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 | |||
VisualStudioVersion = 16.0.29020.237 | |||
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}" | |||
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}" | |||
EndProject | |||
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 | |||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.AV.Benchmark", "JT1078.AV.Benchmark\JT1078.AV.Benchmark.csproj", "{93D6C094-5A3A-4DFA-B52B-605FDFFB6094}" | |||
EndProject | |||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.SignalR.Test", "JT1078.SignalR.Test\JT1078.SignalR.Test.csproj", "{6A063AF3-611F-4A1C-ACCF-BF903B7C7014}" | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
Debug|Any CPU = Debug|Any CPU | |||
@@ -50,30 +44,6 @@ Global | |||
Release|x86 = Release|x86 | |||
EndGlobalSection | |||
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.Build.0 = 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|x86.ActiveCfg = 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.Build.0 = 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|x86.ActiveCfg = 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 | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
EndGlobalSection | |||
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} | |||
{77402142-54E5-4E64-8F9E-BCAAC2CD0E8D} = {807ADB1F-FED4-4A56-82D2-F08F1FB7C886} | |||
{D13FE092-1D11-4545-A322-9F06BCDAC0FD} = {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} | |||
{93D6C094-5A3A-4DFA-B52B-605FDFFB6094} = {807ADB1F-FED4-4A56-82D2-F08F1FB7C886} | |||
{6A063AF3-611F-4A1C-ACCF-BF903B7C7014} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | |||
EndGlobalSection | |||
GlobalSection(ExtensibilityGlobals) = postSolution | |||
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); | |||
} | |||
} | |||
} |