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 @@
+
+