|
48 | 48 | mediaSourceId = invalid |
49 | 49 | audio_stream_idx = m.top.selectedAudioStreamIndex |
50 | 50 | forceTranscoding = false |
| 51 | + bypassDoviPreservation = m.top.bypassDoviPreservation |
51 | 52 |
|
52 | | - m.top.content = [LoadItems_VideoPlayer(id, mediaSourceId, audio_stream_idx, forceTranscoding)] |
| 53 | + m.top.content = [LoadItems_VideoPlayer(id, mediaSourceId, audio_stream_idx, forceTranscoding, bypassDoviPreservation)] |
53 | 54 | end sub |
54 | 55 |
|
55 | | -function LoadItems_VideoPlayer(id as string, mediaSourceId as dynamic, audio_stream_idx = 1 as integer, forceTranscoding = false as boolean) as dynamic |
| 56 | +function LoadItems_VideoPlayer(id as string, mediaSourceId as dynamic, audio_stream_idx = 1 as integer, forceTranscoding = false as boolean, bypassDoviPreservation = false as boolean) as dynamic |
56 | 57 |
|
57 | 58 | video = {} |
58 | 59 | video.id = id |
59 | 60 | video.content = createObject("RoSGNode", "ContentNode") |
60 | 61 | video.content.addField("trickplayMetadata", "assocarray", false) |
61 | 62 |
|
62 | | - LoadItems_AddVideoContent(video, mediaSourceId, audio_stream_idx, forceTranscoding) |
| 63 | + LoadItems_AddVideoContent(video, mediaSourceId, audio_stream_idx, forceTranscoding, bypassDoviPreservation) |
63 | 64 |
|
64 | 65 | if not isValid(video.content) |
65 | 66 | return invalid |
|
68 | 69 | return video |
69 | 70 | end function |
70 | 71 |
|
71 | | -sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_stream_idx = 1 as integer, forceTranscoding = false as boolean) |
| 72 | +sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_stream_idx = 1 as integer, forceTranscoding = false as boolean, bypassDoviPreservation = false as boolean) |
72 | 73 |
|
73 | 74 | meta = ItemMetaData(video.id) |
74 | 75 | if not isValid(meta) |
|
220 | 221 | if meta.live then mediaSourceId = "" |
221 | 222 |
|
222 | 223 | ' Call ItemPostPlaybackInfo ONCE with final subtitle_idx |
223 | | - m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition, meta) |
| 224 | + m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition, meta, bypassDoviPreservation) |
224 | 225 | if not isValid(m.playbackInfo) |
225 | 226 | video.errorMsg = "Error loading playback info" |
226 | 227 | video.content = invalid |
|
312 | 313 | video.transcodeReasons = getTranscodeReasons(m.playbackInfo.MediaSources[0].TranscodingUrl) |
313 | 314 | video.content.url = buildURL(m.playbackInfo.MediaSources[0].TranscodingUrl) |
314 | 315 | video.isTranscoded = true |
| 316 | + |
| 317 | + ' If DoVi preservation caused this transcode, flag that direct play is a viable buffer-overflow fallback. |
| 318 | + ' VideoRangeTypeNotSupported is the reason Jellyfin returns when our DoVi container profile blocks the MKV. |
| 319 | + ' On a buffer:loop: error the player will retry with bypassDoviPreservation=true, letting the server |
| 320 | + ' re-evaluate without the DoVi constraint and (usually) grant direct play instead. |
| 321 | + if userSettings.playbackPreserveDovi and not bypassDoviPreservation |
| 322 | + if arrayHasValue(video.transcodeReasons, "VideoRangeTypeNotSupported") |
| 323 | + video.doviDirectPlayFallbackAvailable = true |
| 324 | + end if |
| 325 | + end if |
315 | 326 | end if |
316 | 327 |
|
317 | 328 | setCertificateAuthority(video.content) |
|
0 commit comments