Skip to content

Commit

Permalink
Fix TSDemuxer parsing error handling in sync path (#6469)
Browse files Browse the repository at this point in the history
* Add type safety to worker message handler and replace deprecated vendor field with empty string
Related to #6445

(cherry picked from commit 1e4e605)

* Fix TSDemuxer parsing error handling
Fixes #6445

* Warn with error message when available
  • Loading branch information
robwalch authored May 31, 2024
1 parent 7fd0fe3 commit adf87aa
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 39 deletions.
2 changes: 1 addition & 1 deletion src/controller/base-stream-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ export default class BaseStreamController
if (this.state === State.STOPPED || this.state === State.ERROR) {
return;
}
this.warn(reason);
this.warn(`Frag error: ${reason?.message || reason}`);
this.resetFragmentLoading(frag);
});
}
Expand Down
42 changes: 26 additions & 16 deletions src/demux/transmuxer-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { EventEmitter } from 'eventemitter3';
import { Fragment, Part } from '../loader/fragment';
import type { ChunkMetadata, TransmuxerResult } from '../types/transmuxer';
import type Hls from '../hls';
import type { HlsEventEmitter } from '../events';
import type { HlsEventEmitter, HlsListeners } from '../events';
import type { PlaylistLevelType } from '../types/loader';
import type { TypeSupported } from './tsdemuxer';
import type { RationalTimestamp } from '../utils/timescale-conversion';
Expand All @@ -31,7 +31,9 @@ export default class TransmuxerInterface {
private part: Part | null = null;
private useWorker: boolean;
private workerContext: WorkerContext | null = null;
private onwmsg?: Function;
private onwmsg?: (
event: MessageEvent<{ event: string; data?: any } | null>,
) => void;
private transmuxer: Transmuxer | null = null;
private onTransmuxComplete: (transmuxResult: TransmuxerResult) => void;
private onFlush: (chunkMeta: ChunkMetadata) => void;
Expand Down Expand Up @@ -75,9 +77,6 @@ export default class TransmuxerInterface {
: false,
};

// navigator.vendor is not always available in Web Worker
// refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
const vendor = navigator.vendor;
if (this.useWorker && typeof Worker !== 'undefined') {
const canCreateWorker = config.workerPath || hasUMDWorker();
if (canCreateWorker) {
Expand All @@ -89,9 +88,9 @@ export default class TransmuxerInterface {
logger.log(`injecting Web Worker for "${id}"`);
this.workerContext = injectWorker();
}
this.onwmsg = (ev: any) => this.onWorkerMessage(ev);
this.onwmsg = (event) => this.onWorkerMessage(event);
const { worker } = this.workerContext;
worker.addEventListener('message', this.onwmsg as any);
worker.addEventListener('message', this.onwmsg);
worker.onerror = (event) => {
const error = new Error(
`${event.message} (${event.filename}:${event.lineno})`,
Expand All @@ -109,7 +108,7 @@ export default class TransmuxerInterface {
worker.postMessage({
cmd: 'init',
typeSupported: m2tsTypeSupported,
vendor: vendor,
vendor: '',
id: id,
config: JSON.stringify(config),
});
Expand All @@ -124,7 +123,7 @@ export default class TransmuxerInterface {
this.observer,
m2tsTypeSupported,
config,
vendor,
'',
id,
);
}
Expand All @@ -136,12 +135,12 @@ export default class TransmuxerInterface {
this.observer,
m2tsTypeSupported,
config,
vendor,
'',
id,
);
}

resetWorker(): void {
resetWorker() {
if (this.workerContext) {
const { worker, objectURL } = this.workerContext;
if (objectURL) {
Expand All @@ -155,7 +154,7 @@ export default class TransmuxerInterface {
}
}

destroy(): void {
destroy() {
if (this.workerContext) {
this.resetWorker();
this.onwmsg = undefined;
Expand Down Expand Up @@ -188,7 +187,7 @@ export default class TransmuxerInterface {
accurateTimeOffset: boolean,
chunkMeta: ChunkMetadata,
defaultInitPTS?: RationalTimestamp,
): void {
) {
chunkMeta.transmuxing.start = self.performance.now();
const { transmuxer } = this;
const timeOffset = part ? part.start : frag.start;
Expand Down Expand Up @@ -355,9 +354,20 @@ export default class TransmuxerInterface {
this.onFlush(chunkMeta);
}

private onWorkerMessage(ev: any): void {
const data = ev.data;
private onWorkerMessage(
event: MessageEvent<{ event: string; data?: any } | null>,
) {
const data = event.data;
if (!data?.event) {
logger.warn(
`worker message received with no ${data ? 'event name' : 'data'}`,
);
return;
}
const hls = this.hls;
if (!this.hls) {
return;
}
switch (data.event) {
case 'init': {
const objectURL = this.workerContext?.objectURL;
Expand Down Expand Up @@ -389,7 +399,7 @@ export default class TransmuxerInterface {
data.data = data.data || {};
data.data.frag = this.frag;
data.data.id = this.id;
hls.trigger(data.event, data.data);
hls.trigger(data.event as keyof HlsListeners, data.data);
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/demux/transmuxer-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function startWorker(self) {
observer,
data.typeSupported,
config,
data.vendor,
'',
data.id,
);
enableLogs(config.debug, data.id);
Expand Down
49 changes: 28 additions & 21 deletions src/demux/tsdemuxer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ class TSDemuxer implements Demuxer {
offset,
this.typeSupported,
isSampleAes,
this.observer,
);

// only update track id if track PID found while parsing PMT
Expand Down Expand Up @@ -411,16 +412,12 @@ class TSDemuxer implements Demuxer {
}

if (tsPacketErrors > 0) {
const error = new Error(
`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`,
emitParsingError(
this.observer,
new Error(
`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`,
),
);
this.observer.emit(Events.ERROR, Events.ERROR, {
type: ErrorTypes.MEDIA_ERROR,
details: ErrorDetails.FRAG_PARSING_ERROR,
fatal: false,
error,
reason: error.message,
});
}

videoTrack.pesData = videoData;
Expand Down Expand Up @@ -603,16 +600,7 @@ class TSDemuxer implements Demuxer {
} else {
reason = 'No ADTS header found in AAC PES';
}
const error = new Error(reason);
logger.warn(`parsing error: ${reason}`);
this.observer.emit(Events.ERROR, Events.ERROR, {
type: ErrorTypes.MEDIA_ERROR,
details: ErrorDetails.FRAG_PARSING_ERROR,
fatal: false,
levelRetry: recoverable,
error,
reason,
});
emitParsingError(this.observer, new Error(reason), recoverable);
if (!recoverable) {
return;
}
Expand Down Expand Up @@ -743,6 +731,7 @@ function parsePMT(
offset: number,
typeSupported: TypeSupported,
isSampleAes: boolean,
observer: HlsEventEmitter,
) {
const result = {
audioPid: -1,
Expand Down Expand Up @@ -872,10 +861,12 @@ function parsePMT(
case 0xc2: // SAMPLE-AES EC3
/* falls through */
case 0x87:
throw new Error('Unsupported EC-3 in M2TS found');
emitParsingError(observer, new Error('Unsupported EC-3 in M2TS found'));
return result;

case 0x24:
throw new Error('Unsupported HEVC in M2TS found');
emitParsingError(observer, new Error('Unsupported HEVC in M2TS found'));
return result;

default:
// logger.log('unknown stream type:' + data[offset]);
Expand All @@ -888,6 +879,22 @@ function parsePMT(
return result;
}

function emitParsingError(
observer: HlsEventEmitter,
error: Error,
levelRetry?: boolean,
) {
logger.warn(`parsing error: ${error.message}`);
observer.emit(Events.ERROR, Events.ERROR, {
type: ErrorTypes.MEDIA_ERROR,
details: ErrorDetails.FRAG_PARSING_ERROR,
fatal: false,
levelRetry,
error,
reason: error.message,
});
}

function logEncryptedSamplesFoundInUnencryptedStream(type: string) {
logger.log(`${type} with AES-128-CBC encryption found in unencrypted stream`);
}
Expand Down

0 comments on commit adf87aa

Please sign in to comment.