Exemplo n.º 1
0
        public async Task <EmptyResponseDto> GoToSeconds()
        {
            try
            {
                var request = await HttpContext.GetRequestDataAsync <PlayCliFileRequestDto>();

                if (request == null)
                {
                    _logger.LogWarning($"{nameof(GoToSeconds)}: Nothing will be played, because no request was provided");
                    return(new EmptyResponseDto(false, "Invalid request"));
                }

                _logger.LogInformation($"{nameof(GoToSeconds)}: Getting file info for = {request.Mrl}...");
                var fileInfo = await _ffmpegService.GetFileInfo(request.Mrl, default);

                if (fileInfo == null && _fileService.IsUrlFile(request.Mrl))
                {
                    fileInfo = new FFProbeFileInfo();
                }

                _logger.LogInformation(
                    $"{nameof(GoToSeconds)}: Trying to play file = {JsonConvert.SerializeObject(request)} with seconds = {request.Seconds}...");

                await _castService.GoToSeconds(request, fileInfo);

                _logger.LogInformation($"{nameof(GoToSeconds)}: Mrl = {request.Mrl} was successfully loaded");

                return(new EmptyResponseDto(true));
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"{nameof(GoToSeconds)}: Unknown error occurred");
                return(new EmptyResponseDto(false, e.Message));
            }
        }
Exemplo n.º 2
0
        public Task GoToSeconds(
            int videoStreamIndex,
            int audioStreamIndex,
            int subtitleStreamIndex,
            int quality,
            double seconds,
            FFProbeFileInfo fileInfo)
        {
            if (seconds >= _player.CurrentMediaDuration)
            {
                _logger.LogWarning(
                    $"{nameof(GoToSeconds)}: Cant go to = {seconds} because is bigger or equal than " +
                    $"the media duration = {_player.CurrentMediaDuration}");
                return(Task.CompletedTask);
            }
            if (seconds < 0)
            {
                _logger.LogWarning($"{nameof(GoToSeconds)}: Wont go to = {seconds}, instead we will go to 0");
                seconds = 0;
            }

            if (_fileService.IsLocalFile(_currentFilePath))
            {
                return(StartPlay(_currentFilePath, videoStreamIndex, audioStreamIndex, subtitleStreamIndex, quality, fileInfo, seconds));
            }

            return(_player.SeekAsync(seconds));
        }
Exemplo n.º 3
0
        public HwAccelDeviceType GetHwAccelToUse(
            int videoStreamIndex,
            FFProbeFileInfo fileInfo,
            bool shouldUseHwAccel = true,
            bool isHls            = false)
        {
            if (fileInfo == null)
            {
                throw new ArgumentNullException(nameof(fileInfo), "The provided file info can't be null");
            }

            var videoInfo = fileInfo.Videos.FirstOrDefault(f => f.Index == videoStreamIndex && f.IsVideo);

            if (videoInfo is null)
            {
                throw new NullReferenceException($"The file does not have a valid video stream");
            }

            bool videoCodecIsValid = videoInfo.VideoCodecIsValid(AllowedVideoCodecs);
            var  hwAccelType       = GetHwAccelDeviceType();

            if (!videoCodecIsValid || !shouldUseHwAccel || isHls)
            {
                hwAccelType = HwAccelDeviceType.None;
            }

            return(hwAccelType);
        }
Exemplo n.º 4
0
        public Task AddSeconds(
            int videoStreamIndex,
            int audioStreamIndex,
            int subtitleStreamIndex,
            int quality,
            double seconds,
            FFProbeFileInfo fileInfo)
        {
            if (seconds >= _player.CurrentMediaDuration || _player.CurrentMediaDuration + seconds < 0)
            {
                _logger.LogWarning(
                    $"{nameof(AddSeconds)}: Cant add seconds = {seconds} because is bigger or equal than " +
                    $"the media duration = {_player.CurrentMediaDuration} or the diff is less than 0");
                return(Task.CompletedTask);
            }

            var newValue = _player.ElapsedSeconds + seconds;

            if (newValue < 0)
            {
                _logger.LogWarning($"{nameof(AddSeconds)}: The seconds to add are = {newValue}. They will be set to 0");
                newValue = 0;
            }
            else if (newValue >= _player.CurrentMediaDuration)
            {
                _logger.LogWarning(
                    $"{nameof(AddSeconds)}: The seconds to add exceeds the media duration, " +
                    $"they will be set to = {_player.CurrentMediaDuration}");
                newValue = _player.CurrentMediaDuration;
            }
            if (!_fileService.IsLocalFile(_currentFilePath))
            {
                return(_player.SeekAsync(newValue));
            }
            return(StartPlay(_currentFilePath, videoStreamIndex, audioStreamIndex, subtitleStreamIndex, quality, fileInfo, newValue));
        }
Exemplo n.º 5
0
        public Task GoToPosition(
            string filePath,
            int videoStreamIndex,
            int audioStreamIndex,
            int subtitleStreamIndex,
            int quality,
            double position,
            double totalSeconds,
            FFProbeFileInfo fileInfo)
        {
            if (position >= 0 && position <= 100)
            {
                double seconds = position * totalSeconds / 100;
                if (_fileService.IsLocalFile(filePath))
                {
                    return(StartPlay(filePath, videoStreamIndex, audioStreamIndex, subtitleStreamIndex, quality, fileInfo, seconds));
                }

                return(_player.SeekAsync(seconds));
            }

            _logger.LogWarning($"{nameof(GoToPosition)} Cant go to position = {position}");
            return(Task.CompletedTask);
        }
Exemplo n.º 6
0
        public bool AudioNeedsTranscode(int audioStreamIndex, bool forceAudioTranscode, FFProbeFileInfo fileInfo, bool checkIfAudioIsNull = false)
        {
            if (fileInfo == null)
            {
                throw new ArgumentNullException(nameof(fileInfo), "The provided file info can't be null");
            }

            var audioInfo = fileInfo.Streams.FirstOrDefault(f => f.Index == audioStreamIndex && f.IsAudio);

            if (checkIfAudioIsNull && audioInfo is null)
            {
                throw new NullReferenceException("The file does not have a valid audio stream");
            }

            return(audioInfo != null &&
                   (!audioInfo.AudioCodecIsValid(AllowedMusicCodecs) ||
                    !audioInfo.AudioProfileIsValid(AllowedMusicProfiles) ||
                    forceAudioTranscode));
        }
Exemplo n.º 7
0
        public bool VideoNeedsTranscode(int videoStreamIndex, bool forceVideoTranscode, VideoScaleType selectedScale, FFProbeFileInfo fileInfo)
        {
            if (fileInfo == null)
            {
                throw new ArgumentNullException(nameof(fileInfo), "The provided file info can't be null");
            }

            var videoInfo = fileInfo.Videos.FirstOrDefault(f => f.Index == videoStreamIndex && f.IsVideo);

            if (videoInfo is null)
            {
                throw new NullReferenceException("The file does not have a valid video stream");
            }

            bool videoCodecIsValid   = videoInfo.VideoCodecIsValid(AllowedVideoCodecs);
            bool videoLevelIsValid   = videoInfo.VideoLevelIsValid(MaxVideoLevel);
            bool videoProfileIsValid = videoInfo.VideoProfileIsValid(AllowedVideoProfiles);

            return(!videoCodecIsValid ||
                   !videoProfileIsValid ||
                   !videoLevelIsValid ||
                   forceVideoTranscode ||
                   selectedScale != VideoScaleType.Original);
        }
Exemplo n.º 8
0
 public Task StartPlay(PlayCliFileRequestDto request, FFProbeFileInfo fileInfo)
 => StartPlay(request.Mrl, request.VideoStreamIndex, request.AudioStreamIndex, request.SubtitleStreamIndex, request.Quality, fileInfo, request.Seconds);
Exemplo n.º 9
0
 public Task GoToSeconds(PlayCliFileRequestDto request, FFProbeFileInfo fileInfo)
 => GoToSeconds(request.VideoStreamIndex, request.AudioStreamIndex, request.SubtitleStreamIndex, request.Quality, request.Seconds, fileInfo);
Exemplo n.º 10
0
        public async Task StartPlay(
            string mrl,
            int videoStreamIndex,
            int audioStreamIndex,
            int subtitleStreamIndex,
            int quality,
            FFProbeFileInfo fileInfo,
            double seconds = 0)
        {
            if (fileInfo == null)
            {
                _logger.LogWarning($"{nameof(StartPlay)}: No file info was provided for mrl = {mrl}");
                throw new ArgumentNullException(nameof(fileInfo), "A file info must be provided");
            }
            _ffmpegService.KillTranscodeProcess();
            bool isLocal     = _fileService.IsLocalFile(mrl);
            bool isUrlFile   = _fileService.IsUrlFile(mrl);
            bool isVideoFile = _fileService.IsVideoFile(mrl);
            bool isMusicFile = _fileService.IsMusicFile(mrl);

            if (!isLocal && !isUrlFile)
            {
                var msg = "Invalid = {mrl}. Its not a local file and its not a url file";
                _logger.LogWarning($"{nameof(StartPlay)}: {msg}");
                throw new NotSupportedException(msg);
            }

            if (AvailableDevices.Count == 0)
            {
                _logger.LogWarning($"{nameof(StartPlay)}: No renders were found, file = {mrl}");
                throw new NoDevicesException();
            }

            if (_connecting)
            {
                _logger.LogWarning($"{nameof(StartPlay)}: We are in the middle of connecting to a device, can't play file = {mrl} right now");
                throw new ConnectingException();
            }

            if (!_renderWasSet && AvailableDevices.Count > 0)
            {
                await SetCastRenderer(AvailableDevices.First()).ConfigureAwait(false);
            }
            // create new media
            bool videoNeedsTranscode = isVideoFile && _ffmpegService.VideoNeedsTranscode(videoStreamIndex, _appSettings.ForceVideoTranscode, _appSettings.VideoScale, fileInfo);
            bool audioNeedsTranscode = _ffmpegService.AudioNeedsTranscode(audioStreamIndex, _appSettings.ForceAudioTranscode, fileInfo, isMusicFile);
            var  hwAccelToUse        = isVideoFile ? _ffmpegService.GetHwAccelToUse(videoStreamIndex, fileInfo, _appSettings.EnableHardwareAcceleration) : HwAccelDeviceType.None;

            _currentFilePath = mrl;
            string title = isLocal ? Path.GetFileName(mrl) : mrl;
            string url   = isLocal
                ? _appWebServer.GetMediaUrl(
                mrl,
                videoStreamIndex,
                audioStreamIndex,
                seconds,
                videoNeedsTranscode,
                audioNeedsTranscode,
                hwAccelToUse,
                _appSettings.VideoScale,
                fileInfo.Videos.Find(f => f.Index == videoStreamIndex)?.WidthAndHeightText)
                : mrl;

            var metadata = isVideoFile ? new MovieMetadata
            {
                Title = title,
            } : new GenericMediaMetadata
            {
                Title = title,
            };
            var media = new MediaInformation
            {
                ContentId = url,
                Metadata  = metadata,
                //You have to set the contenttype before hand, with that, the album art of a music file will be shown
                ContentType = _ffmpegService.GetOutputTranscodeMimeType(mrl)
            };

            var  activeTrackIds    = new List <int>();
            bool useSubTitleStream = subtitleStreamIndex >= 0;

            if (useSubTitleStream || !string.IsNullOrEmpty(GetSubTitles?.Invoke()))
            {
                _logger.LogInformation($"{nameof(StartPlay)}: Subtitles were specified, generating a compatible one...");
                string subtitleLocation = useSubTitleStream ? mrl : GetSubTitles.Invoke();
                string subTitleFilePath = _fileService.GetSubTitleFilePath();
                await _ffmpegService.GenerateSubTitles(
                    subtitleLocation,
                    subTitleFilePath,
                    seconds,
                    useSubTitleStream?subtitleStreamIndex : 0,
                    _appSettings.SubtitleDelayInSeconds,
                    default);

                _subtitle.TrackContentId = _appWebServer.GetSubTitlePath(subTitleFilePath);
                _logger.LogInformation($"{nameof(StartPlay)}: Subtitles were generated");
                media.Tracks.Add(_subtitle);
                media.TextTrackStyle = GetSubtitleStyle();
                activeTrackIds.Add(SubTitleDefaultTrackId);
            }

            string firstThumbnail = await GetFirstThumbnail().ConfigureAwait(false);

            string imgUrl = string.Empty;

            if (isLocal)
            {
                _logger.LogInformation($"{nameof(StartPlay)}: File is a local one, generating metadata...");
                imgUrl = _appWebServer.GetPreviewPath(firstThumbnail);

                if (isVideoFile)
                {
                    media.StreamType = StreamType.Live;
                }
                media.Duration = fileInfo.Format.Duration;
                if (isMusicFile)
                {
                    media.Metadata = new MusicTrackMediaMetadata
                    {
                        Title     = title,
                        AlbumName = fileInfo.Format.Tag?.Album,
                        Artist    = fileInfo.Format.Tag?.Artist,
                    };
                }
            }
            else if (_youtubeUrlDecoder.IsYoutubeUrl(media.ContentId))
            {
                _logger.LogInformation($"{nameof(StartPlay)}: File is a youtube link, parsing it...");
                var ytMedia = await _youtubeUrlDecoder.Parse(media.ContentId, quality);

                QualitiesChanged?.Invoke(ytMedia.SelectedQuality, ytMedia.Qualities);

                imgUrl                  = ytMedia.ThumbnailUrl;
                media.ContentId         = ytMedia.Url;
                media.Metadata.Title    = ytMedia.Title;
                media.Metadata.Subtitle = ytMedia.Description;
                if (ytMedia.IsHls)
                {
                    fileInfo = await _ffmpegService.GetFileInfo(ytMedia.Url, default);

                    if (fileInfo == null)
                    {
                        _logger.LogWarning($"{nameof(StartPlay)}: Couldn't get the file info for url = {ytMedia.Url}");
                        throw new Exception($"File info is null for yt hls = {ytMedia.Url}");
                    }

                    var closestQuality = fileInfo.Videos
                                         .Select(v => v.Height)
                                         .GetClosest(quality);
                    var videoInfo = fileInfo.Videos.First(v => v.Height == closestQuality);
                    videoStreamIndex = videoInfo.Index;
                    audioStreamIndex = fileInfo.Audios.Any()
                        ? fileInfo.Audios.Select(a => a.Index).GetClosest(videoStreamIndex)
                        : -1;

                    videoNeedsTranscode = _ffmpegService.VideoNeedsTranscode(videoStreamIndex, _appSettings.ForceVideoTranscode, _appSettings.VideoScale, fileInfo);
                    audioNeedsTranscode = _ffmpegService.AudioNeedsTranscode(audioStreamIndex, _appSettings.ForceAudioTranscode, fileInfo);

                    media.Duration   = -1;
                    media.StreamType = StreamType.Live;
                    media.ContentId  = _appWebServer.GetMediaUrl(
                        ytMedia.Url,
                        videoStreamIndex,
                        audioStreamIndex,
                        seconds,
                        videoNeedsTranscode,
                        audioNeedsTranscode,
                        HwAccelDeviceType.None,
                        VideoScaleType.Original,
                        videoInfo.WidthAndHeightText);
                    media.ContentType = _ffmpegService.GetOutputTranscodeMimeType(media.ContentId);
                }
            }

            if (!string.IsNullOrEmpty(imgUrl))
            {
                media.Metadata.Images.Add(new GoogleCast.Models.Image
                {
                    Url = imgUrl
                });
            }

            _logger.LogInformation($"{nameof(StartPlay)}: Trying to load url = {media.ContentId}");
            var status = await _player.LoadAsync(media, true, seconds, activeTrackIds.ToArray());

            if (status is null)
            {
                var msg = $"Couldn't load url = {media.ContentId}";
                _logger.LogWarning($"{nameof(StartPlay)}: {msg}");
                throw new Exception(msg);
            }
            _logger.LogInformation($"{nameof(StartPlay)}: Url was successfully loaded");

            FileLoaded(metadata.Title, imgUrl, _player.CurrentMediaDuration, _player.CurrentVolumeLevel, _player.IsMuted);
        }