Skip to content

Commit

Permalink
Create source elements for remote playback
Browse files Browse the repository at this point in the history
  • Loading branch information
robwalch committed Jun 8, 2023
1 parent fd98a06 commit d76c81f
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 18 deletions.
74 changes: 62 additions & 12 deletions src/controller/buffer-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type {
BufferFlushingData,
FragParsedData,
FragChangedData,
ManifestLoadingData,
} from '../types/events';
import type { ComponentAPI } from '../types/component-api';
import type { ChunkMetadata } from '../types/transmuxer';
Expand Down Expand Up @@ -57,6 +58,9 @@ export default class BufferController implements ComponentAPI {
// A reference to the active media source
public mediaSource: MediaSource | null = null;

// Source URL for remote play
private detachedHlsUrl?: string;

// Last MP3 audio chunk appended
private lastMpegAudioChunk: ChunkMetadata | null = null;

Expand Down Expand Up @@ -129,7 +133,22 @@ export default class BufferController implements ComponentAPI {
this.lastMpegAudioChunk = null;
}

private onManifestLoading() {
private onManifestLoading(
event: Events.MANIFEST_LOADING,
data: ManifestLoadingData
) {
const { media } = this;
if (media && media.childNodes?.length === 1) {
try {
addSource(media, data.url);
} catch (error) {
/* no-op */
}
media.disableRemotePlayback = false;
this.detachedHlsUrl = undefined;
} else {
this.detachedHlsUrl = data.url;
}
this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = 0;
this.details = null;
}
Expand Down Expand Up @@ -167,13 +186,24 @@ export default class BufferController implements ComponentAPI {
ms.addEventListener('startstreaming', this._onStartStreaming);
ms.addEventListener('endstreaming', this._onEndStreaming);

// Disable AirPlay for ManagedMediaSource to work
media.disableRemotePlayback = true;

// link video and media Source
media.src = self.URL.createObjectURL(ms);
// cache the locally generated object url
this._objectUrl = media.src;
const objectUrl = (this._objectUrl = self.URL.createObjectURL(ms));
// link video and media Source
try {
media.removeAttribute('src');
removeChildren(media);
addSource(media, objectUrl);
if (this.detachedHlsUrl) {
addSource(media, this.detachedHlsUrl);
this.detachedHlsUrl = undefined;
} else {
// Disable remote playback until source URL is added
media.disableRemotePlayback = true;
}
media.load();
} catch (error) {
media.src = objectUrl;
}
media.addEventListener('emptied', this._onMediaEmptied);
}
}
Expand Down Expand Up @@ -241,12 +271,13 @@ export default class BufferController implements ComponentAPI {

// clean up video tag src only if it's our own url. some external libraries might
// hijack the video tag and change its 'src' without destroying the Hls instance first
if (media.src === _objectUrl) {
if (this.mediaSrc === _objectUrl) {
media.removeAttribute('src');
removeChildren(media);
media.load();
} else {
logger.warn(
'[buffer-controller]: media.src was changed by a third party - skip cleanup'
'[buffer-controller]: media|source.src was changed by a third party - skip cleanup'
);
}
}
Expand Down Expand Up @@ -874,14 +905,20 @@ export default class BufferController implements ComponentAPI {
};

private _onMediaEmptied = () => {
const { media, _objectUrl } = this;
if (media && media.src !== _objectUrl) {
const { mediaSrc, _objectUrl } = this;
if (mediaSrc !== _objectUrl) {
logger.error(
`Media element src was set while attaching MediaSource (${_objectUrl} > ${media.src})`
`Media element src was set while attaching MediaSource (${_objectUrl} > ${mediaSrc})`
);
}
};

private get mediaSrc(): string | undefined {
const media =
(this.media?.firstChild as HTMLSourceElement | null) || this.media;
return media?.src;
}

private _onSBUpdateStart(type: SourceBufferName) {
const { operationQueue } = this;
const operation = operationQueue.current(type);
Expand Down Expand Up @@ -1027,3 +1064,16 @@ export default class BufferController implements ComponentAPI {
});
}
}

function removeChildren(node: HTMLElement) {
while (node.firstChild) {
node.removeChild(node.firstChild);
}
}

function addSource(media: HTMLMediaElement, url: string) {
const source = self.document.createElement('source');
source.type = 'video/mp4';
source.src = url;
media.appendChild(source);
}
8 changes: 2 additions & 6 deletions src/controller/eme-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1164,9 +1164,7 @@ class EMEController implements ComponentAPI {
)
.concat(
media?.setMediaKeys(null).catch((error) => {
this.log(
`Could not clear media keys: ${error}. media.src: ${media?.src}`
);
this.log(`Could not clear media keys: ${error}`);
})
)
)
Expand All @@ -1177,9 +1175,7 @@ class EMEController implements ComponentAPI {
}
})
.catch((error) => {
this.log(
`Could not close sessions and clear media keys: ${error}. media.src: ${media?.src}`
);
this.log(`Could not close sessions and clear media keys: ${error}`);
});
}

Expand Down

0 comments on commit d76c81f

Please sign in to comment.