public Command ConvertToPng(string ffmpegPath, string inputFile, string outputFile) { Process process = new FFmpegProcessBuilder(ffmpegPath, false, _logger) .WithThreads(1) .WithQuiet() .WithInput(inputFile) .WithOutputFormat("apng", outputFile) .Build(); return(Cli.Wrap(process.StartInfo.FileName) .WithArguments(process.StartInfo.ArgumentList) .WithValidation(CommandResultValidation.None) .WithEnvironmentVariables(process.StartInfo.Environment.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)) .WithStandardErrorPipe(PipeTarget.ToStream(Stream.Null))); }
public Command WrapSegmenter(string ffmpegPath, bool saveReports, Channel channel, string scheme, string host) { FFmpegPlaybackSettings playbackSettings = _playbackSettingsCalculator.ConcatSettings; Process process = new FFmpegProcessBuilder(ffmpegPath, saveReports, _logger) .WithThreads(1) .WithQuiet() .WithFormatFlags(playbackSettings.FormatFlags) .WithRealtimeOutput(true) .WithInput($"http://localhost:{Settings.ListenPort}/iptv/channel/{channel.Number}.m3u8?mode=segmenter") .WithMap("0") .WithCopyCodec() .WithMetadata(channel, None) .WithFormat("mpegts") .WithPipe() .Build(); return(Cli.Wrap(process.StartInfo.FileName) .WithArguments(process.StartInfo.ArgumentList) .WithValidation(CommandResultValidation.None) .WithEnvironmentVariables(process.StartInfo.Environment.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)) .WithStandardErrorPipe(PipeTarget.ToStream(Stream.Null))); }
public async Task <Command> ForError( string ffmpegPath, Channel channel, Option <TimeSpan> duration, string errorMessage, bool hlsRealtime, long ptsOffset) { FFmpegPlaybackSettings playbackSettings = _playbackSettingsCalculator.CalculateErrorSettings(channel.FFmpegProfile); IDisplaySize desiredResolution = channel.FFmpegProfile.Resolution; var fontSize = (int)Math.Round(channel.FFmpegProfile.Resolution.Height / 20.0); var margin = (int)Math.Round(channel.FFmpegProfile.Resolution.Height * 0.05); string subtitleFile = await new SubtitleBuilder(_tempFilePool) .WithResolution(desiredResolution) .WithFontName("Roboto") .WithFontSize(fontSize) .WithAlignment(2) .WithMarginV(margin) .WithPrimaryColor("&HFFFFFF") .WithFormattedContent(errorMessage.Replace(Environment.NewLine, "\\N")) .BuildFile(); var videoStream = new MediaStream { Index = 0 }; var audioStream = new MediaStream { Index = 0 }; string videoCodec = playbackSettings.VideoFormat switch { FFmpegProfileVideoFormat.Hevc => "libx265", FFmpegProfileVideoFormat.Mpeg2Video => "mpeg2video", _ => "libx264" }; string audioCodec = playbackSettings.AudioFormat switch { FFmpegProfileAudioFormat.Ac3 => "ac3", _ => "aac" }; FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath, false, _logger) .WithThreads(1) .WithQuiet() .WithFormatFlags(playbackSettings.FormatFlags) .WithRealtimeOutput(playbackSettings.RealtimeOutput) .WithLoopedImage(Path.Combine(FileSystemLayout.ResourcesCacheFolder, "background.png")) .WithLibavfilter() .WithInput("anullsrc") .WithSubtitleFile(subtitleFile) .WithFilterComplex( videoStream, audioStream, Path.Combine(FileSystemLayout.ResourcesCacheFolder, "background.png"), "fake-audio-path", playbackSettings.VideoFormat) .WithPixfmt("yuv420p") .WithPlaybackArgs(playbackSettings, videoCodec, audioCodec) .WithMetadata(channel, None); await duration.IfSomeAsync(d => builder = builder.WithDuration(d)); Process process = channel.StreamingMode switch { // HLS needs to segment and generate playlist StreamingMode.HttpLiveStreamingSegmenter => builder.WithHls( channel.Number, None, ptsOffset, playbackSettings.VideoTrackTimeScale, playbackSettings.FrameRate) .Build(), _ => builder.WithFormat("mpegts") .WithPipe() .Build() }; return(Cli.Wrap(process.StartInfo.FileName) .WithArguments(process.StartInfo.ArgumentList) .WithValidation(CommandResultValidation.None) .WithEnvironmentVariables(process.StartInfo.Environment.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)) .WithStandardErrorPipe(PipeTarget.ToStream(Stream.Null))); }
public async Task <Either <BaseError, string> > GenerateSongImage( string ffmpegPath, string ffprobePath, Option <string> subtitleFile, Channel channel, Option <ChannelWatermark> playoutItemWatermark, Option <ChannelWatermark> globalWatermark, MediaVersion videoVersion, string videoPath, bool boxBlur, Option <string> watermarkPath, WatermarkLocation watermarkLocation, int horizontalMarginPercent, int verticalMarginPercent, int watermarkWidthPercent, CancellationToken cancellationToken) { try { string outputFile = _tempFilePool.GetNextTempFile(TempFileCategory.SongBackground); MediaStream videoStream = await _ffmpegStreamSelector.SelectVideoStream(videoVersion); Option <ChannelWatermark> watermarkOverride = videoVersion is FallbackMediaVersion or CoverArtMediaVersion ? new ChannelWatermark { Mode = ChannelWatermarkMode.Permanent, HorizontalMarginPercent = horizontalMarginPercent, VerticalMarginPercent = verticalMarginPercent, Location = watermarkLocation, Size = WatermarkSize.Scaled, WidthPercent = watermarkWidthPercent, Opacity = 100 } : None; Option <WatermarkOptions> watermarkOptions = await GetWatermarkOptions( ffprobePath, channel, playoutItemWatermark, globalWatermark, videoVersion, watermarkOverride, watermarkPath); FFmpegPlaybackSettings playbackSettings = _playbackSettingsCalculator.CalculateErrorSettings(channel.FFmpegProfile); FFmpegPlaybackSettings scalePlaybackSettings = _playbackSettingsCalculator.CalculateSettings( StreamingMode.TransportStream, channel.FFmpegProfile, videoVersion, videoStream, None, DateTimeOffset.UnixEpoch, DateTimeOffset.UnixEpoch, TimeSpan.Zero, TimeSpan.Zero, false, Option <int> .None); FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath, false, _logger) .WithThreads(1) .WithQuiet() .WithFormatFlags(playbackSettings.FormatFlags) .WithSongInput(videoPath, videoStream.Codec, videoStream.PixelFormat, boxBlur) .WithWatermark(watermarkOptions, None, channel.FFmpegProfile.Resolution) .WithSubtitleFile(subtitleFile); foreach (IDisplaySize scaledSize in scalePlaybackSettings.ScaledSize) { builder = builder.WithScaling(scaledSize); if (NeedToPad(channel.FFmpegProfile.Resolution, scaledSize)) { builder = builder.WithBlackBars(channel.FFmpegProfile.Resolution); } } using Process process = builder .WithFilterComplex( videoStream, None, videoPath, None, playbackSettings.VideoFormat) .WithOutputFormat("apng", outputFile) .Build(); _logger.LogInformation( "ffmpeg song arguments {FFmpegArguments}", string.Join(" ", process.StartInfo.ArgumentList)); await Cli.Wrap(process.StartInfo.FileName) .WithArguments(process.StartInfo.ArgumentList) .WithValidation(CommandResultValidation.None) .ExecuteAsync(cancellationToken); return(outputFile); } catch (Exception ex) { _logger.LogWarning(ex, "Error generating song image"); _client.Notify(ex); return(Left(BaseError.New(ex.Message))); } }