fix Start stream 3 seconds behind edge
This commit is contained in:
@@ -71,8 +71,9 @@ public interface IStreamProxyService
|
||||
/// <param name="manifestContent">The variant manifest content.</param>
|
||||
/// <param name="baseProxyUrl">The base proxy URL (without query params).</param>
|
||||
/// <param name="queryParams">Query parameters to append to rewritten URLs (e.g., "?token=abc").</param>
|
||||
/// <param name="isLiveStream">Whether this is a livestream; if so an EXT-X-START offset is injected.</param>
|
||||
/// <returns>The rewritten manifest content.</returns>
|
||||
string RewriteVariantManifestUrls(string manifestContent, string baseProxyUrl, string queryParams);
|
||||
string RewriteVariantManifestUrls(string manifestContent, string baseProxyUrl, string queryParams, bool isLiveStream = false);
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up old and expired stream mappings.
|
||||
|
||||
@@ -900,9 +900,15 @@ public class StreamProxyService : IStreamProxyService
|
||||
/// <param name="manifestContent">The variant manifest content.</param>
|
||||
/// <param name="baseProxyUrl">The base proxy URL (without query params).</param>
|
||||
/// <param name="queryParams">Query parameters to append to rewritten URLs (e.g., "?token=abc").</param>
|
||||
/// <param name="isLiveStream">Whether this is a livestream; if so an EXT-X-START offset is injected.</param>
|
||||
/// <returns>The rewritten manifest content.</returns>
|
||||
public string RewriteVariantManifestUrls(string manifestContent, string baseProxyUrl, string queryParams)
|
||||
public string RewriteVariantManifestUrls(string manifestContent, string baseProxyUrl, string queryParams, bool isLiveStream = false)
|
||||
{
|
||||
if (isLiveStream)
|
||||
{
|
||||
manifestContent = InjectLiveStartOffset(manifestContent);
|
||||
}
|
||||
|
||||
string RewriteUrl(string url)
|
||||
{
|
||||
if (url.Contains("://", StringComparison.Ordinal))
|
||||
@@ -953,6 +959,82 @@ public class StreamProxyService : IStreamProxyService
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Injects an <c>#EXT-X-START:TIME-OFFSET</c> tag into a live media playlist so that players
|
||||
/// (notably ExoPlayer on Android TV) begin a few segments back from the volatile live edge
|
||||
/// instead of at the edge itself, which otherwise causes stalling/jumping until a manual skip.
|
||||
/// The offset is sized relative to the playlist's own <c>EXT-X-TARGETDURATION</c> to stay
|
||||
/// RFC 8216 compliant (≥ 3 target durations from the edge for live playlists).
|
||||
/// </summary>
|
||||
/// <param name="manifestContent">The media playlist content.</param>
|
||||
/// <returns>The playlist with the tag injected, or unchanged if it is not applicable.</returns>
|
||||
private string InjectLiveStartOffset(string manifestContent)
|
||||
{
|
||||
var segmentsBack = Plugin.Instance?.Configuration?.LiveStartSegmentsBack ?? 3;
|
||||
if (segmentsBack <= 0)
|
||||
{
|
||||
return manifestContent; // Disabled by config.
|
||||
}
|
||||
|
||||
// Only a media playlist (has segments, no variant list) should carry EXT-X-START.
|
||||
// A master/multivariant playlist or a VOD playlist (#EXT-X-ENDLIST) is left untouched.
|
||||
if (!manifestContent.Contains("#EXTINF", StringComparison.Ordinal)
|
||||
|| manifestContent.Contains("#EXT-X-STREAM-INF", StringComparison.Ordinal)
|
||||
|| manifestContent.Contains("#EXT-X-ENDLIST", StringComparison.Ordinal))
|
||||
{
|
||||
return manifestContent;
|
||||
}
|
||||
|
||||
// Don't add a second tag if the source already specified a start point.
|
||||
if (manifestContent.Contains("#EXT-X-START", StringComparison.Ordinal))
|
||||
{
|
||||
_logger.LogDebug("Live playlist already contains #EXT-X-START; leaving as-is");
|
||||
return manifestContent;
|
||||
}
|
||||
|
||||
var targetDurationMatch = Regex.Match(manifestContent, @"#EXT-X-TARGETDURATION:(\d+(?:\.\d+)?)");
|
||||
if (!targetDurationMatch.Success
|
||||
|| !double.TryParse(
|
||||
targetDurationMatch.Groups[1].Value,
|
||||
System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out var targetDuration)
|
||||
|| targetDuration <= 0)
|
||||
{
|
||||
_logger.LogDebug("Could not read EXT-X-TARGETDURATION; skipping EXT-X-START injection");
|
||||
return manifestContent;
|
||||
}
|
||||
|
||||
// RFC 8216: TIME-OFFSET SHOULD NOT be within 3 target durations of the live edge.
|
||||
var clampedSegments = Math.Max(3, segmentsBack);
|
||||
var offsetSeconds = clampedSegments * targetDuration;
|
||||
var startTag = string.Format(
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
"#EXT-X-START:TIME-OFFSET=-{0:0.###},PRECISE=YES",
|
||||
offsetSeconds);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Injecting {StartTag} into live playlist (targetDuration={TargetDuration}s, segmentsBack={SegmentsBack})",
|
||||
startTag,
|
||||
targetDuration,
|
||||
clampedSegments);
|
||||
|
||||
// Insert after the #EXTM3U header so it sits with the other top-level tags.
|
||||
var headerIndex = manifestContent.IndexOf("#EXTM3U", StringComparison.Ordinal);
|
||||
if (headerIndex < 0)
|
||||
{
|
||||
return startTag + "\n" + manifestContent;
|
||||
}
|
||||
|
||||
var lineEnd = manifestContent.IndexOf('\n', headerIndex);
|
||||
if (lineEnd < 0)
|
||||
{
|
||||
return manifestContent + "\n" + startTag;
|
||||
}
|
||||
|
||||
return manifestContent[..(lineEnd + 1)] + startTag + "\n" + manifestContent[(lineEnd + 1)..];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up old and expired stream mappings.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user