IVA/app/HxVideoDecoder.h
hehaoyang ff66a2e0d6 V1.02
1. 删除图片存储到本地的方式;
2. 取流方式由Opencv修改为FFmpeg方式;
3. 解码后的数据直接转为RK_FORMAT_YCbCr_422_SP格式发送给算法;
4. 视频裸流数据存储在内存中,保存30s;
5. 报警图片从报警录像视频中获取;
2023-12-08 14:17:14 +08:00

482 lines
15 KiB
C++
Raw Permalink 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) {}
static int duration(QString fullpath)
{
/* 始化网络设备 */
avformat_network_init();
/* 初始化 */
AVFormatContext *ifmt_ctx = avformat_alloc_context();
/* 打开输入文件 */
if (avformat_open_input(&ifmt_ctx, fullpath.toUtf8().data(), nullptr, nullptr) < 0)
return 0;
/* 获取流信息 */
if (avformat_find_stream_info(ifmt_ctx, nullptr) < 0)
{
avformat_close_input(&ifmt_ctx);
return -1;
}
/* 这里获取的是微秒,需要转成秒 */
int64_t tduration = ifmt_ctx->duration;
/* 打开文件流后,需要关闭,否则会一直占用视频文件,无法进行视频文件的后续操作 */
avformat_close_input(&ifmt_ctx);
/* 销毁 */
avformat_free_context(ifmt_ctx);
return tduration / 1000000;
}
#ifndef USE_ALGORITHM
void initialization(AVFormatContext *ifmt_ctx)
{
status = false;
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;
/* 配置解码器 */
video_dec_ctx = avcodec_alloc_context3(nullptr);
if (video_dec_ctx == nullptr)
{
printf("Could not allocate AVCodecContext");
return;
}
/* 拷贝 */
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;
/* 打开解码器 */
if (avcodec_open2(video_dec_ctx, codec, nullptr) < 0)
{
codec = nullptr;
avcodec_free_context(&video_dec_ctx);
return;
}
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 */
);
status = true;
}
#else
void initialization(void)
{
status = false;
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;
}
/* 配置解器 按帧输入码流 */
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;
}
/* 配置解器 队列输入 */
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;
}
/* 初始化 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;
}
status = 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);
}
}
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;
}
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_YCbCr_422_SP);
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_YCbCr_422_SP;
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
public:
bool status = false;
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