diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index 2d54d5c9..c3373651 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -1073,3 +1073,7 @@ msgstr "" msgctxt "#30332" msgid "Helix API" msgstr "" + +msgctxt "#30333" +msgid "Use inputstream.ffmpegdirect for live streams" +msgstr "" diff --git a/resources/lib/twitch_addon/addon/utils.py b/resources/lib/twitch_addon/addon/utils.py index 85431754..e8d0d0bc 100644 --- a/resources/lib/twitch_addon/addon/utils.py +++ b/resources/lib/twitch_addon/addon/utils.py @@ -107,6 +107,24 @@ def inputstream_adpative_supports(feature): return False +def use_ffmpegdirect_for_live(): + """Whether live streams should be played through inputstream.ffmpegdirect. + + Optional alternative live-playback backend (off by default): ffmpegdirect uses + ffmpeg's own demuxer and manages realtime buffering itself, instead of + inputstream.adaptive / Kodi's direct player. Some devices and setups play + Twitch live more reliably through it (e.g. it avoids the inputstream.adaptive + live-HLS demuxer issues seen on some Kodi 22 / CoreELEC builds, and the + occasional "cache full 100%" buffering stall). Returns False when the option + is off or the add-on isn't installed/enabled, leaving the default IA / direct + behaviour untouched. VODs and clips are never affected. + """ + if kodi.get_setting('live_use_ffmpegdirect') != 'true': + return False + # addon_enabled() returns True/False when installed, None when missing. + return kodi.addon_enabled('inputstream.ffmpegdirect') is True + + def format_isa_headers(headers): # key=value&... (url-encoded) for InputStream Adaptive stream_headers / manifest_headers properties. return '&'.join(['%s=%s' % (key, quote_plus(headers[key])) for key in headers]) diff --git a/resources/lib/twitch_addon/routes/play.py b/resources/lib/twitch_addon/routes/play.py index 0ae427de..ed346870 100644 --- a/resources/lib/twitch_addon/routes/play.py +++ b/resources/lib/twitch_addon/routes/play.py @@ -151,9 +151,15 @@ def _set_seek_time(value): if result: quality_label = result['name'] + # Optional alternative live-playback backend (off by default): when enabled + # and available, play live streams through inputstream.ffmpegdirect instead + # of inputstream.adaptive / the direct player. Live only -- VODs and clips + # keep the default behaviour. + use_ffmpegdirect = is_live and utils.use_ffmpegdirect_for_live() + request = None play_url = None - if quality_label == 'Adaptive' and use_ia: + if quality_label == 'Adaptive' and (use_ia or use_ffmpegdirect): if video_id: request = api.video_request(video_id) elif is_live: @@ -161,7 +167,12 @@ def _set_seek_time(value): if request: if kodi.get_kodi_version().major >= 18: request['headers']['verifypeer'] = 'false' - play_url = request['url'] # headers passed via ISA stream/manifest_headers below + if use_ffmpegdirect: + # ffmpegdirect (open_mode=ffmpeg) reads HTTP headers from the + # URL's |Header=... suffix, not from ISA manifest_headers. + play_url = request['url'] + utils.append_headers(request['headers']) + else: + play_url = request['url'] # headers passed via ISA stream/manifest_headers below if not play_url: play_url = result['url'] @@ -197,7 +208,17 @@ def _set_seek_time(value): playback_item.setMimeType('video/mp4') except AttributeError: pass - if quality_label == 'Adaptive' and use_ia: + if use_ffmpegdirect: + inputstream_property = 'inputstream' + if kodi.get_kodi_version().major < 19: + inputstream_property += 'addon' + # ffmpeg's demuxer + its own realtime buffering. Covers both a specific + # quality (variant URL) and Adaptive (master URL). + playback_item.setProperty(inputstream_property, 'inputstream.ffmpegdirect') + playback_item.setProperty('inputstream.ffmpegdirect.is_realtime_stream', 'true') + playback_item.setProperty('inputstream.ffmpegdirect.manifest_type', 'hls') + playback_item.setProperty('inputstream.ffmpegdirect.open_mode', 'ffmpeg') + elif quality_label == 'Adaptive' and use_ia: inputstream_property = 'inputstream' if kodi.get_kodi_version().major < 19: inputstream_property += 'addon' diff --git a/resources/settings.xml b/resources/settings.xml index caf3fdc5..cb432f37 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -39,6 +39,10 @@ + +