/* * 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(); } /// 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)); } } }