From fa6c6c50d04ba30abf3fb57d4e7c94ab6d6fd437 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Fri, 13 Oct 2023 18:17:09 -0600 Subject: [PATCH] Support ManagedMediaSource and update docs to reflect iOS 17.1+ supports MSE (#8160) * Refactor media source handling in MsePlayer.js and Birdseye.jsx to support ManagedMediaSource * lint * Update docs to reflect iOS supporting mse --------- Co-authored-by: Sergey Krashevich --- docs/docs/configuration/live.md | 10 +++---- web/src/components/MsePlayer.js | 46 +++++++++++++++++++-------------- web/src/routes/Birdseye.jsx | 4 +-- web/src/routes/Camera.jsx | 4 +-- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/docs/docs/configuration/live.md b/docs/docs/configuration/live.md index d5ed1f033..f061d65ff 100644 --- a/docs/docs/configuration/live.md +++ b/docs/docs/configuration/live.md @@ -9,11 +9,11 @@ Frigate has different live view options, some of which require the bundled `go2r Live view options can be selected while viewing the live stream. The options are: -| Source | Latency | Frame Rate | Resolution | Audio | Requires go2rtc | Other Limitations | -| ------ | ------- | ------------------------------------- | -------------- | ---------------------------- | --------------- | -------------------------------------------- | -| jsmpeg | low | same as `detect -> fps`, capped at 10 | same as detect | no | no | none | -| mse | low | native | native | yes (depends on audio codec) | yes | not supported on iOS, Firefox is h.264 only | -| webrtc | lowest | native | native | yes (depends on audio codec) | yes | requires extra config, doesn't support h.265 | +| Source | Latency | Frame Rate | Resolution | Audio | Requires go2rtc | Other Limitations | +| ------ | ------- | ------------------------------------- | -------------- | ---------------------------- | --------------- | ------------------------------------------------- | +| jsmpeg | low | same as `detect -> fps`, capped at 10 | same as detect | no | no | none | +| mse | low | native | native | yes (depends on audio codec) | yes | iPhone requires iOS 17.1+, Firefox is h.264 only | +| webrtc | lowest | native | native | yes (depends on audio codec) | yes | requires extra config, doesn't support h.265 | ### Audio Support diff --git a/web/src/components/MsePlayer.js b/web/src/components/MsePlayer.js index ac0062391..6607bc2f4 100644 --- a/web/src/components/MsePlayer.js +++ b/web/src/components/MsePlayer.js @@ -157,12 +157,9 @@ class VideoRTC extends HTMLElement { if (this.ws) this.ws.send(JSON.stringify(value)); } - codecs(type) { - const test = - type === 'mse' - ? (codec) => MediaSource.isTypeSupported(`video/mp4; codecs="${codec}"`) - : (codec) => this.video.canPlayType(`video/mp4; codecs="${codec}"`); - return this.CODECS.filter(test).join(); + /** @param {Function} isSupported */ + codecs(isSupported) { + return this.CODECS.filter(codec => isSupported(`video/mp4; codecs="${codec}"`)).join(); } /** @@ -311,7 +308,7 @@ class VideoRTC extends HTMLElement { const modes = []; - if (this.mode.indexOf('mse') >= 0 && 'MediaSource' in window) { + if (this.mode.indexOf('mse') >= 0 && ('MediaSource' in window || 'ManagedMediaSource' in window)) { // iPhone modes.push('mse'); this.onmse(); @@ -363,18 +360,29 @@ class VideoRTC extends HTMLElement { } onmse() { - const ms = new MediaSource(); - ms.addEventListener( - 'sourceopen', - () => { - URL.revokeObjectURL(this.video.src); - this.send({ type: 'mse', value: this.codecs('mse') }); - }, - { once: true } - ); + /** @type {MediaSource} */ + let ms; - this.video.src = URL.createObjectURL(ms); - this.video.srcObject = null; + if ('ManagedMediaSource' in window) { + const MediaSource = window.ManagedMediaSource; + + ms = new MediaSource(); + ms.addEventListener('sourceopen', () => { + this.send({type: 'mse', value: this.codecs(MediaSource.isTypeSupported)}); + }, {once: true}); + + this.video.disableRemotePlayback = true; + this.video.srcObject = ms; + } else { + ms = new MediaSource(); + ms.addEventListener('sourceopen', () => { + URL.revokeObjectURL(this.video.src); + this.send({type: 'mse', value: this.codecs(MediaSource.isTypeSupported)}); + }, {once: true}); + + this.video.src = URL.createObjectURL(ms); + this.video.srcObject = null; + } this.play(); this.mseCodecs = ''; @@ -580,7 +588,7 @@ class VideoRTC extends HTMLElement { video2.src = `data:video/mp4;base64,${VideoRTC.btoa(data)}`; }; - this.send({ type: 'mp4', value: this.codecs('mp4') }); + this.send({ type: 'mp4', value: this.codecs(this.video.canPlayType) }); } static btoa(buffer) { diff --git a/web/src/routes/Birdseye.jsx b/web/src/routes/Birdseye.jsx index d41e04b04..3ff1ffac1 100644 --- a/web/src/routes/Birdseye.jsx +++ b/web/src/routes/Birdseye.jsx @@ -35,7 +35,7 @@ export default function Birdseye() { let player; const playerClass = ptzCameras.length || isMaxWidth ? 'w-full' : 'max-w-5xl xl:w-1/2'; if (viewSource == 'mse' && config.birdseye.restream) { - if ('MediaSource' in window) { + if ('MediaSource' in window || 'ManagedMediaSource' in window) { player = (
@@ -50,7 +50,7 @@ export default function Birdseye() { player = (
- MSE is not supported on iOS devices. You'll need to use jsmpeg or webRTC. See the docs for more info. + MSE is only supported on iOS 17.1+. You'll need to update if available or use jsmpeg / webRTC streams. See the docs for more info.
); diff --git a/web/src/routes/Camera.jsx b/web/src/routes/Camera.jsx index a134209df..9260b41fc 100644 --- a/web/src/routes/Camera.jsx +++ b/web/src/routes/Camera.jsx @@ -116,7 +116,7 @@ export default function Camera({ camera }) { let player; if (viewMode === 'live') { if (viewSource == 'mse' && restreamEnabled) { - if ('MediaSource' in window) { + if ('MediaSource' in window || 'ManagedMediaSource' in window) { player = (
@@ -133,7 +133,7 @@ export default function Camera({ camera }) { player = (
- MSE is not supported on iOS devices. You'll need to use jsmpeg or webRTC. See the docs for more info. + MSE is only supported on iOS 17.1+. You'll need to update if available or use jsmpeg / webRTC streams. See the docs for more info.
);