/// <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
                });
            }
        }
Exemple #7
0
        /// <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);
                }
            }
        }