【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硬解解决方案,具有良好的错误处理和资源管理。

相关推荐
Hi202402174 小时前
消除FFmpeg库的SONAME依赖
linux·ffmpeg
lht6319356129 小时前
从Windows通过XRDP远程访问和控制银河麒麟 v10服务器
linux·运维·服务器·windows
阿豪学编程9 小时前
环境变量与程序地址空间
linux·运维·windows
墨倾许10 小时前
《Windows 11 + Docker:极简DVWA靶场搭建全记录》—— 附详细排错指南与最终解决方案
windows·笔记·网络安全·docker·容器·靶场
stark张宇12 小时前
盘点Nfs 文件服务在Windows上的坑??
linux·windows·centos
杨凯凡14 小时前
Docker环境搭建:Windows/macOS/Linux全平台教程
windows·macos·docker
阿昭L14 小时前
COM组件
windows
歪歪10019 小时前
解决多 Linux 客户端向 Windows 服务端的文件上传、持久化与生命周期管理问题
linux·运维·服务器·开发语言·前端·数据库·windows
山川而川-R20 小时前
ubuntu摄像头型号匹配不上_11-6
linux·windows·ubuntu
一般社员1 天前
Windows导入大型sql文件到mysql
windows·sql·mysql