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); }
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; } }
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 } }; }
public async Task PassesJobToImageGenerator() { var job = new TranscodeJob(); await _viewModel.Load(job); await _imageGenerator.Received().Generate(job); }
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()); }
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())); }
private async Task CalcTranscodeProperties() { Progress = CalcTranscodeProgress(TranscodeJob.Progress); if (IsFinalTranscodingState(TranscodeJob.Status)) { TranscodeJob.PropertyChanged -= TranscodeJobPropertyChanged; await TranscodeJob.Abort(); if (Status == DownloadStatus.Transcoding) { await PrepareLoresDownload(); } } }
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; } }
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; }
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; } }
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); }
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)); }