【win32】FFmpeg 硬件解码器
下面是一个完整的FFmpeg Win32硬解H265解码器设计:
头文件声明 (H265HardwareDecoder.h)
#pragma once#include <Windows.h>
#include <vector>extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/hwcontext.h>
#include <libavutil/pixdesc.h>
}enum class DecoderState {UNINITIALIZED,INITIALIZED,DECODING,FLUSHING,CLOSED
};enum class HardwareType {AUTO, // 自动选择DXVA2, // Windows DXVA2D3D11VA, // Windows D3D11CUDA, // NVIDIA CUDANONE // 软件解码
};class H265HardwareDecoder {
public:H265HardwareDecoder();~H265HardwareDecoder();// 初始化解码器bool Initialize(HardwareType hwType = HardwareType::AUTO);// 解码H265数据bool Decode(const uint8_t* data, size_t size, int64_t pts = AV_NOPTS_VALUE);// 刷新解码器,获取所有剩余帧bool Flush();// 关闭解码器void Close();// 获取解码后的帧AVFrame* GetDecodedFrame();// 状态查询bool IsInitialized() const { return state_ == DecoderState::INITIALIZED || state_ == DecoderState::DECODING; }bool IsHardwareAccelerated() const { return hw_device_ctx_ != nullptr; }DecoderState GetState() const { return state_; }private:bool CreateHardwareDevice(HardwareType hwType);bool CreateCodecContext();bool SetupHardwareConfig();AVFrame* DownloadFrameFromGPU(AVFrame* hw_frame);void Cleanup();private:DecoderState state_ = DecoderState::UNINITIALIZED;// FFmpeg 核心对象const AVCodec* codec_ = nullptr;AVCodecContext* codec_ctx_ = nullptr;AVBufferRef* hw_device_ctx_ = nullptr;AVPacket* packet_ = nullptr;AVFrame* frame_ = nullptr;AVFrame* sw_frame_ = nullptr;// 硬件配置AVHWDeviceType hw_device_type_ = AV_HWDEVICE_TYPE_NONE;AVPixelFormat hw_pix_fmt_ = AV_PIX_FMT_NONE;// 统计信息int frames_decoded_ = 0;int hardware_frames_ = 0;
};
实现文件 (H265HardwareDecoder.cpp)
#include "H265HardwareDecoder.h"
#include <iostream>H265HardwareDecoder::H265HardwareDecoder() {av_log_set_level(AV_LOG_WARNING);
}H265HardwareDecoder::~H265HardwareDecoder() {Close();
}bool H265HardwareDecoder::Initialize(HardwareType hwType) {if (state_ != DecoderState::UNINITIALIZED) {return false;}// 查找H265解码器codec_ = avcodec_find_decoder(AV_CODEC_ID_HEVC);if (!codec_) {std::cerr << "HEVC decoder not found" << std::endl;return false;}// 创建硬件设备if (!CreateHardwareDevice(hwType)) {std::cerr << "Failed to create hardware device" << std::endl;return false;}// 创建解码器上下文if (!CreateCodecContext()) {std::cerr << "Failed to create codec context" << std::endl;return false;}// 分配帧和包frame_ = av_frame_alloc();sw_frame_ = av_frame_alloc();packet_ = av_packet_alloc();if (!frame_ || !sw_frame_ || !packet_) {std::cerr << "Failed to allocate frame or packet" << std::endl;Cleanup();return false;}state_ = DecoderState::INITIALIZED;std::cout << "H265 Hardware Decoder initialized successfully" << std::endl;std::cout << "Hardware device: " << av_hwdevice_get_type_name(hw_device_type_) << std::endl;return true;
}bool H265HardwareDecoder::CreateHardwareDevice(HardwareType hwType) {// 硬件类型映射std::vector<AVHWDeviceType> preferred_types;switch (hwType) {case HardwareType::DXVA2:preferred_types = { AV_HWDEVICE_TYPE_DXVA2 };break;case HardwareType::D3D11VA:preferred_types = { AV_HWDEVICE_TYPE_D3D11VA };break;case HardwareType::CUDA:preferred_types = { AV_HWDEVICE_TYPE_CUDA };break;case HardwareType::AUTO:// Windows平台推荐的硬件解码顺序preferred_types = {AV_HWDEVICE_TYPE_D3D11VA, // Win8+ 推荐AV_HWDEVICE_TYPE_DXVA2, // Win7+AV_HWDEVICE_TYPE_CUDA, // NVIDIA GPUAV_HWDEVICE_TYPE_QSV // Intel QuickSync};break;default:preferred_types = { AV_HWDEVICE_TYPE_NONE };break;}// 尝试创建硬件设备for (auto type : preferred_types) {if (type == AV_HWDEVICE_TYPE_NONE) {break;}int ret = av_hwdevice_ctx_create(&hw_device_ctx_, type, nullptr, nullptr, 0);if (ret >= 0) {hw_device_type_ = type;std::cout << "Using hardware device: " << av_hwdevice_get_type_name(type) << std::endl;return true;}}// 硬件解码失败,回退到软件解码std::cout << "Hardware decoding not available, falling back to software" << std::endl;hw_device_type_ = AV_HWDEVICE_TYPE_NONE;return true;
}bool H265HardwareDecoder::CreateCodecContext() {codec_ctx_ = avcodec_alloc_context3(codec_);if (!codec_ctx_) {return false;}// 设置硬件设备if (hw_device_ctx_) {codec_ctx_->hw_device_ctx = av_buffer_ref(hw_device_ctx_);if (!codec_ctx_->hw_device_ctx) {return false;}}// 打开解码器int ret = avcodec_open2(codec_ctx_, codec_, nullptr);if (ret < 0) {char error[AV_ERROR_MAX_STRING_SIZE];av_strerror(ret, error, sizeof(error));std::cerr << "Failed to open codec: " << error << std::endl;return false;}// 获取硬件像素格式if (hw_device_ctx_) {for (int i = 0;; i++) {const AVCodecHWConfig* config = avcodec_get_hw_config(codec_, i);if (!config) {break;}if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&config->device_type == hw_device_type_) {hw_pix_fmt_ = config->pix_fmt;break;}}}return true;
}bool H265HardwareDecoder::Decode(const uint8_t* data, size_t size, int64_t pts) {if (state_ != DecoderState::INITIALIZED && state_ != DecoderState::DECODING) {return false;}state_ = DecoderState::DECODING;// 准备数据包packet_->data = const_cast<uint8_t*>(data);packet_->size = static_cast<int>(size);packet_->pts = pts;// 发送数据到解码器int ret = avcodec_send_packet(codec_ctx_, packet_);if (ret < 0 && ret != AVERROR(EAGAIN)) {char error[AV_ERROR_MAX_STRING_SIZE];av_strerror(ret, error, sizeof(error));std::cerr << "Error sending packet: " << error << std::endl;return false;}return true;
}AVFrame* H265HardwareDecoder::GetDecodedFrame() {if (state_ != DecoderState::DECODING && state_ != DecoderState::FLUSHING) {return nullptr;}// 从解码器接收帧int ret = avcodec_receive_frame(codec_ctx_, frame_);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return nullptr;}if (ret < 0) {char error[AV_ERROR_MAX_STRING_SIZE];av_strerror(ret, error, sizeof(error));std::cerr << "Error receiving frame: " << error << std::endl;return nullptr;}frames_decoded_++;// 如果是硬件帧,需要下载到系统内存if (frame_->format == hw_pix_fmt_) {hardware_frames_++;return DownloadFrameFromGPU(frame_);}return frame_;
}AVFrame* H265HardwareDecoder::DownloadFrameFromGPU(AVFrame* hw_frame) {// 将硬件帧下载到系统内存int ret = av_hwframe_transfer_data(sw_frame_, hw_frame, 0);if (ret < 0) {std::cerr << "Error transferring frame from GPU" << std::endl;return nullptr;}// 复制元数据av_frame_copy_props(sw_frame_, hw_frame);return sw_frame_;
}bool H265HardwareDecoder::Flush() {if (state_ != DecoderState::DECODING) {return true;}state_ = DecoderState::FLUSHING;// 发送刷新包AVPacket flush_pkt = {};int ret = avcodec_send_packet(codec_ctx_, &flush_pkt);if (ret < 0 && ret != AVERROR_EOF) {return false;}state_ = DecoderState::INITIALIZED;return true;
}void H265HardwareDecoder::Close() {if (state_ == DecoderState::CLOSED) {return;}// 刷新剩余帧if (state_ == DecoderState::DECODING) {Flush();}Cleanup();state_ = DecoderState::CLOSED;std::cout << "Decoder closed. Frames decoded: " << frames_decoded_ << " (Hardware: " << hardware_frames_ << ")" << std::endl;
}void H265HardwareDecoder::Cleanup() {// 按正确顺序释放资源if (frame_) {av_frame_free(&frame_);}if (sw_frame_) {av_frame_free(&sw_frame_);}if (packet_) {av_packet_free(&packet_);}// 重要:先解除硬件关联if (codec_ctx_ && codec_ctx_->hw_device_ctx) {av_buffer_unref(&codec_ctx_->hw_device_ctx);}if (codec_ctx_) {avcodec_free_context(&codec_ctx_);}if (hw_device_ctx_) {av_buffer_unref(&hw_device_ctx_);}codec_ = nullptr;
}
使用示例
// 使用示例
int main() {H265HardwareDecoder decoder;// 初始化硬件解码器(自动选择最佳硬件)if (!decoder.Initialize(HardwareType::AUTO)) {std::cerr << "Failed to initialize decoder" << std::endl;return -1;}// 解码循环示例while (has_h265_data) {// 获取H265数据std::vector<uint8_t> h265_data = GetH265Data();// 解码if (decoder.Decode(h265_data.data(), h265_data.size())) {// 获取解码后的帧AVFrame* frame = nullptr;while ((frame = decoder.GetDecodedFrame()) != nullptr) {// 处理YUV帧数据ProcessDecodedFrame(frame);}}}// 关闭解码器decoder.Close();return 0;
}
关键特性
- 自动硬件选择:支持DXVA2、D3D11VA、CUDA等
- 优雅降级:硬件解码失败时自动回退到软件解码
- 内存安全:正确的资源管理和释放顺序
- 状态管理:完整的状态机控制
- GPU到CPU传输:自动处理硬件帧下载
这个设计提供了完整的Win32平台H265硬解解决方案,具有良好的错误处理和资源管理。