Skip to content

Commit

Permalink
Handle ManagedMediaSource endStreaming events without aborting requests
Browse files Browse the repository at this point in the history
Fixes #6918

(cherry picked from #6186 commit 6de6587)
  • Loading branch information
robwalch committed Dec 17, 2024
1 parent 91bb1e7 commit 3a509a3
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 20 deletions.
10 changes: 10 additions & 0 deletions api-extractor/report/hls.js.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP
// (undocumented)
protected bufferFragmentData(data: RemuxedTrack, frag: Fragment, part: Part | null, chunkMeta: ChunkMetadata, noBacktracking?: boolean): void;
// (undocumented)
protected buffering: boolean;
// (undocumented)
protected checkLiveUpdate(details: LevelDetails): void;
// (undocumented)
protected clearTrackerIfNeeded(frag: Fragment): void;
Expand Down Expand Up @@ -453,6 +455,8 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP
// (undocumented)
protected onvseeking: EventListener | null;
// (undocumented)
pauseBuffering(): void;
// (undocumented)
protected playlistType: PlaylistLevelType;
// (undocumented)
protected recoverWorkerError(data: ErrorData): void;
Expand All @@ -475,6 +479,8 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP
// (undocumented)
protected resetWhenMissingContext(chunkMeta: ChunkMetadata): void;
// (undocumented)
resumeBuffering(): void;
// (undocumented)
protected retryDate: number;
// (undocumented)
protected seekToStartPos(): void;
Expand Down Expand Up @@ -2917,6 +2923,10 @@ export type MP4RemuxerConfig = {
//
// @public (undocumented)
export interface NetworkComponentAPI extends ComponentAPI {
// (undocumented)
pauseBuffering?(): void;
// (undocumented)
resumeBuffering?(): void;
// (undocumented)
startLoad(startPosition: number): void;
// (undocumented)
Expand Down
6 changes: 4 additions & 2 deletions src/controller/audio-stream-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,14 @@ class AudioStreamController
const { hls, levels, media, trackId } = this;
const config = hls.config;

// 1. if video not attached AND
// 1. if buffering is suspended
// 2. if video not attached AND
// start fragment already requested OR start frag prefetch not enabled
// 2. if tracks or track not loaded and selected
// 3. if tracks or track not loaded and selected
// then exit loop
// => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop
if (
!this.buffering ||
(!media && (this.startFragRequested || !config.startFragPrefetch)) ||
!levels?.[trackId]
) {
Expand Down
9 changes: 9 additions & 0 deletions src/controller/base-stream-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export default class BaseStreamController
protected startFragRequested: boolean = false;
protected decrypter: Decrypter;
protected initPTS: RationalTimestamp[] = [];
protected buffering: boolean = true;
protected onvseeking: EventListener | null = null;
protected onvended: EventListener | null = null;

Expand Down Expand Up @@ -150,6 +151,14 @@ export default class BaseStreamController
this.state = State.STOPPED;
}

public pauseBuffering() {
this.buffering = false;
}

public resumeBuffering() {
this.buffering = true;
}

protected _streamEnded(
bufferInfo: BufferInfo,
levelDetails: LevelDetails,
Expand Down
1 change: 1 addition & 0 deletions src/controller/buffer-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ export default class BufferController implements ComponentAPI {
this.resetBuffer(type);
});
this._initSourceBuffer();
this.hls.resumeBuffering();
}

private resetBuffer(type: SourceBufferName) {
Expand Down
5 changes: 4 additions & 1 deletion src/controller/stream-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ export default class StreamController
return;
}

const level = hls.nextLoadLevel;
const level = this.buffering ? hls.nextLoadLevel : hls.loadLevel;
if (!levels?.[level]) {
return;
}
Expand All @@ -262,6 +262,9 @@ export default class StreamController
this.state = State.ENDED;
return;
}
if (!this.buffering) {
return;
}

// set next load level : this will trigger a playlist load if needed
if (hls.loadLevel !== level && hls.manualLevel === -1) {
Expand Down
41 changes: 24 additions & 17 deletions src/hls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,9 +430,13 @@ export default class Hls implements HlsEventEmitter {
startLoad(startPosition: number = -1) {
logger.log(`startLoad(${startPosition})`);
this.started = true;
this.networkControllers.forEach((controller) => {
controller.startLoad(startPosition);
});
this.resumeBuffering();
for (let i = 0; i < this.networkControllers.length; i++) {
this.networkControllers[i].startLoad(startPosition);
if (!this.started || !this.networkControllers) {
break;
}
}
}

/**
Expand All @@ -441,32 +445,35 @@ export default class Hls implements HlsEventEmitter {
stopLoad() {
logger.log('stopLoad');
this.started = false;
this.networkControllers.forEach((controller) => {
controller.stopLoad();
});
for (let i = 0; i < this.networkControllers.length; i++) {
this.networkControllers[i].stopLoad();
if (this.started || !this.networkControllers) {
break;
}
}
}

/**
* Resumes stream controller segment loading if previously started.
* Resumes stream controller segment loading after `pauseBuffering` has been called.
*/
resumeBuffering() {
if (this.started) {
this.networkControllers.forEach((controller) => {
if ('fragmentLoader' in controller) {
controller.startLoad(-1);
}
});
}
logger.log(`resume buffering`);
this.networkControllers.forEach((controller) => {
if (controller.resumeBuffering) {
controller.resumeBuffering();
}
});
}

/**
* Stops stream controller segment loading without changing 'started' state like stopLoad().
* Prevents stream controller from loading new segments until `resumeBuffering` is called.
* This allows for media buffering to be paused without interupting playlist loading.
*/
pauseBuffering() {
logger.log(`pause buffering`);
this.networkControllers.forEach((controller) => {
if ('fragmentLoader' in controller) {
controller.stopLoad();
if (controller.pauseBuffering) {
controller.pauseBuffering();
}
});
}
Expand Down
2 changes: 2 additions & 0 deletions src/types/component-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ export interface AbrComponentAPI extends ComponentAPI {
export interface NetworkComponentAPI extends ComponentAPI {
startLoad(startPosition: number): void;
stopLoad(): void;
pauseBuffering?(): void;
resumeBuffering?(): void;
}

0 comments on commit 3a509a3

Please sign in to comment.