【win32】FFmpeg 硬件解码器

下面是一个完整的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;
}

关键特性

  1. 自动硬件选择:支持DXVA2、D3D11VA、CUDA等
  2. 优雅降级:硬件解码失败时自动回退到软件解码
  3. 内存安全:正确的资源管理和释放顺序
  4. 状态管理:完整的状态机控制
  5. GPU到CPU传输:自动处理硬件帧下载

这个设计提供了完整的Win32平台H265硬解解决方案,具有良好的错误处理和资源管理。

相关推荐
淮北49414 小时前
windows安装minicoda
windows·python·conda
takashi_void15 小时前
如何在本地部署大语言模型(Windows,Mac,Linux)三系统教程
linux·人工智能·windows·macos·语言模型·nlp
非凡ghost17 小时前
Typora(跨平台MarkDown编辑器) v1.12.2 中文绿色版
前端·windows·智能手机·编辑器·软件需求
十五年专注C++开发17 小时前
CFF Explorer: 一款Windows PE 文件分析的好工具
c++·windows·microsoft
Bruce_Liuxiaowei18 小时前
Windows系统错误6118全面解决方案:修复此工作组的服务器列表当前无法使用
运维·服务器·windows·网络安全
水饺编程19 小时前
第3章,[标签 Win32] :窗口类03,窗口过程函数字段
c语言·c++·windows·visual studio
一苓二肆20 小时前
代码加密技术
linux·windows·python·spring·eclipse
LinXunFeng20 小时前
如何舒适地沉浸式编程,这是我的答案
windows·程序员·mac
彷徨而立20 小时前
【FFmpeg】对比 d3d12va 、d3d11va、dxva2 这三种视频硬解方案
ffmpeg
初听于你21 小时前
深入了解—揭秘计算机底层奥秘
windows·tcp/ip·计算机网络·面试·架构·电脑·vim