设计使用 ffmpeg 头文件和库 实现 win32 上 H265 解码器
下面是一个使用FFmpeg在Win32上实现H265解码器的完整设计:
项目结构
H265Decoder/
├── include/
│ └── H265Decoder.h
├── src/
│ └── H265Decoder.cpp
├── lib/
│ ├── avcodec.lib
│ ├── avformat.lib
│ ├── avutil.lib
│ └── swscale.lib
├── dll/
│ ├── avcodec-58.dll
│ ├── avformat-58.dll
│ ├── avutil-56.dll
│ └── swscale-5.dll
└── examples/
└── main.cpp
头文件设计 (H265Decoder.h)
cpp
#pragma once
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
}
#include <string>
#include <vector>
#include <functional>
class H265Decoder {
public:
// 解码器状态
enum class DecoderState {
UNINITIALIZED,
INITIALIZED,
DECODING,
ERROR,
CLOSED
};
// 帧数据回调函数
using FrameCallback = std::function<void(const uint8_t* data, int width, int height, int stride)>;
H265Decoder();
~H265Decoder();
// 初始化解码器
bool initialize();
// 解码H265数据
bool decode(const uint8_t* data, size_t size, int64_t pts = AV_NOPTS_VALUE);
// 刷新解码器(处理剩余帧)
bool flush();
// 关闭解码器
void close();
// 设置帧回调
void setFrameCallback(FrameCallback callback);
// 获取解码器状态
DecoderState getState() const;
// 获取视频信息
int getWidth() const;
int getHeight() const;
AVPixelFormat getPixelFormat() const;
// 错误信息
std::string getLastError() const;
private:
bool initCodec();
bool processPacket(AVPacket* packet);
bool processFrame(AVFrame* frame);
void cleanup();
AVCodec* codec_;
AVCodecContext* codecContext_;
AVFrame* frame_;
AVPacket* packet_;
SwsContext* swsContext_;
DecoderState state_;
std::string lastError_;
FrameCallback frameCallback_;
int width_;
int height_;
AVPixelFormat pixelFormat_;
// 用于颜色空间转换
uint8_t* swsBuffer_;
int swsBufferSize_;
};
实现文件 (H265Decoder.cpp)
cpp
#include "H265Decoder.h"
#include <iostream>
#include <cstring>
#define LOG_ERROR(msg) lastError_ = std::string(__FUNCTION__) + ": " + (msg)
H265Decoder::H265Decoder()
: codec_(nullptr)
, codecContext_(nullptr)
, frame_(nullptr)
, packet_(nullptr)
, swsContext_(nullptr)
, state_(DecoderState::UNINITIALIZED)
, width_(0)
, height_(0)
, pixelFormat_(AV_PIX_FMT_NONE)
, swsBuffer_(nullptr)
, swsBufferSize_(0) {
}
H265Decoder::~H265Decoder() {
close();
}
bool H265Decoder::initialize() {
if (state_ != DecoderState::UNINITIALIZED) {
LOG_ERROR("Decoder already initialized");
return false;
}
// 注册所有编解码器
avcodec_register_all();
// 查找HEVC解码器
codec_ = avcodec_find_decoder(AV_CODEC_ID_HEVC);
if (!codec_) {
LOG_ERROR("HEVC codec not found");
return false;
}
codecContext_ = avcodec_alloc_context3(codec_);
if (!codecContext_) {
LOG_ERROR("Could not allocate codec context");
return false;
}
// 设置解码器参数
codecContext_->thread_count = 4; // 多线程解码
codecContext_->thread_type = FF_THREAD_FRAME;
// 打开解码器
if (avcodec_open2(codecContext_, codec_, nullptr) < 0) {
LOG_ERROR("Could not open codec");
cleanup();
return false;
}
// 分配帧和包
frame_ = av_frame_alloc();
packet_ = av_packet_alloc();
if (!frame_ || !packet_) {
LOG_ERROR("Could not allocate frame or packet");
cleanup();
return false;
}
state_ = DecoderState::INITIALIZED;
return true;
}
bool H265Decoder::decode(const uint8_t* data, size_t size, int64_t pts) {
if (state_ != DecoderState::INITIALIZED && state_ != DecoderState::DECODING) {
LOG_ERROR("Decoder not initialized");
return false;
}
if (size == 0 || !data) {
LOG_ERROR("Invalid input data");
return false;
}
state_ = DecoderState::DECODING;
// 准备AVPacket
av_packet_unref(packet_);
packet_->data = const_cast<uint8_t*>(data);
packet_->size = static_cast<int>(size);
packet_->pts = pts;
return processPacket(packet_);
}
bool H265Decoder::processPacket(AVPacket* packet) {
int ret = avcodec_send_packet(codecContext_, packet);
if (ret < 0) {
char errorBuf[256];
av_strerror(ret, errorBuf, sizeof(errorBuf));
LOG_ERROR("Error sending packet: " + std::string(errorBuf));
return false;
}
while (ret >= 0) {
ret = avcodec_receive_frame(codecContext_, frame_);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
if (ret < 0) {
char errorBuf[256];
av_strerror(ret, errorBuf, sizeof(errorBuf));
LOG_ERROR("Error receiving frame: " + std::string(errorBuf));
return false;
}
if (!processFrame(frame_)) {
av_frame_unref(frame_);
return false;
}
av_frame_unref(frame_);
}
return true;
}
bool H265Decoder::processFrame(AVFrame* frame) {
// 更新视频信息
if (width_ != frame->width || height_ != frame->height ||
pixelFormat_ != static_cast<AVPixelFormat>(frame->format)) {
width_ = frame->width;
height_ = frame->height;
pixelFormat_ = static_cast<AVPixelFormat>(frame->format);
// 释放旧的转换上下文
if (swsContext_) {
sws_freeContext(swsContext_);
swsContext_ = nullptr;
}
// 创建新的转换上下文(转换为RGB24)
swsContext_ = sws_getContext(
width_, height_, pixelFormat_,
width_, height_, AV_PIX_FMT_RGB24,
SWS_BILINEAR, nullptr, nullptr, nullptr);
if (!swsContext_) {
LOG_ERROR("Could not create scale context");
return false;
}
// 重新分配缓冲区
int requiredSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24, width_, height_, 1);
if (requiredSize > swsBufferSize_) {
delete[] swsBuffer_;
swsBuffer_ = new uint8_t[requiredSize];
swsBufferSize_ = requiredSize;
}
}
if (!frameCallback_) {
return true; // 没有回调,直接返回成功
}
// 转换颜色空间到RGB24
uint8_t* dstData[1] = { swsBuffer_ };
int dstLinesize[1] = { width_ * 3 }; // RGB24 stride
sws_scale(swsContext_, frame->data, frame->linesize, 0,
height_, dstData, dstLinesize);
// 调用回调函数
frameCallback_(swsBuffer_, width_, height_, dstLinesize[0]);
return true;
}
bool H265Decoder::flush() {
if (state_ != DecoderState::DECODING) {
return true;
}
// 发送空包刷新解码器
AVPacket flushPacket = {0};
flushPacket.data = nullptr;
flushPacket.size = 0;
int ret = avcodec_send_packet(codecContext_, &flushPacket);
if (ret < 0) {
return false;
}
// 处理所有剩余帧
while (ret >= 0) {
ret = avcodec_receive_frame(codecContext_, frame_);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
if (ret < 0) {
return false;
}
processFrame(frame_);
av_frame_unref(frame_);
}
return true;
}
void H265Decoder::close() {
flush();
cleanup();
state_ = DecoderState::CLOSED;
}
void H265Decoder::cleanup() {
if (frame_) {
av_frame_free(&frame_);
frame_ = nullptr;
}
if (packet_) {
av_packet_free(&packet_);
packet_ = nullptr;
}
if (codecContext_) {
avcodec_free_context(&codecContext_);
codecContext_ = nullptr;
}
if (swsContext_) {
sws_freeContext(swsContext_);
swsContext_ = nullptr;
}
delete[] swsBuffer_;
swsBuffer_ = nullptr;
swsBufferSize_ = 0;
codec_ = nullptr;
}
void H265Decoder::setFrameCallback(FrameCallback callback) {
frameCallback_ = callback;
}
H265Decoder::DecoderState H265Decoder::getState() const {
return state_;
}
int H265Decoder::getWidth() const {
return width_;
}
int H265Decoder::getHeight() const {
return height_;
}
AVPixelFormat H265Decoder::getPixelFormat() const {
return pixelFormat_;
}
std::string H265Decoder::getLastError() const {
return lastError_;
}
示例使用 (main.cpp)
cpp
#include "H265Decoder.h"
#include <fstream>
#include <iostream>
#include <Windows.h>
// 简单的帧回调,保存为BMP文件
void saveFrameAsBMP(const uint8_t* rgbData, int width, int height, int stride,
const std::string& filename) {
std::ofstream file(filename, std::ios::binary);
if (!file) return;
// BMP文件头
BITMAPFILEHEADER fileHeader = {0};
BITMAPINFOHEADER infoHeader = {0};
int imageSize = stride * height;
fileHeader.bfType = 0x4D42; // "BM"
fileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + imageSize;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
infoHeader.biSize = sizeof(BITMAPINFOHEADER);
infoHeader.biWidth = width;
infoHeader.biHeight = -height; // 负值表示从上到下的位图
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 24;
infoHeader.biCompression = BI_RGB;
infoHeader.biSizeImage = imageSize;
file.write(reinterpret_cast<char*>(&fileHeader), sizeof(fileHeader));
file.write(reinterpret_cast<char*>(&infoHeader), sizeof(infoHeader));
file.write(reinterpret_cast<const char*>(rgbData), imageSize);
}
class FrameSaver {
public:
FrameSaver() : frameCount_(0) {}
void operator()(const uint8_t* data, int width, int height, int stride) {
std::string filename = "frame_" + std::to_string(frameCount_++) + ".bmp";
saveFrameAsBMP(data, width, height, stride, filename);
std::cout << "Saved frame " << frameCount_ << " as " << filename << std::endl;
}
private:
int frameCount_;
};
int main() {
// 初始化FFmpeg
av_register_all();
avcodec_register_all();
H265Decoder decoder;
FrameSaver frameSaver;
if (!decoder.initialize()) {
std::cerr << "Failed to initialize decoder: " << decoder.getLastError() << std::endl;
return 1;
}
decoder.setFrameCallback(frameSaver);
// 读取H265文件并解码
std::ifstream file("test.h265", std::ios::binary);
if (!file) {
std::cerr << "Cannot open test.h265 file" << std::endl;
return 1;
}
const size_t bufferSize = 1024 * 1024; // 1MB缓冲区
std::vector<uint8_t> buffer(bufferSize);
int64_t pts = 0;
while (file.read(reinterpret_cast<char*>(buffer.data()), bufferSize)) {
size_t bytesRead = file.gcount();
if (!decoder.decode(buffer.data(), bytesRead, pts)) {
std::cerr << "Decode error: " << decoder.getLastError() << std::endl;
break;
}
pts += 1000; // 假设每帧33ms
}
// 刷新解码器
decoder.flush();
decoder.close();
std::cout << "Decoding completed successfully!" << std::endl;
return 0;
}
Visual Studio项目配置
1. 包含目录设置
$(SolutionDir)..\ffmpeg-build\include
2. 库目录设置
$(SolutionDir)..\ffmpeg-build\lib
3. 附加依赖项
avcodec.lib
avformat.lib
avutil.lib
swscale.lib
4. 预处理器定义
_CRT_SECURE_NO_WARNINGS
编译命令
batch
cl /EHsc /I".\include" /I"..\ffmpeg-build\include" ^
main.cpp src\H265Decoder.cpp ^
/link /LIBPATH:"..\ffmpeg-build\lib" ^
avcodec.lib avformat.lib avutil.lib swscale.lib
这个设计提供了完整的H265解码功能,包括错误处理、内存管理和帧回调机制,适合在Win32平台上使用。