Files
djk/internal/webui/player-capabilities.js
T

40 lines
2.4 KiB
JavaScript

(() => {
function createPlaybackCapabilities(environment, video, agent) {
const touchApple = /iPad|iPhone|iPod/i.test(agent.userAgent || '') || /Mac/i.test(agent.platform || '') && Number(agent.maxTouchPoints) > 1;
function native() {
if (touchApple) return true;
try {return ['application/vnd.apple.mpegurl', 'application/x-mpegURL'].some(type => ['maybe', 'probably'].includes(video.canPlayType(type)));}
catch (_) {return false;}
}
function mse(type) {
try {return typeof type === 'string' && Boolean(type) && typeof environment.MediaSource === 'function' && typeof environment.MediaSource.isTypeSupported === 'function' && environment.MediaSource.isTypeSupported(type);}
catch (_) {return false;}
}
function choose(type) {
if (touchApple && native()) return 'hls';
if (mse(type)) return 'mse';
return native() ? 'hls' : '';
}
function remux() {
return !touchApple && ['42E02A', '4D002A', '64002A'].every(profile => mse('video/mp4; codecs="avc1.' + profile + ', mp4a.40.2"'));
}
function playable(type) {
try {return ['maybe', 'probably'].includes(video.canPlayType(type));} catch (_) {return false;}
}
function describe() {
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'};
// 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] + '"'))};
}
return {native, mse, choose, remux, playable, describe};
}
if (typeof module !== 'undefined' && module.exports) module.exports = createPlaybackCapabilities;
else window.JukuPlaybackCapabilities = createPlaybackCapabilities;
})();