1064 lines
29 KiB
C
Executable File
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;
|
|
}
|