aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api/Playback
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api/Playback')
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs140
-rw-r--r--MediaBrowser.Api/Playback/Dash/MpegDashService.cs4
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs31
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs232
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs6
-rw-r--r--MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs5
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs4
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs2
8 files changed, 158 insertions, 266 deletions
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index dc5858e86..531d67eed 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -263,38 +263,27 @@ namespace MediaBrowser.Api.Playback
return returnFirstIfNoIndex ? streams.FirstOrDefault() : null;
}
- protected EncodingQuality GetQualitySetting()
- {
- var quality = ApiEntryPoint.Instance.GetEncodingOptions().EncodingQuality;
-
- if (quality == EncodingQuality.Auto)
- {
- var cpuCount = Environment.ProcessorCount;
-
- if (cpuCount >= 4)
- {
- //return EncodingQuality.HighQuality;
- }
-
- return EncodingQuality.HighSpeed;
- }
-
- return quality;
- }
-
/// <summary>
/// Gets the number of threads.
/// </summary>
/// <returns>System.Int32.</returns>
protected int GetNumberOfThreads(StreamState state, bool isWebm)
{
+ var threads = ApiEntryPoint.Instance.GetEncodingOptions().EncodingThreadCount;
+
if (isWebm)
{
// Recommended per docs
return Math.Max(Environment.ProcessorCount - 1, 2);
}
- return 0;
+ // Automatic
+ if (threads == -1)
+ {
+ return 0;
+ }
+
+ return threads;
}
protected string H264Encoder
@@ -326,77 +315,31 @@ namespace MediaBrowser.Api.Playback
var isVc1 = state.VideoStream != null &&
string.Equals(state.VideoStream.Codec, "vc1", StringComparison.OrdinalIgnoreCase);
- var qualitySetting = GetQualitySetting();
-
if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
{
param = "-preset superfast";
- switch (qualitySetting)
- {
- case EncodingQuality.HighSpeed:
- param += " -crf 23";
- break;
- case EncodingQuality.HighQuality:
- param += " -crf 20";
- break;
- case EncodingQuality.MaxQuality:
- param += " -crf 18";
- break;
- }
+ param += " -crf 23";
}
else if (string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase))
{
param = "-preset fast";
- switch (qualitySetting)
- {
- case EncodingQuality.HighSpeed:
- param += " -crf 28";
- break;
- case EncodingQuality.HighQuality:
- param += " -crf 25";
- break;
- case EncodingQuality.MaxQuality:
- param += " -crf 21";
- break;
- }
+ param += " -crf 28";
}
// h264 (h264_qsv)
else if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
- switch (qualitySetting)
- {
- case EncodingQuality.HighSpeed:
- param = "-preset 7";
- break;
- case EncodingQuality.HighQuality:
- param = "-preset 4";
- break;
- case EncodingQuality.MaxQuality:
- param = "-preset 1";
- break;
- }
+ param = "-preset 7";
}
// h264 (libnvenc)
else if (string.Equals(videoCodec, "libnvenc", StringComparison.OrdinalIgnoreCase))
{
- switch (qualitySetting)
- {
- case EncodingQuality.HighSpeed:
- param = "-preset high-performance";
- break;
- case EncodingQuality.HighQuality:
- param = "";
- break;
- case EncodingQuality.MaxQuality:
- param = "-preset high-quality";
- break;
- }
+ param = "-preset high-performance";
}
// webm
@@ -409,20 +352,7 @@ namespace MediaBrowser.Api.Playback
var qmin = "0";
var qmax = "50";
- switch (qualitySetting)
- {
- case EncodingQuality.HighSpeed:
- crf = "10";
- break;
- case EncodingQuality.HighQuality:
- crf = "6";
- break;
- case EncodingQuality.MaxQuality:
- crf = "4";
- break;
- default:
- throw new ArgumentException("Unrecognized quality setting");
- }
+ crf = "10";
if (isVc1)
{
@@ -689,7 +619,7 @@ namespace MediaBrowser.Api.Playback
// TODO: Perhaps also use original_size=1920x800 ??
return string.Format("subtitles=filename='{0}'{1},setpts=PTS -{2}/TB",
- subtitlePath.Replace('\\', '/').Replace("'", "\\'").Replace(":/", "\\:/"),
+ MediaEncoder.EscapeSubtitleFilterPath(subtitlePath),
charsetParam,
seconds.ToString(UsCulture));
}
@@ -697,7 +627,7 @@ namespace MediaBrowser.Api.Playback
var mediaPath = state.MediaPath ?? string.Empty;
return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
- mediaPath.Replace('\\', '/').Replace("'", "\\'").Replace(":/", "\\:/"),
+ MediaEncoder.EscapeSubtitleFilterPath(mediaPath),
state.InternalSubtitleStreamOffset.ToString(UsCulture),
seconds.ToString(UsCulture));
}
@@ -794,7 +724,7 @@ namespace MediaBrowser.Api.Playback
var channelLimit = codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1
? 2
- : 5;
+ : 6;
// If we don't have any media info then limit it to 5 to prevent encoding errors due to asking for too many channels
return Math.Min(request.MaxAudioChannels.Value, channelLimit);
@@ -819,11 +749,11 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the audio encoder.
/// </summary>
- /// <param name="request">The request.</param>
+ /// <param name="state">The state.</param>
/// <returns>System.String.</returns>
- protected string GetAudioEncoder(StreamRequest request)
+ protected string GetAudioEncoder(StreamState state)
{
- var codec = request.AudioCodec;
+ var codec = state.OutputAudioCodec;
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase))
{
@@ -848,11 +778,11 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the name of the output video codec
/// </summary>
- /// <param name="request">The request.</param>
+ /// <param name="state">The state.</param>
/// <returns>System.String.</returns>
- protected string GetVideoEncoder(VideoStreamRequest request)
+ protected string GetVideoEncoder(StreamState state)
{
- var codec = request.VideoCodec;
+ var codec = state.OutputVideoCodec;
if (!string.IsNullOrEmpty(codec))
{
@@ -924,7 +854,7 @@ namespace MediaBrowser.Api.Playback
state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
}
- if (state.MediaSource.RequiresOpening)
+ if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.Request.LiveStreamId))
{
var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
{
@@ -1704,11 +1634,6 @@ namespace MediaBrowser.Api.Playback
private void TryStreamCopy(StreamState state, VideoStreamRequest videoRequest)
{
- if (!EnableStreamCopy)
- {
- return;
- }
-
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
{
state.OutputVideoCodec = "copy";
@@ -1720,14 +1645,6 @@ namespace MediaBrowser.Api.Playback
}
}
- protected virtual bool EnableStreamCopy
- {
- get
- {
- return true;
- }
- }
-
private void AttachMediaSourceInfo(StreamState state,
MediaSourceInfo mediaSource,
VideoStreamRequest videoRequest,
@@ -1811,13 +1728,18 @@ namespace MediaBrowser.Api.Playback
state.MediaSource = mediaSource;
}
- private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
+ protected virtual bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
{
if (videoStream.IsInterlaced)
{
return false;
}
+ if (videoStream.IsAnamorphic ?? false)
+ {
+ return false;
+ }
+
// Can't stream copy if we're burning in subtitles
if (request.SubtitleStreamIndex.HasValue)
{
@@ -1954,7 +1876,7 @@ namespace MediaBrowser.Api.Playback
return Array.FindIndex(list.ToArray(), t => string.Equals(t, profile, StringComparison.OrdinalIgnoreCase));
}
- private bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
+ protected virtual bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
{
// Source and target codecs must match
if (string.IsNullOrEmpty(audioStream.Codec) || !supportedAudioCodecs.Contains(audioStream.Codec, StringComparer.OrdinalIgnoreCase))
diff --git a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
index 47eb38b2d..c201ffd58 100644
--- a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
+++ b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
@@ -378,7 +378,7 @@ namespace MediaBrowser.Api.Playback.Dash
protected override string GetAudioArguments(StreamState state)
{
- var codec = GetAudioEncoder(state.Request);
+ var codec = GetAudioEncoder(state);
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
{
@@ -408,7 +408,7 @@ namespace MediaBrowser.Api.Playback.Dash
protected override string GetVideoArguments(StreamState state)
{
- var codec = GetVideoEncoder(state.VideoRequest);
+ var codec = GetVideoEncoder(state);
var args = "-codec:v:0 " + codec;
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index b2ffeca3d..5d377366a 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
@@ -310,5 +311,35 @@ namespace MediaBrowser.Api.Playback.Hls
{
return 0;
}
+
+ protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
+ {
+ if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0)
+ {
+ Logger.Debug("Cannot stream copy video due to missing keyframe info");
+ return false;
+ }
+
+ var previousSegment = 0;
+ foreach (var frame in videoStream.KeyFrames)
+ {
+ var length = frame - previousSegment;
+
+ // Don't allow really long segments because this could result in long download times
+ if (length > 10000)
+ {
+ Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length);
+ return false;
+ }
+ previousSegment = frame;
+ }
+
+ return base.CanStreamCopyVideo(request, videoStream);
+ }
+
+ protected override bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 0a432a580..cbea1ca0c 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -13,7 +13,6 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using ServiceStack;
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -161,7 +160,6 @@ namespace MediaBrowser.Api.Playback.Hls
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
var segmentPath = GetSegmentPath(state, playlistPath, requestedIndex);
- var segmentLength = state.SegmentLength;
var segmentExtension = GetSegmentFileExtension(state);
@@ -170,7 +168,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (File.Exists(segmentPath))
{
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
+ return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
@@ -179,7 +177,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (File.Exists(segmentPath))
{
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
+ return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
else
{
@@ -210,14 +208,12 @@ namespace MediaBrowser.Api.Playback.Hls
{
ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, p => false);
- await ReadSegmentLengths(playlistPath).ConfigureAwait(false);
-
if (currentTranscodingIndex.HasValue)
{
DeleteLastFile(playlistPath, segmentExtension, 0);
}
- request.StartTimeTicks = GetSeekPositionTicks(state, playlistPath, requestedIndex);
+ request.StartTimeTicks = GetStartPositionTicks(state, requestedIndex);
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
}
@@ -252,84 +248,76 @@ namespace MediaBrowser.Api.Playback.Hls
Logger.Info("returning {0}", segmentPath);
job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
+ return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
- private static readonly ConcurrentDictionary<string, double> SegmentLengths = new ConcurrentDictionary<string, double>(StringComparer.OrdinalIgnoreCase);
- private async Task ReadSegmentLengths(string playlist)
- {
- try
- {
- using (var fileStream = GetPlaylistFileStream(playlist))
- {
- using (var reader = new StreamReader(fileStream))
- {
- double duration = -1;
-
- while (!reader.EndOfStream)
- {
- var text = await reader.ReadLineAsync().ConfigureAwait(false);
+ // 256k
+ private const int BufferSize = 262144;
- if (text.StartsWith("#EXTINF", StringComparison.OrdinalIgnoreCase))
- {
- var parts = text.Split(new[] { ':' }, 2);
- if (parts.Length == 2)
- {
- var time = parts[1].Trim(new[] { ',' }).Trim();
- double timeValue;
- if (double.TryParse(time, NumberStyles.Any, CultureInfo.InvariantCulture, out timeValue))
- {
- duration = timeValue;
- continue;
- }
- }
- }
- else if (duration != -1)
- {
- SegmentLengths.AddOrUpdate(text, duration, (k, v) => duration);
- Logger.Debug("Added segment length of {0} for {1}", duration, text);
- }
+ private long GetStartPositionTicks(StreamState state, int requestedIndex)
+ {
+ double startSeconds = 0;
+ var lengths = GetSegmentLengths(state);
- duration = -1;
- }
- }
- }
- }
- catch (DirectoryNotFoundException)
+ for (var i = 0; i < requestedIndex; i++)
{
-
+ startSeconds += lengths[requestedIndex];
}
- catch (FileNotFoundException)
- {
- }
+ var position = TimeSpan.FromSeconds(startSeconds).Ticks;
+ return position;
}
- private long GetSeekPositionTicks(StreamState state, string playlist, int requestedIndex)
+ private long GetEndPositionTicks(StreamState state, int requestedIndex)
{
double startSeconds = 0;
+ var lengths = GetSegmentLengths(state);
- for (var i = 0; i < requestedIndex; i++)
+ for (var i = 0; i <= requestedIndex; i++)
{
- var segmentPath = GetSegmentPath(state, playlist, i);
-
- //double length;
- //if (SegmentLengths.TryGetValue(Path.GetFileName(segmentPath), out length))
- //{
- // Logger.Debug("Found segment length of {0} for index {1}", length, i);
- // startSeconds += length;
- //}
- //else
- //{
- // startSeconds += state.SegmentLength;
- //}
- startSeconds += state.SegmentLength;
+ startSeconds += lengths[requestedIndex];
}
var position = TimeSpan.FromSeconds(startSeconds).Ticks;
return position;
}
+ private double[] GetSegmentLengths(StreamState state)
+ {
+ var result = new List<double>();
+ var encoder = GetVideoEncoder(state);
+
+ if (string.Equals(encoder, "copy", StringComparison.OrdinalIgnoreCase))
+ {
+ var videoStream = state.VideoStream;
+ if (videoStream.KeyFrames != null && videoStream.KeyFrames.Count > 0)
+ {
+ foreach (var frame in videoStream.KeyFrames)
+ {
+ var seconds = TimeSpan.FromMilliseconds(frame).TotalSeconds;
+ seconds -= result.Sum();
+ result.Add(seconds);
+ }
+ return result.ToArray();
+ }
+ }
+
+ var ticks = state.RunTimeTicks ?? 0;
+
+ var segmentLengthTicks = TimeSpan.FromSeconds(state.SegmentLength).Ticks;
+
+ while (ticks > 0)
+ {
+ var length = ticks >= segmentLengthTicks ? segmentLengthTicks : ticks;
+
+ result.Add(TimeSpan.FromTicks(length).TotalSeconds);
+
+ ticks -= length;
+ }
+
+ return result.ToArray();
+ }
+
public int? GetCurrentTranscodingIndex(string playlist, string segmentExtension)
{
var job = ApiEntryPoint.Instance.GetTranscodingJob(playlist, TranscodingJobType);
@@ -434,17 +422,16 @@ namespace MediaBrowser.Api.Playback.Hls
return Path.Combine(folder, filename + index.ToString(UsCulture) + GetSegmentFileExtension(state));
}
- private async Task<object> GetSegmentResult(string playlistPath,
+ private async Task<object> GetSegmentResult(StreamState state, string playlistPath,
string segmentPath,
int segmentIndex,
- int segmentLength,
TranscodingJob transcodingJob,
CancellationToken cancellationToken)
{
// If all transcoding has completed, just return immediately
if (transcodingJob != null && transcodingJob.HasExited && File.Exists(segmentPath))
{
- return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
+ return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
}
var segmentFilename = Path.GetFileName(segmentPath);
@@ -455,21 +442,18 @@ namespace MediaBrowser.Api.Playback.Hls
{
using (var fileStream = GetPlaylistFileStream(playlistPath))
{
- using (var reader = new StreamReader(fileStream))
+ using (var reader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize))
{
- while (!reader.EndOfStream)
- {
- var text = await reader.ReadLineAsync().ConfigureAwait(false);
+ var text = await reader.ReadToEndAsync().ConfigureAwait(false);
- // If it appears in the playlist, it's done
- if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
+ // If it appears in the playlist, it's done
+ if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ if (File.Exists(segmentPath))
{
- if (File.Exists(segmentPath))
- {
- return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
- }
- break;
+ return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
}
+ //break;
}
}
}
@@ -518,13 +502,12 @@ namespace MediaBrowser.Api.Playback.Hls
//}
cancellationToken.ThrowIfCancellationRequested();
- return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
+ return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
}
- private object GetSegmentResult(string segmentPath, int index, int segmentLength, TranscodingJob transcodingJob)
+ private object GetSegmentResult(StreamState state, string segmentPath, int index, TranscodingJob transcodingJob)
{
- var segmentEndingSeconds = (1 + index) * segmentLength;
- var segmentEndingPositionTicks = TimeSpan.FromSeconds(segmentEndingSeconds).Ticks;
+ var segmentEndingPositionTicks = GetEndPositionTicks(state, index);
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
{
@@ -751,25 +734,23 @@ namespace MediaBrowser.Api.Playback.Hls
{
var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
+ var segmentLengths = GetSegmentLengths(state);
+
var builder = new StringBuilder();
builder.AppendLine("#EXTM3U");
builder.AppendLine("#EXT-X-VERSION:3");
- builder.AppendLine("#EXT-X-TARGETDURATION:" + (state.SegmentLength).ToString(UsCulture));
+ builder.AppendLine("#EXT-X-TARGETDURATION:" + Math.Ceiling((segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength)).ToString(UsCulture));
builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
var queryStringIndex = Request.RawUrl.IndexOf('?');
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
- var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;
-
var index = 0;
- while (seconds > 0)
+ foreach (var length in segmentLengths)
{
- var length = seconds >= state.SegmentLength ? state.SegmentLength : seconds;
-
- builder.AppendLine("#EXTINF:" + length.ToString(UsCulture) + ",");
+ builder.AppendLine("#EXTINF:" + length.ToString("0.000000", UsCulture) + ",");
builder.AppendLine(string.Format("hlsdynamic/{0}/{1}{2}{3}",
@@ -778,7 +759,6 @@ namespace MediaBrowser.Api.Playback.Hls
GetSegmentFileExtension(isOutputVideo),
queryString));
- seconds -= state.SegmentLength;
index++;
}
@@ -791,7 +771,7 @@ namespace MediaBrowser.Api.Playback.Hls
protected override string GetAudioArguments(StreamState state)
{
- var codec = GetAudioEncoder(state.Request);
+ var codec = GetAudioEncoder(state);
if (!state.IsOutputVideo)
{
@@ -856,7 +836,7 @@ namespace MediaBrowser.Api.Playback.Hls
return string.Empty;
}
- var codec = GetVideoEncoder(state.VideoRequest);
+ var codec = GetVideoEncoder(state);
var args = "-codec:v:0 " + codec;
@@ -877,7 +857,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
else
{
- var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",
+ var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
state.SegmentLength.ToString(UsCulture));
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
@@ -889,7 +869,7 @@ namespace MediaBrowser.Api.Playback.Hls
// Add resolution params, if specified
if (!hasGraphicalSubs)
{
- args += GetOutputSizeParam(state, codec, false);
+ args += GetOutputSizeParam(state, codec, EnableCopyTs(state));
}
// This is for internal graphical subs
@@ -898,17 +878,17 @@ namespace MediaBrowser.Api.Playback.Hls
args += GetGraphicalSubtitleParam(state, codec);
}
- args += " -flags +loop-global_header -sc_threshold 0";
- }
-
- if (!EnableSplitTranscoding(state))
- {
- //args += " -copyts";
+ args += " -flags -global_header -sc_threshold 0";
}
return args;
}
+ private bool EnableCopyTs(StreamState state)
+ {
+ return state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream;
+ }
+
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{
var threads = GetNumberOfThreads(state, false);
@@ -921,22 +901,7 @@ namespace MediaBrowser.Api.Playback.Hls
var toTimeParam = string.Empty;
var timestampOffsetParam = string.Empty;
- if (EnableSplitTranscoding(state))
- {
- var startTime = state.Request.StartTimeTicks ?? 0;
- var durationSeconds = ApiEntryPoint.Instance.GetEncodingOptions().ThrottleThresholdInSeconds;
-
- var endTime = startTime + TimeSpan.FromSeconds(durationSeconds).Ticks;
- endTime = Math.Min(endTime, state.RunTimeTicks.Value);
-
- if (endTime < state.RunTimeTicks.Value)
- {
- //toTimeParam = " -to " + MediaEncoder.GetTimeParameter(endTime);
- toTimeParam = " -t " + MediaEncoder.GetTimeParameter(TimeSpan.FromSeconds(durationSeconds).Ticks);
- }
- }
-
- if (state.IsOutputVideo && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0)
+ if (state.IsOutputVideo && !EnableCopyTs(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0)
{
timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0).ToString(CultureInfo.InvariantCulture);
}
@@ -978,36 +943,7 @@ namespace MediaBrowser.Api.Playback.Hls
protected override bool EnableThrottling(StreamState state)
{
- return !EnableSplitTranscoding(state);
- }
-
- private bool EnableSplitTranscoding(StreamState state)
- {
- return false;
- if (string.Equals(Request.QueryString["EnableSplitTranscoding"], "false", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- return state.RunTimeTicks.HasValue && state.IsOutputVideo;
- }
-
- protected override bool EnableStreamCopy
- {
- get
- {
- return false;
- }
+ return true;
}
/// <summary>
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index d8e3423fc..2d1abf7e9 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -48,7 +48,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// <returns>System.String.</returns>
protected override string GetAudioArguments(StreamState state)
{
- var codec = GetAudioEncoder(state.Request);
+ var codec = GetAudioEncoder(state);
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
{
@@ -83,7 +83,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// <returns>System.String.</returns>
protected override string GetVideoArguments(StreamState state)
{
- var codec = GetVideoEncoder(state.VideoRequest);
+ var codec = GetVideoEncoder(state);
var args = "-codec:v:0 " + codec;
@@ -100,7 +100,7 @@ namespace MediaBrowser.Api.Playback.Hls
args;
}
- var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",
+ var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
state.SegmentLength.ToString(UsCulture));
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
index 6a3443f35..adedd9461 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
@@ -91,6 +91,9 @@ namespace MediaBrowser.Api.Playback.Progressive
private readonly IFileSystem _fileSystem;
private readonly TranscodingJob _job;
+ // 256k
+ private const int BufferSize = 262144;
+
private long _bytesWritten = 0;
public ProgressiveFileCopier(IFileSystem fileSystem, TranscodingJob job)
@@ -108,7 +111,7 @@ namespace MediaBrowser.Api.Playback.Progressive
{
while (eofCount < 15)
{
- CopyToInternal(fs, outputStream, 81920);
+ CopyToInternal(fs, outputStream, BufferSize);
var fsPosition = fs.Position;
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index ebd72b2ce..84ae26248 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -89,7 +89,7 @@ namespace MediaBrowser.Api.Playback.Progressive
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{
// Get the output codec name
- var videoCodec = GetVideoEncoder(state.VideoRequest);
+ var videoCodec = GetVideoEncoder(state);
var format = string.Empty;
var keyFrame = string.Empty;
@@ -183,7 +183,7 @@ namespace MediaBrowser.Api.Playback.Progressive
}
// Get the output codec name
- var codec = GetAudioEncoder(state.Request);
+ var codec = GetAudioEncoder(state);
var args = "-codec:a:0 " + codec;
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index 02b7720a4..34dc5ea12 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -185,7 +185,7 @@ namespace MediaBrowser.Api.Playback
private async void DisposeLiveStream()
{
- if (MediaSource.RequiresClosing && string.IsNullOrWhiteSpace(Request.LiveStreamId))
+ if ((MediaSource.RequiresClosing) && string.IsNullOrWhiteSpace(Request.LiveStreamId))
{
try
{