UNPKG

@tianfeng98/hls.js

Version:

HLS.js is a JavaScript library that supports playing MPEG-TS and HEVC encoded HLS streams in browsers with support for MSE.

188 lines (177 loc) 5.41 kB
import Transmuxer, { isPromise } from '../demux/transmuxer'; import { Events } from '../events'; import { ILogFunction, enableLogs, logger } from '../utils/logger'; import { EventEmitter } from 'eventemitter3'; import { ErrorDetails, ErrorTypes } from '../errors'; import type { RemuxedTrack, RemuxerResult } from '../types/remuxer'; import type { TransmuxerResult, ChunkMetadata } from '../types/transmuxer'; if (typeof __IN_WORKER__ !== 'undefined' && __IN_WORKER__) { startWorker(self); } function startWorker(self) { const observer = new EventEmitter(); const forwardMessage = (ev, data) => { self.postMessage({ event: ev, data: data }); }; // forward events to main thread observer.on(Events.FRAG_DECRYPTED, forwardMessage); observer.on(Events.ERROR, forwardMessage); // forward logger events to main thread const forwardWorkerLogs = () => { for (const logFn in logger) { const func: ILogFunction = (message?) => { forwardMessage('workerLog', { logType: logFn, message, }); }; logger[logFn] = func; } }; self.addEventListener('message', (ev) => { const data = ev.data; switch (data.cmd) { case 'init': { const config = JSON.parse(data.config); self.transmuxer = new Transmuxer( observer, data.typeSupported, config, data.vendor, data.id, ); enableLogs(config.debug, data.id); forwardWorkerLogs(); forwardMessage('init', null); break; } case 'configure': { self.transmuxer.configure(data.config); break; } case 'demux': { const transmuxResult: TransmuxerResult | Promise<TransmuxerResult> = self.transmuxer.push( data.data, data.decryptdata, data.chunkMeta, data.state, ); if (isPromise(transmuxResult)) { self.transmuxer.async = true; transmuxResult .then((data) => { emitTransmuxComplete(self, data); }) .catch((error) => { forwardMessage(Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, chunkMeta: data.chunkMeta, fatal: false, error, err: error, reason: `transmuxer-worker push error`, }); }); } else { self.transmuxer.async = false; emitTransmuxComplete(self, transmuxResult); } break; } case 'flush': { const id = data.chunkMeta; let transmuxResult = self.transmuxer.flush(id); const asyncFlush = isPromise(transmuxResult); if (asyncFlush || self.transmuxer.async) { if (!isPromise(transmuxResult)) { transmuxResult = Promise.resolve(transmuxResult); } transmuxResult .then((results: Array<TransmuxerResult>) => { handleFlushResult(self, results as Array<TransmuxerResult>, id); }) .catch((error) => { forwardMessage(Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, chunkMeta: data.chunkMeta, fatal: false, error, err: error, reason: `transmuxer-worker flush error`, }); }); } else { handleFlushResult( self, transmuxResult as Array<TransmuxerResult>, id, ); } break; } default: break; } }); } function emitTransmuxComplete( self: any, transmuxResult: TransmuxerResult, ): boolean { if (isEmptyResult(transmuxResult.remuxResult)) { return false; } const transferable: Array<ArrayBuffer> = []; const { audio, video } = transmuxResult.remuxResult; if (audio) { addToTransferable(transferable, audio); } if (video) { addToTransferable(transferable, video); } self.postMessage( { event: 'transmuxComplete', data: transmuxResult }, transferable, ); return true; } // Converts data to a transferable object https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast) // in order to minimize message passing overhead function addToTransferable( transferable: Array<ArrayBuffer>, track: RemuxedTrack, ) { if (track.data1) { transferable.push(track.data1.buffer); } if (track.data2) { transferable.push(track.data2.buffer); } } function handleFlushResult( self: any, results: Array<TransmuxerResult>, chunkMeta: ChunkMetadata, ) { const parsed = results.reduce( (parsed, result) => emitTransmuxComplete(self, result) || parsed, false, ); if (!parsed) { // Emit at least one "transmuxComplete" message even if media is not found to update stream-controller state to PARSING self.postMessage({ event: 'transmuxComplete', data: results[0] }); } self.postMessage({ event: 'flush', data: chunkMeta }); } function isEmptyResult(remuxResult: RemuxerResult) { return ( !remuxResult.audio && !remuxResult.video && !remuxResult.text && !remuxResult.id3 && !remuxResult.initSegment ); }