// Copyright 2019 Fuzhou Rockchip Electronics Co., Ltd. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "mpp_encoder.h" #include #include #include #include #include #include "buffer.h" #include "utils.h" namespace easymedia { MPPEncoder::MPPEncoder() : coding_type(MPP_VIDEO_CodingAutoDetect), output_mb_flags(0), encoder_sta_en(false), stream_size_1s(0), frame_cnt_1s(0), last_ts(0), cur_ts(0), userdata_len(0), userdata_frame_id(0), userdata_all_frame_en(0) { #ifdef MPP_SUPPORT_HW_OSD // reset osd data. memset(&osd_data, 0, sizeof(osd_data)); #endif memset(&roi_cfg, 0, sizeof(roi_cfg)); rc_api_brief_name = "default"; } MPPEncoder::~MPPEncoder() { #ifdef MPP_SUPPORT_HW_OSD if (osd_data.buf) { LOGD("MPP Encoder: free osd buff\n"); mpp_buffer_put(osd_data.buf); osd_data.buf = NULL; } #endif if (roi_cfg.regions) { LOGD("MPP Encoder: free enc roi region buff\n"); free(roi_cfg.regions); roi_cfg.regions = NULL; } } void MPPEncoder::SetMppCodeingType(MppCodingType type) { coding_type = type; if (type == MPP_VIDEO_CodingMJPEG) { codec_type = CODEC_TYPE_JPEG; } else if (type == MPP_VIDEO_CodingAVC) { codec_type = CODEC_TYPE_H264; } else if (type == MPP_VIDEO_CodingHEVC) { codec_type = CODEC_TYPE_H265; } // mpp always return a single nal if (type == MPP_VIDEO_CodingAVC || type == MPP_VIDEO_CodingHEVC) { output_mb_flags |= MediaBuffer::kSingleNalUnit; } } bool MPPEncoder::Init() { if (coding_type == MPP_VIDEO_CodingUnused) { return false; } mpp_ctx = std::make_shared(); if (!mpp_ctx) { return false; } MppCtx ctx = NULL; MppApi* mpi = NULL; int ret = mpp_create(&ctx, &mpi); if (ret) { LOG("mpp_create failed\n"); return false; } mpp_ctx->ctx = ctx; mpp_ctx->mpi = mpi; ret = mpp_init(ctx, MPP_CTX_ENC, coding_type); if (ret != MPP_OK) { LOG("mpp_init failed with type %d\n", coding_type); mpp_destroy(ctx); ctx = NULL; mpi = NULL; return false; } return true; } int MPPEncoder::PrepareMppFrame(const std::shared_ptr& input, std::shared_ptr& mdinfo, MppFrame& frame) { MppBuffer pic_buf = nullptr; if (input->GetType() != Type::Image) { LOG("mpp encoder input source only support image buffer\n"); return -EINVAL; } PixelFormat fmt = input->GetPixelFormat(); if (fmt == PIX_FMT_NONE) { LOG("mpp encoder input source invalid pixel format\n"); return -EINVAL; } ImageBuffer* hw_buffer = static_cast(input.get()); assert(input->GetValidSize() > 0); mpp_frame_set_pts(frame, hw_buffer->GetUSTimeStamp()); mpp_frame_set_dts(frame, hw_buffer->GetUSTimeStamp()); mpp_frame_set_width(frame, hw_buffer->GetWidth()); mpp_frame_set_height(frame, hw_buffer->GetHeight()); mpp_frame_set_fmt(frame, ConvertToMppPixFmt(fmt)); if (fmt == PIX_FMT_YUYV422 || fmt == PIX_FMT_UYVY422) { mpp_frame_set_hor_stride(frame, hw_buffer->GetVirWidth() * 2); } else { mpp_frame_set_hor_stride(frame, hw_buffer->GetVirWidth()); } mpp_frame_set_ver_stride(frame, hw_buffer->GetVirHeight()); MppMeta meta = mpp_frame_get_meta(frame); auto& related_vec = input->GetRelatedSPtrs(); if (!related_vec.empty()) { mdinfo = std::static_pointer_cast(related_vec[0]); LOGD("MPP Encoder: set mdinfo(%p, %zuBytes) to frame\n", mdinfo->GetPtr(), mdinfo->GetValidSize()); mpp_meta_set_ptr(meta, KEY_MV_LIST, mdinfo->GetPtr()); } if (roi_cfg.number && roi_cfg.regions) { LOGD("MPP Encoder: set roi cfg(cnt:%d,%p) to frame\n", roi_cfg.number, roi_cfg.regions); mpp_meta_set_ptr(meta, KEY_ROI_DATA, &roi_cfg); } #ifdef MPP_SUPPORT_HW_OSD if (osd_data.num_region && osd_data.buf) { LOGD("MPP Encoder: set osd data(%d regions) to frame\n", osd_data.num_region); mpp_meta_set_ptr(meta, KEY_OSD_DATA, (void*)&osd_data); } #endif // MPP_SUPPORT_HW_OSD if (userdata_len) { LOGD("MPP Encoder: set userdata(%dBytes) to frame\n", userdata_len); bool skip_frame = false; if (!userdata_all_frame_en) { MediaConfig& cfg = GetConfig(); // userdata_frame_id = 0 : first gop frame. if (userdata_frame_id) { skip_frame = true; } userdata_frame_id++; if (userdata_frame_id == cfg.vid_cfg.gop_size) { userdata_frame_id = 0; } } if (!skip_frame) { mpp_ud.pdata = userdata; mpp_ud.len = userdata_len; mpp_meta_set_ptr(meta, KEY_USER_DATA, &mpp_ud); } } MPP_RET ret = init_mpp_buffer_with_content(pic_buf, input); if (ret) { LOG("prepare picture buffer failed\n"); return ret; } mpp_frame_set_buffer(frame, pic_buf); if (input->IsEOF()) { mpp_frame_set_eos(frame, 1); } mpp_buffer_put(pic_buf); return 0; } int MPPEncoder::PrepareMppPacket(std::shared_ptr& output, MppPacket& packet) { MppBuffer mpp_buf = nullptr; if (!output->IsHwBuffer()) { return 0; } MPP_RET ret = init_mpp_buffer(mpp_buf, output, 0); if (ret) { LOG("import output stream buffer failed\n"); return ret; } if (mpp_buf) { mpp_packet_init_with_buffer(&packet, mpp_buf); mpp_buffer_put(mpp_buf); } return 0; } int MPPEncoder::PrepareMppExtraBuffer(std::shared_ptr extra_output, MppBuffer& buffer) { MppBuffer mpp_buf = nullptr; if (!extra_output || !extra_output->IsValid()) { return 0; } MPP_RET ret = init_mpp_buffer(mpp_buf, extra_output, extra_output->GetValidSize()); if (ret) { LOG("import extra stream buffer failed\n"); return ret; } buffer = mpp_buf; return 0; } class MPPPacketContext { public: MPPPacketContext(std::shared_ptr ctx, MppPacket p) : mctx(ctx), packet(p) { } ~MPPPacketContext() { if (packet) { mpp_packet_deinit(&packet); } } private: std::shared_ptr mctx; MppPacket packet; }; static int __free_mpppacketcontext(void* p) { assert(p); delete (MPPPacketContext*)p; return 0; } int MPPEncoder::Process(const std::shared_ptr& input, std::shared_ptr& output, std::shared_ptr extra_output) { MppFrame frame = nullptr; MppPacket packet = nullptr; MppPacket import_packet = nullptr; MppBuffer mv_buf = nullptr; size_t packet_len = 0; RK_U32 packet_flag = 0; RK_U32 out_eof = 0; RK_S64 pts = 0; std::shared_ptr mdinfo; RK_S32 temporal_id = -1; Type out_type; if (!input) { return 0; } if (!output) { return -EINVAL; } // all changes must set before encode and among the same thread while (HasChangeReq()) { auto change = PeekChange(); if (change.first && !CheckConfigChange(change)) { return -1; } } int ret = mpp_frame_init(&frame); if (MPP_OK != ret) { LOG("mpp_frame_init failed\n"); goto ENCODE_OUT; } ret = PrepareMppFrame(input, mdinfo, frame); if (ret) { LOG("PrepareMppFrame failed\n"); goto ENCODE_OUT; } if (output->IsValid()) { ret = PrepareMppPacket(output, packet); if (ret) { LOG("PrepareMppPacket failed\n"); goto ENCODE_OUT; } import_packet = packet; } ret = PrepareMppExtraBuffer(extra_output, mv_buf); if (ret) { LOG("PrepareMppExtraBuffer failed\n"); goto ENCODE_OUT; } ret = Process(frame, packet, mv_buf); if (ret) { goto ENCODE_OUT; } if (!packet) { LOG("ERROR: MPP Encoder: input frame:%p, %zuBytes; output null packet!\n", frame, mpp_buffer_get_size(mpp_frame_get_buffer(frame))); goto ENCODE_OUT; } packet_len = mpp_packet_get_length(packet); { MppMeta packet_meta = mpp_packet_get_meta(packet); RK_S32 is_intra = 0; mpp_meta_get_s32(packet_meta, KEY_OUTPUT_INTRA, &is_intra); packet_flag = (is_intra) ? MediaBuffer::kIntra : MediaBuffer::kPredicted; mpp_meta_get_s32(packet_meta, KEY_TEMPORAL_ID, &temporal_id); } out_eof = mpp_packet_get_eos(packet); pts = mpp_packet_get_pts(packet); if (pts <= 0) { pts = mpp_packet_get_dts(packet); } // out fps < in fps ? if (packet_len == 0) { output->SetValidSize(0); if (extra_output) { extra_output->SetValidSize(0); } goto ENCODE_OUT; } // Calculate bit rate statistics. if (encoder_sta_en) { MediaConfig& cfg = GetConfig(); int target_fps = cfg.vid_cfg.frame_rate; int target_bpsmax = cfg.vid_cfg.bit_rate_max; int enable_bps = 1; frame_cnt_1s += 1; stream_size_1s += packet_len; if (target_fps <= 0) { target_fps = 30; enable_bps = 0; } // Refresh every second if ((frame_cnt_1s % target_fps) == 0) { // Calculate the frame rate based on the system time. cur_ts = gettimeofday(); if (last_ts) { encoded_fps = ((float)target_fps / (cur_ts - last_ts)) * 1000000; } else { encoded_fps = 0; } last_ts = cur_ts; if (enable_bps) { // convert bytes to bits encoded_bps = stream_size_1s * 8; LOG("MPP ENCODER: bps:%d, actual_bps:%d, fps:%d, actual_fps:%f\n", target_bpsmax, encoded_bps, target_fps, encoded_fps); } else { LOG("MPP ENCODER: fps statistical period:%d, actual_fps:%f\n", target_fps, encoded_fps); } // reset 1s variable stream_size_1s = 0; frame_cnt_1s = 0; } } else if (cur_ts) { // clear tmp statistics variable. stream_size_1s = 0; frame_cnt_1s = 0; cur_ts = 0; last_ts = 0; } if (output->IsValid()) { if (!import_packet) { // !!time-consuming operation void* ptr = output->GetPtr(); assert(ptr); LOGD("extra time-consuming memcpy to cpu!\n"); memcpy(ptr, mpp_packet_get_data(packet), packet_len); // sync to cpu? } } else { MPPPacketContext* ctx = new MPPPacketContext(mpp_ctx, packet); if (!ctx) { LOG_NO_MEMORY(); ret = -ENOMEM; goto ENCODE_OUT; } output->SetFD(mpp_buffer_get_fd(mpp_packet_get_buffer(packet))); output->SetPtr(mpp_packet_get_data(packet)); output->SetSize(mpp_packet_get_size(packet)); output->SetUserData(ctx, __free_mpppacketcontext); packet = nullptr; } output->SetValidSize(packet_len); output->SetUserFlag(packet_flag | output_mb_flags); output->SetTsvcLevel(temporal_id); output->SetUSTimeStamp(pts); output->SetEOF(out_eof ? true : false); out_type = output->GetType(); if (out_type == Type::Image) { auto out_img = std::static_pointer_cast(output); auto& info = out_img->GetImageInfo(); const auto& in_cfg = GetConfig(); info = (coding_type == MPP_VIDEO_CodingMJPEG) ? in_cfg.img_cfg.image_info : in_cfg.vid_cfg.image_cfg.image_info; // info.pix_fmt = codec_type; } else { output->SetType(Type::Video); } if (mv_buf) { if (extra_output->GetFD() < 0) { void* ptr = extra_output->GetPtr(); assert(ptr); memcpy(ptr, mpp_buffer_get_ptr(mv_buf), mpp_buffer_get_size(mv_buf)); } extra_output->SetValidSize(mpp_buffer_get_size(mv_buf)); extra_output->SetUserFlag(packet_flag); extra_output->SetUSTimeStamp(pts); } ENCODE_OUT: if (frame) { mpp_frame_deinit(&frame); } if (packet) { mpp_packet_deinit(&packet); } if (mv_buf) { mpp_buffer_put(mv_buf); } return ret; } int MPPEncoder::Process(MppFrame frame, MppPacket& packet, MppBuffer& mv_buf) { MppCtx ctx = mpp_ctx->ctx; MppApi* mpi = mpp_ctx->mpi; if (mv_buf) { LOG("TODO move detection frome mpp encoder...\n"); } int ret = mpi->encode_put_frame(ctx, frame); if (ret) { LOG("mpp encode put frame failed\n"); return -1; } ret = mpi->encode_get_packet(ctx, &packet); if (ret) { LOG("mpp encode get packet failed\n"); return -1; } return 0; } int MPPEncoder::SendInput(const std::shared_ptr&) { errno = ENOSYS; return -1; } std::shared_ptr MPPEncoder::FetchOutput() { errno = ENOSYS; return nullptr; } int MPPEncoder::EncodeControl(int cmd, void* param) { MpiCmd mpi_cmd = (MpiCmd)cmd; int ret = mpp_ctx->mpi->control(mpp_ctx->ctx, mpi_cmd, (MppParam)param); if (ret) { LOG("mpp control cmd 0x%08x param %p failed\n", cmd, param); return ret; } return 0; } void MPPEncoder::QueryChange(uint32_t change, void* value, int32_t size) { if (!value || !size) { LOG("ERROR: MPP ENCODER: %s invalid argument!\n", __func__); return; } switch (change) { case VideoEncoder::kMoveDetectionFlow: if (size < (int)sizeof(int32_t)) { LOG("ERROR: MPP ENCODER: %s change:[%d], size invalid!\n", __func__, VideoEncoder::kMoveDetectionFlow); return; } if (rc_api_brief_name == "smart") { *((int32_t*)value) = 1; } else { *((int32_t*)value) = 0; } break; default: LOG("WARN: MPP ENCODER: %s change:[%d] not support!\n", __func__, change); } } void MPPEncoder::set_statistics_switch(bool value) { LOG("[INFO] MPP ENCODER %s statistics\n", value ? "enable" : "disable"); encoder_sta_en = value; } int MPPEncoder::get_statistics_bps() { if (!encoder_sta_en) { LOG("[WARN] MPP ENCODER statistics should enable first!\n"); } return encoded_bps; } int MPPEncoder::get_statistics_fps() { if (!encoder_sta_en) { LOG("[WARN] MPP ENCODER statistics should enable first!\n"); } return encoded_fps; } #ifdef MPP_SUPPORT_HW_OSD #define OSD_PTL_SIZE 1024 // Bytes. #ifndef NDEBUG static void OsdDummpRegions(OsdRegionData* rdata) { if (!rdata) { return; } LOGD("#RegionData:%p:\n", rdata->buffer); LOG("\t enable:%u\n", rdata->enable); LOG("\t region_id:%u\n", rdata->region_id); LOG("\t inverse:%u\n", rdata->inverse); LOG("\t pos_x:%u\n", rdata->pos_x); LOG("\t pos_y:%u\n", rdata->pos_y); LOG("\t width:%u\n", rdata->width); LOG("\t height:%u\n", rdata->height); } static void OsdDummpMppOsd(MppEncOSDData* osd) { LOGD("#MPP OsdData: cnt:%d buff:%p, bufSize:%zu\n", osd->num_region, osd->buf, mpp_buffer_get_size(osd->buf)); for (int i = 0; i < OSD_REGIONS_CNT; i++) { LOGD("#MPP OsdData[%d]:\n", i); LOG("\t enable:%u\n", osd->region[i].enable); LOG("\t inverse:%u\n", osd->region[i].inverse); LOG("\t pos_x:%u\n", osd->region[i].start_mb_x * 16); LOG("\t pos_y:%u\n", osd->region[i].start_mb_y * 16); LOG("\t width:%u\n", osd->region[i].num_mb_x * 16); LOG("\t height:%u\n", osd->region[i].num_mb_y * 16); LOG("\t buf_offset:%u\n", osd->region[i].buf_offset); } } static void SaveOsdImg(MppEncOSDData* _data, int index) { if (!_data->buf) { return; } char _path[64] = {0}; sprintf(_path, "/tmp/osd_img%d", index); LOGD("MPP Encoder: save osd img to %s\n", _path); int fd = open(_path, O_WRONLY | O_CREAT); if (fd <= 0) { return; } int size = _data->region[index].num_mb_x * 16; size *= _data->region[index].num_mb_y * 16; uint8_t* ptr = (uint8_t*)mpp_buffer_get_ptr(_data->buf); ptr += _data->region[index].buf_offset; if (ptr && size) { write(fd, ptr, size); } close(fd); } #endif // NDEBUG int MPPEncoder::OsdPaletteSet(uint32_t* ptl_data) { if (!ptl_data) { return -1; } LOGD("MPP Encoder: setting yuva palette...\n"); MppCtx ctx = mpp_ctx->ctx; MppApi* mpi = mpp_ctx->mpi; MppEncOSDPltCfg osd_plt_cfg; MppEncOSDPlt osd_plt; // TODO rgba plt to yuva plt. for (int k = 0; k < 256; k++) { osd_plt.data[k].val = *(ptl_data + k); } osd_plt_cfg.change = MPP_ENC_OSD_PLT_CFG_CHANGE_ALL; osd_plt_cfg.type = MPP_ENC_OSD_PLT_TYPE_USERDEF; osd_plt_cfg.plt = &osd_plt; int ret = mpi->control(ctx, MPP_ENC_SET_OSD_PLT_CFG, &osd_plt_cfg); if (ret) { LOG("ERROR: MPP Encoder: set osd plt failed ret %d\n", ret); } return ret; } static int OsdUpdateRegionInfo(MppEncOSDData* osd, OsdRegionData* region_data) { uint32_t new_size = 0; uint32_t old_size = 0; uint8_t rid = region_data->region_id; uint8_t* region_src = NULL; uint8_t* region_dst = NULL; if (!region_data->enable) { osd->region[rid].enable = 0; osd->num_region = 0; for (int i = 0; i < OSD_REGIONS_CNT; i++) { if (osd->region[i].enable) { osd->num_region = i + 1; } } assert(osd->num_region <= 8); return 0; } // get buffer size to compare. new_size = region_data->width * region_data->height; // If there is enough space, reuse the previous buffer. // However, the current area must be active, so as to // avoid opening up too large a buffer at the beginning, // and it will not be reduced later. if (osd->region[rid].enable) { old_size = osd->region[rid].num_mb_x * osd->region[rid].num_mb_y * 256; } // update region info. osd->region[rid].enable = 1; osd->region[rid].inverse = region_data->inverse; osd->region[rid].start_mb_x = region_data->pos_x / 16; osd->region[rid].start_mb_y = region_data->pos_y / 16; osd->region[rid].num_mb_x = region_data->width / 16; osd->region[rid].num_mb_y = region_data->height / 16; // 256 * 16 => 4096 is enough for osd. assert(osd->region[rid].start_mb_x <= 256); assert(osd->region[rid].start_mb_y <= 256); assert(osd->region[rid].num_mb_x <= 256); assert(osd->region[rid].num_mb_y <= 256); // region[rid] buffer size is enough, copy data directly. if (old_size >= new_size) { LOGD("MPP Encoder: Region[%d] reuse old buff:%u, new_size:%u\n", rid, old_size, new_size); region_src = region_data->buffer; region_dst = (uint8_t*)mpp_buffer_get_ptr(osd->buf); region_dst += osd->region[rid].buf_offset; memcpy(region_dst, region_src, new_size); #ifndef NDEBUG SaveOsdImg(osd, rid); #endif return 0; } // region[rid] buffer size too small, resize buffer. MppBuffer new_buff = NULL; MppBuffer old_buff = NULL; uint32_t old_offset[OSD_REGIONS_CNT] = {0}; uint32_t total_size = 0; uint32_t current_size = 0; osd->num_region = 0; for (int i = 0; i < OSD_REGIONS_CNT; i++) { if (osd->region[i].enable) { old_offset[i] = osd->region[i].buf_offset; osd->region[i].buf_offset = total_size; total_size += osd->region[i].num_mb_x * osd->region[i].num_mb_y * 256; osd->num_region = i + 1; } else { osd->region[i].start_mb_x = 0; osd->region[i].start_mb_y = 0; osd->region[i].buf_offset = 0; osd->region[i].num_mb_x = 0; osd->region[i].num_mb_y = 0; } } old_buff = osd->buf; int ret = mpp_buffer_get(NULL, &new_buff, total_size); if (ret) { LOG("ERROR: MPP Encoder: get osd %dBytes buffer failed(%d)\n", total_size, ret); // reset target region. osd->region[rid].enable = 0; osd->region[rid].start_mb_x = 0; osd->region[rid].start_mb_y = 0; osd->region[rid].buf_offset = 0; return -1; } for (int i = 0; i < OSD_REGIONS_CNT; i++) { if (!osd->region[i].enable) { continue; } if (i != rid) { // copy other region data to new buffer. region_src = (uint8_t*)mpp_buffer_get_ptr(old_buff); region_src += old_offset[i]; region_dst = (uint8_t*)mpp_buffer_get_ptr(new_buff); region_dst += osd->region[i].buf_offset; current_size = osd->region[i].num_mb_x * osd->region[i].num_mb_y * 256; } else { // copy current region data to new buffer. region_src = region_data->buffer; region_dst = (uint8_t*)mpp_buffer_get_ptr(new_buff); region_dst += osd->region[i].buf_offset; current_size = new_size; } assert(region_src); assert(region_dst); memcpy(region_dst, region_src, current_size); #ifndef NDEBUG SaveOsdImg(osd, i); #endif } // replace old buff with new buff. osd->buf = new_buff; if (old_buff) { mpp_buffer_put(old_buff); } return 0; } int MPPEncoder::OsdRegionSet(OsdRegionData* rdata) { if (!rdata) { return -EINVAL; } LOGD("MPP Encoder: setting osd regions...\n"); if ((rdata->region_id >= OSD_REGIONS_CNT)) { LOG("ERROR: MPP Encoder: invalid region id(%d), should be [0, %d).\n", rdata->region_id, OSD_REGIONS_CNT); return -EINVAL; } if (rdata->enable && !rdata->buffer) { LOG("ERROR: MPP Encoder: invalid region data"); return -EINVAL; } if ((rdata->width % 16) || (rdata->height % 16) || (rdata->pos_x % 16) || (rdata->pos_y % 16)) { LOG("WARN: MPP Encoder: osd size must be 16 aligned\n"); rdata->width = UPALIGNTO16(rdata->width); rdata->height = UPALIGNTO16(rdata->height); rdata->pos_x = UPALIGNTO16(rdata->pos_x); rdata->pos_y = UPALIGNTO16(rdata->pos_y); } #ifndef NDEBUG OsdDummpRegions(rdata); #endif int ret = OsdUpdateRegionInfo(&osd_data, rdata); #ifndef NDEBUG OsdDummpMppOsd(&osd_data); #endif return ret; } int MPPEncoder::OsdRegionGet(OsdRegionData* rdata) { LOG("ToDo...%p\n", rdata); return 0; } #endif // MPP_SUPPORT_HW_OSD int MPPEncoder::RoiUpdateRegions(EncROIRegion* regions, int region_cnt) { if (!regions || region_cnt == 0) { roi_cfg.number = 0; if (roi_cfg.regions) { free(roi_cfg.regions); roi_cfg.regions = NULL; } LOG("MPP Encoder: disable roi function."); return 0; } int msize = region_cnt * sizeof(MppEncROIRegion); MppEncROIRegion* region = (MppEncROIRegion*)malloc(msize); if (!region) { LOG_NO_MEMORY(); return -ENOMEM; } for (int i = 0; i < region_cnt; i++) { if ((regions[i].x % 16) || (regions[i].y % 16) || (regions[i].w % 16) || (regions[i].h % 16)) { LOG("WARN: MPP Encoder: region parameter should be an integer multiple " "of 16\n"); LOG("WARN: MPP Encoder: reset region[%d] frome <%d,%d,%d,%d> to " "<%d,%d,%d,%d>\n", i, regions[i].x, regions[i].y, regions[i].w, regions[i].h, UPALIGNTO16(regions[i].x), UPALIGNTO16(regions[i].y), UPALIGNTO16(regions[i].w), UPALIGNTO16(regions[i].h)); regions[i].x = UPALIGNTO16(regions[i].x); regions[i].y = UPALIGNTO16(regions[i].y); regions[i].w = UPALIGNTO16(regions[i].w); regions[i].h = UPALIGNTO16(regions[i].h); } LOGD("MPP Encoder: roi region[%d]:<%d,%d,%d,%d>\n", i, regions[i].x, regions[i].y, regions[i].w, regions[i].h); LOGD("MPP Encoder: roi region[%d].intra=%d,\n", i, regions[i].intra); LOGD("MPP Encoder: roi region[%d].quality=%d,\n", i, regions[i].quality); LOGD("MPP Encoder: roi region[%d].abs_qp_en=%d,\n", i, regions[i].abs_qp_en); LOGD("MPP Encoder: roi region[%d].qp_area_idx=%d,\n", i, regions[i].qp_area_idx); LOGD("MPP Encoder: roi region[%d].area_map_en=%d,\n", i, regions[i].area_map_en); assert(regions[i].x < 8192); assert(regions[i].y < 8192); assert(regions[i].w < 8192); assert(regions[i].h < 8192); assert(regions[i].x < 8192); assert(regions[i].intra <= 1); assert(regions[i].abs_qp_en <= 1); assert(regions[i].qp_area_idx <= 7); assert(regions[i].area_map_en <= 1); VALUE_SCOPE_CHECK(regions[i].quality, -48, 51); region[i].x = regions[i].x; region[i].y = regions[i].y; region[i].w = regions[i].w; region[i].h = regions[i].h; region[i].intra = regions[i].intra; region[i].quality = regions[i].quality; region[i].abs_qp_en = regions[i].abs_qp_en; region[i].qp_area_idx = regions[i].qp_area_idx; region[i].area_map_en = regions[i].area_map_en; } roi_cfg.number = region_cnt; if (roi_cfg.regions) { free(roi_cfg.regions); } roi_cfg.regions = region; return 0; } int MPPEncoder::SetUserData(const char* data, uint16_t len) { uint16_t valid_size = len; if (!data && len) { LOG("ERROR: Mpp Encoder: invalid userdata!\n"); return -1; } if (valid_size > MPP_ENCODER_USERDATA_MAX_SIZE) { valid_size = MPP_ENCODER_USERDATA_MAX_SIZE; LOG("WARN: Mpp Encoder: UserData exceeds maximum length(%d)," "Reset to %d\n", valid_size, valid_size); } if (valid_size) { memcpy(userdata, data, valid_size); } userdata_len = valid_size; return 0; } void MPPEncoder::ClearUserData() { userdata_len = 0; } void MPPEncoder::RestartUserData() { userdata_frame_id = 0; } void MPPEncoder::EnableUserDataAllFrame(bool value) { userdata_all_frame_en = value ? 1 : 0; } } // namespace easymedia