/*
|
* Copyright 2015 Rockchip Electronics Co. LTD
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
#define MODULE_TAG "mpp_enc"
|
|
#include <string.h>
|
|
#include "mpp_env.h"
|
#include "mpp_mem.h"
|
#include "mpp_info.h"
|
#include "mpp_common.h"
|
#include "mpp_2str.h"
|
|
#include "mpp.h"
|
#include "mpp_enc_debug.h"
|
#include "mpp_enc_cfg_impl.h"
|
#include "mpp_enc_impl.h"
|
#include "mpp_enc_cb_param.h"
|
|
RK_U32 mpp_enc_debug = 0;
|
|
MPP_RET mpp_enc_init_v2(MppEnc *enc, MppEncInitCfg *cfg)
|
{
|
MPP_RET ret;
|
MppCodingType coding = cfg->coding;
|
EncImpl impl = NULL;
|
MppEncImpl *p = NULL;
|
MppEncHal enc_hal = NULL;
|
MppEncHalCfg enc_hal_cfg;
|
EncImplCfg ctrl_cfg;
|
|
mpp_env_get_u32("mpp_enc_debug", &mpp_enc_debug, 0);
|
|
if (NULL == enc) {
|
mpp_err_f("failed to malloc context\n");
|
return MPP_ERR_NULL_PTR;
|
}
|
|
*enc = NULL;
|
|
p = mpp_calloc(MppEncImpl, 1);
|
if (NULL == p) {
|
mpp_err_f("failed to malloc context\n");
|
return MPP_ERR_MALLOC;
|
}
|
|
ret = mpp_enc_refs_init(&p->refs);
|
if (ret) {
|
mpp_err_f("could not init enc refs\n");
|
goto ERR_RET;
|
}
|
|
p->output_cb.callBack = mpp_enc_callback;
|
p->output_cb.ctx = p;
|
p->output_cb.cmd = ENC_CALLBACK_BASE;
|
|
// H.264 encoder use mpp_enc_hal path
|
// create hal first
|
enc_hal_cfg.coding = coding;
|
enc_hal_cfg.cfg = &p->cfg;
|
enc_hal_cfg.output_cb = &p->output_cb;
|
enc_hal_cfg.task_cnt = cfg->task_cnt;
|
enc_hal_cfg.type = VPU_CLIENT_BUTT;
|
enc_hal_cfg.dev = NULL;
|
enc_hal_cfg.cap_recn_out = 0;
|
|
ctrl_cfg.coding = coding;
|
ctrl_cfg.type = VPU_CLIENT_BUTT;
|
ctrl_cfg.cfg = &p->cfg;
|
ctrl_cfg.refs = p->refs;
|
|
ret = mpp_enc_hal_init(&enc_hal, &enc_hal_cfg);
|
if (ret) {
|
mpp_err_f("could not init enc hal\n");
|
goto ERR_RET;
|
}
|
|
ctrl_cfg.type = enc_hal_cfg.type;
|
|
ret = enc_impl_init(&impl, &ctrl_cfg);
|
if (ret) {
|
mpp_err_f("could not init impl\n");
|
goto ERR_RET;
|
}
|
|
ret = hal_info_init(&p->hal_info, MPP_CTX_ENC, coding);
|
if (ret) {
|
mpp_err_f("could not init hal info\n");
|
goto ERR_RET;
|
}
|
|
p->coding = coding;
|
p->impl = impl;
|
p->enc_hal = enc_hal;
|
p->dev = enc_hal_cfg.dev;
|
p->mpp = cfg->mpp;
|
p->tasks = enc_hal_cfg.tasks;
|
p->sei_mode = MPP_ENC_SEI_MODE_ONE_SEQ;
|
p->version_info = get_mpp_version();
|
p->version_length = strlen(p->version_info);
|
p->rc_cfg_size = SZ_1K;
|
p->rc_cfg_info = mpp_calloc_size(char, p->rc_cfg_size);
|
|
if (enc_hal_cfg.cap_recn_out)
|
p->support_hw_deflicker = 1;
|
|
{
|
// create header packet storage
|
size_t size = SZ_1K;
|
p->hdr_buf = mpp_calloc_size(void, size);
|
|
mpp_packet_init(&p->hdr_pkt, p->hdr_buf, size);
|
mpp_packet_set_length(p->hdr_pkt, 0);
|
}
|
{
|
Mpp *mpp = (Mpp *)p->mpp;
|
|
p->input = mpp_task_queue_get_port(mpp->mInputTaskQueue, MPP_PORT_OUTPUT);
|
p->output = mpp_task_queue_get_port(mpp->mOutputTaskQueue, MPP_PORT_INPUT);
|
}
|
|
/* NOTE: setup configure coding for check */
|
p->cfg.codec.coding = coding;
|
p->cfg.plt_cfg.plt = &p->cfg.plt_data;
|
mpp_enc_ref_cfg_init(&p->cfg.ref_cfg);
|
ret = mpp_enc_ref_cfg_copy(p->cfg.ref_cfg, mpp_enc_ref_default());
|
ret = mpp_enc_refs_set_cfg(p->refs, mpp_enc_ref_default());
|
|
sem_init(&p->enc_reset, 0, 0);
|
sem_init(&p->cmd_start, 0, 0);
|
sem_init(&p->cmd_done, 0, 0);
|
|
*enc = p;
|
return ret;
|
ERR_RET:
|
mpp_enc_deinit_v2(p);
|
return ret;
|
}
|
|
MPP_RET mpp_enc_deinit_v2(MppEnc ctx)
|
{
|
MppEncImpl *enc = (MppEncImpl *)ctx;
|
|
if (NULL == enc) {
|
mpp_err_f("found NULL input\n");
|
return MPP_ERR_NULL_PTR;
|
}
|
|
if (enc->hal_info) {
|
hal_info_deinit(enc->hal_info);
|
enc->hal_info = NULL;
|
}
|
|
if (enc->impl) {
|
enc_impl_deinit(enc->impl);
|
enc->impl = NULL;
|
}
|
|
if (enc->enc_hal) {
|
mpp_enc_hal_deinit(enc->enc_hal);
|
enc->enc_hal = NULL;
|
}
|
|
if (enc->hdr_pkt)
|
mpp_packet_deinit(&enc->hdr_pkt);
|
|
MPP_FREE(enc->hdr_buf);
|
|
if (enc->cfg.ref_cfg) {
|
mpp_enc_ref_cfg_deinit(&enc->cfg.ref_cfg);
|
enc->cfg.ref_cfg = NULL;
|
}
|
|
if (enc->refs) {
|
mpp_enc_refs_deinit(&enc->refs);
|
enc->refs = NULL;
|
}
|
|
if (enc->rc_ctx) {
|
rc_deinit(enc->rc_ctx);
|
enc->rc_ctx = NULL;
|
}
|
|
MPP_FREE(enc->rc_cfg_info);
|
enc->rc_cfg_size = 0;
|
enc->rc_cfg_length = 0;
|
|
sem_destroy(&enc->enc_reset);
|
sem_destroy(&enc->cmd_start);
|
sem_destroy(&enc->cmd_done);
|
|
mpp_free(enc);
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_enc_start_v2(MppEnc ctx)
|
{
|
MppEncImpl *enc = (MppEncImpl *)ctx;
|
char name[16];
|
|
enc_dbg_func("%p in\n", enc);
|
|
snprintf(name, sizeof(name) - 1, "mpp_%se_%d",
|
strof_coding_type(enc->coding), getpid());
|
|
enc->thread_enc = new MppThread(mpp_enc_thread, enc->mpp, name);
|
enc->thread_enc->start();
|
|
enc_dbg_func("%p out\n", enc);
|
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_enc_start_async(MppEnc ctx)
|
{
|
MppEncImpl *enc = (MppEncImpl *)ctx;
|
char name[16];
|
|
enc_dbg_func("%p in\n", enc);
|
|
snprintf(name, sizeof(name) - 1, "mpp_%se_%d",
|
strof_coding_type(enc->coding), getpid());
|
|
enc->thread_enc = new MppThread(mpp_enc_async_thread, enc->mpp, name);
|
enc->thread_enc->start();
|
|
enc_dbg_func("%p out\n", enc);
|
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_enc_stop_v2(MppEnc ctx)
|
{
|
MPP_RET ret = MPP_OK;
|
MppEncImpl *enc = (MppEncImpl *)ctx;
|
|
enc_dbg_func("%p in\n", enc);
|
|
if (enc->thread_enc) {
|
enc->thread_enc->stop();
|
delete enc->thread_enc;
|
enc->thread_enc = NULL;
|
}
|
|
enc_dbg_func("%p out\n", enc);
|
return ret;
|
|
}
|
|
MPP_RET mpp_enc_reset_v2(MppEnc ctx)
|
{
|
MppEncImpl *enc = (MppEncImpl *)ctx;
|
|
enc_dbg_func("%p in\n", enc);
|
if (NULL == enc) {
|
mpp_err_f("found NULL input enc\n");
|
return MPP_ERR_NULL_PTR;
|
}
|
|
MppThread *thd = enc->thread_enc;
|
|
thd->lock(THREAD_CONTROL);
|
enc->reset_flag = 1;
|
mpp_enc_notify_v2(enc, MPP_ENC_RESET);
|
thd->unlock(THREAD_CONTROL);
|
sem_wait(&enc->enc_reset);
|
mpp_assert(enc->reset_flag == 0);
|
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_enc_notify_v2(MppEnc ctx, RK_U32 flag)
|
{
|
MppEncImpl *enc = (MppEncImpl *)ctx;
|
|
enc_dbg_func("%p in flag %08x\n", enc, flag);
|
MppThread *thd = enc->thread_enc;
|
|
thd->lock();
|
if (flag == MPP_ENC_CONTROL) {
|
enc->notify_flag |= flag;
|
enc_dbg_notify("%p status %08x notify control signal\n", enc,
|
enc->status_flag);
|
thd->signal();
|
} else {
|
RK_U32 old_flag = enc->notify_flag;
|
|
enc->notify_flag |= flag;
|
if ((old_flag != enc->notify_flag) &&
|
(enc->notify_flag & enc->status_flag)) {
|
enc_dbg_notify("%p status %08x notify %08x signal\n", enc,
|
enc->status_flag, enc->notify_flag);
|
thd->signal();
|
}
|
}
|
thd->unlock();
|
enc_dbg_func("%p out\n", enc);
|
return MPP_OK;
|
}
|
|
/*
|
* preprocess config and rate-control config is common config then they will
|
* be done in mpp_enc layer
|
*
|
* codec related config will be set in each hal component
|
*/
|
MPP_RET mpp_enc_control_v2(MppEnc ctx, MpiCmd cmd, void *param)
|
{
|
MppEncImpl *enc = (MppEncImpl *)ctx;
|
|
if (NULL == enc) {
|
mpp_err_f("found NULL enc\n");
|
return MPP_ERR_NULL_PTR;
|
}
|
|
if (NULL == param && cmd != MPP_ENC_SET_IDR_FRAME && cmd != MPP_ENC_SET_REF_CFG) {
|
mpp_err_f("found NULL param enc %p cmd %x\n", enc, cmd);
|
return MPP_ERR_NULL_PTR;
|
}
|
|
AutoMutex auto_lock(&enc->lock);
|
MPP_RET ret = MPP_OK;
|
|
enc_dbg_ctrl("sending cmd %d param %p\n", cmd, param);
|
|
switch (cmd) {
|
case MPP_ENC_GET_CFG : {
|
MppEncCfgImpl *p = (MppEncCfgImpl *)param;
|
MppEncCfgSet *cfg = &p->cfg;
|
|
enc_dbg_ctrl("get all config\n");
|
memcpy(cfg, &enc->cfg, sizeof(enc->cfg));
|
if (cfg->prep.rotation == MPP_ENC_ROT_90 ||
|
cfg->prep.rotation == MPP_ENC_ROT_270) {
|
MPP_SWAP(RK_S32, cfg->prep.width, cfg->prep.height);
|
}
|
/* cleanup output change flag to avoid extra change flag bit when user resend the cfg */
|
cfg->rc.change = 0;
|
cfg->prep.change = 0;
|
cfg->hw.change = 0;
|
cfg->codec.change = 0;
|
cfg->split.change = 0;
|
cfg->tune.change = 0;
|
} break;
|
case MPP_ENC_GET_PREP_CFG : {
|
enc_dbg_ctrl("get prep config\n");
|
memcpy(param, &enc->cfg.prep, sizeof(enc->cfg.prep));
|
} break;
|
case MPP_ENC_GET_RC_CFG : {
|
enc_dbg_ctrl("get rc config\n");
|
memcpy(param, &enc->cfg.rc, sizeof(enc->cfg.rc));
|
} break;
|
case MPP_ENC_GET_CODEC_CFG : {
|
enc_dbg_ctrl("get codec config\n");
|
memcpy(param, &enc->cfg.codec, sizeof(enc->cfg.codec));
|
} break;
|
case MPP_ENC_GET_HEADER_MODE : {
|
enc_dbg_ctrl("get header mode\n");
|
memcpy(param, &enc->hdr_mode, sizeof(enc->hdr_mode));
|
} break;
|
case MPP_ENC_GET_OSD_PLT_CFG : {
|
enc_dbg_ctrl("get osd plt cfg\n");
|
memcpy(param, &enc->cfg.plt_cfg, sizeof(enc->cfg.plt_cfg));
|
} break;
|
default : {
|
// Cmd which is not get configure will handle by enc_impl
|
enc->cmd = cmd;
|
enc->param = param;
|
enc->cmd_ret = &ret;
|
enc->cmd_send++;
|
mpp_enc_notify_v2(ctx, MPP_ENC_CONTROL);
|
sem_post(&enc->cmd_start);
|
sem_wait(&enc->cmd_done);
|
|
/* check the command is processed */
|
mpp_assert(!enc->cmd);
|
mpp_assert(!enc->param);
|
} break;
|
}
|
|
enc_dbg_ctrl("sending cmd %d done\n", cmd);
|
return ret;
|
}
|