完整教程:【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 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)

#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)

#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

编译命令

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平台上使用。

posted @ 2025-09-24 15:41  wzzkaifa  阅读(12)  评论(0)    收藏  举报