android13/external/uvc-gadget/libuvc-mpi/uvc_encode.c

1064 lines
29 KiB
C
Executable File

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* rockchip uvc mpp encode
*
* Copyright (C) 2022 Rockchip Electronics Co., Ltd.
*
* Author: Bin Yang <yangbin@rock-chips.com>
*/
#include <stdlib.h>
#include <string.h>
#include <linux/videodev2.h>
#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;
}