Example #1
0
        protected virtual IList <MappedStream> MapStreams(FFmpegConfig config, TranscodeJob job)
        {
            IList <MappedStream>          result = new List <MappedStream>();
            IDictionary <int, StreamInfo> sourceStreamsByIndex = job.SourceInfo.Streams.ToDictionary(s => s.Index);

            for (int i = 0; i < job.Streams.Count; i++)
            {
                OutputStream outputStream = job.Streams[i];
                StreamInfo   sourceStream;

                if (!sourceStreamsByIndex.TryGetValue(outputStream.SourceStreamIndex, out sourceStream))
                {
                    throw new ArgumentException(
                              $"{nameof(job.SourceInfo)} does not contain a stream with index {outputStream.SourceStreamIndex}.",
                              nameof(job.SourceInfo));
                }

                MappedStream mappedStream = MapStream(config, sourceStream, outputStream);

                if (mappedStream != null)
                {
                    result.Add(mappedStream);
                }
            }

            return(result);
        }
Example #2
0
        public void Start(TranscodeJob job)
        {
            if (job == null)
            {
                throw new ArgumentNullException(nameof(job));
            }

            if (IsRunning)
            {
                throw new InvalidOperationException("A job is already running.");
            }

            string arguments = GenerateArguments(job);
            var    startInfo = new ProcessStartInfo()
            {
                CreateNoWindow         = true,
                FileName               = _ffmpegFileName,
                Arguments              = arguments,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                UseShellExecute        = false
            };

            _process = _processCreator.Invoke();

            SubscribeToEvents(_process);
            _process.Start(startInfo);

            if (job.SourceInfo != null)
            {
                _sourceDuration = job.SourceInfo.Duration;
            }
        }
Example #3
0
        protected override FFmpegJob Map(TranscodeJob job, FFmpegConfig config)
        {
            var result = base.Map(job, config);

            result.Format   = GetFormatName(job.Format);
            result.Metadata = job.Metadata;

            // This is a workaround for subtitle overlays with MKV reporting an incorrect duration
            if ((job.Format == ContainerFormat.Mkv) && (job.HardSubtitles?.SourceStreamIndex != null))
            {
                result.Duration = job.SourceInfo.Duration;
            }

            var trueHdStreams =
                from o in job.Streams
                where o.GetType() == typeof(OutputStream)
                join s in job.SourceInfo.Streams.OfType <AudioStreamInfo>() on o.SourceStreamIndex equals s.Index
                where s.Format == AudioFormat.DolbyTrueHd
                select o;

            if (trueHdStreams.Any())
            {
                result.MaxMuxingQueueSize = 1024;
            }

            return(result);
        }
        public void Setup()
        {
            _configManager     = Substitute.For <IConfigManager <FFmpegConfig> >();
            _argumentGenerator = Substitute.For <IFFmpegArgumentGenerator>();
            _jobRunner         = new MockJobRunner(_configManager, _argumentGenerator);

            _videoSource = new VideoStreamInfo()
            {
                Index = 0
            };
            _videoOutput = new VideoOutputStream()
            {
                SourceStreamIndex = 0
            };
            _transcodeJob = new TranscodeJob()
            {
                SourceInfo = new MediaInfo()
                {
                    FileName = "source",
                    Streams  = new List <StreamInfo>()
                    {
                        _videoSource
                    }
                },
                OutputFileName = "destination",
                Streams        = new List <OutputStream>()
                {
                    _videoOutput
                }
            };
        }
Example #5
0
        public async Task PassesJobToImageGenerator()
        {
            var job = new TranscodeJob();

            await _viewModel.Load(job);

            await _imageGenerator.Received().Generate(job);
        }
Example #6
0
        public async Task <IList <string> > Generate(TranscodeJob job)
        {
            if (job == null)
            {
                throw new ArgumentNullException(nameof(job));
            }

            if (job.SourceInfo?.Duration <= TimeSpan.Zero)
            {
                throw new ArgumentException(
                          $"{nameof(job)}.{nameof(job.SourceInfo)}.{nameof(job.SourceInfo.Duration)} is invalid.",
                          nameof(job));
            }

            var    dictionary = new ConcurrentDictionary <TimeSpan, string>();
            var    positions  = GetPositions(job.SourceInfo.Duration);
            string tempPath   = _fileSystem.Path.GetTempPath();

            var tasks = positions.Select(async position =>
            {
                var ffmpegJob = Map(job, _configManager?.Config);

                ffmpegJob.LogLevel       = "panic";
                ffmpegJob.OutputFileName = _fileSystem.Path.Combine(tempPath, $"{Guid.NewGuid()}.png");
                ffmpegJob.StartTime      = position;
                ffmpegJob.FrameCount     = 1;

                string arguments = _argumentGenerator.GenerateArguments(ffmpegJob);

                try
                {
                    var processResult = await _processRunner.Run(_ffmpegFileName, arguments, _timeout);

                    if ((processResult.ExitCode == 0) && _fileSystem.File.Exists(ffmpegJob.OutputFileName))
                    {
                        dictionary[position] = ffmpegJob.OutputFileName;
                    }
                    else
                    {
                        Trace.WriteLine("Failed to generate preview image.");
                    }
                }
                catch (ArgumentException ex)
                {
                    Trace.WriteLine(ex.Message);
                    Debug.WriteLine(ex.StackTrace);
                }
                catch (InvalidOperationException ex)
                {
                    Trace.WriteLine(ex.Message);
                    Debug.WriteLine(ex.StackTrace);
                }
            });

            await Task.WhenAll(tasks);

            return(dictionary.OrderBy(p => p.Key).Select(p => p.Value).ToArray());
        }
Example #7
0
        public void Setup()
        {
            _ffmpegFileName    = "/usr/sbin/ffmpeg";
            _processRunner     = Substitute.For <IProcessRunner>();
            _argumentGenerator = Substitute.For <IFFmpegArgumentGenerator>();
            _configManager     = Substitute.For <IConfigManager <FFmpegConfig> >();
            _fileSystem        = Substitute.For <IFileSystem>();
            _fileService       = Substitute.For <IFile>();
            _pathService       = Substitute.For <IPath>();
            _imageCount        = 3;
            _timeout           = TimeSpan.FromMilliseconds(10);
            _imageGenerator    = new PreviewImageGenerator(_ffmpegFileName,
                                                           _processRunner,
                                                           _configManager,
                                                           _argumentGenerator,
                                                           _fileSystem,
                                                           _imageCount,
                                                           _timeout);
            _tempPath     = "/Users/fred/temp";
            _transcodeJob = new TranscodeJob()
            {
                SourceInfo = new MediaInfo()
                {
                    FileName = "source",
                    Duration = TimeSpan.FromHours(1),
                    Streams  = new StreamInfo[]
                    {
                        new VideoStreamInfo()
                        {
                            Index = 0
                        }
                    }
                },
                OutputFileName = "destination",
                Streams        = new OutputStream[]
                {
                    new VideoOutputStream()
                    {
                        SourceStreamIndex = 0
                    }
                }
            };
            _ffmpegJobs = new List <FFmpegJob>();

            _argumentGenerator.When(x => x.GenerateArguments(Arg.Any <FFmpegJob>()))
            .Do(x => _ffmpegJobs.Add(x[0] as FFmpegJob));
            _fileSystem.File.Returns(_fileService);
            _fileSystem.Path.Returns(_pathService);
            _pathService.GetTempPath().Returns(_tempPath);
            _pathService.Combine(Arg.Any <string>(), Arg.Any <string>()).Returns(x => string.Join('/', x.Args()));
        }
Example #8
0
        private async Task CalcTranscodeProperties()
        {
            Progress = CalcTranscodeProgress(TranscodeJob.Progress);

            if (IsFinalTranscodingState(TranscodeJob.Status))
            {
                TranscodeJob.PropertyChanged -= TranscodeJobPropertyChanged;
                await TranscodeJob.Abort();

                if (Status == DownloadStatus.Transcoding)
                {
                    await PrepareLoresDownload();
                }
            }
        }
Example #9
0
        public async virtual Task Attach(IAppContext appContext)
        {
            _appContext = appContext;
            switch (Status)
            {
            case DownloadStatus.Transcoding:
                TranscodeJob.PropertyChanged += TranscodeJobPropertyChanged;
                TranscodeJob.Attach(Vlc);
                break;

            case DownloadStatus.Downloading:
                Transfer = await _appContext.PlatformServices.BackgroundTransfer.GetTransfer(Transfer.TransferId);

                Transfer.PropertyChanged += Transfer_PropertyChanged;
                CalcProperties();
                break;
            }
        }
Example #10
0
        public void Setup()
        {
            _ffmpegFileName    = "usr/sbin/ffmpeg";
            _process           = Substitute.For <IProcess>();
            _argumentGenerator = Substitute.For <IFFmpegArgumentGenerator>();
            _configManager     = Substitute.For <IConfigManager <FFmpegConfig> >();
            _transcoder        = new MediaTranscoder(_ffmpegFileName,
                                                     () => _process,
                                                     _configManager,
                                                     _argumentGenerator);

            _argumentGenerator.When(x => x.GenerateArguments(Arg.Any <FFmpegJob>()))
            .Do(x => _ffmpegJob = x[0] as FFmpegJob);

            _videoSource = new VideoStreamInfo()
            {
                Index = 0
            };
            _videoOutput = new VideoOutputStream()
            {
                SourceStreamIndex = 0
            };
            _transcodeJob = new TranscodeJob()
            {
                SourceInfo = new MediaInfo()
                {
                    FileName = "source",
                    Streams  = new List <StreamInfo>()
                    {
                        _videoSource
                    }
                },
                OutputFileName = "destination",
                Streams        = new List <OutputStream>()
                {
                    _videoOutput
                }
            };
            _ffmpegJob = null;
        }
Example #11
0
        public async Task Stop()
        {
            try
            {
                switch (Status)
                {
                case DownloadStatus.Transcoding:
                    await TranscodeJob.Abort();

                    break;

                case DownloadStatus.Downloading:
                    _appContext.PlatformServices.BackgroundTransfer.StopTransfer(Transfer.TransferId);
                    break;
                }
                Status = DownloadStatus.Canceled;
            }
            catch (Exception ex)
            {
                Status = DownloadStatus.Error;

                ErrorMessage = ex.Message;
            }
        }
Example #12
0
        protected virtual FFmpegJob Map(TranscodeJob job, FFmpegConfig config)
        {
            if (job == null)
            {
                throw new ArgumentNullException(nameof(job));
            }

            if (job.SourceInfo == null)
            {
                throw new ArgumentException($"{nameof(job)}.{nameof(job.SourceInfo)} is null.", nameof(job));
            }

            if (string.IsNullOrWhiteSpace(job.SourceInfo.FileName))
            {
                throw new ArgumentException(
                          $"{nameof(job)}.{nameof(job.SourceInfo)}.{nameof(job.SourceInfo.FileName)} is null or empty.",
                          nameof(job));
            }

            if (job.SourceInfo.Streams?.Any() != true)
            {
                throw new ArgumentException(
                          $"{nameof(job)}.{nameof(job.SourceInfo)}.{nameof(job.SourceInfo.Streams)} is null or empty.",
                          nameof(job));
            }

            if (string.IsNullOrWhiteSpace(job.OutputFileName))
            {
                throw new ArgumentException($"{nameof(job)}.{nameof(job.OutputFileName)} is null or empty.", nameof(job));
            }

            if (job.Streams?.Any() != true)
            {
                throw new ArgumentException($"{nameof(job)}.{nameof(job.Streams)} is null or empty.", nameof(job));
            }

            var videoSource = job.SourceInfo.Streams.OfType <VideoStreamInfo>().FirstOrDefault();

            if (videoSource == null)
            {
                throw new NotSupportedException($"{nameof(job)}.{nameof(job.SourceInfo)} must contain a video stream.");
            }

            var result = new FFmpegJob()
            {
                HideBanner     = true,
                Overwrite      = true,
                InputFileName  = job.SourceInfo.FileName,
                OutputFileName = job.OutputFileName
            };

            SubtitleInfo subtitleInfo = null;

            if (job.HardSubtitles != null)
            {
                int i = 0;

                subtitleInfo = job.SourceInfo.Streams.OfType <SubtitleStreamInfo>()
                               .Select(s => new SubtitleInfo()
                {
                    AbsoluteIndex = s.Index,
                    RelativeIndex = i++,
                    SubtitleType  = s.SubtitleType,
                    FileName      = job.SourceInfo.FileName
                })
                               .FirstOrDefault(s =>
                                               s.AbsoluteIndex == job.HardSubtitles.SourceStreamIndex);

                if (subtitleInfo == null)
                {
                    throw new ArgumentException(
                              $"{nameof(job)}.{nameof(job.HardSubtitles)} contains an invalid index.", nameof(job));
                }

                if (job.HardSubtitles.ForcedOnly)
                {
                    result.ForcedSubtitlesOnly = true;
                }

                result.CanvasSize = videoSource.Dimensions;
            }

            result.Streams = MapStreams(config, job);

            var videoOutput = job.Streams.OfType <VideoOutputStream>()
                              .FirstOrDefault(s => s.SourceStreamIndex == videoSource.Index);

            if (videoOutput != null)
            {
                result.Filters = GetVideoFilters(config, videoSource, videoOutput, subtitleInfo);
            }

            return(result);
        }
Example #13
0
        protected virtual string GenerateArguments(TranscodeJob job)
        {
            var ffmpegJob = Map(job, _configManager.Config);

            return(_argumentGenerator.GenerateArguments(ffmpegJob));
        }
            protected override FFmpegJob Map(TranscodeJob job, FFmpegConfig config)
            {
                ConfigPassed = config;

                return(JobToMap ?? base.Map(job, config));
            }
 public FFmpegJob CallMap(TranscodeJob job, FFmpegConfig config)
 {
     return(Map(job, config));
 }
 public new string GenerateArguments(TranscodeJob job)
 {
     return(base.GenerateArguments(job));
 }