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