From 84151663464a9cb0e84efef0599aa3ed33af91b3 Mon Sep 17 00:00:00 2001
From: "SmallChi(Koike)" <564952747@qq.com>
Date: Fri, 26 Mar 2021 18:58:52 +0800
Subject: [PATCH] =?UTF-8?q?=E5=B0=861078=E8=BD=ACfmp4=5F8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../H264/.vscode/settings.json | 3 +
src/JT1078.FMp4.Test/H264/index.html | 221 ++++++++++++---
src/JT1078.FMp4.Test/H264/signalr.min.js | 17 ++
src/JT1078.FMp4.Test/JT1078ToFMp4Box_Test.cs | 34 ++-
src/JT1078.FMp4/Boxs/MovieBox.cs | 5 +-
src/JT1078.FMp4/FMp4Encoder.cs | 257 +++++++++++++++++-
src/JT1078.FMp4/JT1078.FMp4.xml | 25 ++
src/JT1078.SignalR.Test/Hubs/FMp4Hub.cs | 48 ++++
.../JT1078.SignalR.Test.csproj | 24 ++
src/JT1078.SignalR.Test/Program.cs | 26 ++
.../Services/ToWebSocketService.cs | 108 ++++++++
src/JT1078.SignalR.Test/Services/WsSession.cs | 33 +++
src/JT1078.SignalR.Test/Startup.cs | 66 +++++
.../appsettings.Development.json | 9 +
src/JT1078.SignalR.Test/appsettings.json | 10 +
src/JT1078.sln | 15 +
16 files changed, 848 insertions(+), 53 deletions(-)
create mode 100644 src/JT1078.FMp4.Test/H264/.vscode/settings.json
create mode 100644 src/JT1078.FMp4.Test/H264/signalr.min.js
create mode 100644 src/JT1078.SignalR.Test/Hubs/FMp4Hub.cs
create mode 100644 src/JT1078.SignalR.Test/JT1078.SignalR.Test.csproj
create mode 100644 src/JT1078.SignalR.Test/Program.cs
create mode 100644 src/JT1078.SignalR.Test/Services/ToWebSocketService.cs
create mode 100644 src/JT1078.SignalR.Test/Services/WsSession.cs
create mode 100644 src/JT1078.SignalR.Test/Startup.cs
create mode 100644 src/JT1078.SignalR.Test/appsettings.Development.json
create mode 100644 src/JT1078.SignalR.Test/appsettings.json
diff --git a/src/JT1078.FMp4.Test/H264/.vscode/settings.json b/src/JT1078.FMp4.Test/H264/.vscode/settings.json
new file mode 100644
index 0000000..6f3a291
--- /dev/null
+++ b/src/JT1078.FMp4.Test/H264/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "liveServer.settings.port": 5501
+}
\ No newline at end of file
diff --git a/src/JT1078.FMp4.Test/H264/index.html b/src/JT1078.FMp4.Test/H264/index.html
index 57ef48a..4f25200 100644
--- a/src/JT1078.FMp4.Test/H264/index.html
+++ b/src/JT1078.FMp4.Test/H264/index.html
@@ -3,47 +3,202 @@
- fmp4 demo
+ WebSocket MSE Fmp4 demo
+
MSE FMp4 Demo
-
+
+
\ No newline at end of file
diff --git a/src/JT1078.FMp4.Test/H264/signalr.min.js b/src/JT1078.FMp4.Test/H264/signalr.min.js
new file mode 100644
index 0000000..d37bd31
--- /dev/null
+++ b/src/JT1078.FMp4.Test/H264/signalr.min.js
@@ -0,0 +1,17 @@
+(function webpackUniversalModuleDefinition(root,factory){if(typeof exports==="object"&&typeof module==="object")module.exports=factory();else if(typeof define==="function"&&define.amd)define([],factory);else if(typeof exports==="object")exports["signalR"]=factory();else root["signalR"]=factory()})(window,function(){return function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!=="undefined"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"})}Object.defineProperty(exports,"__esModule",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value==="object"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,"default",{enumerable:true,value:value});if(mode&2&&typeof value!="string")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module["default"]}:function getModuleExports(){return module};__webpack_require__.d(getter,"a",getter);return getter};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p="";return __webpack_require__(__webpack_require__.s=0)}([function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);var es6_promise_dist_es6_promise_auto_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(1);var es6_promise_dist_es6_promise_auto_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(es6_promise_dist_es6_promise_auto_js__WEBPACK_IMPORTED_MODULE_0__);var _index__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(3);__webpack_require__.d(__webpack_exports__,"VERSION",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["VERSION"]});__webpack_require__.d(__webpack_exports__,"AbortError",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["AbortError"]});__webpack_require__.d(__webpack_exports__,"HttpError",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HttpError"]});__webpack_require__.d(__webpack_exports__,"TimeoutError",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["TimeoutError"]});__webpack_require__.d(__webpack_exports__,"HttpClient",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HttpClient"]});__webpack_require__.d(__webpack_exports__,"HttpResponse",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HttpResponse"]});__webpack_require__.d(__webpack_exports__,"DefaultHttpClient",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["DefaultHttpClient"]});__webpack_require__.d(__webpack_exports__,"HubConnection",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HubConnection"]});__webpack_require__.d(__webpack_exports__,"HubConnectionState",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HubConnectionState"]});__webpack_require__.d(__webpack_exports__,"HubConnectionBuilder",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HubConnectionBuilder"]});__webpack_require__.d(__webpack_exports__,"MessageType",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["MessageType"]});__webpack_require__.d(__webpack_exports__,"LogLevel",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["LogLevel"]});__webpack_require__.d(__webpack_exports__,"HttpTransportType",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HttpTransportType"]});__webpack_require__.d(__webpack_exports__,"TransferFormat",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["TransferFormat"]});__webpack_require__.d(__webpack_exports__,"NullLogger",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["NullLogger"]});__webpack_require__.d(__webpack_exports__,"JsonHubProtocol",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["JsonHubProtocol"]});if(!Uint8Array.prototype.indexOf){Object.defineProperty(Uint8Array.prototype,"indexOf",{value:Array.prototype.indexOf,writable:true})}if(!Uint8Array.prototype.slice){Object.defineProperty(Uint8Array.prototype,"slice",{value:Array.prototype.slice,writable:true})}if(!Uint8Array.prototype.forEach){Object.defineProperty(Uint8Array.prototype,"forEach",{value:Array.prototype.forEach,writable:true})}},function(module,exports,__webpack_require__){(function(global){var require;
+/*!
+ * @overview es6-promise - a tiny implementation of Promises/A+.
+ * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
+ * @license Licensed under MIT license
+ * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
+ * @version v4.2.2+97478eb6
+ */
+/*!
+ * @overview es6-promise - a tiny implementation of Promises/A+.
+ * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
+ * @license Licensed under MIT license
+ * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
+ * @version v4.2.2+97478eb6
+ */
+(function(global,factory){true?module.exports=factory():undefined})(this,function(){"use strict";function objectOrFunction(x){var type=typeof x;return x!==null&&(type==="object"||type==="function")}function isFunction(x){return typeof x==="function"}var _isArray=void 0;if(Array.isArray){_isArray=Array.isArray}else{_isArray=function(x){return Object.prototype.toString.call(x)==="[object Array]"}}var isArray=_isArray;var len=0;var vertxNext=void 0;var customSchedulerFn=void 0;var asap=function asap(callback,arg){queue[len]=callback;queue[len+1]=arg;len+=2;if(len===2){if(customSchedulerFn){customSchedulerFn(flush)}else{scheduleFlush()}}};function setScheduler(scheduleFn){customSchedulerFn=scheduleFn}function setAsap(asapFn){asap=asapFn}var browserWindow=typeof window!=="undefined"?window:undefined;var browserGlobal=browserWindow||{};var BrowserMutationObserver=browserGlobal.MutationObserver||browserGlobal.WebKitMutationObserver;var isNode=typeof self==="undefined"&&typeof process!=="undefined"&&{}.toString.call(process)==="[object process]";var isWorker=typeof Uint8ClampedArray!=="undefined"&&typeof importScripts!=="undefined"&&typeof MessageChannel!=="undefined";function useNextTick(){return function(){return process.nextTick(flush)}}function useVertxTimer(){if(typeof vertxNext!=="undefined"){return function(){vertxNext(flush)}}return useSetTimeout()}function useMutationObserver(){var iterations=0;var observer=new BrowserMutationObserver(flush);var node=document.createTextNode("");observer.observe(node,{characterData:true});return function(){node.data=iterations=++iterations%2}}function useMessageChannel(){var channel=new MessageChannel;channel.port1.onmessage=flush;return function(){return channel.port2.postMessage(0)}}function useSetTimeout(){var globalSetTimeout=setTimeout;return function(){return globalSetTimeout(flush,1)}}var queue=new Array(1e3);function flush(){for(var i=0;i=200&&xhr.status<300){resolve(new _HttpClient__WEBPACK_IMPORTED_MODULE_1__["HttpResponse"](xhr.status,xhr.statusText,xhr.response||xhr.responseText))}else{reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["HttpError"](xhr.statusText,xhr.status))}};xhr.onerror=function(){_this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Warning,"Error from HTTP request. "+xhr.status+": "+xhr.statusText+".");reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["HttpError"](xhr.statusText,xhr.status))};xhr.ontimeout=function(){_this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Warning,"Timeout from HTTP request.");reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["TimeoutError"])};xhr.send(request.content||"")})};return XhrHttpClient}(_HttpClient__WEBPACK_IMPORTED_MODULE_1__["HttpClient"])},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"LogLevel",function(){return LogLevel});var LogLevel;(function(LogLevel){LogLevel[LogLevel["Trace"]=0]="Trace";LogLevel[LogLevel["Debug"]=1]="Debug";LogLevel[LogLevel["Information"]=2]="Information";LogLevel[LogLevel["Warning"]=3]="Warning";LogLevel[LogLevel["Error"]=4]="Error";LogLevel[LogLevel["Critical"]=5]="Critical";LogLevel[LogLevel["None"]=6]="None"})(LogLevel||(LogLevel={}))},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"HubConnectionState",function(){return HubConnectionState});__webpack_require__.d(__webpack_exports__,"HubConnection",function(){return HubConnection});var _HandshakeProtocol__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(11);var _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(15);var _ILogger__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(9);var _Utils__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(13);var __awaiter=undefined&&undefined.__awaiter||function(thisArg,_arguments,P,generator){return new(P||(P=Promise))(function(resolve,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator["throw"](value))}catch(e){reject(e)}}function step(result){result.done?resolve(result.value):new P(function(resolve){resolve(result.value)}).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};var __generator=undefined&&undefined.__generator||function(thisArg,body){var _={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},f,y,t,g;return g={next:verb(0),throw:verb(1),return:verb(2)},typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]responseLength?binaryData.slice(responseLength).buffer:null}else{var textData=data;var separatorIndex=textData.indexOf(_TextMessageFormat__WEBPACK_IMPORTED_MODULE_0__["TextMessageFormat"].RecordSeparator);if(separatorIndex===-1){throw new Error("Message is incomplete.")}var responseLength=separatorIndex+1;messageData=textData.substring(0,responseLength);remainingData=textData.length>responseLength?textData.substring(responseLength):null}var messages=_TextMessageFormat__WEBPACK_IMPORTED_MODULE_0__["TextMessageFormat"].parse(messageData);var response=JSON.parse(messages[0]);if(response.type){throw new Error("Expected a handshake response from the server.")}responseMessage=response;return[remainingData,responseMessage]};return HandshakeProtocol}()},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"TextMessageFormat",function(){return TextMessageFormat});var TextMessageFormat=function(){function TextMessageFormat(){}TextMessageFormat.write=function(output){return""+output+TextMessageFormat.RecordSeparator};TextMessageFormat.parse=function(input){if(input[input.length-1]!==TextMessageFormat.RecordSeparator){throw new Error("Message is incomplete.")}var messages=input.split(TextMessageFormat.RecordSeparator);messages.pop();return messages};TextMessageFormat.RecordSeparatorCode=30;TextMessageFormat.RecordSeparator=String.fromCharCode(TextMessageFormat.RecordSeparatorCode);return TextMessageFormat}()},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"Arg",function(){return Arg});__webpack_require__.d(__webpack_exports__,"getDataDetail",function(){return getDataDetail});__webpack_require__.d(__webpack_exports__,"formatArrayBuffer",function(){return formatArrayBuffer});__webpack_require__.d(__webpack_exports__,"isArrayBuffer",function(){return isArrayBuffer});__webpack_require__.d(__webpack_exports__,"sendMessage",function(){return sendMessage});__webpack_require__.d(__webpack_exports__,"createLogger",function(){return createLogger});__webpack_require__.d(__webpack_exports__,"Subject",function(){return Subject});__webpack_require__.d(__webpack_exports__,"SubjectSubscription",function(){return SubjectSubscription});__webpack_require__.d(__webpack_exports__,"ConsoleLogger",function(){return ConsoleLogger});var _ILogger__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(9);var _Loggers__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(14);var __awaiter=undefined&&undefined.__awaiter||function(thisArg,_arguments,P,generator){return new(P||(P=Promise))(function(resolve,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator["throw"](value))}catch(e){reject(e)}}function step(result){result.done?resolve(result.value):new P(function(resolve){resolve(result.value)}).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};var __generator=undefined&&undefined.__generator||function(thisArg,body){var _={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},f,y,t,g;return g={next:verb(0),throw:verb(1),return:verb(2)},typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]-1){this.subject.observers.splice(index,1)}if(this.subject.observers.length===0&&this.subject.cancelCallback){this.subject.cancelCallback().catch(function(_){})}};return SubjectSubscription}();var ConsoleLogger=function(){function ConsoleLogger(minimumLogLevel){this.minimumLogLevel=minimumLogLevel}ConsoleLogger.prototype.log=function(logLevel,message){if(logLevel>=this.minimumLogLevel){switch(logLevel){case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Critical:case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Error:console.error("["+(new Date).toISOString()+"] "+_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel]+": "+message);break;case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Warning:console.warn("["+(new Date).toISOString()+"] "+_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel]+": "+message);break;case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Information:console.info("["+(new Date).toISOString()+"] "+_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel]+": "+message);break;default:console.log("["+(new Date).toISOString()+"] "+_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel]+": "+message);break}}};return ConsoleLogger}()},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"NullLogger",function(){return NullLogger});var NullLogger=function(){function NullLogger(){}NullLogger.prototype.log=function(_logLevel,_message){};NullLogger.instance=new NullLogger;return NullLogger}()},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"MessageType",function(){return MessageType});var MessageType;(function(MessageType){MessageType[MessageType["Invocation"]=1]="Invocation";MessageType[MessageType["StreamItem"]=2]="StreamItem";MessageType[MessageType["Completion"]=3]="Completion";MessageType[MessageType["StreamInvocation"]=4]="StreamInvocation";MessageType[MessageType["CancelInvocation"]=5]="CancelInvocation";MessageType[MessageType["Ping"]=6]="Ping";MessageType[MessageType["Close"]=7]="Close"})(MessageType||(MessageType={}))},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"HubConnectionBuilder",function(){return HubConnectionBuilder});var _HttpConnection__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(17);var _HubConnection__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(10);var _JsonHubProtocol__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(23);var _Loggers__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(14);var _Utils__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(13);var HubConnectionBuilder=function(){function HubConnectionBuilder(){}HubConnectionBuilder.prototype.configureLogging=function(logging){_Utils__WEBPACK_IMPORTED_MODULE_4__["Arg"].isRequired(logging,"logging");if(isLogger(logging)){this.logger=logging}else{this.logger=new _Utils__WEBPACK_IMPORTED_MODULE_4__["ConsoleLogger"](logging)}return this};HubConnectionBuilder.prototype.withUrl=function(url,transportTypeOrOptions){_Utils__WEBPACK_IMPORTED_MODULE_4__["Arg"].isRequired(url,"url");this.url=url;if(typeof transportTypeOrOptions==="object"){this.httpConnectionOptions=transportTypeOrOptions}else{this.httpConnectionOptions={transport:transportTypeOrOptions}}return this};HubConnectionBuilder.prototype.withHubProtocol=function(protocol){_Utils__WEBPACK_IMPORTED_MODULE_4__["Arg"].isRequired(protocol,"protocol");this.protocol=protocol;return this};HubConnectionBuilder.prototype.build=function(){var httpConnectionOptions=this.httpConnectionOptions||{};if(httpConnectionOptions.logger===undefined){httpConnectionOptions.logger=this.logger}if(!this.url){throw new Error("The 'HubConnectionBuilder.withUrl' method must be called before building the connection.")}var connection=new _HttpConnection__WEBPACK_IMPORTED_MODULE_0__["HttpConnection"](this.url,httpConnectionOptions);return _HubConnection__WEBPACK_IMPORTED_MODULE_1__["HubConnection"].create(connection,this.logger||_Loggers__WEBPACK_IMPORTED_MODULE_3__["NullLogger"].instance,this.protocol||new _JsonHubProtocol__WEBPACK_IMPORTED_MODULE_2__["JsonHubProtocol"])};return HubConnectionBuilder}();function isLogger(logger){return logger.log!==undefined}},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"HttpConnection",function(){return HttpConnection});var _DefaultHttpClient__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(6);var _ILogger__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(9);var _ITransport__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(18);var _LongPollingTransport__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(19);var _ServerSentEventsTransport__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(21);var _Utils__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__(13);var _WebSocketTransport__WEBPACK_IMPORTED_MODULE_6__=__webpack_require__(22);var __awaiter=undefined&&undefined.__awaiter||function(thisArg,_arguments,P,generator){return new(P||(P=Promise))(function(resolve,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator["throw"](value))}catch(e){reject(e)}}function step(result){result.done?resolve(result.value):new P(function(resolve){resolve(result.value)}).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};var __generator=undefined&&undefined.__generator||function(thisArg,body){var _={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},f,y,t,g;return g={next:verb(0),throw:verb(1),return:verb(2)},typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]=0){if(transport===_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].WebSockets&&!this.options.WebSocket||transport===_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].ServerSentEvents&&!this.options.EventSource){this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug,"Skipping transport '"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport]+"' because it is not supported in your environment.'")}else{this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug,"Selecting transport '"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport]+"'.");return transport}}else{this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug,"Skipping transport '"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport]+"' because it does not support the requested transfer format '"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["TransferFormat"][requestedTransferFormat]+"'.")}}else{this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug,"Skipping transport '"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport]+"' because it was disabled by the client.")}}return null};HttpConnection.prototype.isITransport=function(transport){return transport&&typeof transport==="object"&&"connect"in transport};HttpConnection.prototype.changeState=function(from,to){if(this.connectionState===from){this.connectionState=to;return true}return false};HttpConnection.prototype.stopConnection=function(error){this.transport=undefined;error=this.stopError||error;if(error){this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error,"Connection disconnected with error '"+error+"'.")}else{this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Information,"Connection disconnected.")}this.connectionState=2;if(this.onclose){this.onclose(error)}};HttpConnection.prototype.resolveUrl=function(url){if(url.lastIndexOf("https://",0)===0||url.lastIndexOf("http://",0)===0){return url}if(typeof window==="undefined"||!window||!window.document){throw new Error("Cannot resolve '"+url+"'.")}var aTag=window.document.createElement("a");aTag.href=url;this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Information,"Normalizing '"+url+"' to '"+aTag.href+"'.");return aTag.href};HttpConnection.prototype.resolveNegotiateUrl=function(url){var index=url.indexOf("?");var negotiateUrl=url.substring(0,index===-1?url.length:index);if(negotiateUrl[negotiateUrl.length-1]!=="/"){negotiateUrl+="/"}negotiateUrl+="negotiate";negotiateUrl+=index===-1?"":url.substring(index);return negotiateUrl};return HttpConnection}();function transportMatches(requestedTransport,actualTransport){return!requestedTransport||(actualTransport&requestedTransport)!==0}},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"HttpTransportType",function(){return HttpTransportType});__webpack_require__.d(__webpack_exports__,"TransferFormat",function(){return TransferFormat});var HttpTransportType;(function(HttpTransportType){HttpTransportType[HttpTransportType["None"]=0]="None";HttpTransportType[HttpTransportType["WebSockets"]=1]="WebSockets";HttpTransportType[HttpTransportType["ServerSentEvents"]=2]="ServerSentEvents";HttpTransportType[HttpTransportType["LongPolling"]=4]="LongPolling"})(HttpTransportType||(HttpTransportType={}));var TransferFormat;(function(TransferFormat){TransferFormat[TransferFormat["Text"]=1]="Text";TransferFormat[TransferFormat["Binary"]=2]="Binary"})(TransferFormat||(TransferFormat={}))},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"LongPollingTransport",function(){return LongPollingTransport});var _AbortController__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(20);var _Errors__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(4);var _ILogger__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(9);var _ITransport__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(18);var _Utils__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(13);var __awaiter=undefined&&undefined.__awaiter||function(thisArg,_arguments,P,generator){return new(P||(P=Promise))(function(resolve,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator["throw"](value))}catch(e){reject(e)}}function step(result){result.done?resolve(result.value):new P(function(resolve){resolve(result.value)}).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};var __generator=undefined&&undefined.__generator||function(thisArg,body){var _={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},f,y,t,g;return g={next:verb(0),throw:verb(1),return:verb(2)},typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1] s.RawData).ToList();
moofs.Add(fragmentBox);
- foreach(var moof in moofs)
+ foreach (var moof in moofs)
{
moof.ToBuffer(ref writer);
}
@@ -437,6 +437,7 @@ 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");
if (File.Exists(filepath))
@@ -444,15 +445,20 @@ namespace JT1078.FMp4.Test
File.Delete(filepath);
}
using var fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
+ var ftyp = fMp4Encoder.EncoderFtypBox();
+ fileStream.Write(ftyp);
var package1 = packages[0];
- var buffer1 = fMp4Encoder.EncoderFirstVideoBox(package1);
- fileStream.Write(buffer1);
- int moofOffset = buffer1.Length;
- foreach (var package in packages.Take(2))
+ var nalus1 = h264Decoder.ParseNALU(package1);
+ var moov = fMp4Encoder.EncoderMoovBox(nalus1, package1.Bodies.Length);
+ fileStream.Write(moov);
+ int moofOffset = ftyp.Length + moov.Length;
+ var flag = package1.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧 ? 1u : 0u;
+ var otherMoofBuffer = fMp4Encoder.EncoderMoofBox(nalus1, package1.Bodies.Length, package1.Timestamp, flag);
+ foreach (var package in packages)
{
- var otherBuffer = fMp4Encoder.EncoderOtherVideoBox(package, (ulong)moofOffset);
- moofOffset += otherBuffer.Length;
- fileStream.Write(otherBuffer);
+ var otherNalus = h264Decoder.ParseNALU(package);
+ var otherMdatBuffer = fMp4Encoder.EncoderMdatBox(otherNalus, package.Bodies.Length);
+ fileStream.Write(otherMdatBuffer);
}
fileStream.Close();
}
@@ -464,7 +470,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 +501,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])
{
-
+
}
}
}
diff --git a/src/JT1078.FMp4/Boxs/MovieBox.cs b/src/JT1078.FMp4/Boxs/MovieBox.cs
index 231fbb7..4c923f5 100644
--- a/src/JT1078.FMp4/Boxs/MovieBox.cs
+++ b/src/JT1078.FMp4/Boxs/MovieBox.cs
@@ -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);
diff --git a/src/JT1078.FMp4/FMp4Encoder.cs b/src/JT1078.FMp4/FMp4Encoder.cs
index da5cd54..2a149dd 100644
--- a/src/JT1078.FMp4/FMp4Encoder.cs
+++ b/src/JT1078.FMp4/FMp4Encoder.cs
@@ -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;
@@ -23,6 +24,7 @@ namespace JT1078.FMp4
/// moof n
/// mdat n
/// mfra
+ /// ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing
///
public class FMp4Encoder
{
@@ -35,6 +37,233 @@ namespace JT1078.FMp4
h264Decoder = new H264Decoder();
}
+
+ ///
+ /// 编码ftyp盒子
+ ///
+ ///
+ public byte[] EncoderFtypBox()
+ {
+ byte[] buffer = FMp4ArrayPool.Rent(4096);
+ FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
+ try
+ {
+ //ftyp
+ FileTypeBox fileTypeBox = new FileTypeBox();
+ fileTypeBox.MajorBrand = "msdh";
+ fileTypeBox.MinorVersion = "\0\0\0\0";
+ fileTypeBox.CompatibleBrands.Add("isom");
+ fileTypeBox.CompatibleBrands.Add("mp42");
+ fileTypeBox.CompatibleBrands.Add("msdh");
+ fileTypeBox.CompatibleBrands.Add("nsix");
+ fileTypeBox.CompatibleBrands.Add("iso5");
+ fileTypeBox.CompatibleBrands.Add("iso6");
+ fileTypeBox.ToBuffer(ref writer);
+ var data = writer.FlushAndGetArray();
+ return data;
+ }
+ finally
+ {
+ FMp4ArrayPool.Return(buffer);
+ }
+ }
+
+ ///
+ /// 编码moov盒子
+ ///
+ ///
+ public byte[] EncoderMoovBox(List nalus, int naluLength)
+ {
+ byte[] buffer = FMp4ArrayPool.Rent(naluLength + 4096);
+ FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
+ try
+ {
+ 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();
+ //moov
+ MovieBox movieBox = new MovieBox();
+ 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 = 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.Duration = 0;
+ movieBox.TrackBox.TrackHeaderBox.TrackIsAudio = false;
+ movieBox.TrackBox.TrackHeaderBox.Width = (uint)spsInfo.width;
+ movieBox.TrackBox.TrackHeaderBox.Height = (uint)spsInfo.height;
+ movieBox.TrackBox.MediaBox = new MediaBox();
+ 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.Duration = 0;
+ movieBox.TrackBox.MediaBox.HandlerBox = new HandlerBox();
+ movieBox.TrackBox.MediaBox.HandlerBox.HandlerType = HandlerType.vide;
+ movieBox.TrackBox.MediaBox.HandlerBox.Name = "VideoHandler";
+ movieBox.TrackBox.MediaBox.MediaInformationBox = new MediaInformationBox();
+ movieBox.TrackBox.MediaBox.MediaInformationBox.VideoMediaHeaderBox = new VideoMediaHeaderBox();
+ movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox = new DataInformationBox();
+ movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox = new DataReferenceBox();
+ movieBox.TrackBox.MediaBox.MediaInformationBox.DataInformationBox.DataReferenceBox.DataEntryBoxes = new List();
+ 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.SampleEntries = new List();
+ AVC1SampleEntry avc1 = new AVC1SampleEntry();
+ avc1.AVCConfigurationBox = new AVCConfigurationBox();
+ //h264
+ avc1.Width = (ushort)movieBox.TrackBox.TrackHeaderBox.Width;
+ avc1.Height = (ushort)movieBox.TrackBox.TrackHeaderBox.Height;
+ avc1.AVCConfigurationBox.AVCLevelIndication = spsInfo.levelIdc;
+ avc1.AVCConfigurationBox.AVCProfileIndication = spsInfo.profileIdc;
+ avc1.AVCConfigurationBox.ProfileCompatibility = (byte)spsInfo.profileCompat;
+ avc1.AVCConfigurationBox.PPSs = new List() { ppsNALU.RawData };
+ avc1.AVCConfigurationBox.SPSs = new List() { spsNALU.RawData };
+ movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleDescriptionBox.SampleEntries.Add(avc1);
+ movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.TimeToSampleBox = new TimeToSampleBox() {
+ TimeToSampleInfos=new List
+ {
+ new TimeToSampleBox.TimeToSampleInfo
+ {
+ SampleCount=0,
+ SampleDelta=0
+ }
+ }
+ };
+ movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleToChunkBox = new SampleToChunkBox() {
+ SampleToChunkInfos=new List()
+ {
+ new SampleToChunkBox.SampleToChunkInfo
+ {
+
+ }
+ }
+ };
+ movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.SampleSizeBox = new SampleSizeBox() {
+ EntrySize = new List()
+ {
+ 0
+ }
+ };
+ movieBox.TrackBox.MediaBox.MediaInformationBox.SampleTableBox.ChunkOffsetBox = new ChunkOffsetBox() {
+ ChunkOffset=new List()
+ {
+ 0
+ }
+ };
+ movieBox.MovieExtendsBox = new MovieExtendsBox();
+ movieBox.MovieExtendsBox.TrackExtendsBoxs = new List();
+ TrackExtendsBox trex = new TrackExtendsBox();
+ trex.TrackID = 1;
+ trex.DefaultSampleDescriptionIndex = 1;
+ trex.DefaultSampleDuration = 0;
+ trex.DefaultSampleSize = 0;
+ trex.DefaultSampleFlags = 0;
+ movieBox.MovieExtendsBox.TrackExtendsBoxs.Add(trex);
+ movieBox.ToBuffer(ref writer);
+ var data = writer.FlushAndGetArray();
+ return data;
+ }
+ finally
+ {
+ FMp4ArrayPool.Return(buffer);
+ }
+ }
+
+
+ ///
+ /// 编码Moof盒子
+ ///
+ ///
+ public byte[] EncoderMoofBox(List nalus, int naluLength,ulong timestamp, uint keyframeFlag,uint moofOffset=0)
+ {
+ byte[] buffer = FMp4ArrayPool.Rent(naluLength + 4096);
+ FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
+ try
+ {
+ var movieFragmentBox = new MovieFragmentBox();
+ movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox();
+ movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = sn++;
+ movieFragmentBox.TrackFragmentBox = new TrackFragmentBox();
+ //0x39 写文件
+ //0x02 分段
+ movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(2);
+ movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = 1;
+ movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = 48000;
+ movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleSize = (uint)naluLength;
+ movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleFlags = 0x1010000;
+ movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox();
+ //trun
+ //0x39 写文件
+ //0x02 分段
+ uint flag = 0u;
+ if (!first)
+ {
+ flag = 4u;
+ movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = 0;
+ movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: flag);
+ first = true;
+ }
+ else
+ {
+ flag = 0x000400;
+ movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = timestamp * 1000;
+ movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: flag);
+ }
+ movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = 0;
+ movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = new List();
+ movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo());
+
+ movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo()
+ {
+ SampleSize = (uint)naluLength,
+ //SampleCompositionTimeOffset = package.Label3.DataType == JT1078DataType.视频I帧 ? package.LastIFrameInterval : package.LastFrameInterval,
+ SampleFlags = flag
+ });
+
+ movieFragmentBox.ToBuffer(ref writer);
+ var data = writer.FlushAndGetArray();
+ return data;
+ }
+ finally
+ {
+ FMp4ArrayPool.Return(buffer);
+ }
+ }
+
+ ///
+ /// 编码Mdat盒子
+ ///
+ ///
+ public byte[] EncoderMdatBox(List nalus, int naluLength)
+ {
+ byte[] buffer = FMp4ArrayPool.Rent(naluLength + 4096);
+ FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
+ try
+ {
+ var mediaDataBox = new MediaDataBox();
+ mediaDataBox.Data = nalus.Select(s => s.RawData).ToList();
+ mediaDataBox.ToBuffer(ref writer);
+ var data = writer.FlushAndGetArray();
+ return data;
+ }
+ finally
+ {
+ FMp4ArrayPool.Return(buffer);
+ }
+ }
+
///
/// 编码首个视频盒子
///
@@ -43,7 +272,7 @@ namespace JT1078.FMp4
public byte[] EncoderFirstVideoBox(JT1078Package package)
{
byte[] buffer = FMp4ArrayPool.Rent(package.Bodies.Length + 4096);
- FMp4MessagePackWriter writer = new FMp4MessagePackWriter(new byte[10 * 1024 * 1024]);
+ FMp4MessagePackWriter writer = new FMp4MessagePackWriter(buffer);
try
{
var nalus = h264Decoder.ParseNALU(package);
@@ -133,6 +362,10 @@ namespace JT1078.FMp4
}
}
+ uint sn = 1;
+
+ bool first = false;
+
///
/// 编码其他视频数据盒子
///
@@ -148,9 +381,11 @@ namespace JT1078.FMp4
var nalus = h264Decoder.ParseNALU(package);
var movieFragmentBox = new MovieFragmentBox();
movieFragmentBox.MovieFragmentHeaderBox = new MovieFragmentHeaderBox();
- movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = package.SN;
+ movieFragmentBox.MovieFragmentHeaderBox.SequenceNumber = sn++;
movieFragmentBox.TrackFragmentBox = new TrackFragmentBox();
- movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(0x39);
+ //0x39 写文件
+ //0x02 分段
+ movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox = new TrackFragmentHeaderBox(2);
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.TrackID = 1;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.BaseDataOffset = moofOffset;
movieFragmentBox.TrackFragmentBox.TrackFragmentHeaderBox.DefaultSampleDuration = 48000;
@@ -159,10 +394,22 @@ namespace JT1078.FMp4
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox = new TrackFragmentBaseMediaDecodeTimeBox();
movieFragmentBox.TrackFragmentBox.TrackFragmentBaseMediaDecodeTimeBox.BaseMediaDecodeTime = package.Timestamp * 1000;
//trun
- movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: 0x5);
+ //0x39 写文件
+ //0x02 分段
+ uint flag = package.Label3.DataType == JT1078DataType.视频I帧 ? 1u : 0u;
+ movieFragmentBox.TrackFragmentBox.TrackRunBox = new TrackRunBox(flags: 0x000400);
movieFragmentBox.TrackFragmentBox.TrackRunBox.FirstSampleFlags = 0;
movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos = new List();
- movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo());
+ //movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo());
+ foreach (var nalu in nalus)
+ {
+ movieFragmentBox.TrackFragmentBox.TrackRunBox.TrackRunInfos.Add(new TrackRunBox.TrackRunInfo()
+ {
+ SampleSize = (uint)nalu.RawData.Length,
+ SampleCompositionTimeOffset = package.Label3.DataType == JT1078DataType.视频I帧 ? package.LastIFrameInterval : package.LastFrameInterval,
+ SampleFlags = flag
+ });
+ }
movieFragmentBox.ToBuffer(ref writer);
var mediaDataBox = new MediaDataBox();
mediaDataBox.Data = nalus.Select(s => s.RawData).ToList();
diff --git a/src/JT1078.FMp4/JT1078.FMp4.xml b/src/JT1078.FMp4/JT1078.FMp4.xml
index bed1c8a..bad212c 100644
--- a/src/JT1078.FMp4/JT1078.FMp4.xml
+++ b/src/JT1078.FMp4/JT1078.FMp4.xml
@@ -1219,6 +1219,7 @@
moof n
mdat n
mfra
+ ref: https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing
@@ -1226,6 +1227,30 @@
+
+
+ 编码ftyp盒子
+
+
+
+
+
+ 编码moov盒子
+
+
+
+
+
+ 编码Moof盒子
+
+
+
+
+
+ 编码Mdat盒子
+
+
+
编码首个视频盒子
diff --git a/src/JT1078.SignalR.Test/Hubs/FMp4Hub.cs b/src/JT1078.SignalR.Test/Hubs/FMp4Hub.cs
new file mode 100644
index 0000000..cd4aee2
--- /dev/null
+++ b/src/JT1078.SignalR.Test/Hubs/FMp4Hub.cs
@@ -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();
+ }
+
+ 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);
+ }
+ }
+}
diff --git a/src/JT1078.SignalR.Test/JT1078.SignalR.Test.csproj b/src/JT1078.SignalR.Test/JT1078.SignalR.Test.csproj
new file mode 100644
index 0000000..ea59093
--- /dev/null
+++ b/src/JT1078.SignalR.Test/JT1078.SignalR.Test.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net5.0
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
diff --git a/src/JT1078.SignalR.Test/Program.cs b/src/JT1078.SignalR.Test/Program.cs
new file mode 100644
index 0000000..4045d84
--- /dev/null
+++ b/src/JT1078.SignalR.Test/Program.cs
@@ -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();
+ });
+ }
+}
diff --git a/src/JT1078.SignalR.Test/Services/ToWebSocketService.cs b/src/JT1078.SignalR.Test/Services/ToWebSocketService.cs
new file mode 100644
index 0000000..d1f8a06
--- /dev/null
+++ b/src/JT1078.SignalR.Test/Services/ToWebSocketService.cs
@@ -0,0 +1,108 @@
+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;
+
+namespace JT1078.SignalR.Test.Services
+{
+ public class ToWebSocketService: BackgroundService
+ {
+ private readonly ILogger logger;
+
+ private readonly IHubContext _hubContext;
+
+ private readonly FMp4Encoder fMp4Encoder;
+
+ private readonly WsSession wsSession;
+
+ private readonly H264Decoder h264Decoder;
+
+ public ToWebSocketService(
+ H264Decoder h264Decoder,
+ WsSession wsSession,
+ FMp4Encoder fMp4Encoder,
+ ILoggerFactory loggerFactory,
+ IHubContext hubContext)
+ {
+ this.h264Decoder = h264Decoder;
+ logger = loggerFactory.CreateLogger();
+ this.fMp4Encoder = fMp4Encoder;
+ _hubContext = hubContext;
+ this.wsSession = wsSession;
+ }
+
+ public Queue q = new Queue();
+
+ public void a()
+ {
+ List packages = new List();
+ var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "jt1078_3.txt"));
+ int mergeBodyLength = 0;
+ foreach (var line in lines)
+ {
+ 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);
+ }
+ }
+ var ftyp = fMp4Encoder.EncoderFtypBox();
+ q.Enqueue(ftyp);
+ var package1 = packages[0];
+ var nalus1 = h264Decoder.ParseNALU(package1);
+ var moov = fMp4Encoder.EncoderMoovBox(nalus1, package1.Bodies.Length);
+ q.Enqueue(moov);
+ var flag = package1.Label3.DataType == Protocol.Enums.JT1078DataType.视频I帧 ? 1u : 0u;
+ var moofBuffer = fMp4Encoder.EncoderMoofBox(nalus1, package1.Bodies.Length, package1.Timestamp, flag);
+ q.Enqueue(moofBuffer);
+ foreach (var package in packages)
+ {
+ var otherNalus = h264Decoder.ParseNALU(package);
+ var otherMdatBuffer = fMp4Encoder.EncoderMdatBox(otherNalus, package.Bodies.Length);
+ q.Enqueue(otherMdatBuffer);
+ }
+ }
+
+ protected async override Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ a();
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ try
+ {
+ if (wsSession.GetCount() > 0)
+ {
+ if (q.Count > 0)
+ {
+ await _hubContext.Clients.All.SendAsync("video", q.Dequeue(), stoppingToken);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex,"");
+ }
+ await Task.Delay(1000);
+ }
+ }
+ }
+}
diff --git a/src/JT1078.SignalR.Test/Services/WsSession.cs b/src/JT1078.SignalR.Test/Services/WsSession.cs
new file mode 100644
index 0000000..5c7b8e7
--- /dev/null
+++ b/src/JT1078.SignalR.Test/Services/WsSession.cs
@@ -0,0 +1,33 @@
+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 sessions;
+
+ public WsSession()
+ {
+ sessions = new ConcurrentDictionary();
+ }
+
+ public void TryAdd(string connectionId)
+ {
+ sessions.TryAdd(connectionId, connectionId);
+ }
+
+ public int GetCount()
+ {
+ return sessions.Count;
+ }
+
+ public void TryRemove(string connectionId)
+ {
+ sessions.TryRemove(connectionId,out _);
+ }
+ }
+}
diff --git a/src/JT1078.SignalR.Test/Startup.cs b/src/JT1078.SignalR.Test/Startup.cs
new file mode 100644
index 0000000..0e70236
--- /dev/null
+++ b/src/JT1078.SignalR.Test/Startup.cs
@@ -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();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddHostedService();
+ 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");
+ });
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ });
+ }
+ }
+}
diff --git a/src/JT1078.SignalR.Test/appsettings.Development.json b/src/JT1078.SignalR.Test/appsettings.Development.json
new file mode 100644
index 0000000..8983e0f
--- /dev/null
+++ b/src/JT1078.SignalR.Test/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/src/JT1078.SignalR.Test/appsettings.json b/src/JT1078.SignalR.Test/appsettings.json
new file mode 100644
index 0000000..d9d9a9b
--- /dev/null
+++ b/src/JT1078.SignalR.Test/appsettings.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/JT1078.sln b/src/JT1078.sln
index 5486098..1c9bde7 100644
--- a/src/JT1078.sln
+++ b/src/JT1078.sln
@@ -40,6 +40,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
@@ -218,6 +220,18 @@ 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
@@ -231,6 +245,7 @@ Global
{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}