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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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