public static FFmpegVideoDecoder CreateDecoder(FFmpegVideoCodecId videoCodecId)
        {
            int    resultCode;
            IntPtr decoderPtr;

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                resultCode = FFmpegVideoPInvokeWin.CreateVideoDecoder(videoCodecId, out decoderPtr);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                resultCode = FFmpegVideoPInvokeLinux.CreateVideoDecoder(videoCodecId, out decoderPtr);
            }
            else
            {
                throw new PlatformNotSupportedException();
            }

            if (resultCode != 0)
            {
                throw new DecoderException(
                          $"CreateDecoder say: An error occurred while creating video decoder for {videoCodecId} codec, code: {resultCode}");
            }

            return(new FFmpegVideoDecoder(videoCodecId, decoderPtr));
        }
        private void TransformTo(IntPtr buffer, int bufferStride, TransformParameters parameters)
        {
            if (!_scalersMap.TryGetValue(parameters, out FFmpegDecodedVideoScaler videoScaler))
            {
                videoScaler = FFmpegDecodedVideoScaler.Create(_currentFrameParameters, parameters);
                _scalersMap.Add(parameters, videoScaler);
            }

            int resultCode;

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                resultCode = FFmpegVideoPInvokeWin.ScaleDecodedVideoFrame(_decoderHandle, videoScaler.Handle, buffer, bufferStride);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                resultCode = FFmpegVideoPInvokeLinux.ScaleDecodedVideoFrame(_decoderHandle, videoScaler.Handle, buffer, bufferStride);
            }
            else
            {
                throw new PlatformNotSupportedException();
            }

            if (resultCode != 0)
            {
                throw new DecoderException($"An error occurred while converting decoding video frame, {_videoCodecId} codec, code: {resultCode}");
            }
        }
        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                FFmpegVideoPInvokeWin.RemoveVideoScaler(Handle);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                FFmpegVideoPInvokeLinux.RemoveVideoScaler(Handle);
            }
            else
            {
                throw new PlatformNotSupportedException();
            }

            GC.SuppressFinalize(this);
        }
        public unsafe IDecodedVideoFrame TryDecode(RawVideoFrame rawVideoFrame)
        {
            fixed(byte *rawBufferPtr = &rawVideoFrame.FrameSegment.Array[rawVideoFrame.FrameSegment.Offset])
            {
                int resultCode;

                if (rawVideoFrame is RawH264IFrame rawH264IFrame)
                {
                    if (rawH264IFrame.SpsPpsSegment.Array != null &&
                        !_extraData.SequenceEqual(rawH264IFrame.SpsPpsSegment))
                    {
                        if (_extraData.Length != rawH264IFrame.SpsPpsSegment.Count)
                        {
                            _extraData = new byte[rawH264IFrame.SpsPpsSegment.Count];
                        }

                        Buffer.BlockCopy(rawH264IFrame.SpsPpsSegment.Array, rawH264IFrame.SpsPpsSegment.Offset,
                                         _extraData, 0, rawH264IFrame.SpsPpsSegment.Count);

                        fixed(byte *initDataPtr = &_extraData[0])
                        {
                            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                            {
                                resultCode = FFmpegVideoPInvokeWin.SetVideoDecoderExtraData(_decoderHandle, (IntPtr)initDataPtr, _extraData.Length);
                            }
                            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                            {
                                resultCode = FFmpegVideoPInvokeLinux.SetVideoDecoderExtraData(_decoderHandle, (IntPtr)initDataPtr, _extraData.Length);
                            }
                            else
                            {
                                throw new PlatformNotSupportedException();
                            }

                            if (resultCode != 0)
                            {
                                throw new DecoderException(
                                          $"TryDecode say: An error occurred while setting video extra data, {_videoCodecId} codec, code: {resultCode}");
                            }
                        }
                    }
                }

                FFmpegPixelFormat pixelFormat;
                int width, height;

                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    resultCode = FFmpegVideoPInvokeWin.DecodeFrame(_decoderHandle, (IntPtr)rawBufferPtr,
                                                                   rawVideoFrame.FrameSegment.Count, out width, out height,
                                                                   out pixelFormat);
                }
                else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                {
                    resultCode = FFmpegVideoPInvokeLinux.DecodeFrame(_decoderHandle, (IntPtr)rawBufferPtr,
                                                                     rawVideoFrame.FrameSegment.Count, out width, out height,
                                                                     out pixelFormat);
                }
                else
                {
                    throw new PlatformNotSupportedException();
                }

                if (resultCode != 0)
                {
                    return(null);
                }

                if (_currentFrameParameters.Width != width || _currentFrameParameters.Height != height ||
                    _currentFrameParameters.PixelFormat != pixelFormat)
                {
                    _currentFrameParameters = new DecodedVideoFrameParameters(width, height, pixelFormat);
                    DropAllVideoScalers();
                }

                return(new DecodedVideoFrame(TransformTo));
            }
        }
        /// <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;
            IntPtr handle;

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                resultCode = FFmpegVideoPInvokeWin.CreateVideoScaler(sourceLeft, sourceTop, sourceWidth, sourceHeight,
                                                                     decodedVideoFrameParameters.PixelFormat,
                                                                     scaledWidth, scaledHeight, scaledFFmpegPixelFormat, scaleQuality, out handle);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                resultCode = FFmpegVideoPInvokeLinux.CreateVideoScaler(sourceLeft, sourceTop, sourceWidth, sourceHeight,
                                                                       decodedVideoFrameParameters.PixelFormat,
                                                                       scaledWidth, scaledHeight, scaledFFmpegPixelFormat, scaleQuality, out handle);
            }
            else
            {
                throw new PlatformNotSupportedException();
            }


            if (resultCode != 0)
            {
                throw new DecoderException(@"An error occurred while creating scaler, code: {resultCode}");
            }

            return(new FFmpegDecodedVideoScaler(handle, scaledWidth, scaledHeight, scaledPixelFormat));
        }