mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-12-29 20:24:47 +03:00
#514 - Support HLS seeking
This commit is contained in:
@@ -59,10 +59,11 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
/// Processes the request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="isLive">if set to <c>true</c> [is live].</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
protected object ProcessRequest(StreamRequest request)
|
||||
protected object ProcessRequest(StreamRequest request, bool isLive)
|
||||
{
|
||||
return ProcessRequestAsync(request).Result;
|
||||
return ProcessRequestAsync(request, isLive).Result;
|
||||
}
|
||||
|
||||
private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1);
|
||||
@@ -70,13 +71,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
/// Processes the request async.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="isLive">if set to <c>true</c> [is live].</param>
|
||||
/// <returns>Task{System.Object}.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// A video bitrate is required
|
||||
/// <exception cref="ArgumentException">A video bitrate is required
|
||||
/// or
|
||||
/// An audio bitrate is required
|
||||
/// </exception>
|
||||
private async Task<object> ProcessRequestAsync(StreamRequest request)
|
||||
/// An audio bitrate is required</exception>
|
||||
private async Task<object> ProcessRequestAsync(StreamRequest request, bool isLive)
|
||||
{
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
@@ -110,7 +110,8 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
throw;
|
||||
}
|
||||
|
||||
await WaitForMinimumSegmentCount(playlist, GetSegmentWait(), cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
var waitCount = isLive ? 1 : GetSegmentWait();
|
||||
await WaitForMinimumSegmentCount(playlist, waitCount, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -119,6 +120,22 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
}
|
||||
}
|
||||
|
||||
if (isLive)
|
||||
{
|
||||
//var file = request.PlaylistId + Path.GetExtension(Request.PathInfo);
|
||||
|
||||
//file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file);
|
||||
|
||||
try
|
||||
{
|
||||
return ResultFactory.GetStaticFileResult(Request, playlist, FileShare.ReadWrite);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
|
||||
}
|
||||
}
|
||||
|
||||
var audioBitrate = state.OutputAudioBitrate ?? 0;
|
||||
var videoBitrate = state.OutputVideoBitrate ?? 0;
|
||||
|
||||
@@ -188,16 +205,18 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
|
||||
protected async Task WaitForMinimumSegmentCount(string playlist, int segmentCount, CancellationToken cancellationToken)
|
||||
{
|
||||
var count = 0;
|
||||
Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist);
|
||||
|
||||
// Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
|
||||
using (var fileStream = FileSystem.GetFileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
|
||||
while (true)
|
||||
{
|
||||
using (var reader = new StreamReader(fileStream))
|
||||
// Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
|
||||
using (var fileStream = FileSystem.GetFileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
|
||||
{
|
||||
while (true)
|
||||
using (var reader = new StreamReader(fileStream))
|
||||
{
|
||||
if (!reader.EndOfStream)
|
||||
var count = 0;
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = await reader.ReadLineAsync().ConfigureAwait(false);
|
||||
|
||||
@@ -206,11 +225,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
count++;
|
||||
if (count >= segmentCount)
|
||||
{
|
||||
Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
await Task.Delay(25, cancellationToken).ConfigureAwait(false);
|
||||
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,7 +249,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
|
||||
var itsOffsetMs = hlsVideoRequest == null
|
||||
? 0
|
||||
: ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs;
|
||||
: hlsVideoRequest.TimeStampOffsetMs;
|
||||
|
||||
var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture));
|
||||
|
||||
@@ -240,7 +260,15 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
// If isEncoding is true we're actually starting ffmpeg
|
||||
var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0";
|
||||
|
||||
var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
|
||||
var baseUrlParam = string.Empty;
|
||||
|
||||
if (state.Request is GetLiveHlsStream)
|
||||
{
|
||||
baseUrlParam = string.Format(" -hls_base_url \"{0}/\"",
|
||||
"hls/" + Path.GetFileNameWithoutExtension(outputPath));
|
||||
}
|
||||
|
||||
var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"",
|
||||
itsOffset,
|
||||
inputModifier,
|
||||
GetInputArgument(state),
|
||||
@@ -251,6 +279,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
state.SegmentLength.ToString(UsCulture),
|
||||
startNumberParam,
|
||||
state.HlsListSize.ToString(UsCulture),
|
||||
baseUrlParam,
|
||||
outputPath
|
||||
).Trim();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user