mirror of https://github.com/ErsatzTV/ErsatzTV.git
Browse Source
* start to add v4l2m2m accel * add v4l2m2m pipeline * add encoders * fix decoders and encoders * output software frames from decoders * more buffers * hide v4l2m2m from uipull/2417/head
28 changed files with 498 additions and 17 deletions
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
using ErsatzTV.FFmpeg.Format; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Capabilities; |
||||
|
||||
public class V4l2m2mHardwareCapabilities(IFFmpegCapabilities ffmpegCapabilities) : IHardwareCapabilities |
||||
{ |
||||
public FFmpegCapability CanDecode( |
||||
string videoFormat, |
||||
Option<string> videoProfile, |
||||
Option<IPixelFormat> maybePixelFormat, |
||||
bool isHdr) |
||||
{ |
||||
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8); |
||||
|
||||
return (videoFormat, bitDepth) switch |
||||
{ |
||||
(VideoFormat.H264, 8) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.H264V4l2m2m) |
||||
? FFmpegCapability.Hardware |
||||
: FFmpegCapability.Software, |
||||
|
||||
(VideoFormat.Hevc, _) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.HevcV4l2m2m) |
||||
? FFmpegCapability.Hardware |
||||
: FFmpegCapability.Software, |
||||
|
||||
(VideoFormat.Mpeg2Video, 8) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.Mpeg2V4l2m2m) |
||||
? FFmpegCapability.Hardware |
||||
: FFmpegCapability.Software, |
||||
|
||||
(VideoFormat.Mpeg4, _) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.Mpeg4V4l2m2m) |
||||
? FFmpegCapability.Hardware |
||||
: FFmpegCapability.Software, |
||||
|
||||
(VideoFormat.Vc1, _) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.Vc1V4l2m2m) |
||||
? FFmpegCapability.Hardware |
||||
: FFmpegCapability.Software, |
||||
|
||||
(VideoFormat.Vp8, _) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.Vp84V4l2m2m) |
||||
? FFmpegCapability.Hardware |
||||
: FFmpegCapability.Software, |
||||
|
||||
(VideoFormat.Vp9, _) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.Vp94V4l2m2m) |
||||
? FFmpegCapability.Hardware |
||||
: FFmpegCapability.Software, |
||||
|
||||
_ => FFmpegCapability.Software |
||||
}; |
||||
} |
||||
|
||||
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 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,8 @@
@@ -0,0 +1,8 @@
|
||||
namespace ErsatzTV.FFmpeg.Decoder; |
||||
|
||||
public class DecoderV4l2m2m : DecoderBase |
||||
{ |
||||
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; |
||||
public override string Name => "implicit_v4l2m2m"; |
||||
public override string[] InputOptions(InputFile inputFile) => []; |
||||
} |
||||
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
using ErsatzTV.FFmpeg.Format; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m; |
||||
|
||||
public class DecoderH264V4l2m2m : DecoderBase |
||||
{ |
||||
public override string Name => "h264_v4l2m2m"; |
||||
|
||||
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; |
||||
|
||||
public override FrameState NextState(FrameState currentState) |
||||
{ |
||||
FrameState nextState = base.NextState(currentState); |
||||
|
||||
return currentState.PixelFormat.Match( |
||||
pixelFormat => nextState with { PixelFormat = new PixelFormatNv12(pixelFormat.Name) }, |
||||
() => nextState); |
||||
} |
||||
} |
||||
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
using ErsatzTV.FFmpeg.Format; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m; |
||||
|
||||
public class DecoderHevcV4l2m2m : DecoderBase |
||||
{ |
||||
public override string Name => "hevc_v4l2m2m"; |
||||
|
||||
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; |
||||
|
||||
public override FrameState NextState(FrameState currentState) |
||||
{ |
||||
FrameState nextState = base.NextState(currentState); |
||||
|
||||
return currentState.PixelFormat.Match( |
||||
pixelFormat => |
||||
{ |
||||
if (pixelFormat.BitDepth == 10) |
||||
{ |
||||
return nextState with { PixelFormat = new PixelFormatP010() }; |
||||
} |
||||
|
||||
return nextState with { PixelFormat = new PixelFormatNv12(pixelFormat.Name) }; |
||||
}, |
||||
() => nextState); |
||||
} |
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m; |
||||
|
||||
public class DecoderMpeg2V4l2m2m : DecoderBase |
||||
{ |
||||
public override string Name => "mpeg2_v4l2m2m"; |
||||
|
||||
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; |
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m; |
||||
|
||||
public class DecoderMpeg4V4l2m2m : DecoderBase |
||||
{ |
||||
public override string Name => "mpeg4_v4l2m2m"; |
||||
|
||||
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; |
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m; |
||||
|
||||
public class DecoderVc1V4l2m2m : DecoderBase |
||||
{ |
||||
public override string Name => "vc1_v4l2m2m"; |
||||
|
||||
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; |
||||
} |
||||
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
using ErsatzTV.FFmpeg.Format; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m; |
||||
|
||||
public class DecoderVp8V4l2m2m : DecoderBase |
||||
{ |
||||
public override string Name => "vp8_v4l2m2m"; |
||||
|
||||
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; |
||||
|
||||
public override FrameState NextState(FrameState currentState) |
||||
{ |
||||
FrameState nextState = base.NextState(currentState); |
||||
|
||||
return currentState.PixelFormat.Match( |
||||
pixelFormat => |
||||
{ |
||||
if (pixelFormat.BitDepth == 10) |
||||
{ |
||||
return nextState with { PixelFormat = new PixelFormatP010() }; |
||||
} |
||||
|
||||
return nextState with { PixelFormat = new PixelFormatNv12(pixelFormat.Name) }; |
||||
}, |
||||
() => nextState); |
||||
} |
||||
} |
||||
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
using ErsatzTV.FFmpeg.Format; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m; |
||||
|
||||
public class DecoderVp9V4l2m2m : DecoderBase |
||||
{ |
||||
public override string Name => "vp9_v4l2m2m"; |
||||
|
||||
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; |
||||
|
||||
public override FrameState NextState(FrameState currentState) |
||||
{ |
||||
FrameState nextState = base.NextState(currentState); |
||||
|
||||
return currentState.PixelFormat.Match( |
||||
pixelFormat => |
||||
{ |
||||
if (pixelFormat.BitDepth == 10) |
||||
{ |
||||
return nextState with { PixelFormat = new PixelFormatP010() }; |
||||
} |
||||
|
||||
return nextState with { PixelFormat = new PixelFormatNv12(pixelFormat.Name) }; |
||||
}, |
||||
() => nextState); |
||||
} |
||||
} |
||||
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
using ErsatzTV.FFmpeg.Format; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Encoder.V4l2m2m; |
||||
|
||||
public class EncoderH264V4l2m2m : EncoderBase |
||||
{ |
||||
public override string Name => "h264_v4l2m2m"; |
||||
public override StreamKind Kind => StreamKind.Video; |
||||
|
||||
public override string[] OutputOptions => |
||||
[ |
||||
"-c:v", Name, |
||||
"-num_capture_buffers", "16" |
||||
]; |
||||
|
||||
public override FrameState NextState(FrameState currentState) => currentState with |
||||
{ |
||||
VideoFormat = VideoFormat.H264, |
||||
FrameDataLocation = FrameDataLocation.Hardware |
||||
}; |
||||
} |
||||
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
using ErsatzTV.FFmpeg.Format; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Encoder.V4l2m2m; |
||||
|
||||
public class EncoderHevcV4l2m2m : EncoderBase |
||||
{ |
||||
public override string Name => "hevc_v4l2m2m"; |
||||
public override StreamKind Kind => StreamKind.Video; |
||||
|
||||
public override FrameState NextState(FrameState currentState) => currentState with |
||||
{ |
||||
VideoFormat = VideoFormat.Hevc, |
||||
FrameDataLocation = FrameDataLocation.Hardware |
||||
}; |
||||
} |
||||
@ -0,0 +1,158 @@
@@ -0,0 +1,158 @@
|
||||
using ErsatzTV.FFmpeg.Capabilities; |
||||
using ErsatzTV.FFmpeg.Decoder; |
||||
using ErsatzTV.FFmpeg.Decoder.V4l2m2m; |
||||
using ErsatzTV.FFmpeg.Encoder; |
||||
using ErsatzTV.FFmpeg.Encoder.V4l2m2m; |
||||
using ErsatzTV.FFmpeg.Filter; |
||||
using ErsatzTV.FFmpeg.Format; |
||||
using ErsatzTV.FFmpeg.OutputFormat; |
||||
using ErsatzTV.FFmpeg.OutputOption; |
||||
using Microsoft.Extensions.Logging; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Pipeline; |
||||
|
||||
public class V4l2m2mPipelineBuilder : SoftwarePipelineBuilder |
||||
{ |
||||
private readonly IHardwareCapabilities _hardwareCapabilities; |
||||
private readonly ILogger _logger; |
||||
|
||||
public V4l2m2mPipelineBuilder( |
||||
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; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
|
||||
// disable hw accel if decoder/encoder isn't supported
|
||||
return ffmpegState with |
||||
{ |
||||
DecoderHardwareAccelerationMode = decodeCapability == FFmpegCapability.Hardware |
||||
? HardwareAccelerationMode.V4l2m2m |
||||
: HardwareAccelerationMode.None, |
||||
EncoderHardwareAccelerationMode = encodeCapability == FFmpegCapability.Hardware |
||||
? HardwareAccelerationMode.V4l2m2m |
||||
: HardwareAccelerationMode.None |
||||
}; |
||||
} |
||||
|
||||
protected override Option<IDecoder> SetDecoder( |
||||
VideoInputFile videoInputFile, |
||||
VideoStream videoStream, |
||||
FFmpegState ffmpegState, |
||||
PipelineContext context) |
||||
{ |
||||
Option<IDecoder> maybeDecoder = (ffmpegState.DecoderHardwareAccelerationMode, videoStream.Codec) switch |
||||
{ |
||||
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Hevc) => new DecoderHevcV4l2m2m(), |
||||
(HardwareAccelerationMode.V4l2m2m, VideoFormat.H264) => new DecoderH264V4l2m2m(), |
||||
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Mpeg2Video) => new DecoderMpeg2V4l2m2m(), |
||||
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Mpeg4) => new DecoderMpeg4V4l2m2m(), |
||||
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Vc1) => new DecoderVc1V4l2m2m(), |
||||
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Vp8) => new DecoderVp8V4l2m2m(), |
||||
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Vp9) => new DecoderVp9V4l2m2m(), |
||||
|
||||
_ => 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.V4l2m2m, VideoFormat.Hevc) => |
||||
new EncoderHevcV4l2m2m(), |
||||
(HardwareAccelerationMode.V4l2m2m, VideoFormat.H264) => |
||||
new EncoderH264V4l2m2m(), |
||||
|
||||
_ => 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