#ifndef HXVIDEODECODER_H #define HXVIDEODECODER_H #include #include #include "HxUtils.h" #include 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(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(av_malloc(static_cast(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(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