1;(function (root, factory) { 2 const api = factory() 3 if (typeof module === "object" && module.exports) { 4 module.exports = api 5 } 6 if (root) { 7 root.HevcSourceSelector = api 8 } 9})(typeof globalThis !== "undefined" ? globalThis : this, function () { 10 "use strict" 11 12 const SOURCE_CONTRACT = "dual-v1" 13 const HEVC_MIME = 'video/mp4; codecs="hvc1.1.6.L120.B0"' 14 const HEVC_MIMES = [HEVC_MIME, 'video/mp4; codecs="hev1.1.6.L120.B0"'] 15 const HLS_MIME = "application/vnd.apple.mpegurl" 16 const DEFAULT_PROBE_TIMEOUT_MS = 3000 17 18 const nonEmptyString = value => 19 typeof value === "string" && value.trim() ? value.trim() : "" 20 21 const normalizeCodec = codec => { 22 const value = nonEmptyString(codec).toLowerCase() 23 return value === "hevc" || 24 value === "h265" || 25 value.startsWith("hvc1") || 26 value.startsWith("hev1") 27 ? "hevc" 28 : "avc" 29 } 30 31 const normalizePayload = input => { 32 let payload = input 33 if (typeof payload === "string") { 34 const raw = payload.trim() 35 if (!raw) { 36 return { primary: "", fallback: "", codec: "avc", legacy: true } 37 } 38 if (raw[0] !== "{") { 39 return { primary: raw, fallback: raw, codec: "avc", legacy: true } 40 } 41 try { 42 payload = JSON.parse(raw) 43 } catch (_) { 44 return { primary: "", fallback: "", codec: "avc", legacy: false } 45 } 46 } 47 48 if (!payload || typeof payload !== "object" || Array.isArray(payload)) { 49 return { primary: "", fallback: "", codec: "avc", legacy: false } 50 } 51 const codec = normalizeCodec(payload.codec) 52 const primary = nonEmptyString(payload.primary) 53 const fallback = nonEmptyString(payload.fallback) 54 if (codec === "hevc") { 55 // HEVC ååºç¼ºå°æç¡®ç H.264 å åºæ¶ï¼ä¸å¾è¿å ¥ææ¾æµç¨ã 56 if (!primary || !fallback) { 57 return { primary: fallback, fallback, codec: "avc", legacy: false } 58 } 59 return { primary, fallback, codec, legacy: false } 60 } 61 const h264 = fallback || primary 62 return { primary: h264, fallback: h264, codec: "avc", legacy: false } 63 } 64 65 const parseVersion = version => { 66 const match = String(version || "").match(/^(\d+)\.(\d+)(?:\.(\d+))?/) 67 return match ? [Number(match[1]), Number(match[2]), Number(match[3] || 0)] : null 68 } 69 70 const isHlsHevcTsCapable = version => { 71 const parsed = parseVersion(version) 72 if (!parsed) return false 73 const [major, minor] = parsed 74 return major > 1 || (major === 1 && minor >= 6) 75 } 76 77 const canPlay = (video, mime) => { 78 try { 79 return !!video?.canPlayType?.(mime) 80 } catch (_) { 81 return false 82 } 83 } 84 85 const normalizeSmooth = value => (value === true ? true : value === false ? false : null) 86 87 const probeDecodingInfo = (mediaCapabilities, type, mime, timeoutMs) => { 88 if (!mediaCapabilities || typeof mediaCapabilities.decodingInfo !== "function") { 89 return Promise.resolve({ 90 state: "unknown", 91 smooth: null, 92 reason: "media-capabilities-unavailable" 93 }) 94 } 95 let probe 96 try { 97 probe = mediaCapabilities.decodingInfo({ 98 type, 99 video: { 100 contentType: mime, 101 width: 1920, 102 height: 1080, 103 bitrate: 5000000, 104 framerate: 30 105 } 106 }) 107 } catch (_) { 108 return Promise.resolve({ 109 state: "unknown", 110 smooth: null, 111 reason: "media-capabilities-error" 112 }) 113 } 114 115 return new Promise(resolve => { 116 let settled = false 117 let timer = null 118 const finish = value => { 119 if (settled) return 120 settled = true 121 if (timer !== null) clearTimeout(timer) 122 resolve(value) 123 } 124 125 timer = setTimeout( 126 () => 127 finish({ 128 state: "unknown", 129 smooth: null, 130 reason: "media-capabilities-timeout" 131 }), 132 timeoutMs 133 ) 134 Promise.resolve(probe).then( 135 result => { 136 // supported æè¡¨ç¤ºè½å¤è§£ç ï¼smooth åªæ¯æµç åº¦é¢æµï¼ä¸æ¯æ ¼å¼é¨æ§ã
137 if (result?.supported === true) { 138 finish({ 139 state: "supported", 140 smooth: normalizeSmooth(result.smooth), 141 reason: "" 142 }) 143 return 144 } 145 if (result?.supported === false) { 146 finish({ 147 state: "unsupported", 148 smooth: normalizeSmooth(result.smooth), 149 reason: "media-capabilities-unsupported" 150 }) 151 return 152 } 153 finish({ 154 state: "unknown", 155 smooth: null, 156 reason: "media-capabilities-invalid-result" 157 }) 158 }, 159 () => 160 finish({ 161 state: "unknown", 162 smooth: null, 163 reason: "media-capabilities-error" 164 }) 165 ) 166 }) 167 } 168 169 const detectHevcSupport = async (runtime = {}) => { 170 const globalObject = typeof globalThis !== "undefined" ? globalThis : {} 171 const documentObject = runtime.document || globalObject.document 172 const navigatorObject = runtime.navigator || globalObject.navigator 173 const video = runtime.video || documentObject?.createElement?.("video") 174 const mediaSource = 175 runtime.MediaSource || globalObject.MediaSource || globalObject.WebKitMediaSource 176 const HlsClass = runtime.Hls || globalObject.Hls 177 const mimes = runtime.mime 178 ? [runtime.mime] 179 : Array.isArray(runtime.mimes) && runtime.mimes.length 180 ? runtime.mimes 181 : HEVC_MIMES 182 const defaultMime = mimes[0] || HEVC_MIME 183 const requestedTimeout = Number(runtime.probeTimeoutMs) 184 const probeTimeoutMs = 185 Number.isFinite(requestedTimeout) && requestedTimeout > 0 186 ? requestedTimeout 187 : DEFAULT_PROBE_TIMEOUT_MS 188 const probeDeadline = Date.now() + probeTimeoutMs 189 const remainingProbeMs = () => Math.max(0, probeDeadline - Date.now()) 190 let unknownReason = "" 191 let unsupportedSeen = false 192 193 const h264Capability = (state, reason) => ({ 194 supported: false, 195 state, 196 engine: "h264", 197 mime: defaultMime, 198 smooth: null, 199 reason 200 }) 201 const hevcCapability = (engine, mime, probe) => ({ 202 supported: true, 203 state: "supported", 204 engine, 205 mime, 206 smooth: probe.smooth, 207 reason: "" 208 }) 209 210 if (!video) { 211 return h264Capability("unknown", "video-element-unavailable") 212 } 213 if (!navigatorObject?.mediaCapabilities) { 214 return h264Capability("unknown", "media-capabilities-unavailable") 215 } 216 217 if (canPlay(video, HLS_MIME)) { 218 for (const mime of mimes) { 219 if (!canPlay(video, mime)) continue 220 const remainingMs = remainingProbeMs() 221 if (remainingMs <= 0) { 222 unknownReason = unknownReason || "media-capabilities-timeout" 223 break 224 } 225 const probe = await probeDecodingInfo( 226 navigatorObject.mediaCapabilities, 227 "file", 228 mime, 229 remainingMs 230 ) 231 if (probe.state === "supported") { 232 return hevcCapability("native", mime, probe) 233 } 234 if (probe.state === "unknown") unknownReason = unknownReason || probe.reason 235 else unsupportedSeen = true 236 } 237 } 238 239 let mseCandidate = false 240 try { 241 mseCandidate = 242 !!HlsClass && 243 isHlsHevcTsCapable(HlsClass.version) && 244 HlsClass.isSupported?.() === true && 245 typeof mediaSource?.isTypeSupported === "function" 246 } catch (_) { 247 mseCandidate = false 248 } 249 250 if (mseCandidate) { 251 for (const mime of mimes) { 252 const remainingMs = remainingProbeMs() 253 if (remainingMs <= 0) { 254 unknownReason = unknownReason || "media-capabilities-timeout" 255 break 256 } 257 let mseCodecSupported = false 258 try { 259 mseCodecSupported = mediaSource.isTypeSupported(mime) === true 260 } catch (_) { 261 unknownReason = unknownReason || "media-source-error" 262 continue 263 } 264 if (!mseCodecSupported) { 265 unsupportedSeen = true 266 continue 267 } 268 const probe = await probeDecodingInfo( 269 navigatorObject.mediaCapabilities, 270 "media-source", 271 mime, 272 remainingMs 273 ) 274 if (probe.state === "supported") { 275 return hevcCapability("hls", mime, probe) 276 } 277 if (probe.state === "unknown") unknownReason = unknownReason || probe.reason 278 else unsupportedSeen = true 279 } 280 } 281 282 if (unknownReason) return h264Capability("unknown", unknownReason) 283 return h264Capability( 284 "unsupported", 285 unsupportedSeen ? "hevc-codec-unsupported" : "hevc-playback-stack-unavailable" 286 ) 287 } 288
289 const choosePlayback = (input, capability = {}) => { 290 const source = normalizePayload(input) 291 if (source.codec === "hevc" && capability.supported === true) { 292 return { 293 primary: source.primary, 294 fallback: source.fallback, 295 codec: "hevc", 296 engine: capability.engine === "native" ? "native" : "hls" 297 } 298 } 299 const h264 = 300 source.codec === "hevc" ? source.fallback : source.primary || source.fallback 301 return { primary: h264, fallback: h264, codec: "avc", engine: "auto" } 302 } 303 304 const shouldFallbackFromHlsError = (source, error) => { 305 if (normalizePayload(source).codec !== "hevc" || !error || typeof error !== "object") { 306 return false 307 } 308 const responseHasCode = 309 !!error.response && 310 typeof error.response === "object" && 311 Object.prototype.hasOwnProperty.call(error.response, "code") && 312 (error.response.code === 0 || !!error.response.code) 313 return error.fatal === true || responseHasCode 314 } 315 316 const shouldFallbackFromMediaError = (source, errorCode) => 317 normalizePayload(source).codec === "hevc" && [2, 3, 4].includes(Number(errorCode)) 318 319 // ææå 鍿åé½å¿ é¡»ä¸ç¨æ·æååå¼è®°å½ï¼èªå¨æ¢å¤åªè½æä»æå
319䏿¬¡ç¨æ·å½ä»¤ã 320 const createPlaybackIntent = media => { 321 let wantsPlayback = false 322 let internalPausePending = false 323 let destroyed = false 324 325 const disableAutoplay = () => { 326 try { 327 if (media) media.autoplay = false 328 } catch (_) {} 329 } 330 const requestPlay = () => { 331 if (destroyed) return false 332 wantsPlayback = true 333 disableAutoplay() 334 return true 335 } 336 const cancelPlayback = () => { 337 if (destroyed) return false 338 wantsPlayback = false 339 internalPausePending = false 340 disableAutoplay() 341 return true 342 } 343 const onPlay = () => { 344 // åè´´çå¯è½å·²ç»å¨åä¸è½®äºä»¶ä¸æåæ£çï¼ä¸è½æåæ¸ é¤å é¨æåæ è®°ã 345 if (media?.paused === false) internalPausePending = false 346 disableAutoplay() 347 } 348 const onPause = () => { 349 if (internalPausePending) { 350 internalPausePending = false 351 return 352 } 353 if (media?.paused === false) return 354 cancelPlayback() 355 } 356 357 media?.addEventListener?.("play", onPlay) 358 media?.addEventListener?.("pause", onPause) 359 disableAutoplay() 360 361 const isPlaybackWanted = () => !destroyed && wantsPlayback 362 const playIfWanted = () => { 363 if (!isPlaybackWanted() || typeof media?.play !== "function") return false 364 internalPausePending = false 365 disableAutoplay() 366 try { 367 const result = media.play() 368 result?.catch?.(() => {}) 369 return true 370 } catch (_) { 371 return false 372 } 373 } 374 const pauseInternally = () => { 375 if (destroyed || !media || media.paused === true || typeof media.pause !== "function") { 376 disableAutoplay() 377 return false 378 } 379 internalPausePending = true 380 disableAutoplay() 381 try { 382 media.pause() 383 return true 384 } catch (_) { 385 internalPausePending = false 386 return false 387 } 388 } 389 const reloadIfWanted = () => { 390 if (!isPlaybackWanted() || typeof media?.load !== "function") return false 391 pauseInternally() 392 try { 393 media.load() 394 } catch (_) { 395 return false 396 } 397 return playIfWanted() 398 } 399 const destroy = () => { 400 if (destroyed) return 401 destroyed = true 402 wantsPlayback = false 403 internalPausePending = false 404 disableAutoplay() 405 media?.removeEventListener?.("play", onPlay) 406 media?.removeEventListener?.("pause", onPause) 407 } 408 409 return { 410 requestPlay, 411 cancelPlayback, 412 wantsPlayback: isPlaybackWanted, 413 syncAutoplay: () => { 414 const shouldPlay = isPlaybackWanted() 415 disableAutoplay() 416 return shouldPlay 417 }, 418 playIfWanted, 419 pauseInternally, 420 reloadIfWanted, 421 destroy 422 } 423 } 424 425 const createFallbackGate = () => { 426 let used = false 427 return { 428 take(source) { 429 const normalized = normalizePayload(source) 430 if ( 431 used || 432 normalized.codec !== "hevc" || 433 !normalized.fallback || 434 normalized.fallback === normalized.primary 435 ) { 436 return "" 437 } 438 used = true 439 return normalized.fallback 440 }, 441 get used() { 442 return used 443 } 444 } 445 } 446 447 // DPlayer ç true é¦å表示â忥åçåªä½äºä»¶âï¼ä¸è½è¢«è¯¯å¤ä¸ºæ°çç¨æ·å½ä»¤ã 448 const wrapPlayerCommands = (player, intent) => { 449 if (!player || !intent) return () => {} 450 const originalPlay = typeof player.play === "function" ? player.play : null 451 const originalPause = typeof player.pause === "function" ? player.pause : null 452 453 if (originalPlay) { 454 player.play = function () { 455 if (arguments[0] !== true) { 456 intent.requestPlay() 457 } 458 return originalPlay.apply(player, arguments) 459 } 460 } 461 if (originalPause) { 462 player.pause = function () { 463 if (arguments[0] !== true) { 464 intent.cancelPlayback() 465 } 466 return originalPause.apply(player, arguments) 467 } 468 } 469 470 return () => { 471 if (originalPlay) player.play = originalPlay 472 if (originalPause) player.pause = originalPause 473 } 474 } 475 476 return { 477 SOURCE_CONTRACT, 478 HEVC_MIME, 479 HEVC_MIMES, 480 normalizePayload, 481 parseVersion, 482 isHlsHevcTsCapable, 483 detectHevcSupport,
484 choosePlayback, 485 shouldFallbackFromHlsError, 486 shouldFallbackFromMediaError, 487 createPlaybackIntent, 488 createFallbackGate, 489 wrapPlayerCommands 490 } 491})
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.