From 3f8c29b7e2e28e0f4d6a40b3a7b39140d2059646 Mon Sep 17 00:00:00 2001 From: yvan Date: Fri, 18 Sep 2026 15:08:31 +0800 Subject: [PATCH] =?UTF-8?q?iOS=20=E7=AC=AC=E4=B8=89=E6=96=B9=E6=B5=8F?= =?UTF-8?q?=E8=A7=88=E5=99=A8=EF=BC=88Chrome/Firefox/Edge/Opera=EF=BC=89?= =?UTF-8?q?=E6=94=B9=E8=B5=B0=20TS=20=E5=85=BC=E5=AE=B9=E6=B5=81=EF=BC=9A?= =?UTF-8?q?=E8=83=BD=E5=8A=9B=E5=A3=B0=E6=98=8E=E9=9A=90=E8=97=8F=E5=8E=9F?= =?UTF-8?q?=E7=94=9F=20HLS=EF=BC=8C=E6=9C=8D=E5=8A=A1=E7=AB=AF=E8=A7=84?= =?UTF-8?q?=E5=88=92=20legacy=20TS=20=E9=80=9A=E9=81=93=EF=BC=8C=E7=BB=95?= =?UTF-8?q?=E5=BC=80=20fMP4=20=E9=BB=91=E5=B1=8F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/webui/player-capabilities.js | 5 ++++- internal/webui/player-capabilities.test.cjs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/internal/webui/player-capabilities.js b/internal/webui/player-capabilities.js index 796fe83..5adf798 100644 --- a/internal/webui/player-capabilities.js +++ b/internal/webui/player-capabilities.js @@ -25,7 +25,10 @@ const supported = type => playable(type) || mse(type); const videoCodecs = {h264: 'avc1.42E01E', hevc: 'hvc1.1.6.L120.B0', av1: 'av01.0.08M.08', vp9: 'vp09.00.10.08'}; const audioCodecs = {aac: 'mp4a.40.2', mp3: 'mp4a.69', ac3: 'ac-3', eac3: 'ec-3', opus: 'opus'}; - return {mp4: playable('video/mp4'), nativeHls: native(), hlsjs: Boolean(environment.Hls?.isSupported()), + // iOS 上的第三方浏览器(Chrome/Firefox/Edge/Opera)自带的媒体栈对 fMP4 HLS 兼容性差, + // 会黑屏缓冲;让服务端按无 HLS 通道规划,走 legacy 的 TS 实时转码(hls/open)。 + const thirdPartyIOS = touchApple && /CriOS|FxiOS|EdgiOS|OPT\//.test(agent.userAgent || ''); + return {mp4: playable('video/mp4'), nativeHls: native() && !thirdPartyIOS, hlsjs: Boolean(environment.Hls?.isSupported()), video: Object.keys(videoCodecs).filter(key => supported('video/mp4; codecs="' + videoCodecs[key] + '"')), audio: Object.keys(audioCodecs).filter(key => supported('audio/mp4; codecs="' + audioCodecs[key] + '"'))}; } diff --git a/internal/webui/player-capabilities.test.cjs b/internal/webui/player-capabilities.test.cjs index 61b2a45..3464f38 100644 --- a/internal/webui/player-capabilities.test.cjs +++ b/internal/webui/player-capabilities.test.cjs @@ -33,3 +33,17 @@ test('remux requires compatible H.264 profiles and never replaces iOS HLS', () = limited.MediaSource.isTypeSupported = () => {throw new Error('probe unavailable');}; assert.equal(capabilities(limited, native, {}).remux(), false); }); + +test('third-party iOS browsers hide native HLS so the server plans the TS path', () => { + for (const ua of ['iPhone CriOS/154.0.8037.41 Mobile/15E148 Safari/604.1', + 'iPhone FxiOS/130.0 Mobile/15E148 Safari/605.1.15', + 'iPhone EdgiOS/130.0.1 Mobile/15E148 Safari/605.1.15']) { + const view = capabilities(mse(true), native, {userAgent: ua}); + assert.equal(view.describe().nativeHls, false); + assert.equal(view.choose(type), 'hls'); + } + const safari = capabilities(mse(true), native, {userAgent: 'iPhone Version/27.0 Mobile/15E148 Safari/604.1'}); + assert.equal(safari.describe().nativeHls, true); + const opera = capabilities(mse(true), native, {userAgent: 'iPhone OPT/2.4.0 Mobile/15E148 Safari/605.1.15'}); + assert.equal(opera.describe().nativeHls, false); +});