IVA/app/HxVideoDecoder.h
2023-11-27 14:03:29 +08:00

490 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef HXVIDEODECODER_H
#define HXVIDEODECODER_H
#include <string.h>
#include <QDebug>
#include "HxUtils.h"
#include <opencv2/opencv.hpp>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libavutil/imgutils.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
}
#if USE_ALGORITHM
#include "rk_mpi.h"
#include "mpp_mem.h"
#include "mpp_time.h"
#include "mpp_env.h"
#include "mpp_common.h"
#include "mpp_frame.h"
#include "mpp_buffer_impl.h"
#include "RgaUtils.h"
#include "im2d.hpp"
#endif
class HxVideoDecoder
{
public:
HxVideoDecoder(void) {}
#ifndef USE_ALGORITHM
bool initialization(AVFormatContext* ifmt_ctx)
{
auto video_stream_index = -1;
for (uint i = 0; i < ifmt_ctx->nb_streams; i++)
{
if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
video_stream_index = static_cast<int>(i);
}
if (video_stream_index == -1)
return false;
/* 配置解码器 */
video_dec_ctx = avcodec_alloc_context3(nullptr);
if (video_dec_ctx == nullptr)
{
printf("Could not allocate AVCodecContext");
return false;
}
/* 拷贝 */
avcodec_parameters_to_context(video_dec_ctx, ifmt_ctx->streams[video_stream_index]->codecpar);
/* 查找解码器 */
auto codec = avcodec_find_decoder(video_dec_ctx->codec_id);
if (!codec)
return false;
/* 打开解码器 */
if (avcodec_open2(video_dec_ctx, codec, nullptr) < 0)
{
codec = nullptr;
avcodec_free_context(&video_dec_ctx);
return false;
}
video_dec_frame = av_frame_alloc();
video_picture_frame = av_frame_alloc();
/* 通过指定像素格式、图像宽、图像高来计算所需的内存大小 */
auto numBytes = av_image_get_buffer_size(AV_PIX_FMT_BGR24, video_dec_ctx->width, video_dec_ctx->height, 1);
/* 创建 video_out_buffer */
video_out_buffer = static_cast<uint8_t*>(av_malloc(static_cast<ulong>(numBytes) * sizeof(uint8_t)));
/* 填充数据 */
av_image_fill_arrays(video_picture_frame->data, video_picture_frame->linesize, video_out_buffer, AV_PIX_FMT_BGR24, video_dec_ctx->width, video_dec_ctx->height, 1);
auto pix_fmt = video_dec_ctx->pix_fmt;
if (video_dec_ctx->pix_fmt == AVPixelFormat::AV_PIX_FMT_NONE)
pix_fmt = AV_PIX_FMT_YUV420P;
/* 将解码后的YUV数据转换成RGB32 */
video_sws_context = sws_getContext(
video_dec_ctx->width, /* 输入图像的宽度 */
video_dec_ctx->height, /* 输入图像的高度 */
pix_fmt, /* 输入图像的像素格式 */
video_dec_ctx->width, /* 输出图像的宽度 */
video_dec_ctx->height, /* 输出图像的高度 */
AV_PIX_FMT_BGR24, /* 输出图像的像素格式 */
SWS_BICUBIC, /* 选择缩放算法(只有当输入输出图像大小不同时有效),一般选择SWS_FAST_BILINEAR */
nullptr, /* 输入图像的滤波器信息, 若不需要传NULL */
nullptr, /* 输出图像的滤波器信息, 若不需要传NULL */
nullptr /* 特定缩放算法需要的参数(?)默认为NULL */
);
return true;
}
#else
bool initialization(void)
{
MPP_RET ret = MPP_OK;
RK_U32 need_split = 1;
/* 创建 MPP context 和 MPP api 接口 */
ret = mpp_create(&mpp_ctx, &mpp_mpi);
if (ret != MPP_OK)
{
release();
HxLog::append("mpp", QString("mpp_create failed"));
return false;
}
/* 配置解器 按帧输入码流 */
ret = mpp_mpi->control(mpp_ctx, MPP_DEC_SET_PARSER_SPLIT_MODE, (MppParam*)&need_split);
if (MPP_OK != ret)
{
release();
HxLog::append("mpp", QString("mpi->control failed"));
return false;
}
/* 配置解器 队列输入 */
ret = mpp_mpi->control(mpp_ctx, MPP_SET_INPUT_BLOCK, (MppParam*)&need_split);
if (MPP_OK != ret)
{
release();
HxLog::append("mpp", QString("mpi->control failed"));
return false;
}
/* 初始化 MPP 固定为H264 */
ret = mpp_init(mpp_ctx, MPP_CTX_DEC, MPP_VIDEO_CodingAVC);
if (MPP_OK != ret)
{
release();
HxLog::append("mpp", QString("mpp_init failed"));
return false;
}
return true;
}
#endif
void release(void)
{
#ifndef USE_ALGORITHM
if (video_dec_ctx)
{
avcodec_free_context(&video_dec_ctx);
avcodec_close(video_dec_ctx);
av_frame_free(&video_dec_frame);
av_frame_free(&video_picture_frame);
sws_freeContext(video_sws_context);
if (video_out_buffer)
av_free(video_out_buffer);
video_out_buffer = nullptr;
}
#else
if (mpp_ctx)
{
mpp_mpi->reset(mpp_ctx);
mpp_destroy(mpp_ctx);
mpp_ctx = nullptr;
}
if (mpp_frame_group)
{
mpp_buffer_group_put(mpp_frame_group);
mpp_frame_group = nullptr;
}
#endif
}
void decode(AVPacket* packet, cv::Mat* mat)
{
#ifndef USE_ALGORITHM
/* 发送数据到ffmepg放到解码队列中 */
if (avcodec_send_packet(video_dec_ctx, packet) == 0)
{
/* 将成功的解码队列中取出1个frame */
if (avcodec_receive_frame(video_dec_ctx, video_dec_frame) == 0)
{
/* 开始转换 */
sws_scale(video_sws_context, static_cast<const uint8_t* const*>(video_dec_frame->data), video_dec_frame->linesize, 0, video_dec_ctx->height, video_picture_frame->data, video_picture_frame->linesize);
(*mat) = cv::Mat(cv::Size(video_dec_ctx->width, video_dec_ctx->height), CV_8UC3);
(*mat).data = video_out_buffer;
}
}
#else
RK_U32 pkt_done = 0;
RK_U32 pkt_eos = 0;
RK_U32 err_info = 0;
MPP_RET ret = MPP_OK;
MppPacket mpp_packet = nullptr;
MppFrame mpp_frame = nullptr;
ret = mpp_packet_init(&mpp_packet, packet->data, packet->size);
if (ret < 0)
return;
mpp_packet_set_pts(mpp_packet, packet->pts);
// qDebug("av_packet->data:%hhn", packet->data);
do
{
// msleep(1);
RK_S32 count = 5;
// send the packet first if packet is not done
if (!pkt_done)
{
ret = mpp_mpi->decode_put_packet(mpp_ctx, mpp_packet);
if (MPP_OK == ret)
pkt_done = 1;
}
// then get all available frame and release
do
{
// msleep(1);
RK_S32 get_frm = 0;
RK_U32 frame_eos = 0;
try_again:
ret = mpp_mpi->decode_get_frame(mpp_ctx, &mpp_frame);
if (MPP_ERR_TIMEOUT == ret)
{
if (count > 0)
{
count--;
msleep(2);
goto try_again;
}
qDebug("decode_get_frame failed too much time");
}
// qDebug("get MPP_OK:%d", MPP_OK);
// qDebug("get ret:%d", ret);
if (MPP_OK != ret)
{
qDebug("decode_get_frame failed ret %d", ret);
break;
}
// qDebug("get frame:%p", mpp_frame);
if (mpp_frame)
{
if (mpp_frame_get_info_change(mpp_frame))
{
RK_U32 width = mpp_frame_get_width(mpp_frame);
RK_U32 height = mpp_frame_get_height(mpp_frame);
RK_U32 hor_stride = mpp_frame_get_hor_stride(mpp_frame);
RK_U32 ver_stride = mpp_frame_get_ver_stride(mpp_frame);
RK_U32 buf_size = mpp_frame_get_buf_size(mpp_frame);
qDebug("decode_get_frame get info changed found");
qDebug("decoder require buffer w:h [%d:%d] stride [%d:%d] buf_size %d", width, height, hor_stride, ver_stride, buf_size);
ret = mpp_buffer_group_get_internal(&mpp_frame_group, MPP_BUFFER_TYPE_ION);
if (ret)
{
qDebug("get mpp buffer group failed ret %d", ret);
break;
}
mpp_mpi->control(mpp_ctx, MPP_DEC_SET_EXT_BUF_GROUP, mpp_frame_group);
mpp_mpi->control(mpp_ctx, MPP_DEC_SET_INFO_CHANGE_READY, nullptr);
}
else
{
err_info = mpp_frame_get_errinfo(mpp_frame) | mpp_frame_get_discard(mpp_frame);
if (err_info)
{
qDebug("decoder_get_frame get err info:%d discard:%d.",
mpp_frame_get_errinfo(mpp_frame), mpp_frame_get_discard(mpp_frame));
}
frame_count++;
// qDebug("decode_get_frame get frame %d", frame_count);
if (!err_info)
{
RK_U32 width = mpp_frame_get_width(mpp_frame);
RK_U32 height = mpp_frame_get_height(mpp_frame);
MppBuffer mpp_buffer = mpp_frame_get_buffer(mpp_frame);
convert_to_mat(mpp_buffer, width, height, mat);
// RK_U32 h_stride = mpp_frame_get_hor_stride(mpp_frame);
// RK_U32 v_stride = mpp_frame_get_ver_stride(mpp_frame);
// cv::Mat yuv_img(height * 3 / 2, width, CV_8UC1);
// RK_U8 *base = (RK_U8 *)mpp_buffer_get_ptr(mpp_buffer);
// RK_U8 *base_c = base + h_stride * v_stride;
// int idx = 0;
// for (int i = 0; i < height; i++, base += h_stride, idx += width)
// {
// memcpy(yuv_img.data + idx, base, width);
// }
// for (int i = 0; i < height / 2; i++, base_c += h_stride, idx += width)
// {
// memcpy(yuv_img.data + idx, base_c, width);
// }
// (*mat) = cv::Mat(cv::Size(width, height), CV_8UC3);
// cv::cvtColor(yuv_img, (*mat), CV_YUV420sp2RGB);
}
}
frame_eos = mpp_frame_get_eos(mpp_frame);
mpp_frame_deinit(&mpp_frame);
mpp_frame = nullptr;
get_frm = 1;
}
/* 尝试获取运行时帧内存使用情况 */
if (mpp_frame_group)
{
size_t usage = mpp_buffer_group_usage(mpp_frame_group);
if (usage > max_usage)
max_usage = usage;
}
/* 如果发送了最后一个数据包,但没有找到最后一帧,则继续 */
if (pkt_eos && pkt_done && !frame_eos)
{
msleep(10);
continue;
}
if (frame_eos)
{
qDebug("found last frame");
break;
}
if (frame_num > 0 && frame_count >= frame_num)
{
eos = 1;
break;
}
if (get_frm)
continue;
break;
} while (1);
if (frame_num > 0 && frame_count >= frame_num)
{
eos = 1;
qDebug("reach max frame number %d", frame_count);
break;
}
if (pkt_done)
break;
/*
* why sleep here:
* mpi->decode_put_packet will failed when packet in internal queue is
* full,waiting the package is consumed .Usually hardware decode one
* frame which resolution is 1080p needs 2 ms,so here we sleep 3ms
* * is enough.
*/
msleep(3);
} while (1);
mpp_packet_deinit(&mpp_packet);
#endif
}
private:
#if USE_ALGORITHM
size_t mpp_buffer_group_usage(MppBufferGroup group)
{
if (group == nullptr)
{
qDebug("input invalid group %p", group);
return MPP_BUFFER_MODE_BUTT;
}
return ((MppBufferGroupImpl*)group)->usage;
}
int write_image_to_file(void* buf, const char* filename, int sw, int sh, int fmt, int index) {
int size;
// char filePath[100];
// const char* outputFilePath = "%s/out%dw%d-h%d-%s.bin";
// snprintf(filePath, 100, outputFilePath,
// path, index, sw, sh, translate_format_str(fmt));
FILE* file = fopen(filename, "wb+");
if (!file) {
fprintf(stderr, "Could not open %s\n", filename);
return false;
}
else {
fprintf(stderr, "open %s and write ok\n", filename);
}
size = sw * sh * get_bpp_from_format(fmt);
fwrite(buf, size, 1, file);
fclose(file);
return 0;
}
bool convert_to_mat(MppBuffer mpp_buffer, RK_U32 width, RK_U32 height, cv::Mat* mat)
{
int ret = 0;
im_rect src_rect, dst_rect;
rga_buffer_t src_img, dst_img;
mat->create(cv::Size(width, height), CV_8UC3);
memset(&src_rect, 0, sizeof(src_rect));
memset(&dst_rect, 0, sizeof(dst_rect));
memset(&src_img, 0, sizeof(src_img));
memset(&dst_img, 0, sizeof(dst_img));
src_img = wrapbuffer_virtualaddr(mpp_buffer_get_ptr(mpp_buffer), width, height, RK_FORMAT_YCbCr_420_SP);
dst_img = wrapbuffer_virtualaddr(mat->data, width, height, RK_FORMAT_BGR_888);
if (src_img.width == 0 || dst_img.width == 0) {
printf("%s, %s\n", __FUNCTION__, imStrError());
return -1;
}
src_img.format = RK_FORMAT_YCbCr_420_SP;
dst_img.format = RK_FORMAT_BGR_888;
ret = imcheck(src_img, dst_img, src_rect, dst_rect);
if (IM_STATUS_NOERROR != ret) {
printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
return -1;
}
auto STATUS = imcvtcolor(src_img, dst_img, src_img.format, dst_img.format);
return STATUS != IM_STATUS_SUCCESS;
}
#endif
private:
#if USE_ALGORITHM
MppCtx mpp_ctx;
MppApi* mpp_mpi;
size_t max_usage;
RK_U32 frame_count = 0, frame_num, eos;
/* 缓冲区管理器 */
MppBufferGroup mpp_frame_group;
#else
uint8_t* video_out_buffer = nullptr;
struct SwsContext* video_sws_context = nullptr;
AVCodecContext* video_dec_ctx = nullptr;
AVFrame* video_dec_frame = nullptr, * video_picture_frame = nullptr;
#endif
};
#endif // HXVIDEODECODER_H