コード例 #1
0
        private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, CancellationToken cancellationToken)
        {
            if (timer == null)
            {
                throw new ArgumentNullException("timer");
            }

            if (string.IsNullOrWhiteSpace(timer.ProgramId))
            {
                throw new InvalidOperationException("timer.ProgramId is null. Cannot record.");
            }

            var info = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId);

            if (info == null)
            {
                throw new InvalidOperationException(string.Format("Program with Id {0} not found", timer.ProgramId));
            }

            var recordPath = RecordingPath;

            if (info.IsMovie)
            {
                recordPath = Path.Combine(recordPath, "Movies", _fileSystem.GetValidFilename(info.Name).Trim());
            }
            else if (info.IsSeries)
            {
                recordPath = Path.Combine(recordPath, "Series", _fileSystem.GetValidFilename(info.Name).Trim());
            }
            else if (info.IsKids)
            {
                recordPath = Path.Combine(recordPath, "Kids", _fileSystem.GetValidFilename(info.Name).Trim());
            }
            else if (info.IsSports)
            {
                recordPath = Path.Combine(recordPath, "Sports", _fileSystem.GetValidFilename(info.Name).Trim());
            }
            else
            {
                recordPath = Path.Combine(recordPath, "Other", _fileSystem.GetValidFilename(info.Name).Trim());
            }

            var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)).Trim() + ".ts";

            recordPath = Path.Combine(recordPath, recordingFileName);
            _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath));

            var recordingId = info.Id.GetMD5().ToString("N");
            var recording   = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.Id, recordingId, StringComparison.OrdinalIgnoreCase));

            if (recording == null)
            {
                recording = new RecordingInfo
                {
                    ChannelId       = info.ChannelId,
                    Id              = recordingId,
                    StartDate       = info.StartDate,
                    EndDate         = info.EndDate,
                    Genres          = info.Genres,
                    IsKids          = info.IsKids,
                    IsLive          = info.IsLive,
                    IsMovie         = info.IsMovie,
                    IsHD            = info.IsHD,
                    IsNews          = info.IsNews,
                    IsPremiere      = info.IsPremiere,
                    IsSeries        = info.IsSeries,
                    IsSports        = info.IsSports,
                    IsRepeat        = !info.IsPremiere,
                    Name            = info.Name,
                    EpisodeTitle    = info.EpisodeTitle,
                    ProgramId       = info.Id,
                    ImagePath       = info.ImagePath,
                    ImageUrl        = info.ImageUrl,
                    OriginalAirDate = info.OriginalAirDate,
                    Status          = RecordingStatus.Scheduled,
                    Overview        = info.Overview,
                    SeriesTimerId   = timer.SeriesTimerId,
                    TimerId         = timer.Id,
                    ShowId          = info.ShowId
                };
                _recordingProvider.AddOrUpdate(recording);
            }

            try
            {
                var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None);

                var mediaStreamInfo = result.Item1;
                var isResourceOpen  = true;

                // Unfortunately due to the semaphore we have to have a nested try/finally
                try
                {
                    // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
                    await Task.Delay(3000, cancellationToken).ConfigureAwait(false);

                    var duration = recordingEndDate - DateTime.UtcNow;

                    HttpRequestOptions httpRequestOptions = new HttpRequestOptions()
                    {
                        Url = mediaStreamInfo.Path
                    };

                    recording.Path            = recordPath;
                    recording.Status          = RecordingStatus.InProgress;
                    recording.DateLastUpdated = DateTime.UtcNow;
                    _recordingProvider.AddOrUpdate(recording);

                    _logger.Info("Beginning recording.");

                    httpRequestOptions.BufferContent = false;
                    var durationToken = new CancellationTokenSource(duration);
                    var linkedToken   = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
                    httpRequestOptions.CancellationToken = linkedToken;
                    _logger.Info("Writing file to path: " + recordPath);
                    using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET"))
                    {
                        using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read))
                        {
                            result.Item2.Release();
                            isResourceOpen = false;

                            await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken);
                        }
                    }

                    recording.Status = RecordingStatus.Completed;
                    _logger.Info("Recording completed");
                }
                finally
                {
                    if (isResourceOpen)
                    {
                        result.Item2.Release();
                    }
                }
            }
            catch (OperationCanceledException)
            {
                _logger.Info("Recording stopped");
                recording.Status = RecordingStatus.Completed;
            }
            catch (Exception ex)
            {
                _logger.ErrorException("Error recording", ex);
                recording.Status = RecordingStatus.Error;
            }
            finally
            {
                CancellationTokenSource removed;
                _activeRecordings.TryRemove(timer.Id, out removed);
            }

            recording.DateLastUpdated = DateTime.UtcNow;
            _recordingProvider.AddOrUpdate(recording);

            if (recording.Status == RecordingStatus.Completed)
            {
                OnSuccessfulRecording(recording);
                _timerProvider.Delete(timer);
            }
            else if (DateTime.UtcNow < timer.EndDate)
            {
                const int retryIntervalSeconds = 60;
                _logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds);

                _timerProvider.StartTimer(timer, TimeSpan.FromSeconds(retryIntervalSeconds));
            }
            else
            {
                _timerProvider.Delete(timer);
                _recordingProvider.Delete(recording);
            }
        }
コード例 #2
0
        private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, CancellationToken cancellationToken)
        {
            if (timer == null)
            {
                throw new ArgumentNullException("timer");
            }

            ProgramInfo info = null;

            if (string.IsNullOrWhiteSpace(timer.ProgramId))
            {
                _logger.Info("Timer {0} has null programId", timer.Id);
            }
            else
            {
                info = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId);
            }

            if (info == null)
            {
                _logger.Info("Unable to find program with Id {0}. Will search using start date", timer.ProgramId);
                info = GetProgramInfoFromCache(timer.ChannelId, timer.StartDate);
            }

            if (info == null)
            {
                throw new InvalidOperationException(string.Format("Program with Id {0} not found", timer.ProgramId));
            }

            var recordPath = RecordingPath;

            if (info.IsMovie)
            {
                recordPath = Path.Combine(recordPath, "Movies", _fileSystem.GetValidFilename(info.Name).Trim());
            }
            else if (info.IsSeries)
            {
                recordPath = Path.Combine(recordPath, "Series", _fileSystem.GetValidFilename(info.Name).Trim());
            }
            else if (info.IsKids)
            {
                recordPath = Path.Combine(recordPath, "Kids", _fileSystem.GetValidFilename(info.Name).Trim());
            }
            else if (info.IsSports)
            {
                recordPath = Path.Combine(recordPath, "Sports", _fileSystem.GetValidFilename(info.Name).Trim());
            }
            else
            {
                recordPath = Path.Combine(recordPath, "Other", _fileSystem.GetValidFilename(info.Name).Trim());
            }

            var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)).Trim() + ".ts";

            recordPath = Path.Combine(recordPath, recordingFileName);
            _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath));

            var recordingId = info.Id.GetMD5().ToString("N");
            var recording   = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.Id, recordingId, StringComparison.OrdinalIgnoreCase));

            if (recording == null)
            {
                recording = new RecordingInfo
                {
                    ChannelId       = info.ChannelId,
                    Id              = recordingId,
                    StartDate       = info.StartDate,
                    EndDate         = info.EndDate,
                    Genres          = info.Genres,
                    IsKids          = info.IsKids,
                    IsLive          = info.IsLive,
                    IsMovie         = info.IsMovie,
                    IsHD            = info.IsHD,
                    IsNews          = info.IsNews,
                    IsPremiere      = info.IsPremiere,
                    IsSeries        = info.IsSeries,
                    IsSports        = info.IsSports,
                    IsRepeat        = !info.IsPremiere,
                    Name            = info.Name,
                    EpisodeTitle    = info.EpisodeTitle,
                    ProgramId       = info.Id,
                    ImagePath       = info.ImagePath,
                    ImageUrl        = info.ImageUrl,
                    OriginalAirDate = info.OriginalAirDate,
                    Status          = RecordingStatus.Scheduled,
                    Overview        = info.Overview,
                    SeriesTimerId   = timer.SeriesTimerId,
                    TimerId         = timer.Id,
                    ShowId          = info.ShowId
                };
                _recordingProvider.AddOrUpdate(recording);
            }

            try
            {
                var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None).ConfigureAwait(false);

                var mediaStreamInfo = result.Item1;
                var isResourceOpen  = true;

                // Unfortunately due to the semaphore we have to have a nested try/finally
                try
                {
                    // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
                    await Task.Delay(3000, cancellationToken).ConfigureAwait(false);

                    var duration = recordingEndDate - DateTime.UtcNow;

                    var recorder = await GetRecorder().ConfigureAwait(false);

                    if (recorder is EncodedRecorder)
                    {
                        recordPath = Path.ChangeExtension(recordPath, ".mp4");
                    }

                    recording.Path            = recordPath;
                    recording.Status          = RecordingStatus.InProgress;
                    recording.DateLastUpdated = DateTime.UtcNow;
                    _recordingProvider.AddOrUpdate(recording);

                    _logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));

                    var durationToken = new CancellationTokenSource(duration);
                    var linkedToken   = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;

                    _logger.Info("Writing file to path: " + recordPath);
                    _logger.Info("Opening recording stream from tuner provider");

                    Action onStarted = () =>
                    {
                        result.Item2.Release();
                        isResourceOpen = false;
                    };

                    await recorder.Record(mediaStreamInfo, recordPath, onStarted, linkedToken).ConfigureAwait(false);

                    recording.Status = RecordingStatus.Completed;
                    _logger.Info("Recording completed");
                }
                finally
                {
                    if (isResourceOpen)
                    {
                        result.Item2.Release();
                    }
                }
            }
            catch (OperationCanceledException)
            {
                _logger.Info("Recording stopped");
                recording.Status = RecordingStatus.Completed;
            }
            catch (Exception ex)
            {
                _logger.ErrorException("Error recording", ex);
                recording.Status = RecordingStatus.Error;
            }
            finally
            {
                CancellationTokenSource removed;
                _activeRecordings.TryRemove(timer.Id, out removed);
            }

            recording.DateLastUpdated = DateTime.UtcNow;
            _recordingProvider.AddOrUpdate(recording);

            if (recording.Status == RecordingStatus.Completed)
            {
                OnSuccessfulRecording(recording);
                _timerProvider.Delete(timer);
            }
            else if (DateTime.UtcNow < timer.EndDate)
            {
                const int retryIntervalSeconds = 60;
                _logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds);

                _timerProvider.StartTimer(timer, TimeSpan.FromSeconds(retryIntervalSeconds));
            }
            else
            {
                _timerProvider.Delete(timer);
                _recordingProvider.Delete(recording);
            }
        }