123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- /* exported H265Session */
- /* global Uint8Array, H265SPSParser, H265Decoder, inheritObject,
- RtpSession, VideoBufferList, ArrayBuffer, decodeMode */
- /* eslint-disable no-magic-numbers */
- var H265Session = function () {
- 'use strict';
- var rtpTimeStamp = 0,
- inputLength = 0,
- size1K = 1024,
- size1M = size1K * size1K,
- playback = false,
- outputSize = 0,
- curSize = 0,
- inputBuffer = new Uint8Array(size1M),
- PREFIX = new Uint8Array(4),
- SPSParser = null,
- privIRtpTime = 0,
- // frameDiffTime = 0,
- // needDropCnt = 0,
- delayingTime = 0,
- DELAY_LIMIT = 8000,
- decodedData = {
- frameData: null,
- timeStamp: null,
- },
- timeData = {
- 'timestamp': null,
- 'timezone': null,
- },
- frameRate = 0;
- var width = 0,
- height = 0;
- PREFIX[0] = '0x00';
- PREFIX[1] = '0x00';
- PREFIX[2] = '0x00';
- PREFIX[3] = '0x01';
- var setBuffer = function (buffer1, buffer2) {
- var tempBuffer = buffer1;
- if ((inputLength + buffer2.length) > tempBuffer.length) {
- tempBuffer = new Uint8Array(tempBuffer.length + size1M);
- }
- tempBuffer.set(buffer2, inputLength);
- inputLength += buffer2.length;
- return tempBuffer;
- };
- var decodeMode = 'canvas';
- function Constructor() {
- this.decoder = new H265Decoder();
- }
- Constructor.prototype = {
- init: function () {
- SPSParser = new H265SPSParser();
- console.log(SPSParser.parse)
- //this.decoder.setIsFirstFrame(false);
- this.videoBufferList = new VideoBufferList();
- this.firstDiffTime = 0;
- this.checkDelay = true;
- },
- setFrameRate(fps) {
- frameRate = fps;
- //console.log('frameRate: ', frameRate)
- },
- remuxRTPData: function (rtspInterleaved, rtpHeader, rtpPayload, isBackup) {
- var HEADER = rtpHeader,
- PAYLOAD = null,
- timeData = {
- 'timestamp': null,
- 'timezone': null,
- },
- extensionHeaderLen = 0,
- PaddingSize = 0,
- data = {};
- if (rtspInterleaved[0] !== 0x24) {
- console.log("H265Session::it is not valid interleave header (RTSP over TCP)");
- return;
- } else if ((rtpHeader[0] & 0x0F) === 0x0F) {
- console.log("H265Session::There is additional CSRC which is not handled in this version");
- return;
- } else if ((rtpHeader[0] & 0x20) === 0x20) {
- PaddingSize = rtpPayload[rtpPayload.length - 1];
- console.log("H265Session::PaddingSize - " + PaddingSize);
- }
- //Extension bit check in RTPHeader
- if ((rtpHeader[0] & 0x10) === 0x10) {
- extensionHeaderLen = (((rtpPayload[2] << 8) | rtpPayload[3]) * 4) + 4;
- //Playback check
- if (rtpPayload[0] === 0xAB && rtpPayload[1] === 0xAD) {
- var startHeader = 4,
- NTPmsw = new Uint8Array(new ArrayBuffer(4)),
- NTPlsw = new Uint8Array(new ArrayBuffer(4)),
- gmt = new Uint8Array(new ArrayBuffer(2)),
- fsynctime = {
- 'seconds': null,
- 'useconds': null,
- },
- microseconds = null;
- NTPmsw.set(rtpPayload.subarray(startHeader, startHeader + 4), 0);
- startHeader += 4;
- NTPlsw.set(rtpPayload.subarray(startHeader, startHeader + 4), 0);
- startHeader += 6;
- gmt.set(rtpPayload.subarray(startHeader, startHeader + 2), 0);
- microseconds = (this.ntohl(NTPlsw) / 0xffffffff) * 1000;
- fsynctime.seconds = ((this.ntohl(NTPmsw) - 0x83AA7E80) >>> 0);
- fsynctime.useconds = microseconds;
- gmt = (((gmt[0] << 8) | gmt[1]) << 16) >> 16;
- timeData = {
- timestamp: fsynctime.seconds,
- timestamp_usec: fsynctime.useconds,
- timezone: gmt,
- };
- if ((this.getFramerate() === 0 || typeof this.getFramerate() === "undefined") &&
- (typeof this.getTimeStamp() !== "undefined")) {
- var diffUsec = timeData.timestamp_usec - this.getTimeStamp().timestamp_usec;
- this.setFramerate(Math.round(1000 / (((timeData.timestamp -
- this.getTimeStamp().timestamp) === 0 ? 0 : 1000) + (diffUsec))));
- }
- this.setTimeStamp(timeData);
- playback = true;
- }
- }
- PAYLOAD = rtpPayload.subarray(extensionHeaderLen, rtpPayload.length - PaddingSize);
- rtpTimeStamp = this.ntohl(rtpHeader.subarray(4, 8));
- var nalType = (PAYLOAD[0] >> 1) & 0x3f;
- switch (nalType) {
- default:
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD);
- break;
- // Fragmentation unit(FU)
- case 49:
- var startBit = ((PAYLOAD[2] & 0x80) === 0x80);
- var endBit = ((PAYLOAD[2] & 0x40) === 0x40);
- var fuType = PAYLOAD[2] & 0x3f;
- var payloadStartIndex = 3;
- if (startBit === true && endBit === false) {
- var newNalHeader = new Uint8Array(2);
- newNalHeader[0] = (PAYLOAD[0] & 0x81) | (fuType << 1);
- newNalHeader[1] = PAYLOAD[1];
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, newNalHeader);
- inputBuffer = setBuffer(inputBuffer,
- PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
- } else {
- inputBuffer = setBuffer(inputBuffer,
- PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
- }
- break;
- //SPS
- case 33:
- SPSParser.parse(PAYLOAD);
- var resolution = SPSParser.getSizeInfo();
- curSize = resolution.width * resolution.height;
- if (playback && !isBackup) {
- var limitSize = 1280 * 720;
- if (curSize > limitSize) {
- data.error = {
- errorCode: "998",
- description: "Resolution is too big",
- place: "h265Session.js",
- };
- this.rtpReturnCallback(data);
- return; // data;
- }
- }
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD);
- width = resolution.width;
- height = resolution.height;
- break;
- } //end of switch(nalType)
- //case 48 Remove because not use more
- //check marker bit
- var frameType = '';
- if ((HEADER[1] & 0x80) === 0x80) {
- // var DepacketizingTime = Date.now() - beforeDepacketizing;
- if (outputSize !== curSize) {
- outputSize = curSize;
- this.decoder.setOutputSize(outputSize);
- }
- var inputBufferSub = inputBuffer.subarray(0, inputLength);
- if (inputBufferSub[4] === 0x40) {
- frameType = 'I';
- if (this.firstDiffTime == 0) {
- privIRtpTime = rtpTimeStamp;
- delayingTime = 0;
- this.firstDiffTime = (Date.now() - (rtpTimeStamp / 90).toFixed(0));
- // needDropCnt = 0;
- } else {
- // frameDiffTime = Math.round(((rtpTimeStamp / 90).toFixed(0) - privIRtpTime) / this.getGovLength());
- if((rtpTimeStamp - privIRtpTime) < 0){
- this.firstDiffTime = delayingTime + (Date.now() - (rtpTimeStamp / 90).toFixed(0));
- // console.log("firstDiffTime = " + this.firstDiffTime + " rtpTimeStamp = " + rtpTimeStamp + " privIRtpTime = " + privIRtpTime );
- }
- delayingTime = (Date.now() - (rtpTimeStamp / 90).toFixed(0)) - this.firstDiffTime;
- privIRtpTime = rtpTimeStamp;
- if (delayingTime > DELAY_LIMIT) {
- if (this.checkDelay === true && playback === false) {
- data.error = {
- errorCode: "997",
- description: "Delay time is too long",
- place: "h265Session.js",
- };
- // console.log("h265Session::Delay time is too long 997 error ");
- this.rtpReturnCallback(data);
- return; // data;
- }
- }
- // needDropCnt = (needDropCnt > 0) ? Math.round(needDropCnt / frameDiffTime) : 0;
- }
- } else {
- frameType = 'P';
- }
- decodedData.frameData = null;
- if (isBackup !== true || playback !== true) {
- console.log('frameType', frameType)
- decodedData.frameData = this.decoder.decode(inputBufferSub);
- }
- decodedData.timeStamp = null;
- inputLength = 0;
- if (playback === true) {
- timeData = (timeData.timestamp === null ? this.getTimeStamp() : timeData);
- decodedData.timeStamp = timeData;
- }
- if (isBackup) {
- data.backupData = {
- 'stream': inputBufferSub,
- 'frameType': frameType,
- 'width': width,
- 'height': height,
- 'codecType': 'h265',
- };
- if (timeData.timestamp !== null && typeof timeData.timestamp !== "undefined") {
- data.backupData.timestamp_usec = timeData.timestamp_usec;
- } else {
- data.backupData.timestamp = (rtpTimeStamp / 90).toFixed(0);
- }
- }
- data.decodedData = decodedData;
- if (decodeMode !== "canvas") {
- data.decodeMode = "canvas";
- }
- console.log(data)
- this.rtpReturnCallback(data);
- return; // data;
- }
- },
- bufferingRtpData: function (rtspInterleaved, rtpHeader, rtpPayload) {
- var HEADER = rtpHeader,
- PAYLOAD = null,
- extensionHeaderLen = 0,
- PaddingSize = 0;
- if (rtspInterleaved[0] !== 0x24) {
- console.log("H265Session::it is not valid interleave header (RTSP over TCP)");
- return;
- } else if ((rtpHeader[0] & 0x0F) === 0x0F) {
- console.log("H265Session::There is additional CSRC which is not handled in this version");
- return;
- } else if ((rtpHeader[0] & 0x20) === 0x20) {
- PaddingSize = rtpPayload[rtpPayload.length - 1];
- console.log("H265Session::PaddingSize - " + PaddingSize);
- }
- //Extension bit check in RTPHeader
- if ((rtpHeader[0] & 0x10) === 0x10) {
- extensionHeaderLen = (((rtpPayload[2] << 8) | rtpPayload[3]) * 4) + 4;
- //Playback check
- if (rtpPayload[0] === 0xAB && rtpPayload[1] === 0xAD) {
- var startHeader = 4,
- NTPmsw = new Uint8Array(new ArrayBuffer(4)),
- NTPlsw = new Uint8Array(new ArrayBuffer(4)),
- gmt = new Uint8Array(new ArrayBuffer(2)),
- fsynctime = {
- 'seconds': null,
- 'useconds': null,
- },
- microseconds = null;
- NTPmsw.set(rtpPayload.subarray(startHeader, startHeader + 4), 0);
- startHeader += 4;
- NTPlsw.set(rtpPayload.subarray(startHeader, startHeader + 4), 0);
- startHeader += 6;
- gmt.set(rtpPayload.subarray(startHeader, startHeader + 2), 0);
- microseconds = (this.ntohl(NTPlsw) / 0xffffffff) * 1000;
- fsynctime.seconds = ((this.ntohl(NTPmsw) - 0x83AA7E80) >>> 0);
- fsynctime.useconds = microseconds;
- gmt = (((gmt[0] << 8) | gmt[1]) << 16) >> 16;
- timeData = {
- timestamp: fsynctime.seconds,
- timestamp_usec: fsynctime.useconds,
- timezone: gmt,
- };
- playback = true;
- }
- }
- PAYLOAD = rtpPayload.subarray(extensionHeaderLen, rtpPayload.length - PaddingSize);
- rtpTimeStamp = new Uint8Array(new ArrayBuffer(4));
- rtpTimeStamp.set(rtpHeader.subarray(4, 8), 0);
- rtpTimeStamp = this.ntohl(rtpTimeStamp);
- var nalType = (PAYLOAD[0] >> 1) & 0x3f;
- switch (nalType) {
- default:
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD);
- break;
- // Fragmentation unit(FU)
- case 49:
- var startBit = ((PAYLOAD[2] & 0x80) === 0x80);
- var endBit = ((PAYLOAD[2] & 0x40) === 0x40);
- var fuType = PAYLOAD[2] & 0x3f;
- var payloadStartIndex = 3;
- if (startBit === true && endBit === false) {
- var newNalHeader = new Uint8Array(2);
- newNalHeader[0] = (PAYLOAD[0] & 0x81) | (fuType << 1);
- newNalHeader[1] = PAYLOAD[1];
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, newNalHeader);
- inputBuffer = setBuffer(inputBuffer,
- PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
- } else {
- inputBuffer = setBuffer(inputBuffer,
- PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
- }
- break;
- //SPS
- case 33:
- SPSParser.parse(PAYLOAD);
- var resolution = SPSParser.getSizeInfo();
- curSize = resolution.width * resolution.height;
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD);
- width = resolution.width;
- height = resolution.height;
- break;
- } //end of switch(nalType)
- //case 48 Remove because not use more
- //check marker bit
- if ((HEADER[1] & 0x80) === 0x80) {
- if (outputSize !== curSize) {
- outputSize = curSize;
- this.decoder.setOutputSize(outputSize);
- }
- var stepBufferSub = new Uint8Array(inputBuffer.subarray(0, inputLength));
- if (this.videoBufferList !== null) {
- this.videoBufferList.push(stepBufferSub, width, height,
- 'h265', (stepBufferSub[4] === 0x40) ? 'I' : 'P', timeData);
- }
- inputLength = 0;
- }
- },
- findIFrame: function () {
- if (this.videoBufferList !== null) {
- var bufferNode = this.videoBufferList.findIFrame();
- if (bufferNode === null || typeof bufferNode === "undefined") {
- return false;
- } else {
- var data = {};
- this.setTimeStamp(bufferNode.timeStamp);
- data.frameData = this.decoder.decode(bufferNode.buffer);
- data.timeStamp = bufferNode.timeStamp;
- return data;
- }
- }
- },
- ntohl: function (buffer) {
- return (((buffer[0] << 24) + (buffer[1] << 16) +
- (buffer[2] << 8) + buffer[3]) >>> 0);
- },
- set rtpSessionCallback(func) {
- this.rtpReturnCallback = func;
- },
- };
- return new Constructor();
- };
- /* eslint-enable no-magic-numbers */
|