Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix TSDemuxer parsing error handling in sync path #6469

Merged
merged 3 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading