Merge pull request #1159 from Bond-009/streamjob
Trying to make sense of the streaming code
This commit is contained in:
commit
f631b2ecdc
|
@ -415,7 +415,7 @@ namespace MediaBrowser.Api
|
||||||
public void OnTranscodeEndRequest(TranscodingJob job)
|
public void OnTranscodeEndRequest(TranscodingJob job)
|
||||||
{
|
{
|
||||||
job.ActiveRequestCount--;
|
job.ActiveRequestCount--;
|
||||||
//Logger.LogDebug("OnTranscodeEndRequest job.ActiveRequestCount={0}", job.ActiveRequestCount);
|
Logger.LogDebug("OnTranscodeEndRequest job.ActiveRequestCount={0}", job.ActiveRequestCount);
|
||||||
if (job.ActiveRequestCount <= 0)
|
if (job.ActiveRequestCount <= 0)
|
||||||
{
|
{
|
||||||
PingTimer(job, false);
|
PingTimer(job, false);
|
||||||
|
@ -428,7 +428,7 @@ namespace MediaBrowser.Api
|
||||||
throw new ArgumentNullException(nameof(playSessionId));
|
throw new ArgumentNullException(nameof(playSessionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Logger.LogDebug("PingTranscodingJob PlaySessionId={0} isUsedPaused: {1}", playSessionId, isUserPaused);
|
Logger.LogDebug("PingTranscodingJob PlaySessionId={0} isUsedPaused: {1}", playSessionId, isUserPaused);
|
||||||
|
|
||||||
List<TranscodingJob> jobs;
|
List<TranscodingJob> jobs;
|
||||||
|
|
||||||
|
@ -443,7 +443,7 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
if (isUserPaused.HasValue)
|
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;
|
job.IsUserPaused = isUserPaused.Value;
|
||||||
}
|
}
|
||||||
PingTimer(job, true);
|
PingTimer(job, true);
|
||||||
|
@ -601,7 +601,6 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Stopping ffmpeg process with q command for {Path}", job.Path);
|
Logger.LogInformation("Stopping ffmpeg process with q command for {Path}", job.Path);
|
||||||
|
|
||||||
//process.Kill();
|
|
||||||
process.StandardInput.WriteLine("q");
|
process.StandardInput.WriteLine("q");
|
||||||
|
|
||||||
// Need to wait because killing is asynchronous
|
// Need to wait because killing is asynchronous
|
||||||
|
@ -701,7 +700,7 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//Logger.LogDebug("Deleting HLS file {0}", file);
|
Logger.LogDebug("Deleting HLS file {0}", file);
|
||||||
_fileSystem.DeleteFile(file);
|
_fileSystem.DeleteFile(file);
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException)
|
catch (FileNotFoundException)
|
||||||
|
@ -840,12 +839,12 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
if (KillTimer == null)
|
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);
|
KillTimer = new Timer(new TimerCallback(callback), this, intervalMs, Timeout.Infinite);
|
||||||
}
|
}
|
||||||
else
|
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);
|
KillTimer.Change(intervalMs, Timeout.Infinite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -864,7 +863,7 @@ namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
var intervalMs = PingTimeout;
|
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);
|
KillTimer.Change(intervalMs, Timeout.Infinite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Devices;
|
using MediaBrowser.Controller.Devices;
|
||||||
using MediaBrowser.Controller.Dlna;
|
using MediaBrowser.Controller.Dlna;
|
||||||
|
@ -16,7 +15,6 @@ using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Diagnostics;
|
|
||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
@ -32,6 +30,8 @@ namespace MediaBrowser.Api.Playback
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseStreamingService : BaseApiService
|
public abstract class BaseStreamingService : BaseApiService
|
||||||
{
|
{
|
||||||
|
protected virtual bool EnableOutputInSubFolder => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the application paths.
|
/// Gets or sets the application paths.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -65,15 +65,25 @@ namespace MediaBrowser.Api.Playback
|
||||||
protected IFileSystem FileSystem { get; private set; }
|
protected IFileSystem FileSystem { get; private set; }
|
||||||
|
|
||||||
protected IDlnaManager DlnaManager { get; private set; }
|
protected IDlnaManager DlnaManager { get; private set; }
|
||||||
|
|
||||||
protected IDeviceManager DeviceManager { get; private set; }
|
protected IDeviceManager DeviceManager { get; private set; }
|
||||||
|
|
||||||
protected ISubtitleEncoder SubtitleEncoder { get; private set; }
|
protected ISubtitleEncoder SubtitleEncoder { get; private set; }
|
||||||
|
|
||||||
protected IMediaSourceManager MediaSourceManager { get; private set; }
|
protected IMediaSourceManager MediaSourceManager { get; private set; }
|
||||||
|
|
||||||
protected IJsonSerializer JsonSerializer { get; private set; }
|
protected IJsonSerializer JsonSerializer { get; private set; }
|
||||||
|
|
||||||
protected IAuthorizationContext AuthorizationContext { get; private set; }
|
protected IAuthorizationContext AuthorizationContext { get; private set; }
|
||||||
|
|
||||||
protected EncodingHelper EncodingHelper { get; set; }
|
protected EncodingHelper EncodingHelper { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the transcoding job.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The type of the transcoding job.</value>
|
||||||
|
protected abstract TranscodingJobType TranscodingJobType { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
|
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -112,12 +122,6 @@ namespace MediaBrowser.Api.Playback
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding);
|
protected abstract string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the type of the transcoding job.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The type of the transcoding job.</value>
|
|
||||||
protected abstract TranscodingJobType TranscodingJobType { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the output file extension.
|
/// Gets the output file extension.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -133,31 +137,24 @@ namespace MediaBrowser.Api.Playback
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string GetOutputFilePath(StreamState state, EncodingOptions encodingOptions, string outputFileExtension)
|
private string GetOutputFilePath(StreamState state, EncodingOptions encodingOptions, string outputFileExtension)
|
||||||
{
|
{
|
||||||
var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
|
|
||||||
|
|
||||||
var data = GetCommandLineArguments("dummy\\dummy", encodingOptions, state, false);
|
var data = GetCommandLineArguments("dummy\\dummy", encodingOptions, state, false);
|
||||||
|
|
||||||
data += "-" + (state.Request.DeviceId ?? string.Empty);
|
data += "-" + (state.Request.DeviceId ?? string.Empty)
|
||||||
data += "-" + (state.Request.PlaySessionId ?? string.Empty);
|
+ "-" + (state.Request.PlaySessionId ?? string.Empty);
|
||||||
|
|
||||||
var dataHash = data.GetMD5().ToString("N");
|
var filename = data.GetMD5().ToString("N");
|
||||||
|
var ext = outputFileExtension.ToLowerInvariant();
|
||||||
|
var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
|
||||||
|
|
||||||
if (EnableOutputInSubFolder)
|
if (EnableOutputInSubFolder)
|
||||||
{
|
{
|
||||||
return Path.Combine(folder, dataHash, dataHash + (outputFileExtension ?? string.Empty).ToLowerInvariant());
|
return Path.Combine(folder, filename, filename + ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Path.Combine(folder, dataHash + (outputFileExtension ?? string.Empty).ToLowerInvariant());
|
return Path.Combine(folder, filename + ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual bool EnableOutputInSubFolder => false;
|
protected virtual string GetDefaultH264Preset() => "superfast";
|
||||||
|
|
||||||
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
|
||||||
|
|
||||||
protected virtual string GetDefaultH264Preset()
|
|
||||||
{
|
|
||||||
return "superfast";
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource)
|
private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource)
|
||||||
{
|
{
|
||||||
|
@ -171,7 +168,6 @@ namespace MediaBrowser.Api.Playback
|
||||||
var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
|
var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
|
||||||
{
|
{
|
||||||
OpenToken = state.MediaSource.OpenToken
|
OpenToken = state.MediaSource.OpenToken
|
||||||
|
|
||||||
}, cancellationTokenSource.Token).ConfigureAwait(false);
|
}, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
|
|
||||||
EncodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.RequestedUrl);
|
EncodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, state.RequestedUrl);
|
||||||
|
@ -209,22 +205,16 @@ namespace MediaBrowser.Api.Playback
|
||||||
if (state.VideoRequest != null && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
if (state.VideoRequest != null && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
|
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 encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
|
||||||
|
|
||||||
var transcodingId = Guid.NewGuid().ToString("N");
|
|
||||||
var commandLineArgs = GetCommandLineArguments(outputPath, encodingOptions, state, true);
|
|
||||||
|
|
||||||
var process = new Process()
|
var process = new Process()
|
||||||
{
|
{
|
||||||
StartInfo = new ProcessStartInfo()
|
StartInfo = new ProcessStartInfo()
|
||||||
|
@ -239,7 +229,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
RedirectStandardInput = true,
|
RedirectStandardInput = true,
|
||||||
|
|
||||||
FileName = MediaEncoder.EncoderPath,
|
FileName = MediaEncoder.EncoderPath,
|
||||||
Arguments = commandLineArgs,
|
Arguments = GetCommandLineArguments(outputPath, encodingOptions, state, true),
|
||||||
WorkingDirectory = string.IsNullOrWhiteSpace(workingDirectory) ? null : workingDirectory,
|
WorkingDirectory = string.IsNullOrWhiteSpace(workingDirectory) ? null : workingDirectory,
|
||||||
|
|
||||||
ErrorDialog = false
|
ErrorDialog = false
|
||||||
|
@ -250,7 +240,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
|
var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
|
||||||
state.Request.PlaySessionId,
|
state.Request.PlaySessionId,
|
||||||
state.MediaSource.LiveStreamId,
|
state.MediaSource.LiveStreamId,
|
||||||
transcodingId,
|
Guid.NewGuid().ToString("N"),
|
||||||
TranscodingJobType,
|
TranscodingJobType,
|
||||||
process,
|
process,
|
||||||
state.Request.DeviceId,
|
state.Request.DeviceId,
|
||||||
|
@ -261,27 +251,26 @@ namespace MediaBrowser.Api.Playback
|
||||||
Logger.LogInformation(commandLineLogMessage);
|
Logger.LogInformation(commandLineLogMessage);
|
||||||
|
|
||||||
var logFilePrefix = "ffmpeg-transcode";
|
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)
|
if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
&& string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
logFilePrefix = "ffmpeg-directstream";
|
logFilePrefix = "ffmpeg-directstream";
|
||||||
}
|
}
|
||||||
else if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
else
|
||||||
{
|
{
|
||||||
logFilePrefix = "ffmpeg-remux";
|
logFilePrefix = "ffmpeg-remux";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, logFilePrefix + "-" + Guid.NewGuid() + ".txt");
|
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.
|
// 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);
|
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);
|
process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state);
|
||||||
|
|
||||||
|
@ -298,13 +287,10 @@ namespace MediaBrowser.Api.Playback
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
|
|
||||||
//process.BeginOutputReadLine();
|
|
||||||
|
|
||||||
state.TranscodingJob = transcodingJob;
|
state.TranscodingJob = transcodingJob;
|
||||||
|
|
||||||
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
|
// 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
|
// Wait for the file to exist before proceeeding
|
||||||
while (!File.Exists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
|
while (!File.Exists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
|
||||||
|
@ -368,25 +354,16 @@ namespace MediaBrowser.Api.Playback
|
||||||
Logger.LogDebug("Disposing stream resources");
|
Logger.LogDebug("Disposing stream resources");
|
||||||
state.Dispose();
|
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:
|
process.Dispose();
|
||||||
//try
|
|
||||||
//{
|
|
||||||
// // Dispose the process
|
|
||||||
// process.Dispose();
|
|
||||||
//}
|
|
||||||
//catch (Exception ex)
|
|
||||||
//{
|
|
||||||
// Logger.LogError(ex, "Error disposing ffmpeg.");
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -439,55 +416,55 @@ namespace MediaBrowser.Api.Playback
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.AudioStreamIndex = int.Parse(val, UsCulture);
|
videoRequest.AudioStreamIndex = int.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 7)
|
else if (i == 7)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture);
|
videoRequest.SubtitleStreamIndex = int.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 8)
|
else if (i == 8)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.VideoBitRate = int.Parse(val, UsCulture);
|
videoRequest.VideoBitRate = int.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 9)
|
else if (i == 9)
|
||||||
{
|
{
|
||||||
request.AudioBitRate = int.Parse(val, UsCulture);
|
request.AudioBitRate = int.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
else if (i == 10)
|
else if (i == 10)
|
||||||
{
|
{
|
||||||
request.MaxAudioChannels = int.Parse(val, UsCulture);
|
request.MaxAudioChannels = int.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
else if (i == 11)
|
else if (i == 11)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.MaxFramerate = float.Parse(val, UsCulture);
|
videoRequest.MaxFramerate = float.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 12)
|
else if (i == 12)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.MaxWidth = int.Parse(val, UsCulture);
|
videoRequest.MaxWidth = int.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 13)
|
else if (i == 13)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.MaxHeight = int.Parse(val, UsCulture);
|
videoRequest.MaxHeight = int.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 14)
|
else if (i == 14)
|
||||||
{
|
{
|
||||||
request.StartTimeTicks = long.Parse(val, UsCulture);
|
request.StartTimeTicks = long.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
else if (i == 15)
|
else if (i == 15)
|
||||||
{
|
{
|
||||||
|
@ -500,14 +477,14 @@ namespace MediaBrowser.Api.Playback
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.MaxRefFrames = int.Parse(val, UsCulture);
|
videoRequest.MaxRefFrames = int.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 17)
|
else if (i == 17)
|
||||||
{
|
{
|
||||||
if (videoRequest != null)
|
if (videoRequest != null)
|
||||||
{
|
{
|
||||||
videoRequest.MaxVideoBitDepth = int.Parse(val, UsCulture);
|
videoRequest.MaxVideoBitDepth = int.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (i == 18)
|
else if (i == 18)
|
||||||
|
@ -556,7 +533,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
}
|
}
|
||||||
else if (i == 26)
|
else if (i == 26)
|
||||||
{
|
{
|
||||||
request.TranscodingMaxAudioChannels = int.Parse(val, UsCulture);
|
request.TranscodingMaxAudioChannels = int.Parse(val, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
else if (i == 27)
|
else if (i == 27)
|
||||||
{
|
{
|
||||||
|
@ -643,16 +620,25 @@ namespace MediaBrowser.Api.Playback
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.IndexOf("npt=", StringComparison.OrdinalIgnoreCase) != 0)
|
const string Npt = "npt=";
|
||||||
|
if (!value.StartsWith(Npt, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Invalid timeseek header");
|
throw new ArgumentException("Invalid timeseek header");
|
||||||
}
|
}
|
||||||
value = value.Substring(4).Split(new[] { '-' }, 2)[0];
|
int index = value.IndexOf('-');
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
value = value.Substring(Npt.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = value.Substring(Npt.Length, index);
|
||||||
|
}
|
||||||
|
|
||||||
if (value.IndexOf(':') == -1)
|
if (value.IndexOf(':') == -1)
|
||||||
{
|
{
|
||||||
// Parses npt times in the format of '417.33'
|
// Parses npt times in the format of '417.33'
|
||||||
if (double.TryParse(value, NumberStyles.Any, UsCulture, out var seconds))
|
if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var seconds))
|
||||||
{
|
{
|
||||||
return TimeSpan.FromSeconds(seconds).Ticks;
|
return TimeSpan.FromSeconds(seconds).Ticks;
|
||||||
}
|
}
|
||||||
|
@ -667,7 +653,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
|
|
||||||
foreach (var time in tokens)
|
foreach (var time in tokens)
|
||||||
{
|
{
|
||||||
if (double.TryParse(time, NumberStyles.Any, UsCulture, out var digit))
|
if (double.TryParse(time, NumberStyles.Any, CultureInfo.InvariantCulture, out var digit))
|
||||||
{
|
{
|
||||||
secondsSum += digit * timeFactor;
|
secondsSum += digit * timeFactor;
|
||||||
}
|
}
|
||||||
|
@ -707,7 +693,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) /*||
|
var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) /*||
|
||||||
string.Equals(Request.Headers.Get("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase)*/;
|
string.Equals(Request.Headers.Get("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase)*/;
|
||||||
|
|
||||||
var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType)
|
var state = new StreamState(MediaSourceManager, TranscodingJobType)
|
||||||
{
|
{
|
||||||
Request = request,
|
Request = request,
|
||||||
RequestedUrl = url,
|
RequestedUrl = url,
|
||||||
|
@ -728,13 +714,10 @@ namespace MediaBrowser.Api.Playback
|
||||||
// state.SegmentLength = 6;
|
// 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))
|
if (!string.IsNullOrWhiteSpace(request.AudioCodec))
|
||||||
|
@ -779,12 +762,12 @@ namespace MediaBrowser.Api.Playback
|
||||||
var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false)).ToList();
|
var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false)).ToList();
|
||||||
|
|
||||||
mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
|
mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
|
||||||
? mediaSources.First()
|
? mediaSources[0]
|
||||||
: mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));
|
: mediaSources.Find(i => string.Equals(i.Id, request.MediaSourceId));
|
||||||
|
|
||||||
if (mediaSource == null && request.MediaSourceId.Equals(request.Id))
|
if (mediaSource == null && request.MediaSourceId.Equals(request.Id))
|
||||||
{
|
{
|
||||||
mediaSource = mediaSources.First();
|
mediaSource = mediaSources[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -834,11 +817,11 @@ namespace MediaBrowser.Api.Playback
|
||||||
if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var resolution = ResolutionNormalizer.Normalize(
|
var resolution = ResolutionNormalizer.Normalize(
|
||||||
state.VideoStream == null ? (int?)null : state.VideoStream.BitRate,
|
state.VideoStream?.BitRate,
|
||||||
state.VideoStream == null ? (int?)null : state.VideoStream.Width,
|
state.VideoStream?.Width,
|
||||||
state.VideoStream == null ? (int?)null : state.VideoStream.Height,
|
state.VideoStream?.Height,
|
||||||
state.OutputVideoBitrate.Value,
|
state.OutputVideoBitrate.Value,
|
||||||
state.VideoStream == null ? null : state.VideoStream.Codec,
|
state.VideoStream?.Codec,
|
||||||
state.OutputVideoCodec,
|
state.OutputVideoCodec,
|
||||||
videoRequest.MaxWidth,
|
videoRequest.MaxWidth,
|
||||||
videoRequest.MaxHeight);
|
videoRequest.MaxHeight);
|
||||||
|
@ -846,17 +829,13 @@ namespace MediaBrowser.Api.Playback
|
||||||
videoRequest.MaxWidth = resolution.MaxWidth;
|
videoRequest.MaxWidth = resolution.MaxWidth;
|
||||||
videoRequest.MaxHeight = resolution.MaxHeight;
|
videoRequest.MaxHeight = resolution.MaxHeight;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ApplyDeviceProfileSettings(state);
|
ApplyDeviceProfileSettings(state);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ApplyDeviceProfileSettings(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
|
var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
|
||||||
? GetOutputFileExtension(state)
|
? GetOutputFileExtension(state)
|
||||||
: ("." + state.OutputContainer);
|
: ('.' + state.OutputContainer);
|
||||||
|
|
||||||
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
|
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
|
||||||
|
|
||||||
|
@ -970,18 +949,18 @@ namespace MediaBrowser.Api.Playback
|
||||||
responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode;
|
responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode;
|
||||||
responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*";
|
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;
|
var ms = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds;
|
||||||
responseHeaders["MediaInfo.sec"] = string.Format("SEC_Duration={0};", Convert.ToInt32(ms).ToString(CultureInfo.InvariantCulture));
|
responseHeaders["MediaInfo.sec"] = string.Format("SEC_Duration={0};", Convert.ToInt32(ms).ToString(CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (state.RunTimeTicks.HasValue && !isStaticallyStreamed && profile != null)
|
if (!isStaticallyStreamed && profile != null)
|
||||||
{
|
{
|
||||||
AddTimeSeekResponseHeaders(state, responseHeaders);
|
AddTimeSeekResponseHeaders(state, responseHeaders);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile == null)
|
if (profile == null)
|
||||||
|
@ -1046,8 +1025,8 @@ namespace MediaBrowser.Api.Playback
|
||||||
|
|
||||||
private void AddTimeSeekResponseHeaders(StreamState state, IDictionary<string, string> responseHeaders)
|
private void AddTimeSeekResponseHeaders(StreamState state, IDictionary<string, string> responseHeaders)
|
||||||
{
|
{
|
||||||
var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds.ToString(UsCulture);
|
var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds.ToString(CultureInfo.InvariantCulture);
|
||||||
var startSeconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds.ToString(UsCulture);
|
var startSeconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds.ToString(CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
responseHeaders["TimeSeekRange.dlna.org"] = string.Format("npt={0}-{1}/{1}", startSeconds, runtimeSeconds);
|
responseHeaders["TimeSeekRange.dlna.org"] = string.Format("npt={0}-{1}/{1}", startSeconds, runtimeSeconds);
|
||||||
responseHeaders["X-AvailableSeekRange"] = string.Format("1 npt={0}-{1}", startSeconds, runtimeSeconds);
|
responseHeaders["X-AvailableSeekRange"] = string.Format("1 npt={0}-{1}", startSeconds, runtimeSeconds);
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Net;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback
|
namespace MediaBrowser.Api.Playback
|
||||||
{
|
{
|
||||||
public class StreamState : EncodingJobInfo, IDisposable
|
public class StreamState : EncodingJobInfo, IDisposable
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
|
||||||
private readonly IMediaSourceManager _mediaSourceManager;
|
private readonly IMediaSourceManager _mediaSourceManager;
|
||||||
|
private bool _disposed = false;
|
||||||
|
|
||||||
public string RequestedUrl { get; set; }
|
public string RequestedUrl { get; set; }
|
||||||
|
|
||||||
|
@ -30,11 +27,6 @@ namespace MediaBrowser.Api.Playback
|
||||||
|
|
||||||
public VideoStreamRequest VideoRequest => Request as VideoStreamRequest;
|
public VideoStreamRequest VideoRequest => Request as VideoStreamRequest;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the log file stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The log file stream.</value>
|
|
||||||
public Stream LogFileStream { get; set; }
|
|
||||||
public IDirectStreamProvider DirectStreamProvider { get; set; }
|
public IDirectStreamProvider DirectStreamProvider { get; set; }
|
||||||
|
|
||||||
public string WaitForPath { get; set; }
|
public string WaitForPath { get; set; }
|
||||||
|
@ -72,6 +64,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
{
|
{
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,82 +87,57 @@ namespace MediaBrowser.Api.Playback
|
||||||
|
|
||||||
public string UserAgent { get; set; }
|
public string UserAgent { get; set; }
|
||||||
|
|
||||||
public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger, TranscodingJobType transcodingType)
|
|
||||||
: base(transcodingType)
|
|
||||||
{
|
|
||||||
_mediaSourceManager = mediaSourceManager;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool EstimateContentLength { get; set; }
|
public bool EstimateContentLength { get; set; }
|
||||||
|
|
||||||
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
|
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
|
||||||
|
|
||||||
public bool EnableDlnaHeaders { 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 DeviceProfile DeviceProfile { get; set; }
|
||||||
|
|
||||||
public TranscodingJob TranscodingJob;
|
public TranscodingJob TranscodingJob { get; set; }
|
||||||
|
|
||||||
|
public StreamState(IMediaSourceManager mediaSourceManager, TranscodingJobType transcodingType)
|
||||||
|
: base(transcodingType)
|
||||||
|
{
|
||||||
|
_mediaSourceManager = mediaSourceManager;
|
||||||
|
}
|
||||||
|
|
||||||
public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate)
|
public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate)
|
||||||
{
|
{
|
||||||
ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -374,14 +374,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
if (BaseRequest.Static
|
||||||
|
|| string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (AudioStream != null)
|
if (AudioStream != null)
|
||||||
{
|
{
|
||||||
return AudioStream.SampleRate;
|
return AudioStream.SampleRate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (BaseRequest.AudioSampleRate.HasValue)
|
else if (BaseRequest.AudioSampleRate.HasValue)
|
||||||
{
|
{
|
||||||
// Don't exceed what the encoder supports
|
// Don't exceed what the encoder supports
|
||||||
|
@ -397,7 +397,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
if (BaseRequest.Static
|
||||||
|
|| string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (AudioStream != null)
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -446,7 +440,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
if (BaseRequest.Static
|
||||||
|
|| string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return VideoStream?.BitDepth;
|
return VideoStream?.BitDepth;
|
||||||
}
|
}
|
||||||
|
@ -463,7 +458,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
if (BaseRequest.Static
|
||||||
|
|| string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return VideoStream?.RefFrames;
|
return VideoStream?.RefFrames;
|
||||||
}
|
}
|
||||||
|
@ -479,7 +475,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
get
|
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);
|
return VideoStream == null ? null : (VideoStream.AverageFrameRate ?? VideoStream.RealFrameRate);
|
||||||
}
|
}
|
||||||
|
@ -545,7 +542,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
if (BaseRequest.Static
|
||||||
|
|| string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return VideoStream?.CodecTag;
|
return VideoStream?.CodecTag;
|
||||||
}
|
}
|
||||||
|
@ -558,7 +556,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
if (BaseRequest.Static
|
||||||
|
|| string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return VideoStream?.IsAnamorphic;
|
return VideoStream?.IsAnamorphic;
|
||||||
}
|
}
|
||||||
|
@ -571,14 +570,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var codec = OutputVideoCodec;
|
if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
||||||
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
return VideoStream?.Codec;
|
return VideoStream?.Codec;
|
||||||
}
|
}
|
||||||
|
|
||||||
return codec;
|
return OutputVideoCodec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,14 +583,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var codec = OutputAudioCodec;
|
if (string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
||||||
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
return AudioStream?.Codec;
|
return AudioStream?.Codec;
|
||||||
}
|
}
|
||||||
|
|
||||||
return codec;
|
return OutputAudioCodec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,7 +596,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
if (BaseRequest.Static
|
||||||
|
|| string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return VideoStream?.IsInterlaced;
|
return VideoStream?.IsInterlaced;
|
||||||
}
|
}
|
||||||
|
@ -636,6 +632,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
|
return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetMediaStreamCount(MediaStreamType.Video, 1);
|
return GetMediaStreamCount(MediaStreamType.Video, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -648,17 +645,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
|
return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetMediaStreamCount(MediaStreamType.Audio, 1);
|
return GetMediaStreamCount(MediaStreamType.Audio, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int HlsListSize
|
public int HlsListSize => 0;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int? GetMediaStreamCount(MediaStreamType type, int limit)
|
private int? GetMediaStreamCount(MediaStreamType type, int limit)
|
||||||
{
|
{
|
||||||
|
@ -677,10 +669,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
Progress.Report(percentComplete.Value);
|
Progress.Report(percentComplete.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Model.Extensions;
|
using MediaBrowser.Model.Extensions;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
@ -18,10 +19,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void StartStreamingLog(EncodingJobInfo state, Stream source, Stream target)
|
public async Task StartStreamingLog(EncodingJobInfo state, Stream source, Stream target)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
using (target)
|
||||||
using (var reader = new StreamReader(source))
|
using (var reader = new StreamReader(source))
|
||||||
{
|
{
|
||||||
while (!reader.EndOfStream && reader.BaseStream.CanRead)
|
while (!reader.EndOfStream && reader.BaseStream.CanRead)
|
||||||
|
@ -97,8 +99,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
var currentMs = startMs + val.TotalMilliseconds;
|
var currentMs = startMs + val.TotalMilliseconds;
|
||||||
|
|
||||||
var percentVal = currentMs / totalMs;
|
percent = 100.0 * currentMs / totalMs;
|
||||||
percent = 100 * percentVal;
|
|
||||||
|
|
||||||
transcodingPosition = val;
|
transcodingPosition = val;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,8 @@ namespace MediaBrowser.Model.Dlna
|
||||||
_profile = profile;
|
_profile = profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BuildImageHeader(string container,
|
public string BuildImageHeader(
|
||||||
|
string container,
|
||||||
int? width,
|
int? width,
|
||||||
int? height,
|
int? height,
|
||||||
bool isDirectStream,
|
bool isDirectStream,
|
||||||
|
@ -28,8 +29,7 @@ namespace MediaBrowser.Model.Dlna
|
||||||
DlnaFlags.InteractiveTransferMode |
|
DlnaFlags.InteractiveTransferMode |
|
||||||
DlnaFlags.DlnaV15;
|
DlnaFlags.DlnaV15;
|
||||||
|
|
||||||
string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}",
|
string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue));
|
||||||
DlnaMaps.FlagsToString(flagValue));
|
|
||||||
|
|
||||||
ResponseProfile mediaProfile = _profile.GetImageMediaProfile(container,
|
ResponseProfile mediaProfile = _profile.GetImageMediaProfile(container,
|
||||||
width,
|
width,
|
||||||
|
@ -37,7 +37,7 @@ namespace MediaBrowser.Model.Dlna
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(orgPn))
|
if (string.IsNullOrEmpty(orgPn))
|
||||||
{
|
{
|
||||||
orgPn = mediaProfile == null ? null : mediaProfile.OrgPn;
|
orgPn = mediaProfile?.OrgPn;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(orgPn))
|
if (string.IsNullOrEmpty(orgPn))
|
||||||
|
@ -50,7 +50,8 @@ namespace MediaBrowser.Model.Dlna
|
||||||
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
|
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BuildAudioHeader(string container,
|
public string BuildAudioHeader(
|
||||||
|
string container,
|
||||||
string audioCodec,
|
string audioCodec,
|
||||||
int? audioBitrate,
|
int? audioBitrate,
|
||||||
int? audioSampleRate,
|
int? audioSampleRate,
|
||||||
|
@ -102,7 +103,8 @@ namespace MediaBrowser.Model.Dlna
|
||||||
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
|
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> BuildVideoHeader(string container,
|
public List<string> BuildVideoHeader(
|
||||||
|
string container,
|
||||||
string videoCodec,
|
string videoCodec,
|
||||||
string audioCodec,
|
string audioCodec,
|
||||||
int? width,
|
int? width,
|
||||||
|
@ -206,7 +208,7 @@ namespace MediaBrowser.Model.Dlna
|
||||||
return contentFeatureList;
|
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()
|
MediaFormatProfile? format = new MediaFormatProfileResolver()
|
||||||
.ResolveImageFormat(container,
|
.ResolveImageFormat(container,
|
||||||
|
@ -216,7 +218,7 @@ namespace MediaBrowser.Model.Dlna
|
||||||
return format.HasValue ? format.Value.ToString() : null;
|
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()
|
MediaFormatProfile? format = new MediaFormatProfileResolver()
|
||||||
.ResolveAudioFormat(container,
|
.ResolveAudioFormat(container,
|
||||||
|
@ -227,7 +229,7 @@ namespace MediaBrowser.Model.Dlna
|
||||||
return format.HasValue ? format.Value.ToString() : null;
|
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);
|
return new MediaFormatProfileResolver().ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user