140 lines
5.6 KiB
C#
140 lines
5.6 KiB
C#
/*
|
|
* Source code originally from RtspClientSharp's player example:
|
|
* https://github.com/BogdanovKirill/RtspClientSharp
|
|
*/
|
|
// ReSharper disable All
|
|
using System;
|
|
|
|
namespace IPCamLib.FFMPEG
|
|
{
|
|
internal class FFmpegDecodedVideoScaler
|
|
{
|
|
private const double MaxAspectRatioError = 0.1;
|
|
private bool _disposed;
|
|
|
|
public IntPtr Handle { get; }
|
|
public int ScaledWidth { get; }
|
|
public int ScaledHeight { get; }
|
|
public PixelFormat ScaledPixelFormat { get; }
|
|
|
|
private FFmpegDecodedVideoScaler(IntPtr handle, int scaledWidth, int scaledHeight,
|
|
PixelFormat scaledPixelFormat)
|
|
{
|
|
Handle = handle;
|
|
ScaledWidth = scaledWidth;
|
|
ScaledHeight = scaledHeight;
|
|
ScaledPixelFormat = scaledPixelFormat;
|
|
}
|
|
|
|
~FFmpegDecodedVideoScaler()
|
|
{
|
|
Dispose();
|
|
}
|
|
|
|
/// <exception cref="DecoderException"></exception>
|
|
public static FFmpegDecodedVideoScaler Create(DecodedVideoFrameParameters decodedVideoFrameParameters,
|
|
TransformParameters transformParameters)
|
|
{
|
|
if (decodedVideoFrameParameters == null)
|
|
throw new ArgumentNullException(nameof(decodedVideoFrameParameters));
|
|
if (transformParameters == null)
|
|
throw new ArgumentNullException(nameof(transformParameters));
|
|
|
|
int sourceLeft = 0;
|
|
int sourceTop = 0;
|
|
int sourceWidth = decodedVideoFrameParameters.Width;
|
|
int sourceHeight = decodedVideoFrameParameters.Height;
|
|
int scaledWidth = decodedVideoFrameParameters.Width;
|
|
int scaledHeight = decodedVideoFrameParameters.Height;
|
|
|
|
if (!transformParameters.RegionOfInterest.IsEmpty)
|
|
{
|
|
sourceLeft =
|
|
(int) (decodedVideoFrameParameters.Width * transformParameters.RegionOfInterest.Left);
|
|
sourceTop =
|
|
(int) (decodedVideoFrameParameters.Height * transformParameters.RegionOfInterest.Top);
|
|
sourceWidth =
|
|
(int) (decodedVideoFrameParameters.Width * transformParameters.RegionOfInterest.Width);
|
|
sourceHeight =
|
|
(int) (decodedVideoFrameParameters.Height * transformParameters.RegionOfInterest.Height);
|
|
}
|
|
|
|
if (!transformParameters.TargetFrameSize.IsEmpty)
|
|
{
|
|
scaledWidth = transformParameters.TargetFrameSize.Width;
|
|
scaledHeight = transformParameters.TargetFrameSize.Height;
|
|
|
|
ScalingPolicy scalingPolicy = transformParameters.ScalePolicy;
|
|
|
|
float srcAspectRatio = (float) sourceWidth / sourceHeight;
|
|
float destAspectRatio = (float) scaledWidth / scaledHeight;
|
|
|
|
if (scalingPolicy == ScalingPolicy.Auto)
|
|
{
|
|
float relativeChange = Math.Abs(srcAspectRatio - destAspectRatio) / srcAspectRatio;
|
|
|
|
scalingPolicy = relativeChange > MaxAspectRatioError
|
|
? ScalingPolicy.RespectAspectRatio
|
|
: ScalingPolicy.Stretch;
|
|
}
|
|
|
|
if (scalingPolicy == ScalingPolicy.RespectAspectRatio)
|
|
{
|
|
if (destAspectRatio < srcAspectRatio)
|
|
scaledHeight = sourceHeight * scaledWidth / sourceWidth;
|
|
else
|
|
scaledWidth = sourceWidth * scaledHeight / sourceHeight;
|
|
}
|
|
}
|
|
|
|
PixelFormat scaledPixelFormat = transformParameters.TargetFormat;
|
|
FFmpegPixelFormat scaledFFmpegPixelFormat = GetFFmpegPixelFormat(scaledPixelFormat);
|
|
FFmpegScalingQuality scaleQuality = GetFFmpegScaleQuality(transformParameters.ScaleQuality);
|
|
|
|
int resultCode = FFmpegVideoPInvoke.CreateVideoScaler(sourceLeft, sourceTop, sourceWidth, sourceHeight,
|
|
decodedVideoFrameParameters.PixelFormat,
|
|
scaledWidth, scaledHeight, scaledFFmpegPixelFormat, scaleQuality, out var handle);
|
|
|
|
if (resultCode != 0)
|
|
throw new DecoderException(@"An error occurred while creating scaler, code: {resultCode}");
|
|
|
|
return new FFmpegDecodedVideoScaler(handle, scaledWidth, scaledHeight, scaledPixelFormat);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
|
|
_disposed = true;
|
|
FFmpegVideoPInvoke.RemoveVideoScaler(Handle);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private static FFmpegScalingQuality GetFFmpegScaleQuality(ScalingQuality scalingQuality)
|
|
{
|
|
if (scalingQuality == ScalingQuality.Nearest)
|
|
return FFmpegScalingQuality.Point;
|
|
if (scalingQuality == ScalingQuality.Bilinear)
|
|
return FFmpegScalingQuality.Bilinear;
|
|
if (scalingQuality == ScalingQuality.FastBilinear)
|
|
return FFmpegScalingQuality.FastBilinear;
|
|
if (scalingQuality == ScalingQuality.Bicubic)
|
|
return FFmpegScalingQuality.Bicubic;
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(scalingQuality));
|
|
}
|
|
|
|
private static FFmpegPixelFormat GetFFmpegPixelFormat(PixelFormat pixelFormat)
|
|
{
|
|
if (pixelFormat == PixelFormat.Bgra32)
|
|
return FFmpegPixelFormat.BGRA;
|
|
if (pixelFormat == PixelFormat.Grayscale)
|
|
return FFmpegPixelFormat.GRAY8;
|
|
if (pixelFormat == PixelFormat.Bgr24)
|
|
return FFmpegPixelFormat.BGR24;
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(pixelFormat));
|
|
}
|
|
}
|
|
} |