Hi all,
I was about to submit an issue on Github, but saw the rules say I should post here first.
Environment
Amazon Fire TV Stick 4K Max
Jellyfin Android TV client version 0.19.4
Jellyfin Server 10.11.4 (Running in Docker on Debian Linux 13 (Trixie))
Summary of Issue
Since upgrading to Jellyfin Android TV client 0.19.x I've encountered an issue when running any Live TV streams from my backend PVR Dispatcharr.
The errors are displayed on screen as something like "Error encountered, retrying". It does this once more, then it plays. When it eventually does play, the exerperience is less than ideal with stuttering etc.
To rule out an issue with the latest server version, I rolled back the Jellyfin client to 0.18.11 and the issue is resolved.
Android Logcat
Below is a logcat of the issue when it is encountered.
After the 'VideoManager' process states 'Video path set to: xyz' an exception is thrown because the client received a '500' Internal Server Error response code from Jellyfin.
When it eventually does play, it falls back to the Internal player rather than media3/exoplayer.
Jellyfin Server Log
From the Jellyfin Server, it is identified that it threw a 500 Internal Server Error because it was not able to find a suitable file extension.
This is probably because the stream URL's being passed to it do not have a file extension.
Workaround
I was able to resolve this issue by using a URL rewrite rule in my Caddy reverse proxy which appends an additional query string of container=ts to the video stream URL..
Before:
After:
This appears to prevent Jellyfin for throwing an exception.
My skills in Java are crap, otherwise I'd try and work on a PR. I haven't rolled back my Jellyfin client and compared the differences in the stream URL's from old to new versions, but I guess that might help in this instance.
I was about to submit an issue on Github, but saw the rules say I should post here first.
Environment
Amazon Fire TV Stick 4K Max
Jellyfin Android TV client version 0.19.4
Jellyfin Server 10.11.4 (Running in Docker on Debian Linux 13 (Trixie))
Summary of Issue
Since upgrading to Jellyfin Android TV client 0.19.x I've encountered an issue when running any Live TV streams from my backend PVR Dispatcharr.
The errors are displayed on screen as something like "Error encountered, retrying". It does this once more, then it plays. When it eventually does play, the exerperience is less than ideal with stuttering etc.
To rule out an issue with the latest server version, I rolled back the Jellyfin client to 0.18.11 and the issue is resolved.
Android Logcat
Below is a logcat of the issue when it is encountered.
After the 'VideoManager' process states 'Video path set to: xyz' an exception is thrown because the client received a '500' Internal Server Error response code from Jellyfin.
Code:
12-02 22:23:22.835 6200 5587 I org.jellyfin.sdk.api.okhttp.OkHttpClient: GET https://jellyfin.monstercow.com/LiveTv/Channels?sortBy=SortName&sortOrder=Ascending&enableFavoriteSorting=false&addCurrentProgram=true
12-02 22:23:22.843 5689 5765 I chatty : uid=10140(com.amazon.firebat) pool-46-thread- identical 3 lines
12-02 22:23:22.844 6200 6200 I PlaybackController: default audio index set to -1 remote default -1
12-02 22:23:22.844 6200 6200 I PlaybackController: default sub index set to null remote default null
12-02 22:23:22.848 6200 6200 I VideoManager: Video path set to: https://jellyfin.monstercow.com/Videos/b6c95702-4de5-6f93-6f07-98f8cce2b06a/stream?static=true&mediaSourceId=6de4014bdbea74e2604a09d3bd026546&streamOptions=%7B%7D&enableAudioVbrEncoding=true
12-02 22:23:22.848 6200 5587 I org.jellyfin.sdk.api.okhttp.OkHttpClient: GET https://jellyfin.monstercow.com/LiveTv/Channels?sortBy=SortName&sortOrder=Ascending&enableFavoriteSorting=false&addCurrentProgram=true
12-02 22:23:22.852 6200 3401 I org.jellyfin.sdk.api.okhttp.OkHttpClient: GET https://jellyfin.monstercow.com/MediaSegments/b6c95702-4de5-6f93-6f07-98f8cce2b06a?includeSegmentTypes=Intro&includeSegmentTypes=Outro&includeSegmentTypes=Preview&includeSegmentTypes=Recap&includeSegmentTypes=Commercial
12-02 22:23:22.890 5689 5765 I chatty : uid=10140(com.amazon.firebat) pool-46-thread- identical 23 lines
12-02 22:23:22.891 6200 5587 I ReportingHelper$reportStart: Reporting TVNZ 1 playback started at 0
12-02 22:23:22.891 6200 5587 I org.jellyfin.sdk.api.okhttp.OkHttpClient: POST https://jellyfin.monstercow.com/Sessions/Playing
12-02 22:23:22.893 5689 5765 I chatty : uid=10140(com.amazon.firebat) pool-46-thread- identical 1 line
12-02 22:23:22.903 372 372 I WifiHAL : Creating message to get link statistics; iface = 15
12-02 22:23:22.903 372 372 E WifiHAL : Incorrect number of channels = 4
12-02 22:23:22.903 679 798 E WifiVendorHal: getWifiLinkLayerStats_1_3_Internal(l.973) failed {.code = ERROR_UNKNOWN, .description = unknown}
12-02 22:23:22.906 5689 5765 I chatty : uid=10140(com.amazon.firebat) pool-46-thread- identical 1 line
12-02 22:23:22.908 6200 6200 E EventLogger: internalError [eventTime=0.16, mediaPos=0.00, window=0, period=0, loadError
12-02 22:23:22.908 6200 6200 E EventLogger: androidx.media3.datasource.HttpDataSource$InvalidResponseCodeException: Response code: 500
12-02 22:23:22.908 6200 6200 E EventLogger: at androidx.media3.datasource.okhttp.OkHttpDataSource.open(OkHttpDataSource.java:309)
12-02 22:23:22.908 6200 6200 E EventLogger: at androidx.media3.datasource.DefaultDataSource.open(DefaultDataSource.java:275)
12-02 22:23:22.908 6200 6200 E EventLogger: at androidx.media3.datasource.StatsDataSource.open(StatsDataSource.java:88)
12-02 22:23:22.908 6200 6200 E EventLogger: at androidx.media3.exoplayer.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1109)
12-02 22:23:22.908 6200 6200 E EventLogger: at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:453)
12-02 22:23:22.908 6200 6200 E EventLogger: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
12-02 22:23:22.908 6200 6200 E EventLogger: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
12-02 22:23:22.908 6200 6200 E EventLogger: at java.lang.Thread.run(Thread.java:923)
12-02 22:23:22.908 6200 6200 E EventLogger: ]
12-02 22:23:22.908 6200 6200 E EventLogger: internalError [eventTime=0.16, mediaPos=0.00, window=0, period=0, loadError
12-02 22:23:22.908 6200 6200 E EventLogger: androidx.media3.datasource.HttpDataSource$InvalidResponseCodeException: Response code: 500
12-02 22:23:22.908 6200 6200 E EventLogger: at androidx.media3.datasource.okhttp.OkHttpDataSource.open(OkHttpDataSource.java:309)
12-02 22:23:22.908 6200 6200 E EventLogger: at androidx.media3.datasource.DefaultDataSource.open(DefaultDataSource.java:275)
12-02 22:23:22.908 6200 6200 E EventLogger: at androidx.media3.datasource.StatsDataSource.open(StatsDataSource.java:88)
12-02 22:23:22.908 6200 6200 E EventLogger: at androidx.media3.exoplayer.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1109)
12-02 22:23:22.908 6200 6200 E EventLogger: at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:453)
12-02 22:23:22.908 6200 6200 E EventLogger: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
12-02 22:23:22.908 6200 6200 E EventLogger: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
12-02 22:23:22.908 6200 6200 E EventLogger: at java.lang.Thread.run(Thread.java:923)
12-02 22:23:22.908 6200 6200 E EventLogger: ]When it eventually does play, it falls back to the Internal player rather than media3/exoplayer.
Jellyfin Server Log
From the Jellyfin Server, it is identified that it threw a 500 Internal Server Error because it was not able to find a suitable file extension.
This is probably because the stream URL's being passed to it do not have a file extension.
Code:
[22:44:48] [ERR] Error processing request. URL GET /Videos/b6c95702-4de5-6f93-6f07-98f8cce2b06a/stream.
System.InvalidOperationException: Failed to find an appropriate file extension
at Jellyfin.Api.Helpers.StreamingHelpers.GetOutputFileExtension(StreamState state, MediaSourceInfo mediaSource)
at Jellyfin.Api.Helpers.StreamingHelpers.GetStreamingState(StreamingRequestDto streamingRequest, HttpContext httpContext, IMediaSourceManager mediaSourceManager, IUserManager userManager, ILibraryManager libraryManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, EncodingHelper encodingHelper, ITranscodeManager transcodeManager, TranscodingJobType transcodingJobType, CancellationToken cancellationToken)
at Jellyfin.Api.Controllers.VideosController.GetVideoStream(Guid itemId, String container, Nullable`1 static, String params, String tag, String deviceProfileId, String playSessionId, String segmentContainer, Nullable`1 segmentLength, Nullable`1 minSegments, String mediaSourceId, String deviceId, String audioCodec, Nullable`1 enableAutoStreamCopy, Nullable`1 allowVideoStreamCopy, Nullable`1 allowAudioStreamCopy, Nullable`1 breakOnNonKeyFrames, Nullable`1 audioSampleRate, Nullable`1 maxAudioBitDepth, Nullable`1 audioBitRate, Nullable`1 audioChannels, Nullable`1 maxAudioChannels, String profile, String level, Nullable`1 framerate, Nullable`1 maxFramerate, Nullable`1 copyTimestamps, Nullable`1 startTimeTicks, Nullable`1 width, Nullable`1 height, Nullable`1 maxWidth, Nullable`1 maxHeight, Nullable`1 videoBitRate, Nullable`1 subtitleStreamIndex, Nullable`1 subtitleMethod, Nullable`1 maxRefFrames, Nullable`1 maxVideoBitDepth, Nullable`1 requireAvc, Nullable`1 deInterlace, Nullable`1 requireNonAnamorphic, Nullable`1 transcodingMaxAudioChannels, Nullable`1 cpuCoreLimit, String liveStreamId, Nullable`1 enableMpegtsM2TsMode, String videoCodec, String subtitleCodec, String transcodeReasons, Nullable`1 audioStreamIndex, Nullable`1 videoStreamIndex, Nullable`1 context, Dictionary`2 streamOptions, Boolean enableAudioVbrEncoding)
at lambda_method892806(Closure, Object)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Prometheus.HttpMetrics.HttpRequestDurationMiddleware.Invoke(HttpContext context)
at Prometheus.HttpMetrics.HttpRequestCountMiddleware.Invoke(HttpContext context)
at Prometheus.HttpMetrics.HttpInProgressMiddleware.Invoke(HttpContext context)
at Jellyfin.Api.Middleware.ServerStartupMessageMiddleware.Invoke(HttpContext httpContext, IServerApplicationHost serverApplicationHost, ILocalizationManager localizationManager)
at Jellyfin.Api.Middleware.WebSocketHandlerMiddleware.Invoke(HttpContext httpContext, IWebSocketManager webSocketManager)
at Jellyfin.Api.Middleware.IPBasedAccessValidationMiddleware.Invoke(HttpContext httpContext, INetworkManager networkManager)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Jellyfin.Api.Middleware.QueryStringDecodingMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.ReDoc.ReDocMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Jellyfin.Api.Middleware.RobotsRedirectionMiddleware.Invoke(HttpContext httpContext)
at Jellyfin.Api.Middleware.LegacyEmbyRouteRewriteMiddleware.Invoke(HttpContext httpContext)
at Jellyfin.Api.Middleware.ResponseTimeMiddleware.Invoke(HttpContext context, IServerConfigurationManager serverConfigurationManager)
at Jellyfin.Api.Middleware.ExceptionMiddleware.Invoke(HttpContext context)Workaround
I was able to resolve this issue by using a URL rewrite rule in my Caddy reverse proxy which appends an additional query string of container=ts to the video stream URL..
Before:
Code:
https://jellyfin.monstercow.com/Videos/b6c95702-4de5-6f93-6f07-98f8cce2b06a/stream?static=true&mediaSourceId=6de4014bdbea74e2604a09d3bd026546&streamOptions=%7B%7D&enableAudioVbrEncoding=trueCode:
https://jellyfin.monstercow.com/Videos/b6c95702-4de5-6f93-6f07-98f8cce2b06a/stream?static=true&mediaSourceId=6de4014bdbea74e2604a09d3bd026546&streamOptions=%7B%7D&enableAudioVbrEncoding=true&container=tsThis appears to prevent Jellyfin for throwing an exception.
My skills in Java are crap, otherwise I'd try and work on a PR. I haven't rolled back my Jellyfin client and compared the differences in the stream URL's from old to new versions, but I guess that might help in this instance.
