/// <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( string path, string?playSessionId, string?liveStreamId, string transcodingJobId, TranscodingJobType type, Process process, string?deviceId, StreamState state, CancellationTokenSource cancellationTokenSource) { lock (_activeTranscodingJobs) { var job = new TranscodingJobDto(_loggerFactory.CreateLogger <TranscodingJobDto>()) { Type = type, Path = path, Process = process, ActiveRequestCount = 1, DeviceId = deviceId, CancellationTokenSource = cancellationTokenSource, Id = transcodingJobId, PlaySessionId = playSessionId, LiveStreamId = liveStreamId, MediaSource = state.MediaSource }; _activeTranscodingJobs.Add(job); ReportTranscodingProgress(job, state, null, null, null, null, null); return(job); } }
private void PingTimer(TranscodingJobDto job, bool isProgressCheckIn) { if (job.HasExited) { job.StopKillTimer(); return; } var timerDuration = 10000; if (job.Type != TranscodingJobType.Progressive) { timerDuration = 60000; } job.PingTimeout = timerDuration; job.LastPingDate = DateTime.UtcNow; // Don't start the timer for playback checkins with progressive streaming if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn) { job.StartKillTimer(OnTranscodeKillTimerStopped); } else { job.ChangeKillTimerIfStarted(); } }
private void StartThrottler(StreamState state, TranscodingJobDto transcodingJob) { if (EnableThrottling(state)) { transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, new Logger <TranscodingThrottler>(new LoggerFactory()), _serverConfigurationManager, _fileSystem); state.TranscodingThrottler.Start(); } }
private void OnTranscodeBeginRequest(TranscodingJobDto job) { job.ActiveRequestCount++; if (string.IsNullOrWhiteSpace(job.PlaySessionId) || job.Type == TranscodingJobType.Progressive) { job.StopKillTimer(); } }
/// <summary> /// Called when [transcode end]. /// </summary> /// <param name="job">The transcode job.</param> public void OnTranscodeEndRequest(TranscodingJobDto job) { job.ActiveRequestCount--; _logger.LogDebug("OnTranscodeEndRequest job.ActiveRequestCount={ActiveRequestCount}", job.ActiveRequestCount); if (job.ActiveRequestCount <= 0) { PingTimer(job, false); } }
/// <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> public void ReportTranscodingProgress( TranscodingJobDto job, StreamState state, TimeSpan?transcodingPosition, float?framerate, double?percentComplete, long?bytesTranscoded, int?bitRate) { var ticks = transcodingPosition?.Ticks; if (job != null) { job.Framerate = framerate; job.CompletionPercentage = percentComplete; job.TranscodingPositionTicks = ticks; job.BytesTranscoded = bytesTranscoded; job.BitRate = bitRate; } var deviceId = state.Request.DeviceId; if (!string.IsNullOrWhiteSpace(deviceId)) { var audioCodec = state.ActualOutputAudioCodec; var videoCodec = state.ActualOutputVideoCodec; var hardwareAccelerationTypeString = _serverConfigurationManager.GetEncodingOptions().HardwareAccelerationType; HardwareEncodingType?hardwareAccelerationType = null; if (!string.IsNullOrEmpty(hardwareAccelerationTypeString) && Enum.TryParse <HardwareEncodingType>(hardwareAccelerationTypeString, out var parsedHardwareAccelerationType)) { hardwareAccelerationType = parsedHardwareAccelerationType; } _sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo { Bitrate = bitRate ?? state.TotalOutputBitrate, AudioCodec = audioCodec, VideoCodec = videoCodec, Container = state.OutputContainer, Framerate = framerate, CompletionPercentage = percentComplete, Width = state.OutputWidth, Height = state.OutputHeight, AudioChannels = state.OutputAudioChannels, IsAudioDirect = EncodingHelper.IsCopyCodec(state.OutputAudioCodec), IsVideoDirect = EncodingHelper.IsCopyCodec(state.OutputVideoCodec), HardwareAccelerationType = hardwareAccelerationType, TranscodeReasons = state.TranscodeReasons }); } }
/// <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) { job.HasExited = true; _logger.LogDebug("Disposing stream resources"); state.Dispose(); if (process.ExitCode == 0) { _logger.LogInformation("FFMpeg exited with code 0"); } else { _logger.LogError("FFMpeg exited with code {0}", process.ExitCode); } process.Dispose(); }
/// <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> public void ReportTranscodingProgress( TranscodingJobDto job, StreamState state, TimeSpan?transcodingPosition, float?framerate, double?percentComplete, long?bytesTranscoded, int?bitRate) { var ticks = transcodingPosition?.Ticks; if (job != null) { job.Framerate = framerate; job.CompletionPercentage = percentComplete; job.TranscodingPositionTicks = ticks; job.BytesTranscoded = bytesTranscoded; job.BitRate = bitRate; } var deviceId = state.Request.DeviceId; if (!string.IsNullOrWhiteSpace(deviceId)) { var audioCodec = state.ActualOutputAudioCodec; var videoCodec = state.ActualOutputVideoCodec; _sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo { Bitrate = bitRate ?? state.TotalOutputBitrate, AudioCodec = audioCodec, VideoCodec = videoCodec, Container = state.OutputContainer, Framerate = framerate, CompletionPercentage = percentComplete, Width = state.OutputWidth, Height = state.OutputHeight, AudioChannels = state.OutputAudioChannels, IsAudioDirect = EncodingHelper.IsCopyCodec(state.OutputAudioCodec), IsVideoDirect = EncodingHelper.IsCopyCodec(state.OutputVideoCodec), TranscodeReasons = state.TranscodeReasons }); } }
/// <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) { job.DisposeKillTimer(); _logger.LogDebug("KillTranscodingJob - JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId); lock (_activeTranscodingJobs) { _activeTranscodingJobs.Remove(job); if (job.CancellationTokenSource?.IsCancellationRequested == false) { job.CancellationTokenSource.Cancel(); } } lock (_transcodingLocks) { _transcodingLocks.Remove(job.Path !); } lock (job.ProcessLock !) { #pragma warning disable CA1849 // Can't await in lock block 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 } if (delete(job.Path !)) { await DeletePartialStreamFiles(job.Path !, job.Type, 0, 1500).ConfigureAwait(false); } if (closeLiveStream && !string.IsNullOrWhiteSpace(job.LiveStreamId)) { try { await _mediaSourceManager.CloseLiveStream(job.LiveStreamId).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "Error closing live stream for {Path}", job.Path); } } }