/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * rockchip uvc mpp encode * * Copyright (C) 2022 Rockchip Electronics Co., Ltd. * * Author: Bin Yang */ #include #include #include #include "uvc-gadget.h" #include "uvc_encode.h" #include "tools.h" #include "cJSON.h" static MPP_RET uvc_stream_file_open(MpiEncMultiCtxInfo *info) { MpiEncArgs *cmd = info->cmd; MpiEncData *p = &info->ctx; char file_name[256]; MPP_RET ret = MPP_OK; if (cmd->save_in_en || cmd->save_out_en) { if (access(UVC_SAVE_FILE, F_OK)) { if (mkdir(UVC_SAVE_FILE) == -1) mpp_err_f("mkdir %s failed.\n", UVC_SAVE_FILE); } } if (cmd->save_in_en) { sprintf(file_name, "%s/dump.yuv", UVC_SAVE_FILE); mpp_log("save input file %s\n", file_name); p->fp_input = fopen(file_name, "w+b"); if (NULL == p->fp_input) { mpp_err_f("failed to open input file %s\n", file_name); ret = MPP_ERR_OPEN_FILE; } } if (!cmd->save_out_en) return ret; if (cmd->type == MPP_VIDEO_CodingAVC || cmd->type == MPP_VIDEO_CodingHEVC) { sprintf(file_name, "%s/dump.%s", UVC_SAVE_FILE, (cmd->type == MPP_VIDEO_CodingAVC) ? "h264" : "h265"); mpp_log("save output file %s\n", file_name); p->fp_output = fopen(file_name, "w+b"); if (NULL == p->fp_output) { mpp_err_f("failed to open output file %s\n", file_name); ret = MPP_ERR_OPEN_FILE; } } return ret; } static void uvc_stream_file_close(MpiEncMultiCtxInfo *info) { MpiEncArgs *cmd = info->cmd; MpiEncData *p = &info->ctx; if (cmd->save_in_en && p->fp_input) { fclose(p->fp_input); p->fp_input = NULL; } if (cmd->save_out_en && p->fp_output) { if (cmd->type == MPP_VIDEO_CodingAVC || cmd->type == MPP_VIDEO_CodingHEVC) { fclose(p->fp_output); p->fp_output = NULL; } } } static MPP_RET uvc_stream_in_write(MpiEncMultiCtxInfo *info, char *buf, int len) { MpiEncArgs *cmd = info->cmd; MpiEncData *p = &info->ctx; if (cmd->save_in_en && p->fp_input) fwrite(buf, 1, len, p->fp_input); return MPP_OK; } static MPP_RET uvc_stream_out_write(MpiEncMultiCtxInfo *info, char *buf, int len) { MpiEncArgs *cmd = info->cmd; MpiEncData *p = &info->ctx; char file_name[256]; if (cmd->save_out_en) { switch (cmd->type) { case MPP_VIDEO_CodingMJPEG: sprintf(file_name, "%s/dump-%u.jpg", UVC_SAVE_FILE, p->frame_num); p->fp_output = fopen(file_name, "w+b"); if (NULL == p->fp_output) { mpp_err_f("failed to open output file %s\n", file_name); break; } fwrite(buf, 1, len, p->fp_output); fclose(p->fp_output); break; case MPP_VIDEO_CodingAVC: case MPP_VIDEO_CodingHEVC: if (p->fp_output) fwrite(buf, 1, len, p->fp_output); break; default: break; } } return MPP_OK; } static int parse_uvc_enc_cfg(cJSON *root, MpiEncMultiCtxInfo *info) { MpiEncArgs *cmd = info->cmd; UVCEncCfg *cfg = NULL; int ret = 0; if ((NULL == info) || (NULL == cmd)) return -1; info->cfg = mpp_calloc(UVCEncCfg, 1); cfg = info->cfg; if (NULL == cfg) { mpp_err_f("failed to alloc uvc encode cfg\n"); return -1; } cJSON *child = cJSON_GetObjectItem(root, "uvc_enc_cfg"); if (!child) { mpp_err_f("parse uvc enc cfg err!\n"); ret = -1; goto ERR; } cJSON *child_version = cJSON_GetObjectItem(child, "version"); if (child_version) mpp_log("parse_uvc_enc_cfg version:%s\n", child_version->valuestring); cJSON *child_common = cJSON_GetObjectItem(child, "common"); if (!child_common) { mpp_err_f("parse_uvc_enc_cfg common err\n"); ret = -1; goto ERR; } else { cJSON *child_common_fbc = cJSON_GetObjectItem(child_common, "fbc"); if (child_common_fbc) cfg->common_cfg.fbc = strstr(child_common_fbc->valuestring, "off") ? 0 : MPP_FRAME_FBC_AFBC_V1; cJSON *child_common_split_mode = cJSON_GetObjectItem(child_common, "split_mode"); if (child_common_split_mode) cfg->common_cfg.split_mode = strstr(child_common_split_mode->valuestring, "none") ? MPP_ENC_SPLIT_NONE : strstr(child_common_split_mode->valuestring, "byte") ? MPP_ENC_SPLIT_BY_BYTE : strstr(child_common_split_mode->valuestring, "ctu") ? MPP_ENC_SPLIT_BY_CTU : MPP_ENC_SPLIT_NONE; cJSON *child_common_split_arg = cJSON_GetObjectItem(child_common, "split_arg"); if (child_common_split_arg) cfg->common_cfg.split_arg = child_common_split_arg->valueint; } switch (cmd->type) { case MPP_VIDEO_CodingMJPEG: { cJSON *child_mjpeg = cJSON_GetObjectItem(child, "mjpeg"); if (!child_mjpeg) { mpp_err_f("parse_uvc_enc_cfg mjpeg err\n"); ret = -1; } else { cJSON *child_mjpeg_quant = cJSON_GetObjectItem(child_mjpeg, "quant"); if (child_mjpeg_quant) cfg->mjpeg_cfg.quant = clamp(child_mjpeg_quant->valueint, 1U, 10U); cJSON *child_mjpeg_qfactor = NULL; if (cmd->width >= 3840 || cmd->height >= 2160) child_mjpeg_qfactor = cJSON_GetObjectItem(child_mjpeg, "qfactor_low"); else if (cmd->width >= 2560 || cmd->height >= 1440) child_mjpeg_qfactor = cJSON_GetObjectItem(child_mjpeg, "qfactor_middle1"); else if (cmd->width >= 1920 || cmd->height >= 1080) child_mjpeg_qfactor = cJSON_GetObjectItem(child_mjpeg, "qfactor_middle2"); else child_mjpeg_qfactor = cJSON_GetObjectItem(child_mjpeg, "qfactor_high"); if (child_mjpeg_qfactor) cfg->mjpeg_cfg.qfactor = clamp(child_mjpeg_qfactor->valueint, 1U, 100U); cJSON *child_mjpeg_gop = cJSON_GetObjectItem(child_mjpeg, "gop"); if (child_mjpeg_gop) cfg->mjpeg_cfg.gop = clamp(child_mjpeg_gop->valueint, 1U, 100U); cJSON *child_mjpeg_rc_mode = cJSON_GetObjectItem(child_mjpeg, "rc_mode"); if (child_mjpeg_rc_mode) cfg->mjpeg_cfg.rc_mode = strstr(child_mjpeg_rc_mode->valuestring, "cbr") ? MPP_ENC_RC_MODE_CBR : strstr(child_mjpeg_rc_mode->valuestring, "vbr") ? MPP_ENC_RC_MODE_VBR : strstr(child_mjpeg_rc_mode->valuestring, "fixqp") ? MPP_ENC_RC_MODE_FIXQP : MPP_ENC_RC_MODE_CBR; cJSON *child_mjpeg_framerate = cJSON_GetObjectItem(child_mjpeg, "framerate"); if (child_mjpeg_framerate) cfg->mjpeg_cfg.framerate = clamp(child_mjpeg_framerate->valueint, MPP_ENC_CFG_MIN_FPS, MPP_ENC_CFG_MAX_FPS); cJSON *child_mjpeg_bps = cJSON_GetObjectItem(child_mjpeg, "bps"); if (child_mjpeg_bps) cfg->mjpeg_cfg.bps = clamp(child_mjpeg_bps->valueint, MPP_ENC_CFG_MIN_BPS, MPP_ENC_CFG_MAX_BPS); } } break; case MPP_VIDEO_CodingAVC: { cJSON *child_h264 = cJSON_GetObjectItem(child, "h264"); if (!child_h264) { mpp_err_f("parse_mpp_enc_cfg h264 err\n"); ret = -1; } else { cJSON *child_h264_gop = cJSON_GetObjectItem(child_h264, "gop"); if (child_h264_gop) cfg->h264_cfg.gop = clamp(child_h264_gop->valueint, 1U, 1000U); cJSON *child_h264_rc_mode = cJSON_GetObjectItem(child_h264, "rc_mode"); if (child_h264_rc_mode) cfg->h264_cfg.rc_mode = strstr(child_h264_rc_mode->valuestring, "cbr") ? MPP_ENC_RC_MODE_CBR : strstr(child_h264_rc_mode->valuestring, "avbr") ? MPP_ENC_RC_MODE_AVBR : strstr(child_h264_rc_mode->valuestring, "vbr") ? MPP_ENC_RC_MODE_VBR : strstr(child_h264_rc_mode->valuestring, "fixqp") ? MPP_ENC_RC_MODE_FIXQP : MPP_ENC_RC_MODE_CBR; cJSON *child_h264_framerate = cJSON_GetObjectItem(child_h264, "framerate"); if (child_h264_framerate) cfg->h264_cfg.framerate = clamp(child_h264_framerate->valueint, MPP_ENC_CFG_MIN_FPS, MPP_ENC_CFG_MAX_FPS); cJSON *child_h264_qp_init = cJSON_GetObjectItem(child_h264, "qp_init"); if (child_h264_qp_init) cfg->h264_cfg.qp_init = clamp(child_h264_qp_init->valueint, 1U, 51U); cJSON *child_h264_bps = cJSON_GetObjectItem(child_h264, "bps"); if (child_h264_bps) cfg->h264_cfg.bps = clamp(child_h264_bps->valueint, MPP_ENC_CFG_MIN_BPS, MPP_ENC_CFG_MAX_BPS); } } break; case MPP_VIDEO_CodingHEVC: { cJSON *child_h265 = cJSON_GetObjectItem(child, "h265"); if (!child_h265) { mpp_err_f("parse_mpp_enc_cfg h265 err\n"); ret = -1; } else { cJSON *child_h265_gop = cJSON_GetObjectItem(child_h265, "gop"); if (child_h265_gop) cfg->h265_cfg.gop = clamp(child_h265_gop->valueint, 1U, 1000U); cJSON *child_h265_rc_mode = cJSON_GetObjectItem(child_h265, "rc_mode"); if (child_h265_rc_mode) cfg->h265_cfg.rc_mode = strstr(child_h265_rc_mode->valuestring, "cbr") ? MPP_ENC_RC_MODE_CBR : strstr(child_h265_rc_mode->valuestring, "avbr") ? MPP_ENC_RC_MODE_AVBR : strstr(child_h265_rc_mode->valuestring, "vbr") ? MPP_ENC_RC_MODE_VBR : strstr(child_h265_rc_mode->valuestring, "fixqp") ? MPP_ENC_RC_MODE_FIXQP : MPP_ENC_RC_MODE_CBR; cJSON *child_h265_framerate = cJSON_GetObjectItem(child_h265, "framerate"); if (child_h265_framerate) cfg->h265_cfg.framerate = clamp(child_h265_framerate->valueint, MPP_ENC_CFG_MIN_FPS, MPP_ENC_CFG_MAX_FPS); cJSON *child_h265_qp_init = cJSON_GetObjectItem(child_h265, "qp_init"); if (child_h265_qp_init) cfg->h265_cfg.qp_init = clamp(child_h265_qp_init->valueint, 1U, 51U); cJSON *child_h265_bps = cJSON_GetObjectItem(child_h265, "bps"); if (child_h265_bps) cfg->h265_cfg.bps = clamp(child_h265_bps->valueint, MPP_ENC_CFG_MIN_BPS, MPP_ENC_CFG_MAX_BPS); } } break; default: mpp_err_f("unsupport encoder coding type %d\n", cmd->type); ret = -1; break; } ERR: return ret; } static int read_uvc_enc_cfg(MpiEncMultiCtxInfo *info) { int ret = -1; unsigned long read_size = 0; unsigned long size = 0; FILE *file_fd; struct stat buf; char *cfg = NULL; if (NULL == info) return -1; if (access(UVC_ENC_CFG, F_OK)) return -1; file_fd = fopen(UVC_ENC_CFG, "rb"); if (stat(UVC_ENC_CFG, &buf) < 0) { fclose(file_fd); return -1; } else { size = (unsigned long)buf.st_size; } mpp_log("get uvc cfg file size: %ld\n", size); cfg = (char *)malloc(size); while (read_size != size) read_size += fread(cfg, 1, size - read_size, file_fd); cJSON *root = cJSON_Parse(cfg); if (root == NULL) mpp_err_f("the %s is broken\n", UVC_ENC_CFG); else ret = parse_uvc_enc_cfg(root, info); if (file_fd) fclose(file_fd); if (cfg) free(cfg); if (root) cJSON_Delete(root); return ret; } RK_S32 mpi_enc_width_default_stride(RK_S32 width, MppFrameFormat fmt) { RK_S32 stride = 0; switch (fmt & MPP_FRAME_FMT_MASK) { case MPP_FMT_YUV420SP : case MPP_FMT_YUV420SP_VU : { stride = MPP_ALIGN(width, 8); } break; case MPP_FMT_YUV420P : { /* NOTE: 420P need to align to 16 so chroma can align to 8 */ stride = MPP_ALIGN(width, 16); } break; case MPP_FMT_YUV422P: case MPP_FMT_YUV422SP: case MPP_FMT_YUV422SP_VU: { /* NOTE: 422 need to align to 8 so chroma can align to 16 */ stride = MPP_ALIGN(width, 8); } break; case MPP_FMT_YUV444SP : case MPP_FMT_YUV444P : { stride = MPP_ALIGN(width, 8); } break; case MPP_FMT_RGB565: case MPP_FMT_BGR565: case MPP_FMT_RGB555: case MPP_FMT_BGR555: case MPP_FMT_RGB444: case MPP_FMT_BGR444: case MPP_FMT_YUV422_YUYV : case MPP_FMT_YUV422_YVYU : case MPP_FMT_YUV422_UYVY : case MPP_FMT_YUV422_VYUY : { /* NOTE: for vepu limitation */ stride = MPP_ALIGN(width, 8) * 2; } break; case MPP_FMT_RGB888 : case MPP_FMT_BGR888 : { /* NOTE: for vepu limitation */ stride = MPP_ALIGN(width, 8) * 3; } break; case MPP_FMT_RGB101010 : case MPP_FMT_BGR101010 : case MPP_FMT_ARGB8888 : case MPP_FMT_ABGR8888 : case MPP_FMT_BGRA8888 : case MPP_FMT_RGBA8888 : { /* NOTE: for vepu limitation */ stride = MPP_ALIGN(width, 8) * 4; } break; default : { mpp_err_f("do not support type %d\n", fmt); } break; } return stride; } static void uvc_mpp_enc_config(MpiEncMultiCtxInfo *info, struct uvc_source *src, struct v4l2_pix_format *fmt) { MpiEncArgs *cmd = info->cmd; MppFrameFormat input_fmt; bool with_h265 = false; if (src->SRC_Args & SRC_FMT_TYPE_IS_YUYV) input_fmt = MPP_FMT_YUV422_YUYV; else input_fmt = MPP_FMT_YUV420SP; if (src->SRC_Args & SINK_FMT_OUTPUT_H265) with_h265 = true; memset((void *)cmd, 0, sizeof(*cmd)); cmd->format = input_fmt; cmd->width = fmt->width; cmd->height = fmt->height; cmd->hor_stride = mpi_enc_width_default_stride(cmd->width, cmd->format); cmd->ver_stride = cmd->height; cmd->rc_mode = MPP_ENC_RC_MODE_FIXQP; switch (fmt->pixelformat) { case V4L2_PIX_FMT_YUYV: cmd->type = MPP_VIDEO_CodingUnused; mpp_log("%s: yuyv not need mpp encode.\n", __func__); break; case V4L2_PIX_FMT_MJPEG: cmd->type = MPP_VIDEO_CodingMJPEG; break; case V4L2_PIX_FMT_H264: if (with_h265) cmd->type = MPP_VIDEO_CodingHEVC; else cmd->type = MPP_VIDEO_CodingAVC; break; default: cmd->type = MPP_VIDEO_CodingUnused; mpp_log("%s: not support format: %x\n", __func__, fmt->pixelformat); break; } if (read_uvc_enc_cfg(info) < 0) { mpp_err_f("read_uvc_enc_cfg_file failed \n"); return; } if (NULL == info->cfg) { mpp_err_f("uvc mpp cfg is null \n"); return; } UVCEncCfg *cfg = info->cfg; cmd->format |= cfg->common_cfg.fbc; cmd->split_mode = cfg->common_cfg.split_mode; cmd->split_arg = cfg->common_cfg.split_arg; switch (cmd->type) { case MPP_VIDEO_CodingMJPEG: cmd->quant = cfg->mjpeg_cfg.quant; cmd->qfactor = cfg->mjpeg_cfg.qfactor; cmd->bps_target = cfg->mjpeg_cfg.bps; cmd->rc_mode = cfg->mjpeg_cfg.rc_mode; cmd->fps_in_num = cfg->mjpeg_cfg.framerate; cmd->fps_out_num = cfg->mjpeg_cfg.framerate; break; case MPP_VIDEO_CodingAVC: cmd->bps_target = cfg->h264_cfg.bps; cmd->rc_mode = cfg->h264_cfg.rc_mode; cmd->fps_in_num = cfg->h264_cfg.framerate; cmd->fps_out_num = cfg->h264_cfg.framerate; cmd->qp_init = cfg->h264_cfg.qp_init; break; case MPP_VIDEO_CodingHEVC: cmd->bps_target = cfg->h265_cfg.bps; cmd->rc_mode = cfg->h265_cfg.rc_mode; cmd->fps_in_num = cfg->h265_cfg.framerate; cmd->fps_out_num = cfg->h265_cfg.framerate; cmd->qp_init = cfg->h265_cfg.qp_init; break; default: break; } mpp_log("Read UVC MPP CFG: in_fmt:%d, out_fmt:%d, split_mode:%d, split_arg:%d\n", cmd->format, cmd->type, cmd->split_mode, cmd->split_arg); mpp_log("Read UVC MPP CFG: bps:%d, rc_mode:%d, fps_in_num:%d, fps_out_num:%d\n", cmd->bps_target, cmd->rc_mode, cmd->fps_in_num, cmd->fps_out_num); mpp_log("Read UVC MPP CFG: qp_init:%d, quant:%d, qfactor:%d\n", cmd->qp_init, cmd->quant, cmd->qfactor); } static MPP_RET uvc_mpp_enc_cfg_setup(MpiEncMultiCtxInfo *info) { MpiEncArgs *cmd = info->cmd; MpiEncData *p = &info->ctx; MppApi *mpi = p->mpi; MppCtx ctx = p->ctx; MppEncCfg cfg = p->cfg; MPP_RET ret; /* setup default parameter */ if (p->fps_in_den == 0) p->fps_in_den = 1; if (p->fps_in_num == 0) p->fps_in_num = 30; if (p->fps_out_den == 0) p->fps_out_den = 1; if (p->fps_out_num == 0) p->fps_out_num = 30; if (!p->bps) p->bps = p->width * p->height / 8 * (p->fps_out_num / p->fps_out_den); mpp_enc_cfg_set_s32(cfg, "prep:width", p->width); mpp_enc_cfg_set_s32(cfg, "prep:height", p->height); mpp_enc_cfg_set_s32(cfg, "prep:hor_stride", p->hor_stride); mpp_enc_cfg_set_s32(cfg, "prep:ver_stride", p->ver_stride); mpp_enc_cfg_set_s32(cfg, "prep:format", p->fmt); mpp_enc_cfg_set_s32(cfg, "rc:mode", p->rc_mode); /* fix input / output frame rate */ mpp_enc_cfg_set_s32(cfg, "rc:fps_in_flex", p->fps_in_flex); mpp_enc_cfg_set_s32(cfg, "rc:fps_in_num", p->fps_in_num); mpp_enc_cfg_set_s32(cfg, "rc:fps_in_denorm", p->fps_in_den); mpp_enc_cfg_set_s32(cfg, "rc:fps_out_flex", p->fps_out_flex); mpp_enc_cfg_set_s32(cfg, "rc:fps_out_num", p->fps_out_num); mpp_enc_cfg_set_s32(cfg, "rc:fps_out_denorm", p->fps_out_den); mpp_enc_cfg_set_s32(cfg, "rc:gop", p->gop_len ? p->gop_len : p->fps_out_num * 2); /* drop frame or not when bitrate overflow */ mpp_enc_cfg_set_u32(cfg, "rc:drop_mode", MPP_ENC_RC_DROP_FRM_DISABLED); mpp_enc_cfg_set_u32(cfg, "rc:drop_thd", 20); /* 20% of max bps */ mpp_enc_cfg_set_u32(cfg, "rc:drop_gap", 1); /* Do not continuous drop frame */ /* setup bitrate for different rc_mode */ mpp_enc_cfg_set_s32(cfg, "rc:bps_target", p->bps); switch (p->rc_mode) { case MPP_ENC_RC_MODE_FIXQP: /* do not setup bitrate on FIXQP mode */ break; case MPP_ENC_RC_MODE_CBR: /* CBR mode has narrow bound */ mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16); mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 15 / 16); break; case MPP_ENC_RC_MODE_VBR: case MPP_ENC_RC_MODE_AVBR: /* VBR mode has wide bound */ mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16); mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 1 / 16); break; default: /* default use CBR mode */ mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16); mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 15 / 16); break; } /* setup qp for different codec and rc_mode */ /* setup qp for different codec and rc_mode */ switch (p->type) { case MPP_VIDEO_CodingAVC: case MPP_VIDEO_CodingHEVC: switch (p->rc_mode) { case MPP_ENC_RC_MODE_FIXQP: mpp_enc_cfg_set_s32(cfg, "rc:qp_init", cmd->qp_init); mpp_enc_cfg_set_s32(cfg, "rc:qp_max", cmd->qp_init); mpp_enc_cfg_set_s32(cfg, "rc:qp_min", cmd->qp_init); mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", cmd->qp_init); mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", cmd->qp_init); mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 0); break; case MPP_ENC_RC_MODE_CBR: case MPP_ENC_RC_MODE_VBR: case MPP_ENC_RC_MODE_AVBR: mpp_enc_cfg_set_s32(cfg, "rc:qp_init", 26); mpp_enc_cfg_set_s32(cfg, "rc:qp_max", 51); mpp_enc_cfg_set_s32(cfg, "rc:qp_min", 10); mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", 51); mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", 10); mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 2); break; default: mpp_err_f("unsupport encoder rc mode %d\n", p->rc_mode); break; }; break; case MPP_VIDEO_CodingVP8: /* vp8 only setup base qp range */ mpp_enc_cfg_set_s32(cfg, "rc:qp_init", 40); mpp_enc_cfg_set_s32(cfg, "rc:qp_max", 127); mpp_enc_cfg_set_s32(cfg, "rc:qp_min", 0); mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", 127); mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", 0); mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 6); break; case MPP_VIDEO_CodingMJPEG: /* jpeg use special codec config to control qtable */ if (p->qfactor) { mpp_enc_cfg_set_s32(cfg, "jpeg:q_factor", p->qfactor); mpp_enc_cfg_set_s32(cfg, "jpeg:qf_max", 99); mpp_enc_cfg_set_s32(cfg, "jpeg:qf_min", 1); } else if (p->quant){ mpp_enc_cfg_set_s32(cfg, "jpeg:quant", p->quant); } else { mpp_enc_cfg_set_s32(cfg, "jpeg:q_factor", 80); mpp_enc_cfg_set_s32(cfg, "jpeg:qf_max", 99); mpp_enc_cfg_set_s32(cfg, "jpeg:qf_min", 1); } break; default: break; } /* setup codec */ mpp_enc_cfg_set_s32(cfg, "codec:type", p->type); switch (p->type) { case MPP_VIDEO_CodingAVC: /* * H.264 profile_idc parameter * 66 - Baseline profile * 77 - Main profile * 100 - High profile */ mpp_enc_cfg_set_s32(cfg, "h264:profile", 100); /* * H.264 level_idc parameter * 10 / 11 / 12 / 13 - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps * 20 / 21 / 22 - cif@30fps / half-D1@@25fps / D1@12.5fps * 30 / 31 / 32 - D1@25fps / 720p@30fps / 720p@60fps * 40 / 41 / 42 - 1080p@30fps / 1080p@30fps / 1080p@60fps * 50 / 51 / 52 - 4K@30fps */ mpp_enc_cfg_set_s32(cfg, "h264:level", 40); mpp_enc_cfg_set_s32(cfg, "h264:cabac_en", 1); mpp_enc_cfg_set_s32(cfg, "h264:cabac_idc", 0); mpp_enc_cfg_set_s32(cfg, "h264:trans8x8", 1); break; case MPP_VIDEO_CodingHEVC: case MPP_VIDEO_CodingMJPEG: case MPP_VIDEO_CodingVP8: break; default: mpp_err_f("unsupport encoder coding type %d\n", p->type); break; } if (p->split_mode) { mpp_enc_cfg_set_s32(cfg, "split:mode", p->split_mode); mpp_enc_cfg_set_s32(cfg, "split:arg", p->split_arg); } ret = mpi->control(ctx, MPP_ENC_SET_CFG, cfg); if (ret) { mpp_err_f("mpi control enc set cfg failed ret %d\n", ret); return ret; } /* optional */ p->sei_mode = MPP_ENC_SEI_MODE_ONE_FRAME; ret = mpi->control(ctx, MPP_ENC_SET_SEI_CFG, &p->sei_mode); if (ret) { mpp_err_f("mpi control enc set sei cfg failed ret %d\n", ret); return ret; } if (p->type == MPP_VIDEO_CodingAVC || p->type == MPP_VIDEO_CodingHEVC) { p->header_mode = MPP_ENC_HEADER_MODE_EACH_IDR; ret = mpi->control(ctx, MPP_ENC_SET_HEADER_MODE, &p->header_mode); if (ret) { mpp_err_f("mpi control enc set header mode failed ret %d\n", ret); return ret; } } return ret; } static MPP_RET mpi_ctx_init(MpiEncMultiCtxInfo *info) { MpiEncArgs *cmd = info->cmd; MpiEncData *p = &info->ctx; MPP_RET ret = MPP_OK; // get paramter from cmd p->width = cmd->width; p->height = cmd->height; p->hor_stride = (cmd->hor_stride) ? (cmd->hor_stride) : (MPP_ALIGN(cmd->width, 16)); p->ver_stride = (cmd->ver_stride) ? (cmd->ver_stride) : (MPP_ALIGN(cmd->height, 16)); p->fmt = cmd->format; p->type = cmd->type; p->bps = cmd->bps_target; p->bps_min = cmd->bps_min; p->bps_max = cmd->bps_max; p->rc_mode = cmd->rc_mode; p->frame_num = cmd->frame_num; if (cmd->type == MPP_VIDEO_CodingMJPEG && p->frame_num == 0) { mpp_log("jpege default encode only one frame.\n"); p->frame_num = 1; } p->gop_mode = cmd->gop_mode; p->gop_len = cmd->gop_len; p->vi_len = cmd->vi_len; p->quant = cmd->quant; p->qfactor = cmd->qfactor; p->fps_in_flex = cmd->fps_in_flex; p->fps_in_den = cmd->fps_in_den; p->fps_in_num = cmd->fps_in_num; p->fps_out_flex = cmd->fps_out_flex; p->fps_out_den = cmd->fps_out_den; p->fps_out_num = cmd->fps_out_num; p->mdinfo_size = (MPP_VIDEO_CodingHEVC == cmd->type) ? (MPP_ALIGN(p->hor_stride, 64) >> 6) * (MPP_ALIGN(p->ver_stride, 64) >> 6) * 32 : (MPP_ALIGN(p->hor_stride, 64) >> 6) * (MPP_ALIGN(p->ver_stride, 16) >> 4) * 8; // update resource parameter switch (p->fmt & MPP_FRAME_FMT_MASK) { case MPP_FMT_YUV420SP: case MPP_FMT_YUV420P: p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 3 / 2; break; case MPP_FMT_YUV422_YUYV : case MPP_FMT_YUV422_YVYU : case MPP_FMT_YUV422_UYVY : case MPP_FMT_YUV422_VYUY : case MPP_FMT_YUV422P : case MPP_FMT_YUV422SP : p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 2; break; case MPP_FMT_RGB444 : case MPP_FMT_BGR444 : case MPP_FMT_RGB555 : case MPP_FMT_BGR555 : case MPP_FMT_RGB565 : case MPP_FMT_BGR565 : case MPP_FMT_RGB888 : case MPP_FMT_BGR888 : case MPP_FMT_RGB101010 : case MPP_FMT_BGR101010 : case MPP_FMT_ARGB8888 : case MPP_FMT_ABGR8888 : case MPP_FMT_BGRA8888 : case MPP_FMT_RGBA8888 : p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64); break; default: p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 4; break; } if (MPP_FRAME_FMT_IS_FBC(p->fmt)) p->header_size = MPP_ALIGN(MPP_ALIGN(p->width, 16) * MPP_ALIGN(p->height, 16) / 16, SZ_4K); else p->header_size = 0; return ret; } static MPP_RET mpi_ctx_deinit(MpiEncData *data) { if (!data) { mpp_err_f("invalid input data %p\n", data); return MPP_ERR_NULL_PTR; } return MPP_OK; } static int uvc_encode_exit(struct uvc_source *src) { MpiEncMultiCtxInfo *info = src->context; MpiEncData *p = &info->ctx; MPP_RET ret = MPP_OK; mpp_log("uvc mpp encode exit\n"); uvc_stream_file_close(info); if (p->ctx) { ret = p->mpi->reset(p->ctx); if (ret) mpp_err_f("mpi->reset failed\n"); } if (p->cfg) { mpp_enc_cfg_deinit(p->cfg); p->cfg = NULL; } if (p->ctx) { mpp_destroy(p->ctx); p->ctx = NULL; } mpi_ctx_deinit(p); if (info->cfg) MPP_FREE(info->cfg); if (info->cmd) MPP_FREE(info->cmd); if (info) MPP_FREE(info); return ret; } static int uvc_encode_init(struct uvc_source *src, struct v4l2_pix_format *fmt) { MpiEncMultiCtxInfo *info = NULL; MpiEncArgs *cmd = NULL; MpiEncData *p = NULL; MppPollType timeout = MPP_POLL_BLOCK; MPP_RET ret = MPP_OK; info = mpp_calloc(MpiEncMultiCtxInfo, 1); if (NULL == info) { mpp_err_f("failed to alloc context for instances\n"); ret = -1; goto INIT_EXIT; } src->context = info; cmd = mpp_calloc(MpiEncArgs, 1); if (NULL == cmd) { mpp_err_f("failed to alloc MpiEncArgs for instances\n"); ret = -1; goto INIT_EXIT; } info->cmd = cmd; p = &info->ctx; uvc_mpp_enc_config(info, src, fmt); mpp_log("mpi_enc_init start\n"); ret = mpi_ctx_init(info); if (ret) { mpp_err_f("mpp ctx init failed ret %d\n", ret); goto INIT_EXIT; } ret = mpp_create(&p->ctx, &p->mpi); if (ret) { mpp_err_f("mpp_create failed ret %d\n", ret); goto INIT_EXIT; } mpp_log("%p mpi_enc_init encoder test start w %d h %d type %d\n", p->ctx, p->width, p->height, p->type); ret = p->mpi->control(p->ctx, MPP_SET_OUTPUT_TIMEOUT, &timeout); if (MPP_OK != ret) { mpp_err_f("mpi control set output timeout %d ret %d\n", timeout, ret); goto INIT_EXIT; } ret = mpp_init(p->ctx, MPP_CTX_ENC, p->type); if (ret) { mpp_err_f("mpp_init failed ret %d\n", ret); goto INIT_EXIT; } ret = mpp_enc_cfg_init(&p->cfg); if (ret) { mpp_err_f("mpp_enc_cfg_init failed ret %d\n", ret); goto INIT_EXIT; } ret = uvc_mpp_enc_cfg_setup(info); if (ret) { mpp_err_f("test mpp setup failed ret %d\n", ret); goto INIT_EXIT; } if (src->log_level & DUMP_IN) cmd->save_in_en = 1; if (src->log_level & DUMP_OUT) cmd->save_out_en = 1; uvc_stream_file_open(info); return ret; INIT_EXIT: uvc_encode_exit(src); return ret; } static int uvc_encode_process(struct uvc_source *src, struct video_buffer *src_buffer, struct video_buffer *sink_buffer) { MpiEncMultiCtxInfo *info = src->context; MpiEncData *p = &info->ctx; MppApi *mpi; MppCtx ctx; MppFrame frame = NULL; MppPacket packet = NULL; MppBuffer buf_in = NULL; MppBuffer buf_out = NULL; MppBufferInfo inputCommit; MppBufferInfo outputCommit; MppTask task = NULL; MPP_RET ret = MPP_OK; if (NULL == p) return MPP_ERR_NULL_PTR; mpi = p->mpi; ctx = p->ctx; p->frame_num ++; /* workaround: fix h264 stream on slow */ if (p->type == MPP_VIDEO_CodingAVC || p->type == MPP_VIDEO_CodingHEVC) { if (p->frame_num < 10 && p->frame_num % 2 == 0) { ret = mpi->control(ctx, MPP_ENC_SET_IDR_FRAME, NULL); mpp_log("H264 set IDR in first 10 frames, p->frame_num:%d, ret:%d \n", p->frame_num, ret); } } ret = mpp_frame_init(&frame); if (ret) { mpp_err_f("mpp_frame_init failed\n"); goto ERR; } mpp_frame_set_width(frame, p->width); mpp_frame_set_height(frame, p->height); mpp_frame_set_hor_stride(frame, p->hor_stride); mpp_frame_set_ver_stride(frame, p->ver_stride); mpp_frame_set_fmt(frame, p->fmt); /* output buffer */ memset(&outputCommit, 0, sizeof(outputCommit)); outputCommit.type = MPP_BUFFER_TYPE_DRM; outputCommit.size = sink_buffer->size; outputCommit.fd = sink_buffer->dmabuf; ret = mpp_buffer_import(&buf_out, &outputCommit); if (ret) { mpp_err_f("import output buffer failed\n"); goto ERR; } mpp_packet_init_with_buffer(&packet, buf_out); mpp_packet_set_length(packet, 0); /* input buffer */ memset(&inputCommit, 0, sizeof(inputCommit)); inputCommit.type = MPP_BUFFER_TYPE_DRM; inputCommit.size = src_buffer->size; inputCommit.fd = src_buffer->dmabuf; ret = mpp_buffer_import(&buf_in, &inputCommit); if (ret) { mpp_err_f("import input buffer failed\n"); goto ERR; } mpp_frame_set_buffer(frame, buf_in); ret = mpi->poll(ctx, MPP_PORT_INPUT, MPP_POLL_BLOCK); if (ret) { mpp_err_f("mpp task input poll failed ret %d\n", ret); goto ERR; } ret = mpi->dequeue(ctx, MPP_PORT_INPUT, &task); if (ret || NULL == task) { mpp_err_f("mpp task input dequeue failed ret %d task %p\n", ret, task); goto ERR; } mpp_task_meta_set_frame(task, KEY_INPUT_FRAME, frame); mpp_task_meta_set_packet(task, KEY_OUTPUT_PACKET, packet); ret = mpi->enqueue(ctx, MPP_PORT_INPUT, task); if (ret) { mpp_err_f("mpp task input enqueue failed\n"); goto ERR; } task = NULL; ret = mpi->poll(ctx, MPP_PORT_OUTPUT, MPP_POLL_BLOCK); if (ret) { mpp_err_f("mpp task output poll failed ret %d\n", ret); goto ERR; } ret = mpi->dequeue(ctx, MPP_PORT_OUTPUT, &task); if (ret || NULL == task) { mpp_err_f("mpp task output dequeue failed ret %d task %p\n", ret, task); goto ERR; } if (task) { MppFrame packet_out = NULL; mpp_task_meta_get_packet(task, KEY_OUTPUT_PACKET, &packet_out); ret = mpi->enqueue(ctx, MPP_PORT_OUTPUT, task); if (ret) { mpp_err_f("mpp task output enqueue failed\n"); goto ERR; } if (packet_out != packet) { mpp_err_f("mpp get packet failed\n"); goto ERR; } } uvc_stream_in_write(info, src_buffer->mem, src_buffer->bytesused); sink_buffer->bytesused = mpp_packet_get_length(packet); uvc_stream_out_write(info, sink_buffer->mem, sink_buffer->bytesused); ERR: if (packet) mpp_packet_deinit(&packet); if (frame) { mpp_frame_deinit(&frame); frame = NULL; } if (buf_in) { mpp_buffer_put(buf_in); buf_in = NULL; } if (buf_out) { mpp_buffer_put(buf_out); buf_out = NULL; } return ret; } static const struct video_encode_ops uvc_with_encode_ops = { .init = uvc_encode_init, .exit = uvc_encode_exit, .process = uvc_encode_process, }; __attribute__((visibility("default"))) void uvc_gadget_register_encode(struct uvc_source *src) { src->enc_ops = &uvc_with_encode_ops; }