Merge pull request #10758 from barronpm/transcode-manager
Add ITranscodeManager
This commit is contained in:
commit
98177b8649
|
@ -76,6 +76,7 @@ using MediaBrowser.Controller.TV;
|
|||
using MediaBrowser.LocalMetadata.Savers;
|
||||
using MediaBrowser.MediaEncoding.BdInfo;
|
||||
using MediaBrowser.MediaEncoding.Subtitles;
|
||||
using MediaBrowser.MediaEncoding.Transcoding;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
@ -583,7 +584,7 @@ namespace Emby.Server.Implementations
|
|||
|
||||
serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
|
||||
|
||||
serviceCollection.AddSingleton<TranscodingJobHelper>();
|
||||
serviceCollection.AddSingleton<ITranscodeManager, TranscodeManager>();
|
||||
serviceCollection.AddScoped<MediaInfoHelper>();
|
||||
serviceCollection.AddScoped<AudioHelper>();
|
||||
serviceCollection.AddScoped<DynamicHlsHelper>();
|
||||
|
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
|||
using Jellyfin.Api.Helpers;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
|
@ -6,6 +6,7 @@ using Jellyfin.Api.Attributes;
|
|||
using Jellyfin.Api.Helpers;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
|
@ -9,8 +9,8 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Attributes;
|
||||
using Jellyfin.Api.Extensions;
|
||||
using Jellyfin.Api.Helpers;
|
||||
using Jellyfin.Api.Models.PlaybackDtos;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
|
@ -19,6 +19,7 @@ using MediaBrowser.Common.Configuration;
|
|||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.MediaEncoding.Encoder;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
@ -51,7 +52,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly TranscodingJobHelper _transcodingJobHelper;
|
||||
private readonly ITranscodeManager _transcodeManager;
|
||||
private readonly ILogger<DynamicHlsController> _logger;
|
||||
private readonly EncodingHelper _encodingHelper;
|
||||
private readonly IDynamicHlsPlaylistGenerator _dynamicHlsPlaylistGenerator;
|
||||
|
@ -67,7 +68,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
|
||||
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
||||
/// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
|
||||
/// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger{DynamicHlsController}"/> interface.</param>
|
||||
/// <param name="dynamicHlsHelper">Instance of <see cref="DynamicHlsHelper"/>.</param>
|
||||
/// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
|
||||
|
@ -79,7 +80,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
IServerConfigurationManager serverConfigurationManager,
|
||||
IMediaEncoder mediaEncoder,
|
||||
IFileSystem fileSystem,
|
||||
TranscodingJobHelper transcodingJobHelper,
|
||||
ITranscodeManager transcodeManager,
|
||||
ILogger<DynamicHlsController> logger,
|
||||
DynamicHlsHelper dynamicHlsHelper,
|
||||
EncodingHelper encodingHelper,
|
||||
|
@ -91,7 +92,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_fileSystem = fileSystem;
|
||||
_transcodingJobHelper = transcodingJobHelper;
|
||||
_transcodeManager = transcodeManager;
|
||||
_logger = logger;
|
||||
_dynamicHlsHelper = dynamicHlsHelper;
|
||||
_encodingHelper = encodingHelper;
|
||||
|
@ -283,17 +284,17 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
_serverConfigurationManager,
|
||||
_mediaEncoder,
|
||||
_encodingHelper,
|
||||
_transcodingJobHelper,
|
||||
_transcodeManager,
|
||||
TranscodingJobType,
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
TranscodingJobDto? job = null;
|
||||
TranscodingJob? job = null;
|
||||
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
|
||||
|
||||
if (!System.IO.File.Exists(playlistPath))
|
||||
{
|
||||
var transcodingLock = _transcodingJobHelper.GetTranscodingLock(playlistPath);
|
||||
var transcodingLock = _transcodeManager.GetTranscodingLock(playlistPath);
|
||||
await transcodingLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
|
@ -302,11 +303,11 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
// If the playlist doesn't already exist, startup ffmpeg
|
||||
try
|
||||
{
|
||||
job = await _transcodingJobHelper.StartFfMpeg(
|
||||
job = await _transcodeManager.StartFfMpeg(
|
||||
state,
|
||||
playlistPath,
|
||||
GetCommandLineArguments(playlistPath, state, true, 0),
|
||||
Request,
|
||||
Request.HttpContext.User.GetUserId(),
|
||||
TranscodingJobType,
|
||||
cancellationTokenSource)
|
||||
.ConfigureAwait(false);
|
||||
|
@ -331,11 +332,11 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
}
|
||||
}
|
||||
|
||||
job ??= _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
job ??= _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
|
||||
if (job is not null)
|
||||
{
|
||||
_transcodingJobHelper.OnTranscodeEndRequest(job);
|
||||
_transcodeManager.OnTranscodeEndRequest(job);
|
||||
}
|
||||
|
||||
var playlistText = HlsHelpers.GetLivePlaylistText(playlistPath, state);
|
||||
|
@ -1383,7 +1384,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
_serverConfigurationManager,
|
||||
_mediaEncoder,
|
||||
_encodingHelper,
|
||||
_transcodingJobHelper,
|
||||
_transcodeManager,
|
||||
TranscodingJobType,
|
||||
cancellationTokenSource.Token)
|
||||
.ConfigureAwait(false);
|
||||
|
@ -1421,7 +1422,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
_serverConfigurationManager,
|
||||
_mediaEncoder,
|
||||
_encodingHelper,
|
||||
_transcodingJobHelper,
|
||||
_transcodeManager,
|
||||
TranscodingJobType,
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
@ -1432,16 +1433,16 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
|
||||
var segmentExtension = EncodingHelper.GetSegmentFileExtension(state.Request.SegmentContainer);
|
||||
|
||||
TranscodingJobDto? job;
|
||||
TranscodingJob? job;
|
||||
|
||||
if (System.IO.File.Exists(segmentPath))
|
||||
{
|
||||
job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
_logger.LogDebug("returning {0} [it exists, try 1]", segmentPath);
|
||||
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var transcodingLock = _transcodingJobHelper.GetTranscodingLock(playlistPath);
|
||||
var transcodingLock = _transcodeManager.GetTranscodingLock(playlistPath);
|
||||
await transcodingLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
var released = false;
|
||||
var startTranscoding = false;
|
||||
|
@ -1450,7 +1451,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
{
|
||||
if (System.IO.File.Exists(segmentPath))
|
||||
{
|
||||
job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
transcodingLock.Release();
|
||||
released = true;
|
||||
_logger.LogDebug("returning {0} [it exists, try 2]", segmentPath);
|
||||
|
@ -1488,7 +1489,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
// If the playlist doesn't already exist, startup ffmpeg
|
||||
try
|
||||
{
|
||||
await _transcodingJobHelper.KillTranscodingJobs(streamingRequest.DeviceId, streamingRequest.PlaySessionId, p => false)
|
||||
await _transcodeManager.KillTranscodingJobs(streamingRequest.DeviceId, streamingRequest.PlaySessionId, p => false)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (currentTranscodingIndex.HasValue)
|
||||
|
@ -1499,11 +1500,11 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
streamingRequest.StartTimeTicks = streamingRequest.CurrentRuntimeTicks;
|
||||
|
||||
state.WaitForPath = segmentPath;
|
||||
job = await _transcodingJobHelper.StartFfMpeg(
|
||||
job = await _transcodeManager.StartFfMpeg(
|
||||
state,
|
||||
playlistPath,
|
||||
GetCommandLineArguments(playlistPath, state, false, segmentId),
|
||||
Request,
|
||||
Request.HttpContext.User.GetUserId(),
|
||||
TranscodingJobType,
|
||||
cancellationTokenSource).ConfigureAwait(false);
|
||||
}
|
||||
|
@ -1517,7 +1518,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
}
|
||||
else
|
||||
{
|
||||
job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
if (job?.TranscodingThrottler is not null)
|
||||
{
|
||||
await job.TranscodingThrottler.UnpauseTranscoding().ConfigureAwait(false);
|
||||
|
@ -1534,7 +1535,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
}
|
||||
|
||||
_logger.LogDebug("returning {0} [general case]", segmentPath);
|
||||
job ??= _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
job ??= _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
@ -1922,7 +1923,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
string segmentPath,
|
||||
string segmentExtension,
|
||||
int segmentIndex,
|
||||
TranscodingJobDto? transcodingJob,
|
||||
TranscodingJob? transcodingJob,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var segmentExists = System.IO.File.Exists(segmentPath);
|
||||
|
@ -1991,7 +1992,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
return GetSegmentResult(state, segmentPath, transcodingJob);
|
||||
}
|
||||
|
||||
private ActionResult GetSegmentResult(StreamState state, string segmentPath, TranscodingJobDto? transcodingJob)
|
||||
private ActionResult GetSegmentResult(StreamState state, string segmentPath, TranscodingJob? transcodingJob)
|
||||
{
|
||||
var segmentEndingPositionTicks = state.Request.CurrentRuntimeTicks + state.Request.ActualSegmentLengthTicks;
|
||||
|
||||
|
@ -2001,7 +2002,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
if (transcodingJob is not null)
|
||||
{
|
||||
transcodingJob.DownloadPositionTicks = Math.Max(transcodingJob.DownloadPositionTicks ?? segmentEndingPositionTicks, segmentEndingPositionTicks);
|
||||
_transcodingJobHelper.OnTranscodeEndRequest(transcodingJob);
|
||||
_transcodeManager.OnTranscodeEndRequest(transcodingJob);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
@ -2012,7 +2013,7 @@ public class DynamicHlsController : BaseJellyfinApiController
|
|||
|
||||
private int? GetCurrentTranscodingIndex(string playlist, string segmentExtension)
|
||||
{
|
||||
var job = _transcodingJobHelper.GetTranscodingJob(playlist, TranscodingJobType);
|
||||
var job = _transcodeManager.GetTranscodingJob(playlist, TranscodingJobType);
|
||||
|
||||
if (job is null || job.HasExited)
|
||||
{
|
||||
|
|
|
@ -24,22 +24,22 @@ public class HlsSegmentController : BaseJellyfinApiController
|
|||
{
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly TranscodingJobHelper _transcodingJobHelper;
|
||||
private readonly ITranscodeManager _transcodeManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HlsSegmentController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
||||
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||
/// <param name="transcodingJobHelper">Initialized instance of the <see cref="TranscodingJobHelper"/>.</param>
|
||||
/// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
|
||||
public HlsSegmentController(
|
||||
IFileSystem fileSystem,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
TranscodingJobHelper transcodingJobHelper)
|
||||
ITranscodeManager transcodeManager)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_transcodingJobHelper = transcodingJobHelper;
|
||||
_transcodeManager = transcodeManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -112,7 +112,7 @@ public class HlsSegmentController : BaseJellyfinApiController
|
|||
[FromQuery, Required] string deviceId,
|
||||
[FromQuery, Required] string playSessionId)
|
||||
{
|
||||
_transcodingJobHelper.KillTranscodingJobs(deviceId, playSessionId, path => true);
|
||||
_transcodeManager.KillTranscodingJobs(deviceId, playSessionId, _ => true);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
|
@ -174,13 +174,13 @@ public class HlsSegmentController : BaseJellyfinApiController
|
|||
|
||||
private ActionResult GetFileResult(string path, string playlistPath)
|
||||
{
|
||||
var transcodingJob = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
|
||||
var transcodingJob = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
|
||||
|
||||
Response.OnCompleted(() =>
|
||||
{
|
||||
if (transcodingJob is not null)
|
||||
{
|
||||
_transcodingJobHelper.OnTranscodeEndRequest(transcodingJob);
|
||||
_transcodeManager.OnTranscodeEndRequest(transcodingJob);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
|
@ -24,6 +24,8 @@ using MediaBrowser.Controller.Entities;
|
|||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
|
@ -47,7 +49,7 @@ public class LiveTvController : BaseJellyfinApiController
|
|||
private readonly IDtoService _dtoService;
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IConfigurationManager _configurationManager;
|
||||
private readonly TranscodingJobHelper _transcodingJobHelper;
|
||||
private readonly ITranscodeManager _transcodeManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LiveTvController"/> class.
|
||||
|
@ -59,7 +61,7 @@ public class LiveTvController : BaseJellyfinApiController
|
|||
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
|
||||
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
|
||||
/// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
|
||||
/// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
|
||||
/// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
|
||||
public LiveTvController(
|
||||
ILiveTvManager liveTvManager,
|
||||
IUserManager userManager,
|
||||
|
@ -68,7 +70,7 @@ public class LiveTvController : BaseJellyfinApiController
|
|||
IDtoService dtoService,
|
||||
IMediaSourceManager mediaSourceManager,
|
||||
IConfigurationManager configurationManager,
|
||||
TranscodingJobHelper transcodingJobHelper)
|
||||
ITranscodeManager transcodeManager)
|
||||
{
|
||||
_liveTvManager = liveTvManager;
|
||||
_userManager = userManager;
|
||||
|
@ -77,7 +79,7 @@ public class LiveTvController : BaseJellyfinApiController
|
|||
_dtoService = dtoService;
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_configurationManager = configurationManager;
|
||||
_transcodingJobHelper = transcodingJobHelper;
|
||||
_transcodeManager = transcodeManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1171,7 +1173,7 @@ public class LiveTvController : BaseJellyfinApiController
|
|||
return NotFound();
|
||||
}
|
||||
|
||||
var stream = new ProgressiveFileStream(path, null, _transcodingJobHelper);
|
||||
var stream = new ProgressiveFileStream(path, null, _transcodeManager);
|
||||
return new FileStreamResult(stream, MimeTypes.GetMimeType(path));
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ using Jellyfin.Api.ModelBinders;
|
|||
using Jellyfin.Data.Entities;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Session;
|
||||
|
@ -30,7 +31,7 @@ public class PlaystateController : BaseJellyfinApiController
|
|||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly ILogger<PlaystateController> _logger;
|
||||
private readonly TranscodingJobHelper _transcodingJobHelper;
|
||||
private readonly ITranscodeManager _transcodeManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlaystateController"/> class.
|
||||
|
@ -40,14 +41,14 @@ public class PlaystateController : BaseJellyfinApiController
|
|||
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
|
||||
/// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
|
||||
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
|
||||
/// <param name="transcodingJobHelper">Th <see cref="TranscodingJobHelper"/> singleton.</param>
|
||||
/// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
|
||||
public PlaystateController(
|
||||
IUserManager userManager,
|
||||
IUserDataManager userDataRepository,
|
||||
ILibraryManager libraryManager,
|
||||
ISessionManager sessionManager,
|
||||
ILoggerFactory loggerFactory,
|
||||
TranscodingJobHelper transcodingJobHelper)
|
||||
ITranscodeManager transcodeManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_userDataRepository = userDataRepository;
|
||||
|
@ -55,7 +56,7 @@ public class PlaystateController : BaseJellyfinApiController
|
|||
_sessionManager = sessionManager;
|
||||
_logger = loggerFactory.CreateLogger<PlaystateController>();
|
||||
|
||||
_transcodingJobHelper = transcodingJobHelper;
|
||||
_transcodeManager = transcodeManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -188,7 +189,7 @@ public class PlaystateController : BaseJellyfinApiController
|
|||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
public ActionResult PingPlaybackSession([FromQuery, Required] string playSessionId)
|
||||
{
|
||||
_transcodingJobHelper.PingTranscodingJob(playSessionId, null);
|
||||
_transcodeManager.PingTranscodingJob(playSessionId, null);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
|
@ -205,7 +206,7 @@ public class PlaystateController : BaseJellyfinApiController
|
|||
_logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty);
|
||||
if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId))
|
||||
{
|
||||
await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false);
|
||||
await _transcodeManager.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
|
||||
|
@ -354,7 +355,7 @@ public class PlaystateController : BaseJellyfinApiController
|
|||
_logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty);
|
||||
if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId))
|
||||
{
|
||||
await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false);
|
||||
await _transcodeManager.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
|
||||
|
@ -388,7 +389,7 @@ public class PlaystateController : BaseJellyfinApiController
|
|||
{
|
||||
if (method == PlayMethod.Transcode)
|
||||
{
|
||||
var job = string.IsNullOrWhiteSpace(playSessionId) ? null : _transcodingJobHelper.GetTranscodingJob(playSessionId);
|
||||
var job = string.IsNullOrWhiteSpace(playSessionId) ? null : _transcodeManager.GetTranscodingJob(playSessionId);
|
||||
if (job is null)
|
||||
{
|
||||
return PlayMethod.DirectPlay;
|
||||
|
|
|
@ -11,6 +11,7 @@ using Jellyfin.Api.Models.StreamingDtos;
|
|||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
|
|
@ -11,7 +11,6 @@ using Jellyfin.Api.Constants;
|
|||
using Jellyfin.Api.Extensions;
|
||||
using Jellyfin.Api.Helpers;
|
||||
using Jellyfin.Api.ModelBinders;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using MediaBrowser.Common.Api;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Net;
|
||||
|
@ -20,6 +19,7 @@ using MediaBrowser.Controller.Dto;
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
@ -43,7 +43,7 @@ public class VideosController : BaseJellyfinApiController
|
|||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly TranscodingJobHelper _transcodingJobHelper;
|
||||
private readonly ITranscodeManager _transcodeManager;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly EncodingHelper _encodingHelper;
|
||||
|
||||
|
@ -58,7 +58,7 @@ public class VideosController : BaseJellyfinApiController
|
|||
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
|
||||
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
|
||||
/// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
|
||||
/// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
|
||||
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
|
||||
/// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
|
||||
public VideosController(
|
||||
|
@ -68,7 +68,7 @@ public class VideosController : BaseJellyfinApiController
|
|||
IMediaSourceManager mediaSourceManager,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IMediaEncoder mediaEncoder,
|
||||
TranscodingJobHelper transcodingJobHelper,
|
||||
ITranscodeManager transcodeManager,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
EncodingHelper encodingHelper)
|
||||
{
|
||||
|
@ -78,7 +78,7 @@ public class VideosController : BaseJellyfinApiController
|
|||
_mediaSourceManager = mediaSourceManager;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_transcodingJobHelper = transcodingJobHelper;
|
||||
_transcodeManager = transcodeManager;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_encodingHelper = encodingHelper;
|
||||
}
|
||||
|
@ -427,7 +427,7 @@ public class VideosController : BaseJellyfinApiController
|
|||
_serverConfigurationManager,
|
||||
_mediaEncoder,
|
||||
_encodingHelper,
|
||||
_transcodingJobHelper,
|
||||
_transcodeManager,
|
||||
_transcodingJobType,
|
||||
cancellationTokenSource.Token)
|
||||
.ConfigureAwait(false);
|
||||
|
@ -466,7 +466,7 @@ public class VideosController : BaseJellyfinApiController
|
|||
|
||||
if (state.MediaSource.IsInfiniteStream)
|
||||
{
|
||||
var liveStream = new ProgressiveFileStream(state.MediaPath, null, _transcodingJobHelper);
|
||||
var liveStream = new ProgressiveFileStream(state.MediaPath, null, _transcodeManager);
|
||||
return File(liveStream, contentType);
|
||||
}
|
||||
|
||||
|
@ -482,7 +482,7 @@ public class VideosController : BaseJellyfinApiController
|
|||
state,
|
||||
isHeadRequest,
|
||||
HttpContext,
|
||||
_transcodingJobHelper,
|
||||
_transcodeManager,
|
||||
ffmpegCommandLineArguments,
|
||||
_transcodingJobType,
|
||||
cancellationTokenSource).ConfigureAwait(false);
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
@ -26,7 +26,7 @@ public class AudioHelper
|
|||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly TranscodingJobHelper _transcodingJobHelper;
|
||||
private readonly ITranscodeManager _transcodeManager;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly EncodingHelper _encodingHelper;
|
||||
|
@ -39,7 +39,7 @@ public class AudioHelper
|
|||
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
|
||||
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
|
||||
/// <param name="transcodingJobHelper">Instance of <see cref="TranscodingJobHelper"/>.</param>
|
||||
/// <param name="transcodeManager">Instance of <see cref="ITranscodeManager"/> interface.</param>
|
||||
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
|
||||
/// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
|
||||
/// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
|
||||
|
@ -49,7 +49,7 @@ public class AudioHelper
|
|||
IMediaSourceManager mediaSourceManager,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IMediaEncoder mediaEncoder,
|
||||
TranscodingJobHelper transcodingJobHelper,
|
||||
ITranscodeManager transcodeManager,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
EncodingHelper encodingHelper)
|
||||
|
@ -59,7 +59,7 @@ public class AudioHelper
|
|||
_mediaSourceManager = mediaSourceManager;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_transcodingJobHelper = transcodingJobHelper;
|
||||
_transcodeManager = transcodeManager;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_encodingHelper = encodingHelper;
|
||||
|
@ -94,7 +94,7 @@ public class AudioHelper
|
|||
_serverConfigurationManager,
|
||||
_mediaEncoder,
|
||||
_encodingHelper,
|
||||
_transcodingJobHelper,
|
||||
_transcodeManager,
|
||||
transcodingJobType,
|
||||
cancellationTokenSource.Token)
|
||||
.ConfigureAwait(false);
|
||||
|
@ -133,7 +133,7 @@ public class AudioHelper
|
|||
|
||||
if (state.MediaSource.IsInfiniteStream)
|
||||
{
|
||||
var stream = new ProgressiveFileStream(state.MediaPath, null, _transcodingJobHelper);
|
||||
var stream = new ProgressiveFileStream(state.MediaPath, null, _transcodeManager);
|
||||
return new FileStreamResult(stream, contentType);
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ public class AudioHelper
|
|||
state,
|
||||
isHeadRequest,
|
||||
_httpContextAccessor.HttpContext,
|
||||
_transcodingJobHelper,
|
||||
_transcodeManager,
|
||||
ffmpegCommandLineArguments,
|
||||
transcodingJobType,
|
||||
cancellationTokenSource).ConfigureAwait(false);
|
||||
|
|
|
@ -8,7 +8,6 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Extensions;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
|
@ -18,6 +17,7 @@ using MediaBrowser.Common.Net;
|
|||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.Controller.Trickplay;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
@ -39,7 +39,7 @@ public class DynamicHlsHelper
|
|||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly TranscodingJobHelper _transcodingJobHelper;
|
||||
private readonly ITranscodeManager _transcodeManager;
|
||||
private readonly INetworkManager _networkManager;
|
||||
private readonly ILogger<DynamicHlsHelper> _logger;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
@ -54,7 +54,7 @@ public class DynamicHlsHelper
|
|||
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
|
||||
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
|
||||
/// <param name="transcodingJobHelper">Instance of <see cref="TranscodingJobHelper"/>.</param>
|
||||
/// <param name="transcodeManager">Instance of <see cref="ITranscodeManager"/>.</param>
|
||||
/// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger{DynamicHlsHelper}"/> interface.</param>
|
||||
/// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
|
||||
|
@ -66,7 +66,7 @@ public class DynamicHlsHelper
|
|||
IMediaSourceManager mediaSourceManager,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IMediaEncoder mediaEncoder,
|
||||
TranscodingJobHelper transcodingJobHelper,
|
||||
ITranscodeManager transcodeManager,
|
||||
INetworkManager networkManager,
|
||||
ILogger<DynamicHlsHelper> logger,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
|
@ -78,7 +78,7 @@ public class DynamicHlsHelper
|
|||
_mediaSourceManager = mediaSourceManager;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_transcodingJobHelper = transcodingJobHelper;
|
||||
_transcodeManager = transcodeManager;
|
||||
_networkManager = networkManager;
|
||||
_logger = logger;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
|
@ -130,7 +130,7 @@ public class DynamicHlsHelper
|
|||
_serverConfigurationManager,
|
||||
_mediaEncoder,
|
||||
_encodingHelper,
|
||||
_transcodingJobHelper,
|
||||
_transcodeManager,
|
||||
transcodingJobType,
|
||||
cancellationTokenSource.Token)
|
||||
.ConfigureAwait(false);
|
||||
|
|
|
@ -4,9 +4,9 @@ using System.Net.Http;
|
|||
using System.Net.Mime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.PlaybackDtos;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using Jellyfin.Api.Extensions;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
@ -65,7 +65,7 @@ public static class FileStreamResponseHelpers
|
|||
/// <param name="state">The current <see cref="StreamState"/>.</param>
|
||||
/// <param name="isHeadRequest">Whether the current request is a HTTP HEAD request so only the headers get returned.</param>
|
||||
/// <param name="httpContext">The current http context.</param>
|
||||
/// <param name="transcodingJobHelper">The <see cref="TranscodingJobHelper"/> singleton.</param>
|
||||
/// <param name="transcodeManager">The <see cref="ITranscodeManager"/> singleton.</param>
|
||||
/// <param name="ffmpegCommandLineArguments">The command line arguments to start ffmpeg.</param>
|
||||
/// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param>
|
||||
/// <param name="cancellationTokenSource">The <see cref="CancellationTokenSource"/>.</param>
|
||||
|
@ -74,7 +74,7 @@ public static class FileStreamResponseHelpers
|
|||
StreamState state,
|
||||
bool isHeadRequest,
|
||||
HttpContext httpContext,
|
||||
TranscodingJobHelper transcodingJobHelper,
|
||||
ITranscodeManager transcodeManager,
|
||||
string ffmpegCommandLineArguments,
|
||||
TranscodingJobType transcodingJobType,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
|
@ -93,22 +93,28 @@ public static class FileStreamResponseHelpers
|
|||
return new OkResult();
|
||||
}
|
||||
|
||||
var transcodingLock = transcodingJobHelper.GetTranscodingLock(outputPath);
|
||||
var transcodingLock = transcodeManager.GetTranscodingLock(outputPath);
|
||||
await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
TranscodingJobDto? job;
|
||||
TranscodingJob? job;
|
||||
if (!File.Exists(outputPath))
|
||||
{
|
||||
job = await transcodingJobHelper.StartFfMpeg(state, outputPath, ffmpegCommandLineArguments, httpContext.Request, transcodingJobType, cancellationTokenSource).ConfigureAwait(false);
|
||||
job = await transcodeManager.StartFfMpeg(
|
||||
state,
|
||||
outputPath,
|
||||
ffmpegCommandLineArguments,
|
||||
httpContext.User.GetUserId(),
|
||||
transcodingJobType,
|
||||
cancellationTokenSource).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
job = transcodingJobHelper.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
|
||||
job = transcodeManager.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
|
||||
state.Dispose();
|
||||
}
|
||||
|
||||
var stream = new ProgressiveFileStream(outputPath, job, transcodingJobHelper);
|
||||
var stream = new ProgressiveFileStream(outputPath, job, transcodeManager);
|
||||
return new FileStreamResult(stream, contentType);
|
||||
}
|
||||
finally
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ using System.Linq;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Extensions;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
|
@ -14,6 +13,7 @@ using MediaBrowser.Common.Extensions;
|
|||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
@ -38,7 +38,7 @@ public static class StreamingHelpers
|
|||
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
|
||||
/// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
|
||||
/// <param name="transcodingJobHelper">Initialized <see cref="TranscodingJobHelper"/>.</param>
|
||||
/// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
|
||||
/// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> containing the current <see cref="StreamState"/>.</returns>
|
||||
|
@ -51,7 +51,7 @@ public static class StreamingHelpers
|
|||
IServerConfigurationManager serverConfigurationManager,
|
||||
IMediaEncoder mediaEncoder,
|
||||
EncodingHelper encodingHelper,
|
||||
TranscodingJobHelper transcodingJobHelper,
|
||||
ITranscodeManager transcodeManager,
|
||||
TranscodingJobType transcodingJobType,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
|
@ -74,7 +74,7 @@ public static class StreamingHelpers
|
|||
streamingRequest.AudioCodec = encodingHelper.InferAudioCodec(url);
|
||||
}
|
||||
|
||||
var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper)
|
||||
var state = new StreamState(mediaSourceManager, transcodingJobType, transcodeManager)
|
||||
{
|
||||
Request = streamingRequest,
|
||||
RequestedUrl = url,
|
||||
|
@ -115,7 +115,7 @@ public static class StreamingHelpers
|
|||
if (string.IsNullOrWhiteSpace(streamingRequest.LiveStreamId))
|
||||
{
|
||||
var currentJob = !string.IsNullOrWhiteSpace(streamingRequest.PlaySessionId)
|
||||
? transcodingJobHelper.GetTranscodingJob(streamingRequest.PlaySessionId)
|
||||
? transcodeManager.GetTranscodingJob(streamingRequest.PlaySessionId)
|
||||
: null;
|
||||
|
||||
if (currentJob is not null)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace Jellyfin.Api.Models.StreamingDtos;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
|
||||
namespace Jellyfin.Api.Models.StreamingDtos;
|
||||
|
||||
/// <summary>
|
||||
/// The hls video request dto.
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace Jellyfin.Api.Models.StreamingDtos;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
|
||||
namespace Jellyfin.Api.Models.StreamingDtos;
|
||||
|
||||
/// <summary>
|
||||
/// The hls video request dto.
|
||||
|
|
104
MediaBrowser.Controller/MediaEncoding/ITranscodeManager.cs
Normal file
104
MediaBrowser.Controller/MediaEncoding/ITranscodeManager.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding;
|
||||
|
||||
/// <summary>
|
||||
/// A service for managing media transcoding.
|
||||
/// </summary>
|
||||
public interface ITranscodeManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Get transcoding job.
|
||||
/// </summary>
|
||||
/// <param name="playSessionId">Playback session id.</param>
|
||||
/// <returns>The transcoding job.</returns>
|
||||
public TranscodingJob? GetTranscodingJob(string playSessionId);
|
||||
|
||||
/// <summary>
|
||||
/// Get transcoding job.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the transcoding file.</param>
|
||||
/// <param name="type">The <see cref="TranscodingJobType"/>.</param>
|
||||
/// <returns>The transcoding job.</returns>
|
||||
public TranscodingJob? GetTranscodingJob(string path, TranscodingJobType type);
|
||||
|
||||
/// <summary>
|
||||
/// Ping transcoding job.
|
||||
/// </summary>
|
||||
/// <param name="playSessionId">Play session id.</param>
|
||||
/// <param name="isUserPaused">Is user paused.</param>
|
||||
/// <exception cref="ArgumentNullException">Play session id is null.</exception>
|
||||
public void PingTranscodingJob(string playSessionId, bool? isUserPaused);
|
||||
|
||||
/// <summary>
|
||||
/// Kills the single transcoding job.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The device id.</param>
|
||||
/// <param name="playSessionId">The play session identifier.</param>
|
||||
/// <param name="deleteFiles">The delete files.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public Task KillTranscodingJobs(string deviceId, string? playSessionId, Func<string, bool> deleteFiles);
|
||||
|
||||
/// <summary>
|
||||
/// Report the transcoding progress to the session manager.
|
||||
/// </summary>
|
||||
/// <param name="job">The <see cref="TranscodingJob"/> of which the progress will be reported.</param>
|
||||
/// <param name="state">The <see cref="StreamState"/> of the current transcoding job.</param>
|
||||
/// <param name="transcodingPosition">The current transcoding position.</param>
|
||||
/// <param name="framerate">The framerate of the transcoding job.</param>
|
||||
/// <param name="percentComplete">The completion percentage of the transcode.</param>
|
||||
/// <param name="bytesTranscoded">The number of bytes transcoded.</param>
|
||||
/// <param name="bitRate">The bitrate of the transcoding job.</param>
|
||||
public void ReportTranscodingProgress(
|
||||
TranscodingJob job,
|
||||
StreamState state,
|
||||
TimeSpan? transcodingPosition,
|
||||
float? framerate,
|
||||
double? percentComplete,
|
||||
long? bytesTranscoded,
|
||||
int? bitRate);
|
||||
|
||||
/// <summary>
|
||||
/// Starts FFMpeg.
|
||||
/// </summary>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="commandLineArguments">The command line arguments for FFmpeg.</param>
|
||||
/// <param name="userId">The user id.</param>
|
||||
/// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param>
|
||||
/// <param name="cancellationTokenSource">The cancellation token source.</param>
|
||||
/// <param name="workingDirectory">The working directory.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public Task<TranscodingJob> StartFfMpeg(
|
||||
StreamState state,
|
||||
string outputPath,
|
||||
string commandLineArguments,
|
||||
Guid userId,
|
||||
TranscodingJobType transcodingJobType,
|
||||
CancellationTokenSource cancellationTokenSource,
|
||||
string? workingDirectory = null);
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode begin request].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>The <see cref="TranscodingJob"/>.</returns>
|
||||
public TranscodingJob? OnTranscodeBeginRequest(string path, TranscodingJobType type);
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode end].
|
||||
/// </summary>
|
||||
/// <param name="job">The transcode job.</param>
|
||||
public void OnTranscodeEndRequest(TranscodingJob job);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transcoding lock.
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path of the transcoded file.</param>
|
||||
/// <returns>A <see cref="SemaphoreSlim"/>.</returns>
|
||||
public SemaphoreSlim GetTranscodingLock(string outputPath);
|
||||
}
|
|
@ -1,49 +1,39 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Api.Models.PlaybackDtos;
|
||||
namespace MediaBrowser.Controller.MediaEncoding;
|
||||
|
||||
/// <summary>
|
||||
/// Class TranscodingJob.
|
||||
/// </summary>
|
||||
public class TranscodingJobDto : IDisposable
|
||||
public sealed class TranscodingJob : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The process lock.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1051:NoVisibleInstanceFields", MessageId = "ProcessLock", Justification = "Imported from ServiceStack")]
|
||||
[SuppressMessage("Microsoft.Performance", "SA1401:PrivateField", MessageId = "ProcessLock", Justification = "Imported from ServiceStack")]
|
||||
public readonly object ProcessLock = new object();
|
||||
private readonly ILogger<TranscodingJob> _logger;
|
||||
private readonly object _processLock = new();
|
||||
private readonly object _timerLock = new();
|
||||
|
||||
private Timer? _killTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Timer lock.
|
||||
/// </summary>
|
||||
private readonly object _timerLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TranscodingJobDto"/> class.
|
||||
/// Initializes a new instance of the <see cref="TranscodingJob"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger{TranscodingJobDto}"/> interface.</param>
|
||||
public TranscodingJobDto(ILogger<TranscodingJobDto> logger)
|
||||
public TranscodingJob(ILogger<TranscodingJob> logger)
|
||||
{
|
||||
Logger = logger;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the play session identifier.
|
||||
/// </summary>
|
||||
/// <value>The play session identifier.</value>
|
||||
public string? PlaySessionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the live stream identifier.
|
||||
/// </summary>
|
||||
/// <value>The live stream identifier.</value>
|
||||
public string? LiveStreamId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -54,7 +44,6 @@ public class TranscodingJobDto : IDisposable
|
|||
/// <summary>
|
||||
/// Gets or sets the path.
|
||||
/// </summary>
|
||||
/// <value>The path.</value>
|
||||
public MediaSourceInfo? MediaSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -65,32 +54,18 @@ public class TranscodingJobDto : IDisposable
|
|||
/// <summary>
|
||||
/// Gets or sets the type.
|
||||
/// </summary>
|
||||
/// <value>The type.</value>
|
||||
public TranscodingJobType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the process.
|
||||
/// </summary>
|
||||
/// <value>The process.</value>
|
||||
public Process? Process { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets logger.
|
||||
/// </summary>
|
||||
public ILogger<TranscodingJobDto> Logger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the active request count.
|
||||
/// </summary>
|
||||
/// <value>The active request count.</value>
|
||||
public int ActiveRequestCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the kill timer.
|
||||
/// </summary>
|
||||
/// <value>The kill timer.</value>
|
||||
private Timer? KillTimer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets device id.
|
||||
/// </summary>
|
||||
|
@ -178,7 +153,7 @@ public class TranscodingJobDto : IDisposable
|
|||
{
|
||||
lock (_timerLock)
|
||||
{
|
||||
KillTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
_killTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,10 +164,10 @@ public class TranscodingJobDto : IDisposable
|
|||
{
|
||||
lock (_timerLock)
|
||||
{
|
||||
if (KillTimer is not null)
|
||||
if (_killTimer is not null)
|
||||
{
|
||||
KillTimer.Dispose();
|
||||
KillTimer = null;
|
||||
_killTimer.Dispose();
|
||||
_killTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,15 +195,15 @@ public class TranscodingJobDto : IDisposable
|
|||
|
||||
lock (_timerLock)
|
||||
{
|
||||
if (KillTimer is null)
|
||||
if (_killTimer is null)
|
||||
{
|
||||
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);
|
||||
_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);
|
||||
KillTimer.Change(intervalMs, Timeout.Infinite);
|
||||
_logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
|
||||
_killTimer.Change(intervalMs, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,39 +220,61 @@ public class TranscodingJobDto : IDisposable
|
|||
|
||||
lock (_timerLock)
|
||||
{
|
||||
if (KillTimer is not null)
|
||||
if (_killTimer is not null)
|
||||
{
|
||||
var intervalMs = PingTimeout;
|
||||
|
||||
Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
|
||||
KillTimer.Change(intervalMs, Timeout.Infinite);
|
||||
_logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
|
||||
_killTimer.Change(intervalMs, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the transcoding job.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
lock (_processLock)
|
||||
{
|
||||
#pragma warning disable CA1849 // Can't await in lock block
|
||||
TranscodingThrottler?.Stop().GetAwaiter().GetResult();
|
||||
|
||||
var process = Process;
|
||||
|
||||
if (!HasExited)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Stopping ffmpeg process with q command for {Path}", Path);
|
||||
|
||||
process!.StandardInput.WriteLine("q");
|
||||
|
||||
// Need to wait because killing is asynchronous.
|
||||
if (!process.WaitForExit(5000))
|
||||
{
|
||||
_logger.LogInformation("Killing FFmpeg process for {Path}", Path);
|
||||
process.Kill();
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1849
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose all resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">Whether to dispose all resources.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Process?.Dispose();
|
||||
Process = null;
|
||||
KillTimer?.Dispose();
|
||||
KillTimer = null;
|
||||
CancellationTokenSource?.Dispose();
|
||||
CancellationTokenSource = null;
|
||||
TranscodingThrottler?.Dispose();
|
||||
TranscodingThrottler = null;
|
||||
}
|
||||
Process?.Dispose();
|
||||
Process = null;
|
||||
_killTimer?.Dispose();
|
||||
_killTimer = null;
|
||||
CancellationTokenSource?.Dispose();
|
||||
CancellationTokenSource = null;
|
||||
TranscodingThrottler?.Dispose();
|
||||
TranscodingThrottler = null;
|
||||
}
|
||||
}
|
|
@ -2,19 +2,18 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Api.Models.PlaybackDtos;
|
||||
namespace MediaBrowser.Controller.MediaEncoding;
|
||||
|
||||
/// <summary>
|
||||
/// Transcoding throttler.
|
||||
/// </summary>
|
||||
public class TranscodingThrottler : IDisposable
|
||||
{
|
||||
private readonly TranscodingJobDto _job;
|
||||
private readonly TranscodingJob _job;
|
||||
private readonly ILogger<TranscodingThrottler> _logger;
|
||||
private readonly IConfigurationManager _config;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
@ -30,7 +29,7 @@ public class TranscodingThrottler : IDisposable
|
|||
/// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
|
||||
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
||||
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
|
||||
public TranscodingThrottler(TranscodingJobDto job, ILogger<TranscodingThrottler> logger, IConfigurationManager config, IFileSystem fileSystem, IMediaEncoder mediaEncoder)
|
||||
public TranscodingThrottler(TranscodingJob job, ILogger<TranscodingThrottler> logger, IConfigurationManager config, IFileSystem fileSystem, IMediaEncoder mediaEncoder)
|
||||
{
|
||||
_job = job;
|
||||
_logger = logger;
|
||||
|
@ -146,7 +145,7 @@ public class TranscodingThrottler : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private bool IsThrottleAllowed(TranscodingJobDto job, int thresholdSeconds)
|
||||
private bool IsThrottleAllowed(TranscodingJob job, int thresholdSeconds)
|
||||
{
|
||||
var bytesDownloaded = job.BytesDownloaded;
|
||||
var transcodingPositionTicks = job.TranscodingPositionTicks ?? 0;
|
|
@ -3,10 +3,10 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.PlaybackDtos;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace Jellyfin.Api.Helpers;
|
||||
namespace MediaBrowser.Controller.Streaming;
|
||||
|
||||
/// <summary>
|
||||
/// A progressive file stream for transferring transcoded files as they are written to.
|
||||
|
@ -14,8 +14,8 @@ namespace Jellyfin.Api.Helpers;
|
|||
public class ProgressiveFileStream : Stream
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly TranscodingJobDto? _job;
|
||||
private readonly TranscodingJobHelper? _transcodingJobHelper;
|
||||
private readonly TranscodingJob? _job;
|
||||
private readonly ITranscodeManager? _transcodeManager;
|
||||
private readonly int _timeoutMs;
|
||||
private bool _disposed;
|
||||
|
||||
|
@ -24,12 +24,12 @@ public class ProgressiveFileStream : Stream
|
|||
/// </summary>
|
||||
/// <param name="filePath">The path to the transcoded file.</param>
|
||||
/// <param name="job">The transcoding job information.</param>
|
||||
/// <param name="transcodingJobHelper">The transcoding job helper.</param>
|
||||
/// <param name="transcodeManager">The transcode manager.</param>
|
||||
/// <param name="timeoutMs">The timeout duration in milliseconds.</param>
|
||||
public ProgressiveFileStream(string filePath, TranscodingJobDto? job, TranscodingJobHelper transcodingJobHelper, int timeoutMs = 30000)
|
||||
public ProgressiveFileStream(string filePath, TranscodingJob? job, ITranscodeManager transcodeManager, int timeoutMs = 30000)
|
||||
{
|
||||
_job = job;
|
||||
_transcodingJobHelper = transcodingJobHelper;
|
||||
_transcodeManager = transcodeManager;
|
||||
_timeoutMs = timeoutMs;
|
||||
|
||||
_stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||
|
@ -43,7 +43,7 @@ public class ProgressiveFileStream : Stream
|
|||
public ProgressiveFileStream(Stream stream, int timeoutMs = 30000)
|
||||
{
|
||||
_job = null;
|
||||
_transcodingJobHelper = null;
|
||||
_transcodeManager = null;
|
||||
_timeoutMs = timeoutMs;
|
||||
_stream = stream;
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ public class ProgressiveFileStream : Stream
|
|||
|
||||
if (_job is not null)
|
||||
{
|
||||
_transcodingJobHelper?.OnTranscodeEndRequest(_job);
|
||||
_transcodeManager?.OnTranscodeEndRequest(_job);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
using System;
|
||||
using Jellyfin.Api.Helpers;
|
||||
using Jellyfin.Api.Models.PlaybackDtos;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace Jellyfin.Api.Models.StreamingDtos;
|
||||
namespace MediaBrowser.Controller.Streaming;
|
||||
|
||||
/// <summary>
|
||||
/// The stream state dto.
|
||||
|
@ -13,7 +11,7 @@ namespace Jellyfin.Api.Models.StreamingDtos;
|
|||
public class StreamState : EncodingJobInfo, IDisposable
|
||||
{
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly TranscodingJobHelper _transcodingJobHelper;
|
||||
private readonly ITranscodeManager _transcodeManager;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
|
@ -21,12 +19,12 @@ public class StreamState : EncodingJobInfo, IDisposable
|
|||
/// </summary>
|
||||
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager" /> interface.</param>
|
||||
/// <param name="transcodingType">The <see cref="TranscodingJobType" />.</param>
|
||||
/// <param name="transcodingJobHelper">The <see cref="TranscodingJobHelper" /> singleton.</param>
|
||||
public StreamState(IMediaSourceManager mediaSourceManager, TranscodingJobType transcodingType, TranscodingJobHelper transcodingJobHelper)
|
||||
/// <param name="transcodeManager">The <see cref="ITranscodeManager" /> singleton.</param>
|
||||
public StreamState(IMediaSourceManager mediaSourceManager, TranscodingJobType transcodingType, ITranscodeManager transcodeManager)
|
||||
: base(transcodingType)
|
||||
{
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_transcodingJobHelper = transcodingJobHelper;
|
||||
_transcodeManager = transcodeManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -141,7 +139,7 @@ public class StreamState : EncodingJobInfo, IDisposable
|
|||
/// <summary>
|
||||
/// Gets or sets the transcoding job.
|
||||
/// </summary>
|
||||
public TranscodingJobDto? TranscodingJob { get; set; }
|
||||
public TranscodingJob? TranscodingJob { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
|
@ -153,7 +151,7 @@ public class StreamState : EncodingJobInfo, IDisposable
|
|||
/// <inheritdoc />
|
||||
public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
|
||||
{
|
||||
_transcodingJobHelper.ReportTranscodingProgress(TranscodingJob!, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate);
|
||||
_transcodeManager.ReportTranscodingProgress(TranscodingJob!, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate);
|
||||
}
|
||||
|
||||
/// <summary>
|
|
@ -1,6 +1,6 @@
|
|||
using MediaBrowser.Controller.MediaEncoding;
|
||||
|
||||
namespace Jellyfin.Api.Models.StreamingDtos;
|
||||
namespace MediaBrowser.Controller.Streaming;
|
||||
|
||||
/// <summary>
|
||||
/// The audio streaming request dto.
|
|
@ -1,4 +1,4 @@
|
|||
namespace Jellyfin.Api.Models.StreamingDtos;
|
||||
namespace MediaBrowser.Controller.Streaming;
|
||||
|
||||
/// <summary>
|
||||
/// The video request dto.
|
|
@ -8,9 +8,6 @@ using System.Text;
|
|||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Extensions;
|
||||
using Jellyfin.Api.Models.PlaybackDtos;
|
||||
using Jellyfin.Api.Models.StreamingDtos;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
|
@ -19,94 +16,78 @@ using MediaBrowser.Controller.Configuration;
|
|||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Session;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Api.Helpers;
|
||||
namespace MediaBrowser.MediaEncoding.Transcoding;
|
||||
|
||||
/// <summary>
|
||||
/// Transcoding job helpers.
|
||||
/// </summary>
|
||||
public class TranscodingJobHelper : IDisposable
|
||||
/// <inheritdoc cref="ITranscodeManager"/>
|
||||
public sealed class TranscodeManager : ITranscodeManager, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The active transcoding jobs.
|
||||
/// </summary>
|
||||
private static readonly List<TranscodingJobDto> _activeTranscodingJobs = new List<TranscodingJobDto>();
|
||||
|
||||
/// <summary>
|
||||
/// The transcoding locks.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, SemaphoreSlim> _transcodingLocks = new Dictionary<string, SemaphoreSlim>();
|
||||
|
||||
private readonly IAttachmentExtractor _attachmentExtractor;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly EncodingHelper _encodingHelper;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly ILogger<TranscodeManager> _logger;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ILogger<TranscodingJobHelper> _logger;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly EncodingHelper _encodingHelper;
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly IAttachmentExtractor _attachmentExtractor;
|
||||
|
||||
private readonly List<TranscodingJob> _activeTranscodingJobs = new();
|
||||
private readonly Dictionary<string, SemaphoreSlim> _transcodingLocks = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TranscodingJobHelper"/> class.
|
||||
/// Initializes a new instance of the <see cref="TranscodeManager"/> class.
|
||||
/// </summary>
|
||||
/// <param name="attachmentExtractor">Instance of the <see cref="IAttachmentExtractor"/> interface.</param>
|
||||
/// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger{TranscodingJobHelpers}"/> interface.</param>
|
||||
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
|
||||
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
||||
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
|
||||
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||
/// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
|
||||
/// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
|
||||
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
|
||||
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
|
||||
public TranscodingJobHelper(
|
||||
IAttachmentExtractor attachmentExtractor,
|
||||
IApplicationPaths appPaths,
|
||||
ILogger<TranscodingJobHelper> logger,
|
||||
IMediaSourceManager mediaSourceManager,
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
|
||||
/// <param name="fileSystem">The <see cref="IFileSystem"/>.</param>
|
||||
/// <param name="appPaths">The <see cref="IApplicationPaths"/>.</param>
|
||||
/// <param name="serverConfigurationManager">The <see cref="IServerConfigurationManager"/>.</param>
|
||||
/// <param name="userManager">The <see cref="IUserManager"/>.</param>
|
||||
/// <param name="sessionManager">The <see cref="ISessionManager"/>.</param>
|
||||
/// <param name="encodingHelper">The <see cref="EncodingHelper"/>.</param>
|
||||
/// <param name="mediaEncoder">The <see cref="IMediaEncoder"/>.</param>
|
||||
/// <param name="mediaSourceManager">The <see cref="IMediaSourceManager"/>.</param>
|
||||
/// <param name="attachmentExtractor">The <see cref="IAttachmentExtractor"/>.</param>
|
||||
public TranscodeManager(
|
||||
ILoggerFactory loggerFactory,
|
||||
IFileSystem fileSystem,
|
||||
IMediaEncoder mediaEncoder,
|
||||
IApplicationPaths appPaths,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IUserManager userManager,
|
||||
ISessionManager sessionManager,
|
||||
EncodingHelper encodingHelper,
|
||||
ILoggerFactory loggerFactory,
|
||||
IUserManager userManager)
|
||||
IMediaEncoder mediaEncoder,
|
||||
IMediaSourceManager mediaSourceManager,
|
||||
IAttachmentExtractor attachmentExtractor)
|
||||
{
|
||||
_attachmentExtractor = attachmentExtractor;
|
||||
_appPaths = appPaths;
|
||||
_logger = logger;
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_loggerFactory = loggerFactory;
|
||||
_fileSystem = fileSystem;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_appPaths = appPaths;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_userManager = userManager;
|
||||
_sessionManager = sessionManager;
|
||||
_encodingHelper = encodingHelper;
|
||||
_loggerFactory = loggerFactory;
|
||||
_userManager = userManager;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_attachmentExtractor = attachmentExtractor;
|
||||
|
||||
_logger = loggerFactory.CreateLogger<TranscodeManager>();
|
||||
DeleteEncodedMediaCache();
|
||||
|
||||
sessionManager.PlaybackProgress += OnPlaybackProgress;
|
||||
sessionManager.PlaybackStart += OnPlaybackProgress;
|
||||
_sessionManager.PlaybackProgress += OnPlaybackProgress;
|
||||
_sessionManager.PlaybackStart += OnPlaybackProgress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get transcoding job.
|
||||
/// </summary>
|
||||
/// <param name="playSessionId">Playback session id.</param>
|
||||
/// <returns>The transcoding job.</returns>
|
||||
public TranscodingJobDto? GetTranscodingJob(string playSessionId)
|
||||
/// <inheritdoc />
|
||||
public TranscodingJob? GetTranscodingJob(string playSessionId)
|
||||
{
|
||||
lock (_activeTranscodingJobs)
|
||||
{
|
||||
|
@ -114,13 +95,8 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get transcoding job.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the transcoding file.</param>
|
||||
/// <param name="type">The <see cref="TranscodingJobType"/>.</param>
|
||||
/// <returns>The transcoding job.</returns>
|
||||
public TranscodingJobDto? GetTranscodingJob(string path, TranscodingJobType type)
|
||||
/// <inheritdoc />
|
||||
public TranscodingJob? GetTranscodingJob(string path, TranscodingJobType type)
|
||||
{
|
||||
lock (_activeTranscodingJobs)
|
||||
{
|
||||
|
@ -128,19 +104,14 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ping transcoding job.
|
||||
/// </summary>
|
||||
/// <param name="playSessionId">Play session id.</param>
|
||||
/// <param name="isUserPaused">Is user paused.</param>
|
||||
/// <exception cref="ArgumentNullException">Play session id is null.</exception>
|
||||
/// <inheritdoc />
|
||||
public void PingTranscodingJob(string playSessionId, bool? isUserPaused)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(playSessionId);
|
||||
|
||||
_logger.LogDebug("PingTranscodingJob PlaySessionId={0} isUsedPaused: {1}", playSessionId, isUserPaused);
|
||||
|
||||
List<TranscodingJobDto> jobs;
|
||||
List<TranscodingJob> jobs;
|
||||
|
||||
lock (_activeTranscodingJobs)
|
||||
{
|
||||
|
@ -161,7 +132,7 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private void PingTimer(TranscodingJobDto job, bool isProgressCheckIn)
|
||||
private void PingTimer(TranscodingJob job, bool isProgressCheckIn)
|
||||
{
|
||||
if (job.HasExited)
|
||||
{
|
||||
|
@ -190,13 +161,9 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode kill timer stopped].
|
||||
/// </summary>
|
||||
/// <param name="state">The state.</param>
|
||||
private async void OnTranscodeKillTimerStopped(object? state)
|
||||
{
|
||||
var job = state as TranscodingJobDto ?? throw new ArgumentException($"{nameof(state)} is not of type {nameof(TranscodingJobDto)}", nameof(state));
|
||||
var job = state as TranscodingJob ?? throw new ArgumentException($"{nameof(state)} is not of type {nameof(TranscodingJob)}", nameof(state));
|
||||
if (!job.HasExited && job.Type != TranscodingJobType.Progressive)
|
||||
{
|
||||
var timeSinceLastPing = (DateTime.UtcNow - job.LastPingDate).TotalMilliseconds;
|
||||
|
@ -213,43 +180,21 @@ public class TranscodingJobHelper : IDisposable
|
|||
await KillTranscodingJob(job, true, path => true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills the single transcoding job.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The device id.</param>
|
||||
/// <param name="playSessionId">The play session identifier.</param>
|
||||
/// <param name="deleteFiles">The delete files.</param>
|
||||
/// <returns>Task.</returns>
|
||||
/// <inheritdoc />
|
||||
public Task KillTranscodingJobs(string deviceId, string? playSessionId, Func<string, bool> deleteFiles)
|
||||
{
|
||||
return KillTranscodingJobs(
|
||||
j => string.IsNullOrWhiteSpace(playSessionId)
|
||||
? string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase)
|
||||
: string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase),
|
||||
deleteFiles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills the transcoding jobs.
|
||||
/// </summary>
|
||||
/// <param name="killJob">The kill job.</param>
|
||||
/// <param name="deleteFiles">The delete files.</param>
|
||||
/// <returns>Task.</returns>
|
||||
private Task KillTranscodingJobs(Func<TranscodingJobDto, bool> killJob, Func<string, bool> deleteFiles)
|
||||
{
|
||||
var jobs = new List<TranscodingJobDto>();
|
||||
var jobs = new List<TranscodingJob>();
|
||||
|
||||
lock (_activeTranscodingJobs)
|
||||
{
|
||||
// This is really only needed for HLS.
|
||||
// Progressive streams can stop on their own reliably.
|
||||
jobs.AddRange(_activeTranscodingJobs.Where(killJob));
|
||||
jobs.AddRange(_activeTranscodingJobs.Where(j => string.IsNullOrWhiteSpace(playSessionId)
|
||||
? string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase)
|
||||
: string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase)));
|
||||
}
|
||||
|
||||
if (jobs.Count == 0)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
return Task.WhenAll(GetKillJobs());
|
||||
|
||||
IEnumerable<Task> GetKillJobs()
|
||||
{
|
||||
|
@ -258,17 +203,9 @@ public class TranscodingJobHelper : IDisposable
|
|||
yield return KillTranscodingJob(job, false, deleteFiles);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.WhenAll(GetKillJobs());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills the transcoding job.
|
||||
/// </summary>
|
||||
/// <param name="job">The job.</param>
|
||||
/// <param name="closeLiveStream">if set to <c>true</c> [close live stream].</param>
|
||||
/// <param name="delete">The delete.</param>
|
||||
private async Task KillTranscodingJob(TranscodingJobDto job, bool closeLiveStream, Func<string, bool> delete)
|
||||
private async Task KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func<string, bool> delete)
|
||||
{
|
||||
job.DisposeKillTimer();
|
||||
|
||||
|
@ -282,6 +219,7 @@ public class TranscodingJobHelper : IDisposable
|
|||
{
|
||||
#pragma warning disable CA1849 // Can't await in lock block
|
||||
job.CancellationTokenSource.Cancel();
|
||||
#pragma warning restore CA1849
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,35 +228,7 @@ public class TranscodingJobHelper : IDisposable
|
|||
_transcodingLocks.Remove(job.Path!);
|
||||
}
|
||||
|
||||
lock (job.ProcessLock!)
|
||||
{
|
||||
job.TranscodingThrottler?.Stop().GetAwaiter().GetResult();
|
||||
|
||||
var process = job.Process;
|
||||
|
||||
var hasExited = job.HasExited;
|
||||
|
||||
if (!hasExited)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Stopping ffmpeg process with q command for {Path}", job.Path);
|
||||
|
||||
process!.StandardInput.WriteLine("q");
|
||||
|
||||
// Need to wait because killing is asynchronous.
|
||||
if (!process.WaitForExit(5000))
|
||||
{
|
||||
_logger.LogInformation("Killing FFmpeg process for {Path}", job.Path);
|
||||
process.Kill();
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1849
|
||||
}
|
||||
job.Stop();
|
||||
|
||||
if (delete(job.Path!))
|
||||
{
|
||||
|
@ -381,10 +291,6 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the progressive partial stream files.
|
||||
/// </summary>
|
||||
/// <param name="outputFilePath">The output file path.</param>
|
||||
private void DeleteProgressivePartialStreamFiles(string outputFilePath)
|
||||
{
|
||||
if (File.Exists(outputFilePath))
|
||||
|
@ -393,10 +299,6 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the HLS partial stream files.
|
||||
/// </summary>
|
||||
/// <param name="outputFilePath">The output file path.</param>
|
||||
private void DeleteHlsPartialStreamFiles(string outputFilePath)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(outputFilePath)
|
||||
|
@ -428,18 +330,9 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Report the transcoding progress to the session manager.
|
||||
/// </summary>
|
||||
/// <param name="job">The <see cref="TranscodingJobDto"/> of which the progress will be reported.</param>
|
||||
/// <param name="state">The <see cref="StreamState"/> of the current transcoding job.</param>
|
||||
/// <param name="transcodingPosition">The current transcoding position.</param>
|
||||
/// <param name="framerate">The framerate of the transcoding job.</param>
|
||||
/// <param name="percentComplete">The completion percentage of the transcode.</param>
|
||||
/// <param name="bytesTranscoded">The number of bytes transcoded.</param>
|
||||
/// <param name="bitRate">The bitrate of the transcoding job.</param>
|
||||
/// <inheritdoc />
|
||||
public void ReportTranscodingProgress(
|
||||
TranscodingJobDto job,
|
||||
TranscodingJob job,
|
||||
StreamState state,
|
||||
TimeSpan? transcodingPosition,
|
||||
float? framerate,
|
||||
|
@ -490,22 +383,12 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts FFmpeg.
|
||||
/// </summary>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="commandLineArguments">The command line arguments for FFmpeg.</param>
|
||||
/// <param name="request">The <see cref="HttpRequest"/>.</param>
|
||||
/// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param>
|
||||
/// <param name="cancellationTokenSource">The cancellation token source.</param>
|
||||
/// <param name="workingDirectory">The working directory.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public async Task<TranscodingJobDto> StartFfMpeg(
|
||||
/// <inheritdoc />
|
||||
public async Task<TranscodingJob> StartFfMpeg(
|
||||
StreamState state,
|
||||
string outputPath,
|
||||
string commandLineArguments,
|
||||
HttpRequest request,
|
||||
Guid userId,
|
||||
TranscodingJobType transcodingJobType,
|
||||
CancellationTokenSource cancellationTokenSource,
|
||||
string? workingDirectory = null)
|
||||
|
@ -517,7 +400,6 @@ public class TranscodingJobHelper : IDisposable
|
|||
|
||||
if (state.VideoRequest is not null && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
|
||||
{
|
||||
var userId = request.HttpContext.User.GetUserId();
|
||||
var user = userId.Equals(default) ? null : _userManager.GetUserById(userId);
|
||||
if (user is not null && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding))
|
||||
{
|
||||
|
@ -595,13 +477,26 @@ public class TranscodingJobHelper : IDisposable
|
|||
$"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log");
|
||||
|
||||
// FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
||||
Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||
Stream logStream = new FileStream(
|
||||
logFilePath,
|
||||
FileMode.Create,
|
||||
FileAccess.Write,
|
||||
FileShare.Read,
|
||||
IODefaults.FileStreamBufferSize,
|
||||
FileOptions.Asynchronous);
|
||||
|
||||
var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
|
||||
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
|
||||
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(
|
||||
JsonSerializer.Serialize(state.MediaSource)
|
||||
+ Environment.NewLine
|
||||
+ Environment.NewLine
|
||||
+ commandLineLogMessage
|
||||
+ Environment.NewLine
|
||||
+ Environment.NewLine);
|
||||
|
||||
await logStream.WriteAsync(commandLineLogMessageBytes, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
|
||||
process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state);
|
||||
process.Exited += (_, _) => OnFfMpegProcessExited(process, transcodingJob, state);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -610,7 +505,6 @@ public class TranscodingJobHelper : IDisposable
|
|||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error starting FFmpeg");
|
||||
|
||||
this.OnTranscodeFailedToStart(outputPath, transcodingJobType, state);
|
||||
|
||||
throw;
|
||||
|
@ -656,7 +550,7 @@ public class TranscodingJobHelper : IDisposable
|
|||
return transcodingJob;
|
||||
}
|
||||
|
||||
private void StartThrottler(StreamState state, TranscodingJobDto transcodingJob)
|
||||
private void StartThrottler(StreamState state, TranscodingJob transcodingJob)
|
||||
{
|
||||
if (EnableThrottling(state))
|
||||
{
|
||||
|
@ -665,31 +559,14 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private bool EnableThrottling(StreamState state)
|
||||
{
|
||||
var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
|
||||
private static bool EnableThrottling(StreamState state)
|
||||
=> state.InputProtocol == MediaProtocol.File
|
||||
&& state.RunTimeTicks.HasValue
|
||||
&& state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks
|
||||
&& state.IsInputVideo
|
||||
&& state.VideoType == VideoType.VideoFile;
|
||||
|
||||
return state.InputProtocol == MediaProtocol.File &&
|
||||
state.RunTimeTicks.HasValue &&
|
||||
state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks &&
|
||||
state.IsInputVideo &&
|
||||
state.VideoType == VideoType.VideoFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode beginning].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="playSessionId">The play session identifier.</param>
|
||||
/// <param name="liveStreamId">The live stream identifier.</param>
|
||||
/// <param name="transcodingJobId">The transcoding job identifier.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="process">The process.</param>
|
||||
/// <param name="deviceId">The device id.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="cancellationTokenSource">The cancellation token source.</param>
|
||||
/// <returns>TranscodingJob.</returns>
|
||||
public TranscodingJobDto OnTranscodeBeginning(
|
||||
private TranscodingJob OnTranscodeBeginning(
|
||||
string path,
|
||||
string? playSessionId,
|
||||
string? liveStreamId,
|
||||
|
@ -702,7 +579,7 @@ public class TranscodingJobHelper : IDisposable
|
|||
{
|
||||
lock (_activeTranscodingJobs)
|
||||
{
|
||||
var job = new TranscodingJobDto(_loggerFactory.CreateLogger<TranscodingJobDto>())
|
||||
var job = new TranscodingJob(_loggerFactory.CreateLogger<TranscodingJob>())
|
||||
{
|
||||
Type = type,
|
||||
Path = path,
|
||||
|
@ -724,11 +601,8 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode end].
|
||||
/// </summary>
|
||||
/// <param name="job">The transcode job.</param>
|
||||
public void OnTranscodeEndRequest(TranscodingJobDto job)
|
||||
/// <inheritdoc />
|
||||
public void OnTranscodeEndRequest(TranscodingJob job)
|
||||
{
|
||||
job.ActiveRequestCount--;
|
||||
_logger.LogDebug("OnTranscodeEndRequest job.ActiveRequestCount={ActiveRequestCount}", job.ActiveRequestCount);
|
||||
|
@ -738,16 +612,7 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// The progressive
|
||||
/// </summary>
|
||||
/// Called when [transcode failed to start].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
public void OnTranscodeFailedToStart(string path, TranscodingJobType type, StreamState state)
|
||||
private void OnTranscodeFailedToStart(string path, TranscodingJobType type, StreamState state)
|
||||
{
|
||||
lock (_activeTranscodingJobs)
|
||||
{
|
||||
|
@ -770,13 +635,7 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the exited.
|
||||
/// </summary>
|
||||
/// <param name="process">The process.</param>
|
||||
/// <param name="job">The job.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
private void OnFfMpegProcessExited(Process process, TranscodingJobDto job, StreamState state)
|
||||
private void OnFfMpegProcessExited(Process process, TranscodingJob job, StreamState state)
|
||||
{
|
||||
job.HasExited = true;
|
||||
job.ExitCode = process.ExitCode;
|
||||
|
@ -822,44 +681,30 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode begin request].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>The <see cref="TranscodingJobDto"/>.</returns>
|
||||
public TranscodingJobDto? OnTranscodeBeginRequest(string path, TranscodingJobType type)
|
||||
/// <inheritdoc />
|
||||
public TranscodingJob? OnTranscodeBeginRequest(string path, TranscodingJobType type)
|
||||
{
|
||||
lock (_activeTranscodingJobs)
|
||||
{
|
||||
var job = _activeTranscodingJobs.FirstOrDefault(j => j.Type == type && string.Equals(j.Path, path, StringComparison.OrdinalIgnoreCase));
|
||||
var job = _activeTranscodingJobs
|
||||
.FirstOrDefault(j => j.Type == type && string.Equals(j.Path, path, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (job is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
OnTranscodeBeginRequest(job);
|
||||
job.ActiveRequestCount++;
|
||||
if (string.IsNullOrWhiteSpace(job.PlaySessionId) || job.Type == TranscodingJobType.Progressive)
|
||||
{
|
||||
job.StopKillTimer();
|
||||
}
|
||||
|
||||
return job;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTranscodeBeginRequest(TranscodingJobDto job)
|
||||
{
|
||||
job.ActiveRequestCount++;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(job.PlaySessionId) || job.Type == TranscodingJobType.Progressive)
|
||||
{
|
||||
job.StopKillTimer();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transcoding lock.
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path of the transcoded file.</param>
|
||||
/// <returns>A <see cref="SemaphoreSlim"/>.</returns>
|
||||
/// <inheritdoc />
|
||||
public SemaphoreSlim GetTranscodingLock(string outputPath)
|
||||
{
|
||||
lock (_transcodingLocks)
|
||||
|
@ -882,9 +727,6 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the encoded media cache.
|
||||
/// </summary>
|
||||
private void DeleteEncodedMediaCache()
|
||||
{
|
||||
var path = _serverConfigurationManager.GetTranscodePath();
|
||||
|
@ -899,26 +741,10 @@ public class TranscodingJobHelper : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose transcoding job helper.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose throttler.
|
||||
/// </summary>
|
||||
/// <param name="disposing">Disposing.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_loggerFactory.Dispose();
|
||||
_sessionManager.PlaybackProgress -= OnPlaybackProgress;
|
||||
_sessionManager.PlaybackStart -= OnPlaybackProgress;
|
||||
}
|
||||
_sessionManager.PlaybackProgress -= OnPlaybackProgress;
|
||||
_sessionManager.PlaybackStart -= OnPlaybackProgress;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user