video-dev / hls.js

HLS.js is a JavaScript library that plays HLS in browsers with support for MSE.
https://hlsjs.video-dev.org/demo
Other
14.81k stars 2.57k forks source link

AES-128 encrypted fmp4 play only in Safari #4933

Closed phloxic closed 2 years ago

phloxic commented 2 years ago

What version of Hls.js are you using?

v1.2.3, but happened also before

What browser (including version) are you using?

all browsers except (most versions) of macOS Safari

What OS (including version) are you using?

macOS 10.15.7

Test stream

https://hls-js.netlify.app/demo?src=https%3A%2F%2Fd12zt1n3pd4xhr.cloudfront.net%2Fdev%2Ffmp4-aes-en-nif.m3u8&demoConfig=eyJlbmFibGVTdHJlYW1pbmciOnRydWUsImF1dG9SZWNvdmVyRXJyb3IiOnRydWUsInN0b3BPblN0YWxsIjpmYWxzZSwiZHVtcGZNUDQiOmZhbHNlLCJsZXZlbENhcHBpbmciOi0xLCJsaW1pdE1ldHJpY3MiOi0xfQ==

Configuration

{
  "debug": true,
  "enableWorker": true,
  "lowLatencyMode": true,
  "backBufferLength": 90
}

Additional player setup steps

No response

Checklist

Steps to reproduce

  1. Try to play AES-128 encrypted fmp4 in any browser except macOS Safari (disclaimer: I did not test on iOS)
  2. Player hangs without errors

It plays in Safari, both with hls.js and natively. Note however, there is/was a bug with some Safari versions 13.x and 14.x: https://developer.apple.com/forums/thread/131625 but it seems to be fixed.

Expected behaviour

Stream plays.

What actually happened?

Stream hangs.

Console output

034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: Level 4 loaded [0,38], cc [0, 0] duration:114.56
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: WAITING_LEVEL->IDLE
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [level-controller]: switching to level 2 from 4
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [level-controller]: Attempt loading level index 2 with URL-id 0 https://d12zt1n3pd4xhr.cloudfront.net/dev/fmp4-aes/270p.m3u8
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: IDLE->WAITING_LEVEL
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: Level 2 loaded [0,38], cc [0, 0] duration:114.56
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: WAITING_LEVEL->IDLE
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [level-controller]: switching to level 1 from 2
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [level-controller]: Attempt loading level index 1 with URL-id 0 https://d12zt1n3pd4xhr.cloudfront.net/dev/fmp4-aes/216p.m3u8
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: IDLE->WAITING_LEVEL
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: Level 1 loaded [0,38], cc [0, 0] duration:114.56
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: WAITING_LEVEL->IDLE
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [level-controller]: switching to level 0 from 1
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [level-controller]: Attempt loading level index 0 with URL-id 0 https://d12zt1n3pd4xhr.cloudfront.net/dev/fmp4-aes/108p.m3u8
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: IDLE->WAITING_LEVEL
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: Level 0 loaded [0,38], cc [0, 0] duration:114.56
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: WAITING_LEVEL->IDLE
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: Loading key for initSegment of [0-38], level 0
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: IDLE->KEY_LOADING
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: KEY_LOADING->IDLE
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: Loading fragment initSegment cc: 0 level: 0, target: 0
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: IDLE->FRAG_LOADING
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [warn] > [decrypter.ts]: WebCrypto Error, disable WebCrypto API: Error
onWebCryptoError @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
(anonymous) @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
Promise.catch (async)
webCryptoDecrypt @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
(anonymous) @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
Promise.then (async)
_loadInitSegment @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadFragment @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onKeyLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadsuccess @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
readystatechange @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
XMLHttpRequest.send (async)
loadInternal @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onKeyLoading @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadKey @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTickIdle @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
tick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handlePlaylistLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handleTrackOrLevelPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadsuccess @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
readystatechange @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
XMLHttpRequest.send (async)
loadInternal @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoading @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTickIdle @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
tick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handlePlaylistLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handleTrackOrLevelPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadsuccess @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
readystatechange @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
XMLHttpRequest.send (async)
loadInternal @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoading @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTickIdle @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
tick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handlePlaylistLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handleTrackOrLevelPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadsuccess @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
readystatechange @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
XMLHttpRequest.send (async)
loadInternal @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoading @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTickIdle @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
tick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handlePlaylistLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handleTrackOrLevelPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadsuccess @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
readystatechange @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
XMLHttpRequest.send (async)
loadInternal @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoading @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTickIdle @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
tick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handlePlaylistLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handleTrackOrLevelPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadsuccess @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
readystatechange @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
XMLHttpRequest.send (async)
loadInternal @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoading @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTickIdle @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
tick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handlePlaylistLoaded @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
handleTrackOrLevelPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadsuccess @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
readystatechange @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
XMLHttpRequest.send (async)
loadInternal @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
load @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onLevelLoading @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
loadPlaylist @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
set @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTickIdle @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
doTick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
tick @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
fragBufferedComplete @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onFragBuffered @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onUnblocked @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
Promise.then (async)
blockBuffers @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onFragParsed @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
emit @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
trigger @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
updateLevelTiming @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
_handleTransmuxerFlush @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
onWorkerMessage @ 034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [decrypter.ts]: JS AES decrypt
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: FRAG_LOADING->IDLE
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: Buffered main sn: initSegment of level 0 
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: Loading fragment 1 cc: 0 of [0-38] level: 0, target: 3
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [stream-controller]: IDLE->FRAG_LOADING
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > demuxing in webworker
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 [log] > [transmuxer-interface, main]: Starting new transmux session for sn: 1 p: -1 level: 0 id: 1
        discontinuity: true
        trackSwitch: true
        contiguous: false
        accurateTimeOffset: true
        timeOffset: 3
        initSegmentChange: true
034fc5cbc93012ad2b1da2c4fec023ab6dd4bd79.js:1 
[...]


### Chrome media internals output

Can't spot any errors there.
robwalch commented 2 years ago

Could this be an issue with your stream's byte-ranges? Or is it a WebCrypto issue in most browsers? Only certain segments throw "OperationError" exceptions from subtle.decrypt so the issue must be with the input.

phloxic commented 2 years ago

Could this be an issue with your stream's byte-ranges? Or is it a WebCrypto issue in most browsers? Only certain segments throw "OperationError" exceptions from subtle.decrypt so the issue must be with the input.

subtle.decrypt? Can't spot that anywhere.

Stream issue is of course always possible. However:

  1. The streams are segmented by Apple's mediafilesegmenter and mediasubtitlesegmenter, the master manifest is based on the results being fed into variantplaylistcreator.
  2. Unencrypted streams with exactly the same settings but in the clear work fine. As well as the streams do in Safari.
  3. The issue is consistent with all streams created by this toolchain.
  4. It's specific to fmp4, there are no issues with byte-ranges in AES-128 encrypted MPEG-TS created by the otherwise exactly same workflow (demo)
  5. mediastreamvalidator does not bark, but I guess that's irrelevant in this context as encryption is not validated - except by actual playback in Safari where it works.
phloxic commented 2 years ago

@robwalch - can you point me to a sample stream with AES-128 encrypted fmp4 which works with hls.js?

mtoczko commented 2 years ago

Hi @phloxic I tested your stream on other players (theoplayer, bitmovin, shaka), none played the stream correctly in the chrome. FFplay:

[hls @ 0x7f9fddd8eb00] Opening 'https://phloxic.productions/test/bed3b76bcc9e/720p1.key' for reading
[https @ 0x7f9fe1b55e80] HTTP error 416 Requested Range Not Satisfiable
[hls @ 0x7f9fddd8eb00] Unable to open key file https://phloxic.productions/test/bed3b76bcc9e/720p1.key
robwalch commented 2 years ago

Only certain segments throw "OperationError" exceptions from subtle.decrypt so the issue must be with the input.

subtle.decrypt? Can't spot that anywhere.

https://github.com/video-dev/hls.js/blob/c35441b94f7ffef94e12ecf99aa7a5c61d5759a0/src/crypt/aes-crypto.ts

It's the same code and the same input running in both browsers. Works in Safari but throws in Chrome. Our SoftwareAES does not work in either because the number of bytes input does not match the expected range (802 vs 800). We could point the finger in either direction. Filing issues with Chromium and Feedback Assistant would be a good way to get some answers.

phloxic commented 2 years ago

Thanks. Fwiw it's also not working in Firefox.

@mtoczko - yes, doesn't work with video.js' vhs engine either. @robwalch, Firefox error RangeError: buffer length for Int32Array should be a multiple of 4 seems to point in same direction.

I still would be interested to actually see a sample stream working outside Safari, I haven't found one. If I find time, I'll try first a couple of other segmentation scenarios.

phloxic commented 2 years ago

I think the issue can be confined to AES-128 encrypted fmp4 byte-range (single file) assets. Without byte-range it seems to work (example).

mtoczko commented 2 years ago

I would see the problem here, the bit ranges are not divisible by 4

#EXT-X-MAP:URI="360p.mp4",BYTERANGE="802@0"
#EXT-X-MAP:URI="270p.mp4",BYTERANGE="801@0"
#EXT-X-MAP:URI="108p.mp4",BYTERANGE="799@0"
phloxic commented 2 years ago

Thanks @mtoczko, yes, I am aware of that. But I guess I'm still having a hard time wrapping my head around the fact that these tags have the same values in the clear streams which play fine.

At the moment I understand that Safari's decrypter can handle these init segments, whereas webcrypto expects ranges divisible by 4 for init segments as well - I made a few calculations, and the remaining actual segment ranges seem to be all divisible by 4.

robwalch commented 2 years ago

Not a Contribution

Widening the byte-range for AES-128 encrypted segments is described in https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6 (applies to I-frames and init segments)

robwalch commented 2 years ago

I put up a PR (#4941) against the DRM branch I've been working on scheduled for next month (v1.3.0). If you need this fix earlier, feel free to open a PR against master indicating that you'd like the change in a patch (v1.2.5) because the lack of support is blocking your workflow.

phloxic commented 2 years ago

Widening the byte-range for AES-128 encrypted segments is described in https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6 (applies to I-frames and init segments)

@robwalch - thanks for that link; I failed to find that passage.

phloxic commented 2 years ago

I put up a PR (#4941) against the DRM branch I've been working on scheduled for next month (v1.3.0). If you need this fix earlier, feel free to open a PR against master indicating that you'd like the change in a patch (v1.2.5) because the lack of support is blocking your workflow.

@robwalch - brilliant. No rush from my side.

phloxic commented 2 years ago

@robwalch - according to my local testing the fix does the job. Thank you.