mirror of https://github.com/ErsatzTV/ErsatzTV.git
Browse Source
* Add Rockchip Media Process Platform (rkmpp) acceleration * remove fourcc stuff; it's exclusive to videotoolbox * update changelog --------- Co-authored-by: Jason Dove <1695733+jasongdove@users.noreply.github.com>pull/2424/head
22 changed files with 361 additions and 7 deletions
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
using ErsatzTV.FFmpeg.Format; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Capabilities; |
||||
|
||||
public class RkmppHardwareCapabilities : IHardwareCapabilities |
||||
{ |
||||
public FFmpegCapability CanDecode( |
||||
string videoFormat, |
||||
Option<string> videoProfile, |
||||
Option<IPixelFormat> maybePixelFormat, |
||||
bool isHdr) => FFmpegCapability.Hardware; |
||||
|
||||
public FFmpegCapability CanEncode( |
||||
string videoFormat, |
||||
Option<string> videoProfile, |
||||
Option<IPixelFormat> maybePixelFormat) |
||||
{ |
||||
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8); |
||||
|
||||
return (videoFormat, bitDepth) switch |
||||
{ |
||||
// 10-bit hevc encoding is not yet supported by ffmpeg
|
||||
(VideoFormat.Hevc, 10) => FFmpegCapability.Software, |
||||
|
||||
// 10-bit h264 encoding is not support by any hardware
|
||||
(VideoFormat.H264, 10) => FFmpegCapability.Software, |
||||
|
||||
_ => FFmpegCapability.Hardware |
||||
}; |
||||
} |
||||
|
||||
public Option<RateControlMode> GetRateControlMode(string videoFormat, Option<IPixelFormat> maybePixelFormat) => |
||||
Option<RateControlMode>.None; |
||||
} |
||||
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
using ErsatzTV.FFmpeg.Format; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Decoder; |
||||
|
||||
public class DecoderRkmpp : DecoderBase |
||||
{ |
||||
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; |
||||
|
||||
public override string Name => "implicit_rkmpp"; |
||||
public override string[] InputOptions(InputFile inputFile) => Array.Empty<string>(); |
||||
|
||||
/* |
||||
public override string[] InputOptions(InputFile inputFile) => |
||||
new[] { "-hwaccel_output_format", "drm_prime" }; |
||||
*/ |
||||
|
||||
public override FrameState NextState(FrameState currentState) |
||||
{ |
||||
FrameState nextState = base.NextState(currentState); |
||||
|
||||
return currentState.PixelFormat.Match( |
||||
pixelFormat => pixelFormat.BitDepth == 8 |
||||
? nextState with { PixelFormat = new PixelFormatNv12(pixelFormat.Name) } |
||||
: nextState with { PixelFormat = new PixelFormatNv15(pixelFormat.Name) }, |
||||
() => nextState); |
||||
} |
||||
} |
||||
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
using ErsatzTV.FFmpeg.Format; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Encoder.Rkmpp; |
||||
|
||||
public class EncoderH264Rkmpp(Option<string> maybeVideoProfile) : EncoderBase |
||||
{ |
||||
public override string Name => "h264_rkmpp"; |
||||
public override StreamKind Kind => StreamKind.Video; |
||||
|
||||
public override string[] OutputOptions |
||||
{ |
||||
get |
||||
{ |
||||
foreach (string videoProfile in maybeVideoProfile) |
||||
{ |
||||
return |
||||
[ |
||||
"-c:v", Name, |
||||
"-profile:v", videoProfile.ToLowerInvariant() |
||||
]; |
||||
} |
||||
|
||||
return base.OutputOptions; |
||||
} |
||||
} |
||||
|
||||
public override FrameState NextState(FrameState currentState) => currentState with |
||||
{ |
||||
VideoFormat = VideoFormat.H264, |
||||
FrameDataLocation = FrameDataLocation.Hardware |
||||
}; |
||||
} |
||||
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
using ErsatzTV.FFmpeg.Format; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Encoder.Rkmpp; |
||||
|
||||
public class EncoderHevcRkmpp : EncoderBase |
||||
{ |
||||
private readonly int _desiredBitDepth; |
||||
|
||||
public EncoderHevcRkmpp(int desiredBitDepth) => _desiredBitDepth = desiredBitDepth; |
||||
|
||||
public override string Name => "hevc_rkmpp"; |
||||
public override StreamKind Kind => StreamKind.Video; |
||||
|
||||
public override string[] OutputOptions => base.OutputOptions.Concat( |
||||
new[] |
||||
{ |
||||
"-profile:v", |
||||
_desiredBitDepth == 10 ? "main10" : "main" |
||||
}).ToArray(); |
||||
|
||||
public override FrameState NextState(FrameState currentState) => currentState with |
||||
{ |
||||
VideoFormat = VideoFormat.Hevc, |
||||
FrameDataLocation = FrameDataLocation.Hardware |
||||
}; |
||||
} |
||||
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
namespace ErsatzTV.FFmpeg.Format; |
||||
|
||||
public class PixelFormatNv15 : IPixelFormat |
||||
{ |
||||
public PixelFormatNv15(string name) => Name = name; |
||||
|
||||
public string Name { get; } |
||||
|
||||
public string FFmpegName => "nv15"; |
||||
public int BitDepth => 8; |
||||
} |
||||
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
namespace ErsatzTV.FFmpeg.GlobalOption.HardwareAcceleration; |
||||
|
||||
public class RkmppHardwareAccelerationOption : GlobalOption |
||||
{ |
||||
public override string[] GlobalOptions => new[] { "-hwaccel", "rkmpp" }; |
||||
|
||||
public override FrameState NextState(FrameState currentState) => currentState with |
||||
{ |
||||
FrameDataLocation = FrameDataLocation.Hardware |
||||
}; |
||||
} |
||||
@ -0,0 +1,160 @@
@@ -0,0 +1,160 @@
|
||||
using ErsatzTV.FFmpeg.Capabilities; |
||||
using ErsatzTV.FFmpeg.Decoder; |
||||
using ErsatzTV.FFmpeg.Encoder; |
||||
using ErsatzTV.FFmpeg.Encoder.Rkmpp; |
||||
using ErsatzTV.FFmpeg.Filter; |
||||
using ErsatzTV.FFmpeg.Format; |
||||
using ErsatzTV.FFmpeg.GlobalOption.HardwareAcceleration; |
||||
using ErsatzTV.FFmpeg.OutputFormat; |
||||
using ErsatzTV.FFmpeg.OutputOption; |
||||
using Microsoft.Extensions.Logging; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Pipeline; |
||||
|
||||
public class RkmppPipelineBuilder : SoftwarePipelineBuilder |
||||
{ |
||||
private readonly IHardwareCapabilities _hardwareCapabilities; |
||||
private readonly ILogger _logger; |
||||
|
||||
public RkmppPipelineBuilder( |
||||
IFFmpegCapabilities ffmpegCapabilities, |
||||
IHardwareCapabilities hardwareCapabilities, |
||||
HardwareAccelerationMode hardwareAccelerationMode, |
||||
Option<VideoInputFile> videoInputFile, |
||||
Option<AudioInputFile> audioInputFile, |
||||
Option<WatermarkInputFile> watermarkInputFile, |
||||
Option<SubtitleInputFile> subtitleInputFile, |
||||
Option<ConcatInputFile> concatInputFile, |
||||
Option<GraphicsEngineInput> graphicsEngineInput, |
||||
string reportsFolder, |
||||
string fontsFolder, |
||||
ILogger logger) : base( |
||||
ffmpegCapabilities, |
||||
hardwareAccelerationMode, |
||||
videoInputFile, |
||||
audioInputFile, |
||||
watermarkInputFile, |
||||
subtitleInputFile, |
||||
concatInputFile, |
||||
graphicsEngineInput, |
||||
reportsFolder, |
||||
fontsFolder, |
||||
logger) |
||||
{ |
||||
_hardwareCapabilities = hardwareCapabilities; |
||||
_logger = logger; |
||||
_logger.LogDebug("Using RkmppPipelineBuilder"); |
||||
} |
||||
|
||||
protected override FFmpegState SetAccelState( |
||||
VideoStream videoStream, |
||||
FFmpegState ffmpegState, |
||||
FrameState desiredState, |
||||
PipelineContext context, |
||||
ICollection<IPipelineStep> pipelineSteps) |
||||
{ |
||||
FFmpegCapability decodeCapability = _hardwareCapabilities.CanDecode( |
||||
videoStream.Codec, |
||||
videoStream.Profile, |
||||
videoStream.PixelFormat, |
||||
videoStream.ColorParams.IsHdr); |
||||
FFmpegCapability encodeCapability = _hardwareCapabilities.CanEncode( |
||||
desiredState.VideoFormat, |
||||
desiredState.VideoProfile, |
||||
desiredState.PixelFormat); |
||||
|
||||
// use software encoding (rawvideo) when piping to parent hls segmenter
|
||||
if (ffmpegState.OutputFormat is OutputFormatKind.Nut) |
||||
{ |
||||
encodeCapability = FFmpegCapability.Software; |
||||
_logger.LogDebug("Using software encoder"); |
||||
} |
||||
|
||||
if (decodeCapability is FFmpegCapability.Hardware) |
||||
{ |
||||
pipelineSteps.Add(new RkmppHardwareAccelerationOption()); |
||||
_logger.LogDebug("Using RkmppHardwareAccelerationOption decoder"); |
||||
} |
||||
|
||||
// disable hw accel if decoder/encoder isn't supported
|
||||
return ffmpegState with |
||||
{ |
||||
DecoderHardwareAccelerationMode = decodeCapability == FFmpegCapability.Hardware |
||||
? HardwareAccelerationMode.Rkmpp |
||||
: HardwareAccelerationMode.None, |
||||
EncoderHardwareAccelerationMode = encodeCapability == FFmpegCapability.Hardware |
||||
? HardwareAccelerationMode.Rkmpp |
||||
: HardwareAccelerationMode.None |
||||
}; |
||||
} |
||||
|
||||
protected override Option<IDecoder> SetDecoder( |
||||
VideoInputFile videoInputFile, |
||||
VideoStream videoStream, |
||||
FFmpegState ffmpegState, |
||||
PipelineContext context) |
||||
{ |
||||
Option<IDecoder> maybeDecoder = (ffmpegState.DecoderHardwareAccelerationMode, videoStream.Codec) switch |
||||
{ |
||||
(HardwareAccelerationMode.Rkmpp, _) => new DecoderRkmpp(), |
||||
|
||||
_ => GetSoftwareDecoder(videoStream) |
||||
}; |
||||
|
||||
foreach (IDecoder decoder in maybeDecoder) |
||||
{ |
||||
videoInputFile.AddOption(decoder); |
||||
return Some(decoder); |
||||
} |
||||
|
||||
return None; |
||||
} |
||||
|
||||
protected override Option<IEncoder> GetEncoder( |
||||
FFmpegState ffmpegState, |
||||
FrameState currentState, |
||||
FrameState desiredState) => |
||||
(ffmpegState.EncoderHardwareAccelerationMode, desiredState.VideoFormat) switch |
||||
{ |
||||
(HardwareAccelerationMode.Rkmpp, VideoFormat.Hevc) => |
||||
new EncoderHevcRkmpp(desiredState.BitDepth), |
||||
(HardwareAccelerationMode.Rkmpp, VideoFormat.H264) => |
||||
new EncoderH264Rkmpp(desiredState.VideoProfile), |
||||
|
||||
_ => GetSoftwareEncoder(ffmpegState, currentState, desiredState) |
||||
}; |
||||
|
||||
protected override List<IPipelineFilterStep> SetPixelFormat( |
||||
VideoStream videoStream, |
||||
Option<IPixelFormat> desiredPixelFormat, |
||||
FrameState currentState, |
||||
ICollection<IPipelineStep> pipelineSteps) |
||||
{ |
||||
var result = new List<IPipelineFilterStep>(); |
||||
|
||||
foreach (IPixelFormat pixelFormat in desiredPixelFormat) |
||||
{ |
||||
if (!videoStream.ColorParams.IsBt709) |
||||
{ |
||||
// _logger.LogDebug("Adding colorspace filter");
|
||||
var colorspace = new ColorspaceFilter(currentState, videoStream, pixelFormat); |
||||
currentState = colorspace.NextState(currentState); |
||||
result.Add(colorspace); |
||||
} |
||||
|
||||
if (currentState.PixelFormat.Map(f => f.FFmpegName) != pixelFormat.FFmpegName) |
||||
{ |
||||
_logger.LogDebug( |
||||
"Format {A} doesn't equal {B}", |
||||
currentState.PixelFormat.Map(f => f.FFmpegName), |
||||
pixelFormat.FFmpegName); |
||||
|
||||
//result.Add(new PixelFormatFilter(pixelFormat));
|
||||
} |
||||
|
||||
pipelineSteps.Add(new PixelFormatOutputOption(pixelFormat)); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue