windows之FFmpeg 软、硬解码

设备支持qsv硬件加速,则虚线判断是否可以支持硬解码

namespace Coder.FFmpeg
{
    /// <summary>
    /// 视频解码器
    /// </summary>
    public unsafe class FFmpegVideoDecoder : IDisposable
    {
        #region 私有变量

        //格式转换器
        private readonly FrameConverter _frameConverter;

        //解码器正在运行
        private bool _isCodecRunning;

        private readonly int _convertedFrameBufferSize;

        #endregion

        #region 公共属性

        /// <summary>
        /// 编解码格式
        /// </summary>
        public AVCodecID AVCodecID { get; set; } = AVCodecID.AV_CODEC_ID_H264;

        /// <summary>
        /// 解码参数设置(构造时已设置默认值)
        /// </summary>
        public AVCodecContext* DecodeContext { get;}

        #endregion

        /// <summary>
        /// 创建视频解码器
        /// </summary>
        /// <param name="originSize">原始大小</param>
        /// <param name="destinationSize">解码后大小</param>
        /// <param name="originPixelFormat">原始像素格式</param>
        /// <param name="destinationPixelFormat">目标像素格式</param>
        /// <exception cref="InvalidOperationException"></exception>
        public FFmpegVideoDecoder(Size originSize, Size destinationSize, AVPixelFormat originPixelFormat, AVPixelFormat destinationPixelFormat)
        {
            bool isHwDevice = false;
            var pDeccodec = ffmpeg.avcodec_find_decoder_by_name("h264_qsv");
            var type = ffmpeg.av_hwdevice_find_type_by_name("qsv");
            if (pDeccodec != null)
            {
                for (int i = 0; ; i++)
                {
                    AVCodecHWConfig* config = ffmpeg.avcodec_get_hw_config(pDeccodec, i);
                    var avHwdeviceGetTypeName = ffmpeg.av_hwdevice_get_type_name(type);
                    if (config == null)
                    {
                        Console.WriteLine($"decoder qsv  does not support device type {avHwdeviceGetTypeName}");
                        break;
                    }

                    if ((config->methods & 0x01) == 0x01 && config->device_type == type)
                    {
                        isHwDevice = true;
                        originPixelFormat = config->pix_fmt;
                        Console.WriteLine($"decoder qsv  support device type {avHwdeviceGetTypeName}");
                        break;
                    }
                }
            }
            else
            {
                pDeccodec = ffmpeg.avcodec_find_decoder(AVCodecID);
            }

            if (pDeccodec == null) throw new InvalidOperationException("Codec not found.");
            DecodeContext = ffmpeg.avcodec_alloc_context3(pDeccodec);
            if (isHwDevice)
            {
                ffmpeg.av_hwdevice_ctx_create(&DecodeContext->hw_device_ctx, type, null, null, 0).ThrowExceptionIfError();
            }
            #region 默认解码参数

            DecodeContext->width = destinationSize.Width;
            DecodeContext->height = destinationSize.Height;
            DecodeContext->time_base = new AVRational { num = 1, den = 30 };
            DecodeContext->pix_fmt = originPixelFormat;
            DecodeContext->framerate = new AVRational { num = 30, den = 1 };
            DecodeContext->gop_size = 30;
            // 设置预测算法
            DecodeContext->flags |= ffmpeg.AV_CODEC_FLAG_PSNR;
            DecodeContext->flags2 |= ffmpeg.AV_CODEC_FLAG2_FAST;
            DecodeContext->max_b_frames = 0;
            DecodeContext->bit_rate = 8000000;
            ffmpeg.av_opt_set(DecodeContext->priv_data, "preset", "ultrafast", 0);
            ffmpeg.av_opt_set(DecodeContext->priv_data, "tune", "zerolatency", 0);

            #endregion

            //打开解码器
            ffmpeg.avcodec_open2(DecodeContext, pDeccodec, null).ThrowExceptionIfError();
            _frameConverter = new FrameConverter(originSize, AVPixelFormat.AV_PIX_FMT_NV12, destinationSize, destinationPixelFormat);

            _convertedFrameBufferSize = ffmpeg.av_image_get_buffer_size(destinationPixelFormat, destinationSize.Width, destinationSize.Height, 4);
            if (_convertedFrameBufferSize < 0) throw new InvalidOperationException("Could not get buffer size.");

            _isCodecRunning = true;
        }

        /// <summary>
        /// 解码
        /// </summary>
        /// <param name="originData"></param>
        /// <returns>返回解码后数据</returns>
        public byte[] Decode(byte[] originData)
        {
            if (!_isCodecRunning)
            {
                throw new InvalidOperationException("解码器未运行!");
            }

            var waitDecodePacket = ffmpeg.av_packet_alloc();
            var waitDecoderFrame = ffmpeg.av_frame_alloc();
          
            fixed (byte* waitDecodeData = originData)
            {
                waitDecodePacket->data = waitDecodeData;
                waitDecodePacket->size = originData.Length;
                ffmpeg.av_frame_unref(waitDecoderFrame);
                try
                {
                    int error;
                    do
                    {
                        ffmpeg.avcodec_send_packet(DecodeContext, waitDecodePacket);
                        error = ffmpeg.avcodec_receive_frame(DecodeContext, waitDecoderFrame);
                    } 
                    while (error == ffmpeg.AVERROR(ffmpeg.EAGAIN));
                }
                finally
                {
                    ffmpeg.av_packet_unref(waitDecodePacket);
                }

                var decodeAfterFrame = _frameConverter.Convert(*waitDecoderFrame);
                ffmpeg.av_frame_unref(waitDecoderFrame);
                
                var buffer = new byte[_convertedFrameBufferSize];
                Marshal.Copy((IntPtr)decodeAfterFrame.data[0], buffer, 0, buffer.Length);
                return buffer;
            }
        }

        /// <summary>
        /// 解码
        /// </summary>
        /// <param name="originData"></param>
        /// <returns>返回解码后数据指针和长度元组</returns>
        public (IntPtr Pointer, int Length) DecodeToIntPtr(byte[] originData)
        {
            if (!_isCodecRunning)
            {
                throw new InvalidOperationException("解码器未运行!");
            }

            var waitDecodePacket = ffmpeg.av_packet_alloc();
            var waitDecoderFrame = ffmpeg.av_frame_alloc();

            AVFrame* swFrame = ffmpeg.av_frame_alloc();

            fixed (byte* waitDecodeData = originData)
            {
                waitDecodePacket->data = waitDecodeData;
                waitDecodePacket->size = originData.Length;
                ffmpeg.av_frame_unref(waitDecoderFrame);
                try
                {
                    int error;
                    do
                    {
                        ffmpeg.avcodec_send_packet(DecodeContext, waitDecodePacket);
                        error = ffmpeg.avcodec_receive_frame(DecodeContext, waitDecoderFrame);
                    }
                    while (error == ffmpeg.AVERROR(ffmpeg.EAGAIN));
                }
                finally
                {
                    ffmpeg.av_packet_unref(waitDecodePacket);
                }

                //从Gpu拷贝到Cpu,硬件加速数据拷贝出来处理
                ffmpeg.av_hwframe_transfer_data(swFrame, waitDecoderFrame, 0);
                var decodeAfterFrame = _frameConverter.Convert(*swFrame);
                ffmpeg.av_frame_unref(swFrame);

                // 不需要硬编码
                //ffmpeg.av_frame_unref(waitDecoderFrame);
                ffmpeg.av_frame_unref(waitDecoderFrame);
                return ((IntPtr)decodeAfterFrame.data[0],_convertedFrameBufferSize);
            }
        }

        /// <summary>
        /// 释放
        /// </summary>
        public  void Dispose()
        {
            _isCodecRunning = false;
            //释放解码器
            ffmpeg.avcodec_close(DecodeContext);
            ffmpeg.av_free(DecodeContext);
            _frameConverter.Dispose();
        }
    }
   
}

 

posted on 2024-02-28 14:13  TanZhiWei  阅读(106)  评论(0编辑  收藏  举报