|
@@ -59,10 +59,11 @@ namespace MediaBrowser.Api.Playback.Hls
|
|
/// Processes the request.
|
|
/// Processes the request.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <param name="request">The request.</param>
|
|
/// <param name="request">The request.</param>
|
|
|
|
+ /// <param name="isLive">if set to <c>true</c> [is live].</param>
|
|
/// <returns>System.Object.</returns>
|
|
/// <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);
|
|
private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1);
|
|
@@ -70,13 +71,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
|
/// Processes the request async.
|
|
/// Processes the request async.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <param name="request">The request.</param>
|
|
/// <param name="request">The request.</param>
|
|
|
|
+ /// <param name="isLive">if set to <c>true</c> [is live].</param>
|
|
/// <returns>Task{System.Object}.</returns>
|
|
/// <returns>Task{System.Object}.</returns>
|
|
- /// <exception cref="ArgumentException">
|
|
|
|
- /// A video bitrate is required
|
|
|
|
|
|
+ /// <exception cref="ArgumentException">A video bitrate is required
|
|
/// or
|
|
/// 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();
|
|
var cancellationTokenSource = new CancellationTokenSource();
|
|
|
|
|
|
@@ -110,7 +110,8 @@ namespace MediaBrowser.Api.Playback.Hls
|
|
throw;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
- await WaitForMinimumSegmentCount(playlist, GetSegmentWait(), cancellationTokenSource.Token).ConfigureAwait(false);
|
|
|
|
|
|
+ var waitCount = isLive ? 1 : GetSegmentWait();
|
|
|
|
+ await WaitForMinimumSegmentCount(playlist, waitCount, cancellationTokenSource.Token).ConfigureAwait(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
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 audioBitrate = state.OutputAudioBitrate ?? 0;
|
|
var videoBitrate = state.OutputVideoBitrate ?? 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)
|
|
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);
|
|
var line = await reader.ReadLineAsync().ConfigureAwait(false);
|
|
|
|
|
|
@@ -206,11 +225,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
|
count++;
|
|
count++;
|
|
if (count >= segmentCount)
|
|
if (count >= segmentCount)
|
|
{
|
|
{
|
|
|
|
+ Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
|
|
return;
|
|
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
|
|
var itsOffsetMs = hlsVideoRequest == null
|
|
? 0
|
|
? 0
|
|
- : ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs;
|
|
|
|
|
|
+ : hlsVideoRequest.TimeStampOffsetMs;
|
|
|
|
|
|
var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture));
|
|
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
|
|
// If isEncoding is true we're actually starting ffmpeg
|
|
var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0";
|
|
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,
|
|
itsOffset,
|
|
inputModifier,
|
|
inputModifier,
|
|
GetInputArgument(state),
|
|
GetInputArgument(state),
|
|
@@ -251,6 +279,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|
state.SegmentLength.ToString(UsCulture),
|
|
state.SegmentLength.ToString(UsCulture),
|
|
startNumberParam,
|
|
startNumberParam,
|
|
state.HlsListSize.ToString(UsCulture),
|
|
state.HlsListSize.ToString(UsCulture),
|
|
|
|
+ baseUrlParam,
|
|
outputPath
|
|
outputPath
|
|
).Trim();
|
|
).Trim();
|
|
|
|
|