From 93e535d3a143d092effb37e529e5682c3d11802a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 26 Mar 2019 22:56:05 +0100 Subject: [PATCH] Trying to make sense of the streaming code Mostly small changes as I was looking through the code. * async void -> async Task * Properly implemented dispose methods * Pass the logstream directly to the JobLogger * Style fixes --- MediaBrowser.Api/ApiEntryPoint.cs | 15 +- .../Playback/BaseStreamingService.cs | 152 +++++++----------- MediaBrowser.Api/Playback/StreamState.cs | 117 +++++--------- .../MediaEncoding/EncodingJobInfo.cs | 58 +++---- .../MediaEncoding/JobLogger.cs | 7 +- .../Dlna/ContentFeatureBuilder.cs | 20 +-- 6 files changed, 151 insertions(+), 218 deletions(-) diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 700cbb9439..a223a4fe38 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -415,7 +415,7 @@ namespace MediaBrowser.Api public void OnTranscodeEndRequest(TranscodingJob job) { job.ActiveRequestCount--; - //Logger.LogDebug("OnTranscodeEndRequest job.ActiveRequestCount={0}", job.ActiveRequestCount); + Logger.LogDebug("OnTranscodeEndRequest job.ActiveRequestCount={0}", job.ActiveRequestCount); if (job.ActiveRequestCount <= 0) { PingTimer(job, false); @@ -428,7 +428,7 @@ namespace MediaBrowser.Api throw new ArgumentNullException(nameof(playSessionId)); } - //Logger.LogDebug("PingTranscodingJob PlaySessionId={0} isUsedPaused: {1}", playSessionId, isUserPaused); + Logger.LogDebug("PingTranscodingJob PlaySessionId={0} isUsedPaused: {1}", playSessionId, isUserPaused); List jobs; @@ -443,7 +443,7 @@ namespace MediaBrowser.Api { if (isUserPaused.HasValue) { - //Logger.LogDebug("Setting job.IsUserPaused to {0}. jobId: {1}", isUserPaused, job.Id); + Logger.LogDebug("Setting job.IsUserPaused to {0}. jobId: {1}", isUserPaused, job.Id); job.IsUserPaused = isUserPaused.Value; } PingTimer(job, true); @@ -601,7 +601,6 @@ namespace MediaBrowser.Api { Logger.LogInformation("Stopping ffmpeg process with q command for {Path}", job.Path); - //process.Kill(); process.StandardInput.WriteLine("q"); // Need to wait because killing is asynchronous @@ -701,7 +700,7 @@ namespace MediaBrowser.Api { try { - //Logger.LogDebug("Deleting HLS file {0}", file); + Logger.LogDebug("Deleting HLS file {0}", file); _fileSystem.DeleteFile(file); } catch (FileNotFoundException) @@ -840,12 +839,12 @@ namespace MediaBrowser.Api { if (KillTimer == null) { - //Logger.LogDebug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + Logger.LogDebug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); KillTimer = new Timer(new TimerCallback(callback), this, intervalMs, Timeout.Infinite); } else { - //Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); KillTimer.Change(intervalMs, Timeout.Infinite); } } @@ -864,7 +863,7 @@ namespace MediaBrowser.Api { var intervalMs = PingTimeout; - //Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); KillTimer.Change(intervalMs, Timeout.Infinite); } } diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index ae259a4f59..b480d055af 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -8,7 +8,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; @@ -16,7 +15,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -32,6 +30,8 @@ namespace MediaBrowser.Api.Playback /// public abstract class BaseStreamingService : BaseApiService { + protected static readonly CultureInfo UsCulture = CultureInfo.ReadOnly(new CultureInfo("en-US")); + /// /// Gets or sets the application paths. /// @@ -65,15 +65,25 @@ namespace MediaBrowser.Api.Playback protected IFileSystem FileSystem { get; private set; } protected IDlnaManager DlnaManager { get; private set; } + protected IDeviceManager DeviceManager { get; private set; } + protected ISubtitleEncoder SubtitleEncoder { get; private set; } + protected IMediaSourceManager MediaSourceManager { get; private set; } + protected IJsonSerializer JsonSerializer { get; private set; } protected IAuthorizationContext AuthorizationContext { get; private set; } protected EncodingHelper EncodingHelper { get; set; } + /// + /// Gets the type of the transcoding job. + /// + /// The type of the transcoding job. + protected abstract TranscodingJobType TranscodingJobType { get; } + /// /// Initializes a new instance of the class. /// @@ -112,12 +122,6 @@ namespace MediaBrowser.Api.Playback /// protected abstract string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding); - /// - /// Gets the type of the transcoding job. - /// - /// The type of the transcoding job. - protected abstract TranscodingJobType TranscodingJobType { get; } - /// /// Gets the output file extension. /// @@ -133,31 +137,18 @@ namespace MediaBrowser.Api.Playback /// private string GetOutputFilePath(StreamState state, EncodingOptions encodingOptions, string outputFileExtension) { - var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath; - var data = GetCommandLineArguments("dummy\\dummy", encodingOptions, state, false); - data += "-" + (state.Request.DeviceId ?? string.Empty); - data += "-" + (state.Request.PlaySessionId ?? string.Empty); + data += "-" + (state.Request.DeviceId ?? string.Empty) + + "-" + (state.Request.PlaySessionId ?? string.Empty); - var dataHash = data.GetMD5().ToString("N"); + var filename = data.GetMD5().ToString("N") + outputFileExtension.ToLowerInvariant(); + var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath; - if (EnableOutputInSubFolder) - { - return Path.Combine(folder, dataHash, dataHash + (outputFileExtension ?? string.Empty).ToLowerInvariant()); - } - - return Path.Combine(folder, dataHash + (outputFileExtension ?? string.Empty).ToLowerInvariant()); + return Path.Combine(folder, filename); } - protected virtual bool EnableOutputInSubFolder => false; - - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - protected virtual string GetDefaultH264Preset() - { - return "superfast"; - } + protected virtual string GetDefaultH264Preset() => "superfast"; private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource) { @@ -171,7 +162,6 @@ namespace MediaBrowser.Api.Playback var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest { OpenToken = state.MediaSource.OpenToken - }, cancellationTokenSource.Token).ConfigureAwait(false); EncodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.RequestedUrl); @@ -209,22 +199,16 @@ namespace MediaBrowser.Api.Playback if (state.VideoRequest != null && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { var auth = AuthorizationContext.GetAuthorizationInfo(Request); - if (auth.User != null) + if (auth.User != null && !auth.User.Policy.EnableVideoPlaybackTranscoding) { - if (!auth.User.Policy.EnableVideoPlaybackTranscoding) - { - ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state); + ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state); - throw new ArgumentException("User does not have access to video transcoding"); - } + throw new ArgumentException("User does not have access to video transcoding"); } } var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions(); - var transcodingId = Guid.NewGuid().ToString("N"); - var commandLineArgs = GetCommandLineArguments(outputPath, encodingOptions, state, true); - var process = new Process() { StartInfo = new ProcessStartInfo() @@ -239,7 +223,7 @@ namespace MediaBrowser.Api.Playback RedirectStandardInput = true, FileName = MediaEncoder.EncoderPath, - Arguments = commandLineArgs, + Arguments = GetCommandLineArguments(outputPath, encodingOptions, state, true), WorkingDirectory = string.IsNullOrWhiteSpace(workingDirectory) ? null : workingDirectory, ErrorDialog = false @@ -250,7 +234,7 @@ namespace MediaBrowser.Api.Playback var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, state.Request.PlaySessionId, state.MediaSource.LiveStreamId, - transcodingId, + Guid.NewGuid().ToString("N"), TranscodingJobType, process, state.Request.DeviceId, @@ -261,27 +245,26 @@ namespace MediaBrowser.Api.Playback Logger.LogInformation(commandLineLogMessage); var logFilePrefix = "ffmpeg-transcode"; - if (state.VideoRequest != null) + if (state.VideoRequest != null + && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) - && string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) { logFilePrefix = "ffmpeg-directstream"; } - else if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + else { logFilePrefix = "ffmpeg-remux"; } } var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, logFilePrefix + "-" + Guid.NewGuid() + ".txt"); - Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. - state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true); + Stream logStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true); var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(Request.AbsoluteUri + Environment.NewLine + Environment.NewLine + JsonSerializer.SerializeToString(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine); - await state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false); + await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false); process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state); @@ -298,13 +281,10 @@ namespace MediaBrowser.Api.Playback throw; } - // MUST read both stdout and stderr asynchronously or a deadlock may occurr - //process.BeginOutputReadLine(); - state.TranscodingJob = transcodingJob; // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback - new JobLogger(Logger).StartStreamingLog(state, process.StandardError.BaseStream, state.LogFileStream); + _ = new JobLogger(Logger).StartStreamingLog(state, process.StandardError.BaseStream, logStream); // Wait for the file to exist before proceeeding while (!File.Exists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited) @@ -368,25 +348,16 @@ namespace MediaBrowser.Api.Playback Logger.LogDebug("Disposing stream resources"); state.Dispose(); - try + if (process.ExitCode == 0) { - Logger.LogInformation("FFMpeg exited with code {0}", process.ExitCode); + Logger.LogInformation("FFMpeg exited with code 0"); } - catch + else { - Logger.LogError("FFMpeg exited with an error."); + Logger.LogError("FFMpeg exited with code {0}", process.ExitCode); } - // This causes on exited to be called twice: - //try - //{ - // // Dispose the process - // process.Dispose(); - //} - //catch (Exception ex) - //{ - // Logger.LogError(ex, "Error disposing ffmpeg."); - //} + process.Dispose(); } /// @@ -643,11 +614,19 @@ namespace MediaBrowser.Api.Playback return null; } - if (value.IndexOf("npt=", StringComparison.OrdinalIgnoreCase) != 0) + if (!value.StartsWith("npt=", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Invalid timeseek header"); } - value = value.Substring(4).Split(new[] { '-' }, 2)[0]; + int index = value.IndexOf('-'); + if (index == -1) + { + value = value.Substring(4); + } + else + { + value = value.Substring(4, index); + } if (value.IndexOf(':') == -1) { @@ -728,13 +707,10 @@ namespace MediaBrowser.Api.Playback // state.SegmentLength = 6; //} - if (state.VideoRequest != null) + if (state.VideoRequest != null && !string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec)) { - if (!string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec)) - { - state.SupportedVideoCodecs = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(); - state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault(); - } + state.SupportedVideoCodecs = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(); + state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault(); } if (!string.IsNullOrWhiteSpace(request.AudioCodec)) @@ -779,7 +755,7 @@ namespace MediaBrowser.Api.Playback var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false)).ToList(); mediaSource = string.IsNullOrEmpty(request.MediaSourceId) - ? mediaSources.First() + ? mediaSources[0] : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId)); if (mediaSource == null && request.MediaSourceId.Equals(request.Id)) @@ -834,11 +810,11 @@ namespace MediaBrowser.Api.Playback if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { var resolution = ResolutionNormalizer.Normalize( - state.VideoStream == null ? (int?)null : state.VideoStream.BitRate, - state.VideoStream == null ? (int?)null : state.VideoStream.Width, - state.VideoStream == null ? (int?)null : state.VideoStream.Height, + state.VideoStream?.BitRate, + state.VideoStream?.Width, + state.VideoStream?.Height, state.OutputVideoBitrate.Value, - state.VideoStream == null ? null : state.VideoStream.Codec, + state.VideoStream?.Codec, state.OutputVideoCodec, videoRequest.MaxWidth, videoRequest.MaxHeight); @@ -846,17 +822,13 @@ namespace MediaBrowser.Api.Playback videoRequest.MaxWidth = resolution.MaxWidth; videoRequest.MaxHeight = resolution.MaxHeight; } + } - ApplyDeviceProfileSettings(state); - } - else - { - ApplyDeviceProfileSettings(state); - } + ApplyDeviceProfileSettings(state); var ext = string.IsNullOrWhiteSpace(state.OutputContainer) ? GetOutputFileExtension(state) - : ("." + state.OutputContainer); + : ('.' + state.OutputContainer); var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions(); @@ -970,18 +942,18 @@ namespace MediaBrowser.Api.Playback responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode; responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*"; - if (string.Equals(GetHeader("getMediaInfo.sec"), "1", StringComparison.OrdinalIgnoreCase)) + if (state.RunTimeTicks.HasValue) { - if (state.RunTimeTicks.HasValue) + if (string.Equals(GetHeader("getMediaInfo.sec"), "1", StringComparison.OrdinalIgnoreCase)) { var ms = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds; responseHeaders["MediaInfo.sec"] = string.Format("SEC_Duration={0};", Convert.ToInt32(ms).ToString(CultureInfo.InvariantCulture)); } - } - if (state.RunTimeTicks.HasValue && !isStaticallyStreamed && profile != null) - { - AddTimeSeekResponseHeaders(state, responseHeaders); + if (!isStaticallyStreamed && profile != null) + { + AddTimeSeekResponseHeaders(state, responseHeaders); + } } if (profile == null) diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 8d4b0cb3d0..ba3c2bfbaf 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -1,9 +1,7 @@ using System; -using System.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback @@ -12,6 +10,7 @@ namespace MediaBrowser.Api.Playback { private readonly ILogger _logger; private readonly IMediaSourceManager _mediaSourceManager; + private bool _disposed = false; public string RequestedUrl { get; set; } @@ -30,11 +29,6 @@ namespace MediaBrowser.Api.Playback public VideoStreamRequest VideoRequest => Request as VideoStreamRequest; - /// - /// Gets or sets the log file stream. - /// - /// The log file stream. - public Stream LogFileStream { get; set; } public IDirectStreamProvider DirectStreamProvider { get; set; } public string WaitForPath { get; set; } @@ -72,6 +66,7 @@ namespace MediaBrowser.Api.Playback { return 3; } + return 6; } @@ -94,6 +89,16 @@ namespace MediaBrowser.Api.Playback public string UserAgent { get; set; } + public bool EstimateContentLength { get; set; } + + public TranscodeSeekInfo TranscodeSeekInfo { get; set; } + + public bool EnableDlnaHeaders { get; set; } + + public DeviceProfile DeviceProfile { get; set; } + + public TranscodingJob TranscodingJob { get; set; } + public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger, TranscodingJobType transcodingType) : base(transcodingType) { @@ -101,75 +106,41 @@ namespace MediaBrowser.Api.Playback _logger = logger; } - public bool EstimateContentLength { get; set; } - public TranscodeSeekInfo TranscodeSeekInfo { get; set; } - - public bool EnableDlnaHeaders { get; set; } - - public override void Dispose() - { - DisposeTranscodingThrottler(); - DisposeLogStream(); - DisposeLiveStream(); - - TranscodingJob = null; - } - - private void DisposeTranscodingThrottler() - { - if (TranscodingThrottler != null) - { - try - { - TranscodingThrottler.Dispose(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error disposing TranscodingThrottler"); - } - - TranscodingThrottler = null; - } - } - - private void DisposeLogStream() - { - if (LogFileStream != null) - { - try - { - LogFileStream.Dispose(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error disposing log stream"); - } - - LogFileStream = null; - } - } - - private async void DisposeLiveStream() - { - if (MediaSource.RequiresClosing && string.IsNullOrWhiteSpace(Request.LiveStreamId) && !string.IsNullOrWhiteSpace(MediaSource.LiveStreamId)) - { - try - { - await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error closing media source"); - } - } - } - - public DeviceProfile DeviceProfile { get; set; } - - public TranscodingJob TranscodingJob; public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate) { ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate); } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + // REVIEW: Is this the right place for this? + if (MediaSource.RequiresClosing + && string.IsNullOrWhiteSpace(Request.LiveStreamId) + && !string.IsNullOrWhiteSpace(MediaSource.LiveStreamId)) + { + _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).GetAwaiter().GetResult(); + } + + TranscodingThrottler?.Dispose(); + } + + TranscodingThrottler = null; + TranscodingJob = null; + + _disposed = true; + } } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 916d691b80..34af3b1568 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -374,14 +374,14 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static + || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) { if (AudioStream != null) { return AudioStream.SampleRate; } } - else if (BaseRequest.AudioSampleRate.HasValue) { // Don't exceed what the encoder supports @@ -397,7 +397,8 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static + || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) { if (AudioStream != null) { @@ -405,13 +406,6 @@ namespace MediaBrowser.Controller.MediaEncoding } } - //else if (BaseRequest.AudioSampleRate.HasValue) - //{ - // // Don't exceed what the encoder supports - // // Seeing issues of attempting to encode to 88200 - // return Math.Min(44100, BaseRequest.AudioSampleRate.Value); - //} - return null; } } @@ -446,7 +440,8 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static + || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { return VideoStream?.BitDepth; } @@ -463,7 +458,8 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static + || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { return VideoStream?.RefFrames; } @@ -479,7 +475,8 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static + || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { return VideoStream == null ? null : (VideoStream.AverageFrameRate ?? VideoStream.RealFrameRate); } @@ -545,7 +542,8 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static + || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { return VideoStream?.CodecTag; } @@ -558,7 +556,8 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static + || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { return VideoStream?.IsAnamorphic; } @@ -571,14 +570,12 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - var codec = OutputVideoCodec; - - if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { return VideoStream?.Codec; } - return codec; + return OutputVideoCodec; } } @@ -586,14 +583,12 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - var codec = OutputAudioCodec; - - if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) { return AudioStream?.Codec; } - return codec; + return OutputAudioCodec; } } @@ -601,7 +596,8 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static + || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { return VideoStream?.IsInterlaced; } @@ -636,6 +632,7 @@ namespace MediaBrowser.Controller.MediaEncoding { return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); } + return GetMediaStreamCount(MediaStreamType.Video, 1); } } @@ -648,17 +645,12 @@ namespace MediaBrowser.Controller.MediaEncoding { return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); } + return GetMediaStreamCount(MediaStreamType.Audio, 1); } } - public int HlsListSize - { - get - { - return 0; - } - } + public int HlsListSize => 0; private int? GetMediaStreamCount(MediaStreamType type, int limit) { @@ -677,10 +669,6 @@ namespace MediaBrowser.Controller.MediaEncoding { Progress.Report(percentComplete.Value); } - - public virtual void Dispose() - { - } } /// diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index 2755bf5814..d0d5ebfd69 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Text; +using System.Threading.Tasks; using MediaBrowser.Model.Extensions; using Microsoft.Extensions.Logging; @@ -18,10 +19,11 @@ namespace MediaBrowser.Controller.MediaEncoding _logger = logger; } - public async void StartStreamingLog(EncodingJobInfo state, Stream source, Stream target) + public async Task StartStreamingLog(EncodingJobInfo state, Stream source, Stream target) { try { + using (target) using (var reader = new StreamReader(source)) { while (!reader.EndOfStream && reader.BaseStream.CanRead) @@ -97,8 +99,7 @@ namespace MediaBrowser.Controller.MediaEncoding { var currentMs = startMs + val.TotalMilliseconds; - var percentVal = currentMs / totalMs; - percent = 100 * percentVal; + percent = 100 * currentMs / totalMs; transcodingPosition = val; } diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index 901d81c5ff..e52951dd08 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -13,7 +13,8 @@ namespace MediaBrowser.Model.Dlna _profile = profile; } - public string BuildImageHeader(string container, + public string BuildImageHeader( + string container, int? width, int? height, bool isDirectStream, @@ -28,8 +29,7 @@ namespace MediaBrowser.Model.Dlna DlnaFlags.InteractiveTransferMode | DlnaFlags.DlnaV15; - string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}", - DlnaMaps.FlagsToString(flagValue)); + string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue)); ResponseProfile mediaProfile = _profile.GetImageMediaProfile(container, width, @@ -37,7 +37,7 @@ namespace MediaBrowser.Model.Dlna if (string.IsNullOrEmpty(orgPn)) { - orgPn = mediaProfile == null ? null : mediaProfile.OrgPn; + orgPn = mediaProfile?.OrgPn; } if (string.IsNullOrEmpty(orgPn)) @@ -50,7 +50,8 @@ namespace MediaBrowser.Model.Dlna return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); } - public string BuildAudioHeader(string container, + public string BuildAudioHeader( + string container, string audioCodec, int? audioBitrate, int? audioSampleRate, @@ -102,7 +103,8 @@ namespace MediaBrowser.Model.Dlna return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); } - public List BuildVideoHeader(string container, + public List BuildVideoHeader( + string container, string videoCodec, string audioCodec, int? width, @@ -206,7 +208,7 @@ namespace MediaBrowser.Model.Dlna return contentFeatureList; } - private string GetImageOrgPnValue(string container, int? width, int? height) + private static string GetImageOrgPnValue(string container, int? width, int? height) { MediaFormatProfile? format = new MediaFormatProfileResolver() .ResolveImageFormat(container, @@ -216,7 +218,7 @@ namespace MediaBrowser.Model.Dlna return format.HasValue ? format.Value.ToString() : null; } - private string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels) + private static string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels) { MediaFormatProfile? format = new MediaFormatProfileResolver() .ResolveAudioFormat(container, @@ -227,7 +229,7 @@ namespace MediaBrowser.Model.Dlna return format.HasValue ? format.Value.ToString() : null; } - private string[] GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp) + private static string[] GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp) { return new MediaFormatProfileResolver().ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp); }