diff options
Diffstat (limited to 'MediaBrowser.Api/Playback')
| -rw-r--r-- | MediaBrowser.Api/Playback/BaseStreamingService.cs | 140 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Dash/MpegDashService.cs | 4 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 31 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 232 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/VideoHlsService.cs | 6 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs | 5 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Progressive/VideoService.cs | 4 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/StreamState.cs | 2 |
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 { |
