下面是一个完整的FFmpeg Win32硬解H265解码器设计:
头文件声明 (H265HardwareDecoder.h)
cpp
#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 DXVA2
D3D11VA, // Windows D3D11
CUDA, // NVIDIA CUDA
NONE // 软件解码
};
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)
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 GPU
AV_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;
}
使用示例
cpp
// 使用示例
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硬解解决方案,具有良好的错误处理和资源管理。