头文件:
// cpu_h265_decoder.h
#pragma once
#ifndef _CPU_H265_DECODING_HEADER_
#define _CPU_H265_DECODING_HEADER_
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include "libswscale/swscale.h"
}
#include <vector>
#include <iostream>
class CPUDecodeH265 {
public:
CPUDecodeH265() = default;
~CPUDecodeH265();
bool init_decoder();
bool cpu_h265_decode_process_bgr(const std::vector<uint8_t> &in_data, std::vector<uint8_t> &out_data,
uint32_t &out_width, uint32_t &out_height, int64_t *timestamp = NULL);
private:
const AVCodec *codec_ptr_;
AVCodecContext *codec_context_ptr_ = nullptr;
AVCodecParserContext *parser_ptr_ = nullptr;
AVPacket *pkt_ptr_ = nullptr;
AVFrame *frame_ptr_ = nullptr;
AVFrame *rgb_frame_ptr_ = nullptr;
SwsContext *sws_ctx = nullptr;
int rgb_num_bytes = 0;
uint8_t *rgb_out_data_ = nullptr;
};
#endif // _CPU_H265_DECODING_HEADER_
cpp文件:
// // cpu_h265_decoder.cpp
#include "cpu_h265_decoder.h"
CPUDecodeH265::~CPUDecodeH265() {
avcodec_close(codec_context_ptr_);
av_frame_free(&frame_ptr_);
av_frame_free(&rgb_frame_ptr_);
av_packet_free(&pkt_ptr_);
av_free(codec_context_ptr_);
sws_freeContext(sws_ctx);
if(rgb_out_data_){
av_free(rgb_out_data_);
rgb_out_data_ = nullptr;
}
}
bool CPUDecodeH265::init_decoder() {
avcodec_register_all();
// 查找解码器
codec_ptr_ = avcodec_find_decoder(AV_CODEC_ID_HEVC);
if(!codec_ptr_){
std::cout<<"Codec not found" << std::endl;
return false;
}
// 创建一个解码器上下文
codec_context_ptr_ = avcodec_alloc_context3(codec_ptr_);
if (!codec_context_ptr_) {
std::cout<<"Could not allocate video codec context" << std::endl;
return false;
}
// //
// parser_ptr_ = av_parser_init(codec_ptr_->id);
// if (!parser_ptr_)
// {
// std::cout<<"h265 parser not found. " << std::endl;
// return false;
// }
// 用于将输入数据封装成一个AVPacket
pkt_ptr_ = av_packet_alloc();
if(!pkt_ptr_) {
std::cout<< "could not allocate a AVPacket. " << std::endl;
return false;
}
av_init_packet(pkt_ptr_);
// 打开解码器
auto ret = avcodec_open2(codec_context_ptr_, codec_ptr_, NULL);
if(ret<0){
std::cout<< "could not open codec. " << std::endl;
return false;
}
// 用于接收解码后的输出数据
frame_ptr_ = av_frame_alloc();
if(!frame_ptr_) {
std::cout<< "could not allocate video frame. " << std::endl;
return false;
}
rgb_frame_ptr_ = av_frame_alloc();
if(!rgb_frame_ptr_) {
std::cout<< "could not allocate video rgb frame. " << std::endl;
return false;
}
rgb_frame_ptr_->format = AV_PIX_FMT_RGB24;
return true;
}
bool CPUDecodeH265::cpu_h265_decode_process_bgr(const std::vector<uint8_t> &in_data, std::vector<uint8_t> &out_data,
uint32_t &out_width, uint32_t &out_height, int64_t *timestamp) {
auto src_data = in_data;
pkt_ptr_->data = src_data.data();
pkt_ptr_->size = src_data.size();
int count=0;
if(pkt_ptr_->size){
std::cout<<"pkt_ptr_->size = " << pkt_ptr_->size << std::endl;
int ret = avcodec_send_packet(codec_context_ptr_, pkt_ptr_);
std::cout<<"count:"<< count<< ", avcodec_send_packet ret="<<ret << std::endl;
if(ret < 0) {
std::cout << "Error sending a packet for decoding, the res of avcodec_send_packet=" << ret << std::endl;
return false;
}
auto frame_ret = avcodec_receive_frame(codec_context_ptr_, frame_ptr_);
std::cout<<"count:"<< count<< ", avcodec_receive_frame frame_ret="<<frame_ret << std::endl;
if (frame_ret < 0) {
std::cout<< "Error during decoding. " << std::endl;
return false;
} else if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
return false;
}
// init 创建一个用于图像缩放和格式转换的上下文, 并为rgb_frame分配空间
if(sws_ctx == nullptr){
sws_ctx = sws_getContext(frame_ptr_->width, frame_ptr_->height, static_cast<AVPixelFormat>(frame_ptr_->format), // 源图像的宽、高和像素格式
frame_ptr_->width, frame_ptr_->height, AV_PIX_FMT_BGR24, // 目标图像的宽、高和像素格式
SWS_BILINEAR, nullptr, nullptr, nullptr); // 其他可选参数,这里使用默认值
rgb_frame_ptr_->height = frame_ptr_->height;
rgb_frame_ptr_->width = frame_ptr_->width;
av_frame_get_buffer(rgb_frame_ptr_, 0);
}
sws_scale(sws_ctx, frame_ptr_->data, frame_ptr_->linesize, 0, frame_ptr_->height, rgb_frame_ptr_->data, rgb_frame_ptr_->linesize);
// std::cout<< "-----success to convert to rgb fame"<< std::endl;
auto rgb_out_data_size = av_image_get_buffer_size(static_cast<AVPixelFormat>(rgb_frame_ptr_->format), rgb_frame_ptr_->width, rgb_frame_ptr_->height, 1);
// std::cout<< "-----rgb_out_data_size="<<rgb_out_data_size<< std::endl;
if(rgb_out_data_ == nullptr) {
rgb_out_data_ = static_cast<uint8_t *>(av_malloc(rgb_out_data_size));
}
for(int y = 0; y<rgb_frame_ptr_->height; y++) {
memcpy(rgb_out_data_ + y*rgb_frame_ptr_->width*3, rgb_frame_ptr_->data[0] + y*rgb_frame_ptr_->linesize[0], rgb_frame_ptr_->width * 3);
}
out_height = rgb_frame_ptr_->height;
out_width = rgb_frame_ptr_->width;
out_data.assign(rgb_out_data_, rgb_out_data_ + rgb_out_data_size);
memset(rgb_out_data_, 0, rgb_out_data_size);
if(!out_data.size()){
return false;
}
}
av_packet_unref(pkt_ptr_);
return true;
}
main 函数:
#include cpu_h265_decoder.h
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
int mian(int argc, char ** argv){
std::unique_ptr<CPUDecodeH265> h265_decoder_;
h265_decoder_ = std::make_unique<CPUDecodeH265>();
bool h265_res = h265_decoder_->init_decoder();
if(!h265_res) {
printf("h265 decoder init_decoder failure\n");
exit(0);
}
std::vector<uint8_t> data_h265; // h265编码后的数据,入参
std::vector<uint8_t> out_data;
auto ret = h265_decoder_->cpu_h265_decode_process_bgr(data_h264, out_data, out_width, out_height);
if(ret){
cv::Mat img(out_height, out_width, out_data.data());
cv::imshow("decode_img", img);
cv::waitKey(0);
cv::destroyAllWindows();
}
return 0;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.8)
project(cpu_decode_h265_test)
set(CMAKE_CXX_STANDARD 17)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
#OpenCV
include_directories(thirdparty/opencv_4.4.0/include/opencv4/)
link_directories(thirdparty/opencv_4.4.0/lib/)
set(OpenCV_LIBRARIES opencv_core opencv_imgcodecs opencv_highgui opencv_imgproc opencv_video
opencv_videoio opencv_calib3d opencv_features2d opencv_flann)
#ffmpeg
include_directories(thirdparty/ffmpeg_4.2.9/include)
link_directories(thirdparty/ffmpeg_4.2.9/lib)
add_library(cpu_h265_decoder_x86_64
cpu_h265_decoder.cpp)
target_link_libraries( cpu_h265_decoder_x86_64
avcodec
avutil
swresample
swscale
add_executable(${PROJECT_NAME} main.cpp
cpu_h265_decoder.cpp
)
target_link_libraries(${PROJECT_NAME}
avcodec
avutil
swresample
swscale
${OpenCV_LIBRARIES}
)