From 2e297101a7fb6912c237965acbc29be2b7f3ad53 Mon Sep 17 00:00:00 2001 From: Trek H Date: Fri, 24 Jan 2020 19:50:48 +1030 Subject: [PATCH] mjpeg-player: ts code to js code This PR is for the hls.js files where the only modifications made are for typescript to javascript conversion, and updated import statements. --- cmd/mjpeg-player/hlsjs/event-handler.js | 55 +++-- cmd/mjpeg-player/hlsjs/loader/fragment.js | 151 +++++++------ cmd/mjpeg-player/hlsjs/loader/level-key.js | 43 +++- cmd/mjpeg-player/hlsjs/loader/m3u8-parser.js | 222 ++++++++++--------- cmd/mjpeg-player/hlsjs/observer.js | 27 ++- cmd/mjpeg-player/hlsjs/types/loader.js | 146 +++--------- cmd/mjpeg-player/hlsjs/utils/codecs.js | 29 ++- 7 files changed, 352 insertions(+), 321 deletions(-) diff --git a/cmd/mjpeg-player/hlsjs/event-handler.js b/cmd/mjpeg-player/hlsjs/event-handler.js index 06c089da..24735225 100644 --- a/cmd/mjpeg-player/hlsjs/event-handler.js +++ b/cmd/mjpeg-player/hlsjs/event-handler.js @@ -1,13 +1,32 @@ +/* +AUTHOR + Trek Hopton + +LICENSE + This file is Copyright (C) 2020 the Australian Ocean Lab (AusOcean) + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License in gpl.txt. + If not, see http://www.gnu.org/licenses. + + For hls.js Copyright notice and license, see LICENSE file. +*/ + /* * * All objects in the event handling chain should inherit from this class * */ - -import { logger } from './utils/logger'; -import { ErrorTypes, ErrorDetails } from './errors'; -import Event from './events'; -import Hls from './hls'; +import Event from './events.js'; const FORBIDDEN_EVENT_NAMES = { 'hlsEventGeneric': true, @@ -16,11 +35,7 @@ const FORBIDDEN_EVENT_NAMES = { }; class EventHandler { - hls: Hls; - handledEvents: any[]; - useGenericHandler: boolean; - - constructor (hls: Hls, ...events: any[]) { + constructor(hls, ...events) { this.hls = hls; this.onEvent = this.onEvent.bind(this); this.handledEvents = events; @@ -29,20 +44,20 @@ class EventHandler { this.registerListeners(); } - destroy () { + destroy() { this.onHandlerDestroying(); this.unregisterListeners(); this.onHandlerDestroyed(); } - protected onHandlerDestroying () {} - protected onHandlerDestroyed () {} + onHandlerDestroying() { } + onHandlerDestroyed() { } - isEventHandler () { + isEventHandler() { return typeof this.handledEvents === 'object' && this.handledEvents.length && typeof this.onEvent === 'function'; } - registerListeners () { + registerListeners() { if (this.isEventHandler()) { this.handledEvents.forEach(function (event) { if (FORBIDDEN_EVENT_NAMES[event]) { @@ -54,7 +69,7 @@ class EventHandler { } } - unregisterListeners () { + unregisterListeners() { if (this.isEventHandler()) { this.handledEvents.forEach(function (event) { this.hls.off(event, this.onEvent); @@ -65,12 +80,12 @@ class EventHandler { /** * arguments: event (string), data (any) */ - onEvent (event: string, data: any) { + onEvent(event, data) { this.onEventGeneric(event, data); } - onEventGeneric (event: string, data: any) { - let eventToFunction = function (event: string, data: any) { + onEventGeneric(event, data) { + let eventToFunction = function (event, data) { let funcName = 'on' + event.replace('hls', ''); if (typeof this[funcName] !== 'function') { throw new Error(`Event ${event} has no generic handler in this ${this.constructor.name} class (tried ${funcName})`); @@ -81,7 +96,7 @@ class EventHandler { try { eventToFunction.call(this, event, data).call(); } catch (err) { - logger.error(`An internal error happened while handling event ${event}. Error message: "${err.message}". Here is a stacktrace:`, err); + console.error(`An internal error happened while handling event ${event}. Error message: "${err.message}". Here is a stacktrace:`, err); this.hls.trigger(Event.ERROR, { type: ErrorTypes.OTHER_ERROR, details: ErrorDetails.INTERNAL_EXCEPTION, fatal: false, event: event, err: err }); } } diff --git a/cmd/mjpeg-player/hlsjs/loader/fragment.js b/cmd/mjpeg-player/hlsjs/loader/fragment.js index fb7c0532..01fb0509 100644 --- a/cmd/mjpeg-player/hlsjs/loader/fragment.js +++ b/cmd/mjpeg-player/hlsjs/loader/fragment.js @@ -1,69 +1,90 @@ +/* +AUTHOR + Trek Hopton -import { buildAbsoluteURL } from 'url-toolkit'; -import { logger } from '../utils/logger'; -import LevelKey from './level-key'; -import { PlaylistLevelType } from '../types/loader'; +LICENSE + This file is Copyright (C) 2020 the Australian Ocean Lab (AusOcean) -export enum ElementaryStreamTypes { - AUDIO = 'audio', - VIDEO = 'video', + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License in gpl.txt. + If not, see http://www.gnu.org/licenses. + + For hls.js Copyright notice and license, see LICENSE file. +*/ + +import URLToolkit from '../../url-toolkit/url-toolkit.js'; +import LevelKey from './level-key.js'; + +export const ElementaryStreamTypes = { + AUDIO: 'audio', + VIDEO: 'video' } export default class Fragment { - private _url: string | null = null; - private _byteRange: number[] | null = null; - private _decryptdata: LevelKey | null = null; + constructor() { + this._url = null; + this._byteRange = null; + this._decryptdata = null; - // Holds the types of data this fragment supports - private _elementaryStreams: Record = { - [ElementaryStreamTypes.AUDIO]: false, - [ElementaryStreamTypes.VIDEO]: false - }; + // Holds the types of data this fragment supports + this._elementaryStreams = { + [ElementaryStreamTypes.AUDIO]: false, + [ElementaryStreamTypes.VIDEO]: false + }; - // deltaPTS tracks the change in presentation timestamp between fragments - public deltaPTS: number = 0; + // deltaPTS tracks the change in presentation timestamp between fragments + this.deltaPTS = 0; - public rawProgramDateTime: string | null = null; - public programDateTime: number | null = null; - public title: string | null = null; - public tagList: Array = []; + this.rawProgramDateTime = null; + this.programDateTime = null; + this.title = null; + this.tagList = []; - // TODO: Move at least baseurl to constructor. - // Currently we do a two-pass construction as use the Fragment class almost like a object for holding parsing state. - // It may make more sense to just use a POJO to keep state during the parsing phase. - // Have Fragment be the representation once we have a known state? - // Something to think on. + // TODO: Move at least baseurl to constructor. + // Currently we do a two-pass construction as use the Fragment class almost like a object for holding parsing state. + // It may make more sense to just use a POJO to keep state during the parsing phase. + // Have Fragment be the representation once we have a known state? + // Something to think on. - // Discontinuity Counter - public cc!: number; + // Discontinuity Counter + this.cc; + this.type; + // relurl is the portion of the URL that comes from inside the playlist. + this.relurl; + // baseurl is the URL to the playlist + this.baseurl; + // EXTINF has to be present for a m3u8 to be considered valid + this.duration; + // When this segment starts in the timeline + this.start; + // sn notates the sequence number for a segment, and if set to a string can be 'initSegment' + this.sn = 0; - public type!: PlaylistLevelType; - // relurl is the portion of the URL that comes from inside the playlist. - public relurl!: string; - // baseurl is the URL to the playlist - public baseurl!: string; - // EXTINF has to be present for a m3u8 to be considered valid - public duration!: number; - // When this segment starts in the timeline - public start!: number; - // sn notates the sequence number for a segment, and if set to a string can be 'initSegment' - public sn: number | 'initSegment' = 0; + this.urlId = 0; + // level matches this fragment to a index playlist + this.level = 0; + // levelkey is the EXT-X-KEY that applies to this segment for decryption + // core difference from the private field _decryptdata is the lack of the initialized IV + // _decryptdata will set the IV for this segment based on the segment number in the fragment + this.levelkey; - public urlId: number = 0; - // level matches this fragment to a index playlist - public level: number = 0; - // levelkey is the EXT-X-KEY that applies to this segment for decryption - // core difference from the private field _decryptdata is the lack of the initialized IV - // _decryptdata will set the IV for this segment based on the segment number in the fragment - public levelkey?: LevelKey; - - // TODO(typescript-xhrloader) - public loader: any; + // TODO(typescript-xhrloader) + this.loader; + } // setByteRange converts a EXT-X-BYTERANGE attribute into a two element array - setByteRange (value: string, previousFrag?: Fragment) { + setByteRange(value, previousFrag) { const params = value.split('@', 2); - const byteRange: number[] = []; + const byteRange = []; if (params.length === 1) { byteRange[0] = previousFrag ? previousFrag.byteRangeEndOffset : 0; } else { @@ -73,19 +94,19 @@ export default class Fragment { this._byteRange = byteRange; } - get url () { + get url() { if (!this._url && this.relurl) { - this._url = buildAbsoluteURL(this.baseurl, this.relurl, { alwaysNormalize: true }); + this._url = URLToolkit.buildAbsoluteURL(this.baseurl, this.relurl, { alwaysNormalize: true }); } return this._url; } - set url (value) { + set url(value) { this._url = value; } - get byteRange (): number[] { + get byteRange() { if (!this._byteRange) { return []; } @@ -96,15 +117,15 @@ export default class Fragment { /** * @type {number} */ - get byteRangeStartOffset () { + get byteRangeStartOffset() { return this.byteRange[0]; } - get byteRangeEndOffset () { + get byteRangeEndOffset() { return this.byteRange[1]; } - get decryptdata (): LevelKey | null { + get decryptdata() { if (!this.levelkey && !this._decryptdata) { return null; } @@ -116,7 +137,7 @@ export default class Fragment { // If the segment was encrypted with AES-128 // It must have an IV defined. We cannot substitute the Segment Number in. if (this.levelkey && this.levelkey.method === 'AES-128' && !this.levelkey.iv) { - logger.warn(`missing IV for initialization segment with method="${this.levelkey.method}" - compliance issue`); + console.warn(`missing IV for initialization segment with method="${this.levelkey.method}" - compliance issue`); } /* @@ -134,7 +155,7 @@ export default class Fragment { return this._decryptdata; } - get endProgramDateTime () { + get endProgramDateTime() { if (this.programDateTime === null) { return null; } @@ -148,21 +169,21 @@ export default class Fragment { return this.programDateTime + (duration * 1000); } - get encrypted () { + get encrypted() { return !!((this.decryptdata && this.decryptdata.uri !== null) && (this.decryptdata.key === null)); } /** * @param {ElementaryStreamTypes} type */ - addElementaryStream (type: ElementaryStreamTypes) { + addElementaryStream(type) { this._elementaryStreams[type] = true; } /** * @param {ElementaryStreamTypes} type */ - hasElementaryStream (type: ElementaryStreamTypes) { + hasElementaryStream(type) { return this._elementaryStreams[type] === true; } @@ -171,7 +192,7 @@ export default class Fragment { * @param {number} segmentNumber - segment number to generate IV with * @returns {Uint8Array} */ - createInitializationVector (segmentNumber: number): Uint8Array { + createInitializationVector(segmentNumber) { let uint8View = new Uint8Array(16); for (let i = 12; i < 16; i++) { @@ -187,7 +208,7 @@ export default class Fragment { * @param segmentNumber - the fragment's segment number * @returns {LevelKey} - an object to be applied as a fragment's decryptdata */ - setDecryptDataFromLevelKey (levelkey: LevelKey, segmentNumber: number): LevelKey { + setDecryptDataFromLevelKey(levelkey, segmentNumber) { let decryptdata = levelkey; if (levelkey && levelkey.method && levelkey.uri && !levelkey.iv) { diff --git a/cmd/mjpeg-player/hlsjs/loader/level-key.js b/cmd/mjpeg-player/hlsjs/loader/level-key.js index f5abc95e..8bfe4331 100644 --- a/cmd/mjpeg-player/hlsjs/loader/level-key.js +++ b/cmd/mjpeg-player/hlsjs/loader/level-key.js @@ -1,22 +1,45 @@ -import { buildAbsoluteURL } from 'url-toolkit'; +/* +AUTHOR + Trek Hopton + +LICENSE + This file is Copyright (C) 2020 the Australian Ocean Lab (AusOcean) + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License in gpl.txt. + If not, see http://www.gnu.org/licenses. + + For hls.js Copyright notice and license, see LICENSE file. +*/ + +import URLToolkit from '../../url-toolkit/url-toolkit.js'; export default class LevelKey { - private _uri: string | null = null; + constructor(baseURI, relativeURI) { + this._uri = null; - public baseuri: string; - public reluri: string; - public method: string | null = null; - public key: Uint8Array | null = null; - public iv: Uint8Array | null = null; + this.baseuri; + this.reluri; + this.method = null; + this.key = null; + this.iv = null; - constructor (baseURI: string, relativeURI: string) { this.baseuri = baseURI; this.reluri = relativeURI; } - get uri () { + get uri() { if (!this._uri && this.reluri) { - this._uri = buildAbsoluteURL(this.baseuri, this.reluri, { alwaysNormalize: true }); + this._uri = URLToolkit.buildAbsoluteURL(this.baseuri, this.reluri, { alwaysNormalize: true }); } return this._uri; diff --git a/cmd/mjpeg-player/hlsjs/loader/m3u8-parser.js b/cmd/mjpeg-player/hlsjs/loader/m3u8-parser.js index ea4c2538..8778dc75 100644 --- a/cmd/mjpeg-player/hlsjs/loader/m3u8-parser.js +++ b/cmd/mjpeg-player/hlsjs/loader/m3u8-parser.js @@ -1,14 +1,32 @@ -import * as URLToolkit from 'url-toolkit'; +/* +AUTHOR + Trek Hopton -import Fragment from './fragment'; -import Level from './level'; -import LevelKey from './level-key'; +LICENSE + This file is Copyright (C) 2020 the Australian Ocean Lab (AusOcean) -import AttrList from '../utils/attr-list'; -import { logger } from '../utils/logger'; -import { isCodecType, CodecType } from '../utils/codecs'; -import { MediaPlaylist, AudioGroup, MediaPlaylistType } from '../types/media-playlist'; -import { PlaylistLevelType } from '../types/loader'; + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License in gpl.txt. + If not, see http://www.gnu.org/licenses. + + For hls.js Copyright notice and license, see LICENSE file. +*/ + +import URLToolkit from '../../url-toolkit/url-toolkit.js'; +import Fragment from './fragment.js'; +import Level from './level.js'; +import LevelKey from './level-key.js'; +import AttrList from '../utils/attr-list.js'; +import { isCodecType } from '../utils/codecs.js'; /** * M3U8 parser @@ -32,7 +50,7 @@ const LEVEL_PLAYLIST_REGEX_SLOW = /(?:(?:#(EXTM3U))|(?:#EXT-X-(PLAYLIST-TYPE):(. const MP4_REGEX_SUFFIX = /\.(mp4|m4s|m4v|m4a)$/i; export default class M3U8Parser { - static findGroup (groups: Array, mediaGroupId: string): AudioGroup | undefined { + static findGroup(groups, mediaGroupId) { for (let i = 0; i < groups.length; i++) { const group = groups[i]; if (group.id === mediaGroupId) { @@ -41,7 +59,7 @@ export default class M3U8Parser { } } - static convertAVC1ToAVCOTI (codec) { + static convertAVC1ToAVCOTI(codec) { let avcdata = codec.split('.'); let result; if (avcdata.length > 2) { @@ -54,18 +72,18 @@ export default class M3U8Parser { return result; } - static resolve (url, baseUrl) { + static resolve(url, baseUrl) { return URLToolkit.buildAbsoluteURL(baseUrl, url, { alwaysNormalize: true }); } - static parseMasterPlaylist (string: string, baseurl: string) { + static parseMasterPlaylist(string, baseurl) { // TODO(typescript-level) - let levels: Array = []; + let levels = []; MASTER_PLAYLIST_REGEX.lastIndex = 0; // TODO(typescript-level) - function setCodecs (codecs: Array, level: any) { - ['video', 'audio'].forEach((type: CodecType) => { + function setCodecs(codecs, level) { + ['video', 'audio'].forEach((type) => { const filtered = codecs.filter((codec) => isCodecType(codec, type)); if (filtered.length) { const preferred = filtered.filter((codec) => { @@ -81,10 +99,10 @@ export default class M3U8Parser { level.unknownCodecs = codecs; } - let result: RegExpExecArray | null; + let result; while ((result = MASTER_PLAYLIST_REGEX.exec(string)) != null) { // TODO(typescript-level) - const level: any = {}; + const level = {}; const attrs = level.attrs = new AttrList(result[1]); level.url = M3U8Parser.resolve(result[2], baseurl); @@ -108,15 +126,15 @@ export default class M3U8Parser { return levels; } - static parseMasterPlaylistMedia (string: string, baseurl: string, type: MediaPlaylistType, audioGroups: Array = []): Array { - let result: RegExpExecArray | null; - let medias: Array = []; + static parseMasterPlaylistMedia(string, baseurl, type, audioGroups = []) { + let result; + let medias = []; let id = 0; MASTER_PLAYLIST_MEDIA_REGEX.lastIndex = 0; while ((result = MASTER_PLAYLIST_MEDIA_REGEX.exec(string)) !== null) { const attrs = new AttrList(result[1]); if (attrs.TYPE === type) { - const media: MediaPlaylist = { + const media = { id: id++, groupId: attrs['GROUP-ID'], name: attrs.NAME || attrs.LANGUAGE, @@ -146,16 +164,16 @@ export default class M3U8Parser { return medias; } - static parseLevelPlaylist (string: string, baseurl: string, id: number, type: PlaylistLevelType, levelUrlId: number) { + static parseLevelPlaylist(string, baseurl, id, type, levelUrlId) { let currentSN = 0; let totalduration = 0; let level = new Level(baseurl); let discontinuityCounter = 0; - let prevFrag: Fragment | null = null; - let frag: Fragment | null = new Fragment(); - let result: RegExpExecArray | RegExpMatchArray | null; - let i: number; - let levelkey: LevelKey | undefined; + let prevFrag = null; + let frag = new Fragment(); + let result; + let i; + let levelkey; let firstPdtIndex = null; @@ -168,7 +186,7 @@ export default class M3U8Parser { // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 const title = (' ' + result[2]).slice(1); frag.title = title || null; - frag.tagList.push(title ? [ 'INF', duration, title ] : [ 'INF', duration ]); + frag.tagList.push(title ? ['INF', duration, title] : ['INF', duration]); } else if (result[3]) { // url if (Number.isFinite(frag.duration)) { const sn = currentSN++; @@ -209,7 +227,7 @@ export default class M3U8Parser { } else { result = result[0].match(LEVEL_PLAYLIST_REGEX_SLOW); if (!result) { - logger.warn('No matches on slow regex match for level playlist!'); + console.warn('No matches on slow regex match for level playlist!'); continue; } for (i = 1; i < result.length; i++) { @@ -223,84 +241,84 @@ export default class M3U8Parser { const value2 = (' ' + result[i + 2]).slice(1); switch (result[i]) { - case '#': - frag.tagList.push(value2 ? [ value1, value2 ] : [ value1 ]); - break; - case 'PLAYLIST-TYPE': - level.type = value1.toUpperCase(); - break; - case 'MEDIA-SEQUENCE': - currentSN = level.startSN = parseInt(value1); - break; - case 'TARGETDURATION': - level.targetduration = parseFloat(value1); - break; - case 'VERSION': - level.version = parseInt(value1); - break; - case 'EXTM3U': - break; - case 'ENDLIST': - level.live = false; - break; - case 'DIS': - discontinuityCounter++; - frag.tagList.push(['DIS']); - break; - case 'DISCONTINUITY-SEQ': - discontinuityCounter = parseInt(value1); - break; - case 'KEY': { - // https://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.4.4 - const decryptparams = value1; - const keyAttrs = new AttrList(decryptparams); - const decryptmethod = keyAttrs.enumeratedString('METHOD'); - const decrypturi = keyAttrs.URI; - const decryptiv = keyAttrs.hexadecimalInteger('IV'); + case '#': + frag.tagList.push(value2 ? [value1, value2] : [value1]); + break; + case 'PLAYLIST-TYPE': + level.type = value1.toUpperCase(); + break; + case 'MEDIA-SEQUENCE': + currentSN = level.startSN = parseInt(value1); + break; + case 'TARGETDURATION': + level.targetduration = parseFloat(value1); + break; + case 'VERSION': + level.version = parseInt(value1); + break; + case 'EXTM3U': + break; + case 'ENDLIST': + level.live = false; + break; + case 'DIS': + discontinuityCounter++; + frag.tagList.push(['DIS']); + break; + case 'DISCONTINUITY-SEQ': + discontinuityCounter = parseInt(value1); + break; + case 'KEY': { + // https://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.4.4 + const decryptparams = value1; + const keyAttrs = new AttrList(decryptparams); + const decryptmethod = keyAttrs.enumeratedString('METHOD'); + const decrypturi = keyAttrs.URI; + const decryptiv = keyAttrs.hexadecimalInteger('IV'); - if (decryptmethod) { - levelkey = new LevelKey(baseurl, decrypturi); - if ((decrypturi) && (['AES-128', 'SAMPLE-AES', 'SAMPLE-AES-CENC'].indexOf(decryptmethod) >= 0)) { - levelkey.method = decryptmethod; - levelkey.key = null; - // Initialization Vector (IV) - levelkey.iv = decryptiv; + if (decryptmethod) { + levelkey = new LevelKey(baseurl, decrypturi); + if ((decrypturi) && (['AES-128', 'SAMPLE-AES', 'SAMPLE-AES-CENC'].indexOf(decryptmethod) >= 0)) { + levelkey.method = decryptmethod; + levelkey.key = null; + // Initialization Vector (IV) + levelkey.iv = decryptiv; + } } + break; } - break; - } - case 'START': { - const startAttrs = new AttrList(value1); - const startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET'); - // TIME-OFFSET can be 0 - if (Number.isFinite(startTimeOffset)) { - level.startTimeOffset = startTimeOffset; + case 'START': { + const startAttrs = new AttrList(value1); + const startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET'); + // TIME-OFFSET can be 0 + if (Number.isFinite(startTimeOffset)) { + level.startTimeOffset = startTimeOffset; + } + break; } - break; - } - case 'MAP': { - const mapAttrs = new AttrList(value1); - frag.relurl = mapAttrs.URI; - if (mapAttrs.BYTERANGE) { - frag.setByteRange(mapAttrs.BYTERANGE); + case 'MAP': { + const mapAttrs = new AttrList(value1); + frag.relurl = mapAttrs.URI; + if (mapAttrs.BYTERANGE) { + frag.setByteRange(mapAttrs.BYTERANGE); + } + frag.baseurl = baseurl; + frag.level = id; + frag.type = type; + frag.sn = 'initSegment'; + level.initSegment = frag; + frag = new Fragment(); + frag.rawProgramDateTime = level.initSegment.rawProgramDateTime; + break; } - frag.baseurl = baseurl; - frag.level = id; - frag.type = type; - frag.sn = 'initSegment'; - level.initSegment = frag; - frag = new Fragment(); - frag.rawProgramDateTime = level.initSegment.rawProgramDateTime; - break; - } - default: - logger.warn(`line parsed but not handled: ${result}`); - break; + default: + console.warn(`line parsed but not handled: ${result}`); + break; } } } frag = prevFrag; - // logger.log('found ' + level.fragments.length + ' fragments'); + // console.log('found ' + level.fragments.length + ' fragments'); if (frag && !frag.relurl) { level.fragments.pop(); totalduration -= frag.duration; @@ -316,7 +334,7 @@ export default class M3U8Parser { // if the fragments are TS or MP4, except if we download them :/ // but this is to be able to handle SIDX. if (level.fragments.every((frag) => MP4_REGEX_SUFFIX.test(frag.relurl))) { - logger.warn('MP4 fragments found but no init segment (probably no MAP, incomplete M3U8), trying to fetch SIDX'); + console.warn('MP4 fragments found but no init segment (probably no MAP, incomplete M3U8), trying to fetch SIDX'); frag = new Fragment(); frag.relurl = level.fragments[0].relurl; @@ -347,7 +365,7 @@ export default class M3U8Parser { } } -function backfillProgramDateTimes (fragments, startIndex) { +function backfillProgramDateTimes(fragments, startIndex) { let fragPrev = fragments[startIndex]; for (let i = startIndex - 1; i >= 0; i--) { const frag = fragments[i]; @@ -356,7 +374,7 @@ function backfillProgramDateTimes (fragments, startIndex) { } } -function assignProgramDateTime (frag, prevFrag) { +function assignProgramDateTime(frag, prevFrag) { if (frag.rawProgramDateTime) { frag.programDateTime = Date.parse(frag.rawProgramDateTime); } else if (prevFrag && prevFrag.programDateTime) { diff --git a/cmd/mjpeg-player/hlsjs/observer.js b/cmd/mjpeg-player/hlsjs/observer.js index 33d265eb..9f300d23 100644 --- a/cmd/mjpeg-player/hlsjs/observer.js +++ b/cmd/mjpeg-player/hlsjs/observer.js @@ -1,4 +1,27 @@ -import { EventEmitter } from 'eventemitter3'; +/* +AUTHOR + Trek Hopton + +LICENSE + This file is Copyright (C) 2020 the Australian Ocean Lab (AusOcean) + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License in gpl.txt. + If not, see http://www.gnu.org/licenses. + + For hls.js Copyright notice and license, see LICENSE file. +*/ + +import EventEmitter from '../eventemitter3/index.js'; /** * Simple adapter sub-class of Nodejs-like EventEmitter. @@ -9,7 +32,7 @@ export class Observer extends EventEmitter { * in every call to a handler, which is the purpose of our `trigger` method * extending the standard API. */ - trigger (event: string, ...data: Array): void { + trigger(event, ...data) { this.emit(event, event, ...data); } } diff --git a/cmd/mjpeg-player/hlsjs/types/loader.js b/cmd/mjpeg-player/hlsjs/types/loader.js index f99aa1da..2a23cb1b 100644 --- a/cmd/mjpeg-player/hlsjs/types/loader.js +++ b/cmd/mjpeg-player/hlsjs/types/loader.js @@ -1,132 +1,42 @@ -import Level from '../loader/level'; +/* +AUTHOR + Trek Hopton -export interface LoaderContext { - // target URL - url: string - // loader response type (arraybuffer or default response type for playlist) - responseType: string - // start byte range offset - rangeStart?: number - // end byte range offset - rangeEnd?: number - // true if onProgress should report partial chunk of loaded content - progressData?: boolean -} +LICENSE + This file is Copyright (C) 2020 the Australian Ocean Lab (AusOcean) -export interface LoaderConfiguration { - // Max number of load retries - maxRetry: number - // Timeout after which `onTimeOut` callback will be triggered - // (if loading is still not finished after that delay) - timeout: number - // Delay between an I/O error and following connection retry (ms). - // This to avoid spamming the server - retryDelay: number - // max connection retry delay (ms) - maxRetryDelay: number -} + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. -export interface LoaderResponse { - url: string, - // TODO(jstackhouse): SharedArrayBuffer, es2017 extension to TS - data: string | ArrayBuffer -} + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. -export interface LoaderStats { - // performance.now() just after load() has been called - trequest: number - // performance.now() of first received byte - tfirst: number - // performance.now() on load complete - tload: number - // performance.now() on parse completion - tparsed: number - // number of loaded bytes - loaded: number - // total number of bytes - total: number -} + You should have received a copy of the GNU General Public License in gpl.txt. + If not, see http://www.gnu.org/licenses. -type LoaderOnSuccess < T extends LoaderContext > = ( - response: LoaderResponse, - stats: LoaderStats, - context: T, - networkDetails: any -) => void; - -type LoaderOnProgress < T extends LoaderContext > = ( - stats: LoaderStats, - context: T, - data: string | ArrayBuffer, - networkDetails: any, -) => void; - -type LoaderOnError < T extends LoaderContext > = ( - error: { - // error status code - code: number, - // error description - text: string, - }, - context: T, - networkDetails: any, -) => void; - -type LoaderOnTimeout < T extends LoaderContext > = ( - stats: LoaderStats, - context: T, -) => void; - -export interface LoaderCallbacks{ - onSuccess: LoaderOnSuccess, - onError: LoaderOnError, - onTimeout: LoaderOnTimeout, - onProgress?: LoaderOnProgress, -} - -export interface Loader { - destroy(): void - abort(): void - load( - context: LoaderContext, - config: LoaderConfiguration, - callbacks: LoaderCallbacks, - ): void - - context: T -} + For hls.js Copyright notice and license, see LICENSE file. +*/ /** - * `type` property values for this loaders' context object - * @enum - * + * @readonly + * @enum {string} */ -export enum PlaylistContextType { - MANIFEST = 'manifest', - LEVEL = 'level', - AUDIO_TRACK = 'audioTrack', - SUBTITLE_TRACK= 'subtitleTrack' +export const PlaylistContextType = { + MANIFEST: 'manifest', + LEVEL: 'level', + AUDIO_TRACK: 'audioTrack', + SUBTITLE_TRACK: 'subtitleTrack' } /** * @enum {string} */ -export enum PlaylistLevelType { - MAIN = 'main', - AUDIO = 'audio', - SUBTITLE = 'subtitle' -} - -export interface PlaylistLoaderContext extends LoaderContext { - loader?: Loader - - type: PlaylistContextType - // the level index to load - level: number | null - // TODO: what is id? - id: number | null - // defines if the loader is handling a sidx request for the playlist - isSidxRequest?: boolean - // internal reprsentation of a parsed m3u8 level playlist - levelDetails?: Level +export const PlaylistLevelType = { + MAIN: 'main', + AUDIO: 'audio', + SUBTITLE: 'subtitle' } diff --git a/cmd/mjpeg-player/hlsjs/utils/codecs.js b/cmd/mjpeg-player/hlsjs/utils/codecs.js index 60ae8a94..a9b345aa 100644 --- a/cmd/mjpeg-player/hlsjs/utils/codecs.js +++ b/cmd/mjpeg-player/hlsjs/utils/codecs.js @@ -1,3 +1,26 @@ +/* +AUTHOR + Trek Hopton + +LICENSE + This file is Copyright (C) 2020 the Australian Ocean Lab (AusOcean) + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License in gpl.txt. + If not, see http://www.gnu.org/licenses. + + For hls.js Copyright notice and license, see LICENSE file. +*/ + // from http://mp4ra.org/codecs.html const sampleEntryCodesISO = { audio: { @@ -63,14 +86,12 @@ const sampleEntryCodesISO = { } }; -export type CodecType = 'audio' | 'video'; - -function isCodecType (codec: string, type: CodecType): boolean { +function isCodecType(codec, type) { const typeCodes = sampleEntryCodesISO[type]; return !!typeCodes && typeCodes[codec.slice(0, 4)] === true; } -function isCodecSupportedInMp4 (codec: string, type: CodecType): boolean { +function isCodecSupportedInMp4(codec, type) { return MediaSource.isTypeSupported(`${type || 'video'}/mp4;codecs="${codec}"`); }