【win32】ffmpeg 解码器2
设计使用 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)
#pragma onceextern "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)
#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;// 准备AVPacketav_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; // 没有回调,直接返回成功}// 转换颜色空间到RGB24uint8_t* dstData[1] = { swsBuffer_ };int dstLinesize[1] = { width_ * 3 }; // RGB24 stridesws_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)
#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() {// 初始化FFmpegav_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
编译命令
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平台上使用。