/*
|
* 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_dec"
|
|
#include <string.h>
|
|
#include "mpp_env.h"
|
#include "mpp_mem.h"
|
#include "mpp_time.h"
|
#include "mpp_debug.h"
|
|
#include "mpp.h"
|
|
#include "mpp_dec_impl.h"
|
#include "mpp_buffer_impl.h"
|
#include "mpp_packet_impl.h"
|
#include "mpp_frame_impl.h"
|
#include "mpp_dec_cfg_impl.h"
|
|
#include "mpp_dec_vproc.h"
|
#include "mpp_dec_cb_param.h"
|
|
static RK_U32 mpp_dec_debug = 0;
|
|
#define MPP_DEC_DBG_FUNCTION (0x00000001)
|
#define MPP_DEC_DBG_TIMING (0x00000002)
|
#define MPP_DEC_DBG_STATUS (0x00000010)
|
#define MPP_DEC_DBG_DETAIL (0x00000020)
|
#define MPP_DEC_DBG_RESET (0x00000040)
|
#define MPP_DEC_DBG_NOTIFY (0x00000080)
|
|
#define mpp_dec_dbg(flag, fmt, ...) _mpp_dbg(mpp_dec_debug, flag, fmt, ## __VA_ARGS__)
|
#define mpp_dec_dbg_f(flag, fmt, ...) _mpp_dbg_f(mpp_dec_debug, flag, fmt, ## __VA_ARGS__)
|
|
#define dec_dbg_func(fmt, ...) mpp_dec_dbg_f(MPP_DEC_DBG_FUNCTION, fmt, ## __VA_ARGS__)
|
#define dec_dbg_status(fmt, ...) mpp_dec_dbg_f(MPP_DEC_DBG_STATUS, fmt, ## __VA_ARGS__)
|
#define dec_dbg_detail(fmt, ...) mpp_dec_dbg(MPP_DEC_DBG_DETAIL, fmt, ## __VA_ARGS__)
|
#define dec_dbg_reset(fmt, ...) mpp_dec_dbg(MPP_DEC_DBG_RESET, fmt, ## __VA_ARGS__)
|
#define dec_dbg_notify(fmt, ...) mpp_dec_dbg_f(MPP_DEC_DBG_NOTIFY, fmt, ## __VA_ARGS__)
|
|
/* external wait state */
|
#define MPP_DEC_WAIT_PKT_IN (0x00000001) /* input packet not ready */
|
#define MPP_DEC_WAIT_FRM_OUT (0x00000002) /* frame output queue full */
|
|
#define MPP_DEC_WAIT_INFO_CHG (0x00000020) /* wait info change ready */
|
#define MPP_DEC_WAIT_BUF_RDY (0x00000040) /* wait valid frame buffer */
|
#define MPP_DEC_WAIT_TSK_ALL_DONE (0x00000080) /* wait all task done */
|
|
#define MPP_DEC_WAIT_TSK_HND_RDY (0x00000100) /* wait task handle ready */
|
#define MPP_DEC_WAIT_TSK_PREV_DONE (0x00000200) /* wait previous task done */
|
#define MPP_DEC_WAIT_BUF_GRP_RDY (0x00000200) /* wait buffer group change ready */
|
|
/* internal wait state */
|
#define MPP_DEC_WAIT_BUF_SLOT_RDY (0x00001000) /* wait buffer slot ready */
|
#define MPP_DEC_WAIT_PKT_BUF_RDY (0x00002000) /* wait packet buffer ready */
|
#define MPP_DEC_WAIT_BUF_SLOT_KEEP (0x00004000) /* wait buffer slot reservation */
|
|
typedef union PaserTaskWait_u {
|
RK_U32 val;
|
struct {
|
RK_U32 dec_pkt_in : 1; // 0x0001 MPP_DEC_NOTIFY_PACKET_ENQUEUE
|
RK_U32 dis_que_full : 1; // 0x0002 MPP_DEC_NOTIFY_FRAME_DEQUEUE
|
RK_U32 reserv0004 : 1; // 0x0004
|
RK_U32 reserv0008 : 1; // 0x0008
|
|
RK_U32 ext_buf_grp : 1; // 0x0010 MPP_DEC_NOTIFY_EXT_BUF_GRP_READY
|
RK_U32 info_change : 1; // 0x0020 MPP_DEC_NOTIFY_INFO_CHG_DONE
|
RK_U32 dec_pic_unusd : 1; // 0x0040 MPP_DEC_NOTIFY_BUFFER_VALID
|
RK_U32 dec_all_done : 1; // 0x0080 MPP_DEC_NOTIFY_TASK_ALL_DONE
|
|
RK_U32 task_hnd : 1; // 0x0100 MPP_DEC_NOTIFY_TASK_HND_VALID
|
RK_U32 prev_task : 1; // 0x0200 MPP_DEC_NOTIFY_TASK_PREV_DONE
|
RK_U32 dec_pic_match : 1; // 0x0400 MPP_DEC_NOTIFY_BUFFER_MATCH
|
RK_U32 reserv0800 : 1; // 0x0800
|
|
RK_U32 dec_pkt_idx : 1; // 0x1000
|
RK_U32 dec_pkt_buf : 1; // 0x2000
|
RK_U32 dec_slot_idx : 1; // 0x4000
|
};
|
} PaserTaskWait;
|
|
typedef union DecTaskStatus_u {
|
RK_U32 val;
|
struct {
|
RK_U32 task_hnd_rdy : 1;
|
RK_U32 mpp_pkt_in_rdy : 1;
|
RK_U32 dec_pkt_idx_rdy : 1;
|
RK_U32 dec_pkt_buf_rdy : 1;
|
RK_U32 task_valid_rdy : 1;
|
RK_U32 dec_pkt_copy_rdy : 1;
|
RK_U32 prev_task_rdy : 1;
|
RK_U32 info_task_gen_rdy : 1;
|
RK_U32 curr_task_rdy : 1;
|
RK_U32 task_parsed_rdy : 1;
|
};
|
} DecTaskStatus;
|
|
typedef struct MppPktTimestamp_t {
|
struct list_head link;
|
RK_S64 pts;
|
RK_S64 dts;
|
} MppPktTs;
|
|
typedef struct DecTask_t {
|
HalTaskHnd hnd;
|
|
DecTaskStatus status;
|
PaserTaskWait wait;
|
|
HalTaskInfo info;
|
MppPktTs ts_cur;
|
} DecTask;
|
|
static RK_S32 ts_cmp(void *priv, const struct list_head *a, const struct list_head *b)
|
{
|
MppPktTs *ts1, *ts2;
|
(void)priv;
|
|
ts1 = container_of(a, MppPktTs, link);
|
ts2 = container_of(b, MppPktTs, link);
|
|
return ts1->pts - ts2->pts;
|
}
|
|
static MPP_RET dec_task_info_init(HalTaskInfo *task)
|
{
|
HalDecTask *p = &task->dec;
|
|
p->valid = 0;
|
p->flags.val = 0;
|
p->flags.eos = 0;
|
p->input_packet = NULL;
|
p->output = -1;
|
p->input = -1;
|
memset(&task->dec.syntax, 0, sizeof(task->dec.syntax));
|
memset(task->dec.refer, -1, sizeof(task->dec.refer));
|
|
return MPP_OK;
|
}
|
|
static void dec_task_init(DecTask *task)
|
{
|
task->hnd = NULL;
|
task->status.val = 0;
|
task->wait.val = 0;
|
task->status.prev_task_rdy = 1;
|
INIT_LIST_HEAD(&task->ts_cur.link);
|
|
dec_task_info_init(&task->info);
|
}
|
|
/*
|
* return MPP_OK for not wait
|
* return MPP_NOK for wait
|
*/
|
static MPP_RET check_task_wait(MppDecImpl *dec, DecTask *task)
|
{
|
MPP_RET ret = MPP_OK;
|
RK_U32 notify = dec->parser_notify_flag;
|
RK_U32 last_wait = dec->parser_wait_flag;
|
RK_U32 curr_wait = task->wait.val;
|
RK_U32 wait_chg = last_wait & (~curr_wait);
|
|
do {
|
if (dec->reset_flag)
|
break;
|
|
// NOTE: User control should always be processed
|
if (notify & MPP_DEC_CONTROL)
|
break;
|
|
// NOTE: When condition is not fulfilled check nofify flag again
|
if (!curr_wait || (curr_wait & notify))
|
break;
|
|
// wait for condition
|
ret = MPP_NOK;
|
} while (0);
|
|
dec_dbg_status("%p %08x -> %08x [%08x] notify %08x -> %s\n", dec,
|
last_wait, curr_wait, wait_chg, notify, (ret) ? ("wait") : ("work"));
|
|
dec->parser_status_flag = task->status.val;
|
dec->parser_wait_flag = task->wait.val;
|
dec->parser_notify_flag = 0;
|
|
if (ret) {
|
dec->parser_wait_count++;
|
} else {
|
dec->parser_work_count++;
|
}
|
|
return ret;
|
}
|
|
static MPP_RET dec_release_task_in_port(MppPort port)
|
{
|
MPP_RET ret = MPP_OK;
|
MppPacket packet = NULL;
|
MppFrame frame = NULL;
|
MppTask mpp_task;
|
|
do {
|
ret = mpp_port_poll(port, MPP_POLL_NON_BLOCK);
|
if (ret < 0)
|
break;
|
|
ret = mpp_port_dequeue(port, &mpp_task);
|
if (ret || mpp_task == NULL)
|
break;
|
|
packet = NULL;
|
frame = NULL;
|
ret = mpp_task_meta_get_frame(mpp_task, KEY_OUTPUT_FRAME, &frame);
|
if (frame) {
|
mpp_frame_deinit(&frame);
|
frame = NULL;
|
}
|
ret = mpp_task_meta_get_packet(mpp_task, KEY_INPUT_PACKET, &packet);
|
if (packet && NULL == mpp_packet_get_buffer(packet)) {
|
mpp_packet_deinit(&packet);
|
packet = NULL;
|
}
|
|
mpp_port_enqueue(port, mpp_task);
|
mpp_task = NULL;
|
} while (1);
|
|
return ret;
|
}
|
|
static void dec_release_input_packet(MppDecImpl *dec, RK_S32 force)
|
{
|
if (dec->mpp_pkt_in) {
|
if (force || 0 == mpp_packet_get_length(dec->mpp_pkt_in)) {
|
mpp_packet_deinit(&dec->mpp_pkt_in);
|
|
mpp_dec_callback(dec, MPP_DEC_EVENT_ON_PKT_RELEASE, dec->mpp_pkt_in);
|
dec->mpp_pkt_in = NULL;
|
}
|
}
|
}
|
|
static RK_U32 reset_parser_thread(Mpp *mpp, DecTask *task)
|
{
|
MppDecImpl *dec = (MppDecImpl *)mpp->mDec;
|
MppThread *hal = dec->thread_hal;
|
HalTaskGroup tasks = dec->tasks;
|
MppBufSlots frame_slots = dec->frame_slots;
|
MppBufSlots packet_slots = dec->packet_slots;
|
HalDecTask *task_dec = &task->info.dec;
|
|
dec_dbg_reset("reset: parser reset start\n");
|
dec_dbg_reset("reset: parser wait hal proc reset start\n");
|
|
dec_release_task_in_port(mpp->mMppInPort);
|
|
hal->lock();
|
dec->hal_reset_post++;
|
hal->signal();
|
hal->unlock();
|
|
sem_wait(&dec->hal_reset);
|
|
dec_dbg_reset("reset: parser check hal proc task empty start\n");
|
|
if (hal_task_check_empty(tasks, TASK_PROCESSING)) {
|
mpp_err_f("found task not processed put %d get %d\n",
|
mpp->mTaskPutCount, mpp->mTaskGetCount);
|
mpp_abort();
|
}
|
|
dec_dbg_reset("reset: parser check hal proc task empty done\n");
|
|
// do parser reset process
|
{
|
RK_S32 index;
|
task->status.curr_task_rdy = 0;
|
task->status.prev_task_rdy = 1;
|
task_dec->valid = 0;
|
mpp_parser_reset(dec->parser);
|
mpp_hal_reset(dec->hal);
|
if (dec->vproc) {
|
dec_dbg_reset("reset: vproc reset start\n");
|
dec_vproc_reset(dec->vproc);
|
dec_dbg_reset("reset: vproc reset done\n");
|
}
|
|
if (task->status.task_parsed_rdy) {
|
mpp_log("task no send to hal que must clr current frame hal status\n");
|
if (task_dec->output >= 0)
|
mpp_buf_slot_clr_flag(frame_slots, task_dec->output, SLOT_HAL_OUTPUT);
|
|
for (RK_U32 i = 0; i < MPP_ARRAY_ELEMS(task_dec->refer); i++) {
|
index = task_dec->refer[i];
|
if (index >= 0)
|
mpp_buf_slot_clr_flag(frame_slots, index, SLOT_HAL_INPUT);
|
}
|
task->status.task_parsed_rdy = 0;
|
}
|
|
dec_release_input_packet(dec, 1);
|
|
while (MPP_OK == mpp_buf_slot_dequeue(frame_slots, &index, QUEUE_DISPLAY)) {
|
/* release extra ref in slot's MppBuffer */
|
MppBuffer buffer = NULL;
|
mpp_buf_slot_get_prop(frame_slots, index, SLOT_BUFFER, &buffer);
|
if (buffer)
|
mpp_buffer_put(buffer);
|
mpp_buf_slot_clr_flag(frame_slots, index, SLOT_QUEUE_USE);
|
}
|
|
if (dec->cfg.base.sort_pts) {
|
// flush
|
MppPktTs *ts, *pos;
|
|
mpp_spinlock_lock(&dec->ts_lock);
|
list_for_each_entry_safe(ts, pos, &dec->ts_link, MppPktTs, link) {
|
list_del_init(&ts->link);
|
mpp_mem_pool_put(dec->ts_pool, ts);
|
}
|
mpp_spinlock_unlock(&dec->ts_lock);
|
}
|
|
if (task->status.dec_pkt_copy_rdy) {
|
mpp_buf_slot_clr_flag(packet_slots, task_dec->input, SLOT_HAL_INPUT);
|
task->status.dec_pkt_copy_rdy = 0;
|
task_dec->input = -1;
|
}
|
|
// wait hal thread reset ready
|
if (task->wait.info_change)
|
mpp_log("reset at info change\n");
|
|
task->status.task_parsed_rdy = 0;
|
// IMPORTANT: clear flag in MppDec context
|
dec->parser_status_flag = 0;
|
dec->parser_wait_flag = 0;
|
}
|
|
dec_task_init(task);
|
|
dec_dbg_reset("reset: parser reset all done\n");
|
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_dec_update_cfg(MppDecImpl *p)
|
{
|
MppDecCfgSet *cfg = &p->cfg;
|
MppDecBaseCfg *base = &cfg->base;
|
MppDecStatusCfg *status = &cfg->status;
|
|
if (status->hal_task_count && !status->hal_support_fast_mode) {
|
if (!p->parser_fast_mode && base->fast_parse) {
|
mpp_err("can not enable fast parse while hal not support\n");
|
base->fast_parse = 0;
|
}
|
}
|
|
p->parser_fast_mode = base->fast_parse;
|
p->enable_deinterlace = base->enable_vproc;
|
p->disable_error = base->disable_error;
|
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_dec_check_fbc_cap(MppDecImpl *p)
|
{
|
MppDecBaseCfg *base = &p->cfg.base;
|
|
if (MPP_FRAME_FMT_IS_FBC(base->out_fmt)) {
|
RK_U32 fbc = (RK_U32)base->out_fmt & MPP_FRAME_FBC_MASK;
|
RK_U32 fmt = base->out_fmt - fbc;
|
|
if (p->hw_info && p->hw_info->cap_fbc)
|
fmt |= fbc;
|
|
base->out_fmt = (MppFrameFormat)fmt;
|
}
|
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_dec_proc_cfg(MppDecImpl *dec, MpiCmd cmd, void *param)
|
{
|
MPP_RET ret = MPP_OK;
|
|
mpp_parser_control(dec->parser, cmd, param);
|
|
ret = mpp_hal_control(dec->hal, cmd, param);
|
|
if (ret)
|
goto RET;
|
|
switch (cmd) {
|
case MPP_DEC_SET_FRAME_INFO : {
|
MppFrame frame = (MppFrame)param;
|
|
mpp_slots_set_prop(dec->frame_slots, SLOTS_FRAME_INFO, frame);
|
|
mpp_log("setting default w %4d h %4d h_str %4d v_str %4d\n",
|
mpp_frame_get_width(frame),
|
mpp_frame_get_height(frame),
|
mpp_frame_get_hor_stride(frame),
|
mpp_frame_get_ver_stride(frame));
|
|
} break;
|
case MPP_DEC_SET_INFO_CHANGE_READY: {
|
ret = mpp_buf_slot_ready(dec->frame_slots);
|
} break;
|
case MPP_DEC_GET_VPUMEM_USED_COUNT: {
|
RK_S32 *p = (RK_S32 *)param;
|
*p = mpp_slots_get_used_count(dec->frame_slots);
|
dec_dbg_func("used count %d\n", *p);
|
} break;
|
case MPP_DEC_SET_PRESENT_TIME_ORDER :
|
case MPP_DEC_SET_PARSER_SPLIT_MODE :
|
case MPP_DEC_SET_PARSER_FAST_MODE :
|
case MPP_DEC_SET_IMMEDIATE_OUT :
|
case MPP_DEC_SET_OUTPUT_FORMAT :
|
case MPP_DEC_SET_DISABLE_ERROR :
|
case MPP_DEC_SET_ENABLE_DEINTERLACE :
|
case MPP_DEC_SET_ENABLE_FAST_PLAY : {
|
ret = mpp_dec_set_cfg_by_cmd(&dec->cfg, cmd, param);
|
mpp_dec_update_cfg(dec);
|
dec->cfg.base.change = 0;
|
} break;
|
case MPP_DEC_QUERY: {
|
MppDecQueryCfg *query = (MppDecQueryCfg *)param;
|
RK_U32 flag = query->query_flag;
|
|
dec_dbg_func("query %x\n", flag);
|
|
if (flag & MPP_DEC_QUERY_STATUS)
|
query->rt_status = dec->parser_status_flag;
|
|
if (flag & MPP_DEC_QUERY_WAIT)
|
query->rt_wait = dec->parser_wait_flag;
|
|
if (flag & MPP_DEC_QUERY_FPS)
|
query->rt_fps = 0;
|
|
if (flag & MPP_DEC_QUERY_BPS)
|
query->rt_bps = 0;
|
|
if (flag & MPP_DEC_QUERY_DEC_IN_PKT)
|
query->dec_in_pkt_cnt = dec->dec_in_pkt_count;
|
|
if (flag & MPP_DEC_QUERY_DEC_WORK)
|
query->dec_hw_run_cnt = dec->dec_hw_run_count;
|
|
if (flag & MPP_DEC_QUERY_DEC_OUT_FRM)
|
query->dec_out_frm_cnt = dec->dec_out_frame_count;
|
} break;
|
case MPP_DEC_SET_CFG: {
|
MppDecCfgImpl *dec_cfg = (MppDecCfgImpl *)param;
|
|
if (dec_cfg) {
|
mpp_dec_set_cfg(&dec->cfg, &dec_cfg->cfg);
|
mpp_dec_update_cfg(dec);
|
mpp_dec_check_fbc_cap(dec);
|
}
|
|
dec_dbg_func("set dec cfg\n");
|
} break;
|
case MPP_DEC_GET_CFG: {
|
MppDecCfgImpl *dec_cfg = (MppDecCfgImpl *)param;
|
|
if (dec_cfg)
|
memcpy(&dec_cfg->cfg, &dec->cfg, sizeof(dec->cfg));
|
|
dec_dbg_func("get dec cfg\n");
|
} break;
|
default : {
|
} break;
|
}
|
|
RET:
|
return ret;
|
}
|
|
/* Overall mpp_dec output frame function */
|
static void mpp_dec_put_frame(Mpp *mpp, RK_S32 index, HalDecTaskFlag flags)
|
{
|
MppDecImpl *dec = (MppDecImpl *)mpp->mDec;
|
MppBufSlots slots = dec->frame_slots;
|
MppFrame frame = NULL;
|
RK_U32 eos = flags.eos;
|
RK_U32 change = flags.info_change;
|
RK_U32 error = flags.parse_err || flags.ref_err;
|
RK_U32 refer = flags.used_for_ref;
|
RK_U32 fake_frame = 0;
|
|
if (index >= 0) {
|
mpp_buf_slot_get_prop(slots, index, SLOT_FRAME_PTR, &frame);
|
if (mpp_frame_get_mode(frame) && dec->enable_deinterlace &&
|
NULL == dec->vproc) {
|
MppDecVprocCfg cfg = { mpp, NULL };
|
MPP_RET ret = dec_vproc_init(&dec->vproc, &cfg);
|
if (ret) {
|
// When iep is failed to open disable deinterlace function to
|
// avoid noisy log.
|
dec->enable_deinterlace = 0;
|
dec->vproc = NULL;
|
} else {
|
dec->vproc_tasks = cfg.task_group;
|
dec_vproc_start(dec->vproc);
|
}
|
}
|
} else {
|
// when post-process is needed and eos without slot index case
|
// we need to create a slot index for it
|
mpp_assert(eos);
|
mpp_assert(!change);
|
|
if (dec->vproc) {
|
HalTaskGroup group = dec->vproc_tasks;
|
HalTaskHnd hnd = NULL;
|
HalTaskInfo task;
|
HalDecVprocTask *vproc_task = &task.dec_vproc;
|
|
MPP_RET ret = hal_task_get_hnd(group, TASK_IDLE, &hnd);
|
|
mpp_assert(ret == MPP_OK);
|
|
vproc_task->flags.val = 0;
|
vproc_task->flags.eos = eos;
|
vproc_task->input = index;
|
|
hal_task_hnd_set_info(hnd, &task);
|
hal_task_hnd_set_status(hnd, TASK_PROCESSING);
|
dec_vproc_signal(dec->vproc);
|
|
return ;
|
} else {
|
mpp_frame_init(&frame);
|
fake_frame = 1;
|
index = 0;
|
}
|
|
mpp_frame_set_eos(frame, eos);
|
}
|
|
mpp_assert(index >= 0);
|
mpp_assert(frame);
|
|
if (dec->cfg.base.disable_error) {
|
mpp_frame_set_errinfo(frame, 0);
|
mpp_frame_set_discard(frame, 0);
|
}
|
|
if (!change) {
|
if (dec->cfg.base.sort_pts) {
|
MppPktTs *pkt_ts;
|
|
mpp_spinlock_lock(&dec->ts_lock);
|
pkt_ts = list_first_entry_or_null(&dec->ts_link, MppPktTs, link);
|
if (pkt_ts)
|
list_del_init(&pkt_ts->link);
|
mpp_spinlock_unlock(&dec->ts_lock);
|
if (pkt_ts) {
|
mpp_frame_set_dts(frame, pkt_ts->dts);
|
mpp_frame_set_pts(frame, pkt_ts->pts);
|
mpp_mem_pool_put(dec->ts_pool, pkt_ts);
|
}
|
}
|
}
|
mpp_frame_set_info_change(frame, change);
|
|
if (eos) {
|
mpp_frame_set_eos(frame, 1);
|
if (error) {
|
if (refer)
|
mpp_frame_set_errinfo(frame, 1);
|
else
|
mpp_frame_set_discard(frame, 1);
|
}
|
}
|
|
dec->dec_out_frame_count++;
|
dec_dbg_detail("detail: %p put frm pts %llu fd %d\n", dec,
|
mpp_frame_get_pts(frame),
|
(NULL == mpp_frame_get_buffer(frame)) ? (-1) :
|
mpp_buffer_get_fd(mpp_frame_get_buffer(frame)));
|
|
if (dec->vproc) {
|
HalTaskGroup group = dec->vproc_tasks;
|
HalTaskHnd hnd = NULL;
|
HalTaskInfo task;
|
HalDecVprocTask *vproc_task = &task.dec_vproc;
|
MPP_RET ret = MPP_OK;
|
|
do {
|
ret = hal_task_get_hnd(group, TASK_IDLE, &hnd);
|
if (ret) {
|
msleep(10);
|
}
|
} while (ret);
|
|
mpp_assert(ret == MPP_OK);
|
|
vproc_task->flags.eos = eos;
|
vproc_task->flags.info_change = change;
|
vproc_task->input = index;
|
|
if (!change) {
|
mpp_buf_slot_set_flag(slots, index, SLOT_QUEUE_USE);
|
mpp_buf_slot_enqueue(slots, index, QUEUE_DEINTERLACE);
|
}
|
|
hal_task_hnd_set_info(hnd, &task);
|
hal_task_hnd_set_status(hnd, TASK_PROCESSING);
|
|
dec_vproc_signal(dec->vproc);
|
} else {
|
// direct output -> copy a new MppFrame and output
|
mpp_list *list = mpp->mFrmOut;
|
MppFrame out = NULL;
|
|
mpp_frame_init(&out);
|
mpp_frame_copy(out, frame);
|
|
mpp_dbg_pts("output frame pts %lld\n", mpp_frame_get_pts(out));
|
|
list->lock();
|
list->add_at_tail(&out, sizeof(out));
|
mpp->mFramePutCount++;
|
list->signal();
|
list->unlock();
|
|
if (fake_frame)
|
mpp_frame_deinit(&frame);
|
|
mpp_dec_callback(dec, MPP_DEC_EVENT_ON_FRM_READY, out);
|
}
|
}
|
|
static void mpp_dec_push_display(Mpp *mpp, HalDecTaskFlag flags)
|
{
|
RK_S32 index = -1;
|
MppDecImpl *dec = (MppDecImpl *)mpp->mDec;
|
MppBufSlots frame_slots = dec->frame_slots;
|
RK_U32 eos = flags.eos;
|
HalDecTaskFlag tmp = flags;
|
|
tmp.eos = 0;
|
/**
|
* After info_change is encountered by parser thread, HalDecTaskFlag will
|
* have this flag set. Although mpp_dec_flush is called there may be some
|
* frames still remaining in display queue and waiting to be output. So
|
* these frames shouldn't have info_change set since their resolution and
|
* bit depth are the same as before. What's more, the info_change flag has
|
* nothing to do with frames being output.
|
*/
|
tmp.info_change = 0;
|
|
dec->thread_hal->lock(THREAD_OUTPUT);
|
while (MPP_OK == mpp_buf_slot_dequeue(frame_slots, &index, QUEUE_DISPLAY)) {
|
/* deal with current frame */
|
if (eos && mpp_slots_is_empty(frame_slots, QUEUE_DISPLAY))
|
tmp.eos = 1;
|
|
mpp_dec_put_frame(mpp, index, tmp);
|
mpp_buf_slot_clr_flag(frame_slots, index, SLOT_QUEUE_USE);
|
}
|
dec->thread_hal->unlock(THREAD_OUTPUT);
|
}
|
|
static void mpp_dec_put_task(Mpp *mpp, DecTask *task)
|
{
|
MppDecImpl *dec = (MppDecImpl *)mpp->mDec;
|
|
hal_task_hnd_set_info(task->hnd, &task->info);
|
dec->thread_hal->lock();
|
hal_task_hnd_set_status(task->hnd, TASK_PROCESSING);
|
mpp->mTaskPutCount++;
|
dec->thread_hal->signal();
|
dec->thread_hal->unlock();
|
task->hnd = NULL;
|
}
|
|
static void reset_hal_thread(Mpp *mpp)
|
{
|
MppDecImpl *dec = (MppDecImpl *)mpp->mDec;
|
HalTaskGroup tasks = dec->tasks;
|
MppBufSlots frame_slots = dec->frame_slots;
|
HalDecTaskFlag flag;
|
RK_S32 index = -1;
|
HalTaskHnd task = NULL;
|
|
/* when hal thread reset output all frames */
|
flag.val = 0;
|
mpp_dec_flush(dec);
|
|
dec->thread_hal->lock(THREAD_OUTPUT);
|
while (MPP_OK == mpp_buf_slot_dequeue(frame_slots, &index, QUEUE_DISPLAY)) {
|
mpp_dec_put_frame(mpp, index, flag);
|
mpp_buf_slot_clr_flag(frame_slots, index, SLOT_QUEUE_USE);
|
}
|
|
// Need to set processed task to idle status
|
while (MPP_OK == hal_task_get_hnd(tasks, TASK_PROC_DONE, &task)) {
|
if (task) {
|
hal_task_hnd_set_status(task, TASK_IDLE);
|
task = NULL;
|
}
|
}
|
|
dec->thread_hal->unlock(THREAD_OUTPUT);
|
}
|
|
static MPP_RET update_dec_hal_info(MppDecImpl *dec, MppFrame frame)
|
{
|
HalInfo hal_info = dec->hal_info;
|
MppDevInfoCfg data[DEC_INFO_BUTT];
|
RK_S32 size = sizeof(data);
|
RK_U64 val = 0;
|
RK_S32 i;
|
|
hal_info_set(hal_info, DEC_INFO_WIDTH, CODEC_INFO_FLAG_NUMBER,
|
mpp_frame_get_width(frame));
|
|
hal_info_set(hal_info, DEC_INFO_HEIGHT, CODEC_INFO_FLAG_NUMBER,
|
mpp_frame_get_height(frame));
|
|
val = hal_info_to_string(hal_info, DEC_INFO_FORMAT, &dec->coding);
|
hal_info_set(hal_info, DEC_INFO_FORMAT, CODEC_INFO_FLAG_STRING, val);
|
|
hal_info_get(hal_info, data, &size);
|
|
if (size) {
|
size /= sizeof(data[0]);
|
for (i = 0; i < size; i++)
|
mpp_dev_ioctl(dec->dev, MPP_DEV_SET_INFO, &data[i]);
|
}
|
|
return MPP_OK;
|
}
|
|
static MPP_RET try_get_input_packet(Mpp *mpp, DecTask *task)
|
{
|
MppDecImpl *dec = (MppDecImpl *)mpp->mDec;
|
MppPort input = mpp->mMppInPort;
|
MppTask mpp_task = NULL;
|
MppPacket packet = NULL;
|
MPP_RET ret = MPP_OK;
|
|
ret = mpp_port_poll(input, MPP_POLL_NON_BLOCK);
|
if (ret < 0) {
|
task->wait.dec_pkt_in = 1;
|
return MPP_NOK;
|
}
|
|
ret = mpp_port_dequeue(input, &mpp_task);
|
mpp_assert(ret == MPP_OK && mpp_task);
|
mpp_task_meta_get_packet(mpp_task, KEY_INPUT_PACKET, &packet);
|
mpp_assert(packet);
|
|
/* when it is copy buffer return packet right here */
|
if (NULL == mpp_packet_get_buffer(packet))
|
mpp_port_enqueue(input, mpp_task);
|
|
dec->mpp_pkt_in = packet;
|
mpp->mPacketGetCount++;
|
dec->dec_in_pkt_count++;
|
|
task->status.mpp_pkt_in_rdy = 1;
|
task->wait.dec_pkt_in = 0;
|
|
return MPP_OK;
|
}
|
|
static MPP_RET try_proc_dec_task(Mpp *mpp, DecTask *task)
|
{
|
MppDecImpl *dec = (MppDecImpl *)mpp->mDec;
|
HalTaskGroup tasks = dec->tasks;
|
MppBufSlots frame_slots = dec->frame_slots;
|
MppBufSlots packet_slots = dec->packet_slots;
|
HalDecTask *task_dec = &task->info.dec;
|
MppBuffer hal_buf_in = NULL;
|
MppBuffer hal_buf_out = NULL;
|
size_t stream_size = 0;
|
RK_S32 output = 0;
|
|
/*
|
* 1. get task handle from hal for parsing one frame
|
*/
|
if (!task->hnd) {
|
hal_task_get_hnd(tasks, TASK_IDLE, &task->hnd);
|
if (task->hnd) {
|
task->wait.task_hnd = 0;
|
} else {
|
task->wait.task_hnd = 1;
|
return MPP_NOK;
|
}
|
}
|
|
/*
|
* 2. get packet for parser preparing
|
*/
|
if (!dec->mpp_pkt_in && !task->status.curr_task_rdy) {
|
if (try_get_input_packet(mpp, task))
|
return MPP_NOK;
|
|
mpp_assert(dec->mpp_pkt_in);
|
|
dec_dbg_detail("detail: %p get pkt pts %llu len %d\n", dec,
|
mpp_packet_get_pts(dec->mpp_pkt_in),
|
mpp_packet_get_length(dec->mpp_pkt_in));
|
}
|
|
/*
|
* 3. send packet data to parser for prepare
|
*
|
* mpp_parser_prepare functioin input / output
|
* input: input MppPacket data from user
|
* output: one packet which contains one frame for hardware processing
|
* output information will be stored in task_dec->input_packet
|
* output data can be stored inside of parser.
|
*
|
* NOTE:
|
* 1. Prepare process is controlled by need_split flag
|
* If need_split flag is zero prepare function is just copy the input
|
* packet to task_dec->input_packet
|
* If need_split flag is non-zero prepare function will call split funciton
|
* of different coding type and find the start and end of one frame. Then
|
* copy data to task_dec->input_packet
|
* 2. On need_split mode if one input MppPacket contain multiple frame for
|
* decoding one mpp_parser_prepare call will only frame for task. Then input
|
* MppPacket->pos/length will be updated. The input MppPacket will not be
|
* released until it is totally consumed.
|
* 3. On spliting frame if one frame contain multiple slices and these multiple
|
* slices have different pts/dts the output frame will use the last pts/dts
|
* as the output frame's pts/dts.
|
*
|
*/
|
if (!task->status.curr_task_rdy) {
|
mpp_dbg_pts("input packet pts %lld\n", mpp_packet_get_pts(dec->mpp_pkt_in));
|
|
mpp_clock_start(dec->clocks[DEC_PRS_PREPARE]);
|
mpp_parser_prepare(dec->parser, dec->mpp_pkt_in, task_dec);
|
mpp_clock_pause(dec->clocks[DEC_PRS_PREPARE]);
|
if (dec->cfg.base.sort_pts && task_dec->valid) {
|
task->ts_cur.pts = mpp_packet_get_pts(dec->mpp_pkt_in);
|
task->ts_cur.dts = mpp_packet_get_dts(dec->mpp_pkt_in);
|
}
|
dec_release_input_packet(dec, 0);
|
}
|
|
task->status.curr_task_rdy = task_dec->valid;
|
/*
|
* We may find eos in prepare step and there will be no anymore vaild task generated.
|
* So here we try push eos task to hal, hal will push all frame to display then
|
* push a eos frame to tell all frame decoded
|
*/
|
if (task_dec->flags.eos && !task_dec->valid)
|
mpp_dec_put_task(mpp, task);
|
|
if (!task->status.curr_task_rdy)
|
return MPP_NOK;
|
|
// NOTE: packet in task should be ready now
|
mpp_assert(task_dec->input_packet);
|
|
/*
|
* 4. look for a unused packet slot index
|
*/
|
if (task_dec->input < 0) {
|
mpp_buf_slot_get_unused(packet_slots, &task_dec->input);
|
}
|
|
task->wait.dec_pkt_idx = (task_dec->input < 0);
|
if (task->wait.dec_pkt_idx)
|
return MPP_NOK;
|
|
/*
|
* 5. malloc hardware buffer for the packet slot index
|
*/
|
stream_size = mpp_packet_get_size(task_dec->input_packet);
|
|
mpp_buf_slot_get_prop(packet_slots, task_dec->input, SLOT_BUFFER, &hal_buf_in);
|
if (NULL == hal_buf_in) {
|
mpp_buffer_get(mpp->mPacketGroup, &hal_buf_in, stream_size);
|
if (hal_buf_in) {
|
mpp_buf_slot_set_prop(packet_slots, task_dec->input, SLOT_BUFFER, hal_buf_in);
|
mpp_buffer_put(hal_buf_in);
|
}
|
} else {
|
MppBufferImpl *buf = (MppBufferImpl *)hal_buf_in;
|
mpp_assert(buf->info.size >= stream_size);
|
}
|
|
task->wait.dec_pkt_buf = (NULL == hal_buf_in);
|
if (task->wait.dec_pkt_buf)
|
return MPP_NOK;
|
|
/*
|
* 6. copy prepared stream to hardware buffer
|
*/
|
if (!task->status.dec_pkt_copy_rdy) {
|
void *dst = mpp_buffer_get_ptr(hal_buf_in);
|
void *src = mpp_packet_get_data(task_dec->input_packet);
|
size_t length = mpp_packet_get_length(task_dec->input_packet);
|
|
memcpy(dst, src, length);
|
mpp_buf_slot_set_flag(packet_slots, task_dec->input, SLOT_CODEC_READY);
|
mpp_buf_slot_set_flag(packet_slots, task_dec->input, SLOT_HAL_INPUT);
|
task->status.dec_pkt_copy_rdy = 1;
|
}
|
|
/* 7.1 if not fast mode wait previous task done here */
|
if (!dec->parser_fast_mode) {
|
// wait previous task done
|
if (!task->status.prev_task_rdy) {
|
HalTaskHnd task_prev = NULL;
|
hal_task_get_hnd(tasks, TASK_PROC_DONE, &task_prev);
|
if (task_prev) {
|
task->status.prev_task_rdy = 1;
|
task->wait.prev_task = 0;
|
hal_task_hnd_set_status(task_prev, TASK_IDLE);
|
task_prev = NULL;
|
} else {
|
task->wait.prev_task = 1;
|
return MPP_NOK;
|
}
|
}
|
}
|
|
// for vp9 only wait all task is processed
|
if (task->wait.dec_all_done) {
|
if (!hal_task_check_empty(dec->tasks, TASK_PROCESSING))
|
task->wait.dec_all_done = 0;
|
else
|
return MPP_NOK;
|
}
|
|
dec_dbg_detail("detail: %p check prev task pass\n", dec);
|
|
/* too many frame delay in dispaly queue */
|
if (mpp->mFrmOut) {
|
task->wait.dis_que_full = (mpp->mFrmOut->list_size() > 4) ? 1 : 0;
|
if (task->wait.dis_que_full)
|
return MPP_ERR_DISPLAY_FULL;
|
}
|
dec_dbg_detail("detail: %p check mframes pass\n", dec);
|
|
/* 7.3 wait for a unused slot index for decoder parse operation */
|
task->wait.dec_slot_idx = (mpp_slots_get_unused_count(frame_slots)) ? (0) : (1);
|
if (task->wait.dec_slot_idx)
|
return MPP_ERR_BUFFER_FULL;
|
|
/*
|
* 8. send packet data to parser
|
*
|
* parser prepare functioin input / output
|
* input: packet data
|
* output: dec task output information (with dxva output slot)
|
* buffer slot usage informatioin
|
*
|
* NOTE:
|
* 1. dpb slot will be set internally in parser process.
|
* 2. parse function need to set valid flag when one frame is ready.
|
* 3. if packet size is zero then next packet is needed.
|
* 4. detect whether output index has MppBuffer and task valid
|
*/
|
if (!task->status.task_parsed_rdy) {
|
mpp_clock_start(dec->clocks[DEC_PRS_PARSE]);
|
mpp_parser_parse(dec->parser, task_dec);
|
mpp_clock_pause(dec->clocks[DEC_PRS_PARSE]);
|
task->status.task_parsed_rdy = 1;
|
}
|
|
if (task_dec->output < 0 || !task_dec->valid) {
|
/*
|
* We may meet an eos in parser step and there will be no anymore vaild
|
* task generated. So here we try push eos task to hal, hal will push
|
* all frame(s) to display, a frame of them with a eos flag will be
|
* used to inform that all frame have decoded
|
*/
|
if (task_dec->flags.eos) {
|
mpp_dec_put_task(mpp, task);
|
} else {
|
hal_task_hnd_set_status(task->hnd, TASK_IDLE);
|
task->hnd = NULL;
|
}
|
|
if (task->status.dec_pkt_copy_rdy) {
|
mpp_buf_slot_clr_flag(packet_slots, task_dec->input, SLOT_HAL_INPUT);
|
task->status.dec_pkt_copy_rdy = 0;
|
}
|
task->status.curr_task_rdy = 0;
|
task->status.task_parsed_rdy = 0;
|
dec_task_info_init(&task->info);
|
return MPP_NOK;
|
}
|
dec_dbg_detail("detail: %p check output index pass\n", dec);
|
|
/*
|
* 9. parse local task and slot to check whether new buffer or info change is needed.
|
*
|
* detect info change from frame slot
|
*/
|
if (mpp_buf_slot_is_changed(frame_slots)) {
|
if (!task->status.info_task_gen_rdy) {
|
RK_U32 eos = task_dec->flags.eos;
|
|
// NOTE: info change should not go with eos flag
|
task_dec->flags.info_change = 1;
|
task_dec->flags.eos = 0;
|
mpp_dec_put_task(mpp, task);
|
task_dec->flags.eos = eos;
|
|
task->status.info_task_gen_rdy = 1;
|
return MPP_ERR_STREAM;
|
}
|
dec->info_updated = 0;
|
}
|
|
task->wait.info_change = mpp_buf_slot_is_changed(frame_slots);
|
if (task->wait.info_change) {
|
return MPP_ERR_STREAM;
|
} else {
|
task->status.info_task_gen_rdy = 0;
|
task_dec->flags.info_change = 0;
|
// NOTE: check the task must be ready
|
mpp_assert(task->hnd);
|
}
|
|
/* 10. whether the frame buffer group is internal or external */
|
if (NULL == mpp->mFrameGroup) {
|
mpp_log("mpp_dec use internal frame buffer group\n");
|
mpp_buffer_group_get_internal(&mpp->mFrameGroup, MPP_BUFFER_TYPE_ION);
|
}
|
|
/* 10.1 look for a unused hardware buffer for output */
|
if (mpp->mFrameGroup) {
|
RK_S32 unused = mpp_buffer_group_unused(mpp->mFrameGroup);
|
|
// NOTE: When dec post-process is enabled reserve 2 buffer for it.
|
task->wait.dec_pic_unusd = (dec->vproc) ? (unused < 3) : (unused < 1);
|
if (task->wait.dec_pic_unusd)
|
return MPP_ERR_BUFFER_FULL;
|
}
|
dec_dbg_detail("detail: %p check frame group count pass\n", dec);
|
|
/*
|
* 11. do buffer operation according to usage information
|
*
|
* possible case:
|
* a. normal case
|
* - wait and alloc(or fetch) a normal frame buffer
|
* b. field mode case
|
* - two field may reuse a same buffer, no need to alloc
|
* c. info change case
|
* - need buffer in different side, need to send a info change
|
* frame to hal loop.
|
*/
|
output = task_dec->output;
|
mpp_buf_slot_get_prop(frame_slots, output, SLOT_BUFFER, &hal_buf_out);
|
if (NULL == hal_buf_out) {
|
size_t size = mpp_buf_slot_get_size(frame_slots);
|
mpp_buffer_get(mpp->mFrameGroup, &hal_buf_out, size);
|
if (hal_buf_out)
|
mpp_buf_slot_set_prop(frame_slots, output, SLOT_BUFFER,
|
hal_buf_out);
|
}
|
|
dec_dbg_detail("detail: %p check output buffer %p\n", hal_buf_out, dec);
|
|
// update codec info
|
if (!dec->info_updated && dec->dev) {
|
MppFrame frame = NULL;
|
|
mpp_buf_slot_get_prop(frame_slots, output, SLOT_FRAME_PTR, &frame);
|
update_dec_hal_info(dec, frame);
|
dec->info_updated = 1;
|
}
|
|
task->wait.dec_pic_match = (NULL == hal_buf_out);
|
if (task->wait.dec_pic_match)
|
return MPP_NOK;
|
|
if (dec->cfg.base.sort_pts) {
|
MppFrame frame = NULL;
|
MppPktTs *pkt_ts = (MppPktTs *)mpp_mem_pool_get(dec->ts_pool);
|
|
mpp_assert(pkt_ts);
|
mpp_buf_slot_get_prop(frame_slots, output, SLOT_FRAME_PTR, &frame);
|
pkt_ts->pts = task->ts_cur.pts;
|
pkt_ts->dts = task->ts_cur.dts;
|
INIT_LIST_HEAD(&pkt_ts->link);
|
if (frame && mpp_frame_get_pts(frame) == pkt_ts->pts) {
|
mpp_spinlock_lock(&dec->ts_lock);
|
list_add_tail(&pkt_ts->link, &dec->ts_link);
|
list_sort(NULL, &dec->ts_link, ts_cmp);
|
mpp_spinlock_unlock(&dec->ts_lock);
|
}
|
}
|
|
/* generating registers table */
|
mpp_clock_start(dec->clocks[DEC_HAL_GEN_REG]);
|
mpp_hal_reg_gen(dec->hal, &task->info);
|
mpp_clock_pause(dec->clocks[DEC_HAL_GEN_REG]);
|
|
/* send current register set to hardware */
|
mpp_clock_start(dec->clocks[DEC_HW_START]);
|
mpp_hal_hw_start(dec->hal, &task->info);
|
mpp_clock_pause(dec->clocks[DEC_HW_START]);
|
|
/*
|
* 12. send dxva output information and buffer information to hal thread
|
* combinate video codec dxva output and buffer information
|
*/
|
mpp_dec_put_task(mpp, task);
|
|
task->wait.dec_all_done = (dec->parser_fast_mode &&
|
task_dec->flags.wait_done) ? 1 : 0;
|
|
task->status.dec_pkt_copy_rdy = 0;
|
task->status.curr_task_rdy = 0;
|
task->status.task_parsed_rdy = 0;
|
task->status.prev_task_rdy = 0;
|
dec_task_info_init(&task->info);
|
|
dec_dbg_detail("detail: %p one task ready\n", dec);
|
|
return MPP_OK;
|
}
|
|
void *mpp_dec_parser_thread(void *data)
|
{
|
Mpp *mpp = (Mpp*)data;
|
MppDecImpl *dec = (MppDecImpl *)mpp->mDec;
|
MppThread *parser = dec->thread_parser;
|
MppBufSlots packet_slots = dec->packet_slots;
|
|
DecTask task;
|
HalDecTask *task_dec = &task.info.dec;
|
|
dec_task_init(&task);
|
|
mpp_clock_start(dec->clocks[DEC_PRS_TOTAL]);
|
|
while (1) {
|
{
|
AutoMutex autolock(parser->mutex());
|
if (MPP_THREAD_RUNNING != parser->get_status())
|
break;
|
|
/*
|
* parser thread need to wait at cases below:
|
* 1. no task slot for output
|
* 2. no packet for parsing
|
* 3. info change on progress
|
* 3. no buffer on analyzing output task
|
*/
|
if (check_task_wait(dec, &task)) {
|
mpp_clock_start(dec->clocks[DEC_PRS_WAIT]);
|
parser->wait();
|
mpp_clock_pause(dec->clocks[DEC_PRS_WAIT]);
|
}
|
}
|
|
// process user control
|
if (dec->cmd_send != dec->cmd_recv) {
|
dec_dbg_detail("ctrl proc %d cmd %08x\n", dec->cmd_recv, dec->cmd);
|
sem_wait(&dec->cmd_start);
|
*dec->cmd_ret = mpp_dec_proc_cfg(dec, dec->cmd, dec->param);
|
dec->cmd_recv++;
|
dec_dbg_detail("ctrl proc %d done send %d\n", dec->cmd_recv,
|
dec->cmd_send);
|
mpp_assert(dec->cmd_send == dec->cmd_send);
|
dec->param = NULL;
|
dec->cmd = (MpiCmd)0;
|
dec->cmd_ret = NULL;
|
sem_post(&dec->cmd_done);
|
continue;
|
}
|
|
if (dec->reset_flag) {
|
reset_parser_thread(mpp, &task);
|
|
AutoMutex autolock(parser->mutex(THREAD_CONTROL));
|
dec->reset_flag = 0;
|
sem_post(&dec->parser_reset);
|
continue;
|
}
|
|
// NOTE: ignore return value here is to fast response to reset.
|
// Otherwise we can loop all dec task until it is failed.
|
mpp_clock_start(dec->clocks[DEC_PRS_PROC]);
|
try_proc_dec_task(mpp, &task);
|
mpp_clock_pause(dec->clocks[DEC_PRS_PROC]);
|
}
|
|
mpp_clock_pause(dec->clocks[DEC_PRS_TOTAL]);
|
|
mpp_dbg_info("mpp_dec_parser_thread is going to exit\n");
|
if (task.hnd && task_dec->valid) {
|
mpp_buf_slot_set_flag(packet_slots, task_dec->input, SLOT_CODEC_READY);
|
mpp_buf_slot_set_flag(packet_slots, task_dec->input, SLOT_HAL_INPUT);
|
mpp_buf_slot_clr_flag(packet_slots, task_dec->input, SLOT_HAL_INPUT);
|
}
|
mpp_buffer_group_clear(mpp->mPacketGroup);
|
dec_release_task_in_port(mpp->mMppInPort);
|
mpp_dbg_info("mpp_dec_parser_thread exited\n");
|
return NULL;
|
}
|
|
void *mpp_dec_hal_thread(void *data)
|
{
|
Mpp *mpp = (Mpp*)data;
|
MppDecImpl *dec = (MppDecImpl *)mpp->mDec;
|
MppThread *hal = dec->thread_hal;
|
HalTaskGroup tasks = dec->tasks;
|
MppBufSlots frame_slots = dec->frame_slots;
|
MppBufSlots packet_slots = dec->packet_slots;
|
|
HalTaskHnd task = NULL;
|
HalTaskInfo task_info;
|
HalDecTask *task_dec = &task_info.dec;
|
|
mpp_clock_start(dec->clocks[DEC_HAL_TOTAL]);
|
|
while (1) {
|
/* hal thread wait for dxva interface intput first */
|
{
|
AutoMutex work_lock(hal->mutex());
|
if (MPP_THREAD_RUNNING != hal->get_status())
|
break;
|
|
if (hal_task_get_hnd(tasks, TASK_PROCESSING, &task)) {
|
// process all task then do reset process
|
if (dec->hal_reset_post != dec->hal_reset_done) {
|
dec_dbg_reset("reset: hal reset start\n");
|
reset_hal_thread(mpp);
|
dec_dbg_reset("reset: hal reset done\n");
|
dec->hal_reset_done++;
|
sem_post(&dec->hal_reset);
|
continue;
|
}
|
|
mpp_dec_notify(dec, MPP_DEC_NOTIFY_TASK_ALL_DONE);
|
mpp_clock_start(dec->clocks[DEC_HAL_WAIT]);
|
hal->wait();
|
mpp_clock_pause(dec->clocks[DEC_HAL_WAIT]);
|
continue;
|
}
|
}
|
|
if (task) {
|
RK_U32 notify_flag = MPP_DEC_NOTIFY_TASK_HND_VALID;
|
|
mpp_clock_start(dec->clocks[DEC_HAL_PROC]);
|
mpp->mTaskGetCount++;
|
|
hal_task_hnd_get_info(task, &task_info);
|
|
/*
|
* check info change flag
|
* if this is a frame with that flag, only output an empty
|
* MppFrame without any image data for info change.
|
*/
|
if (task_dec->flags.info_change) {
|
mpp_dec_flush(dec);
|
mpp_dec_push_display(mpp, task_dec->flags);
|
mpp_dec_put_frame(mpp, task_dec->output, task_dec->flags);
|
|
hal_task_hnd_set_status(task, TASK_IDLE);
|
task = NULL;
|
mpp_dec_notify(dec, notify_flag);
|
mpp_clock_pause(dec->clocks[DEC_HAL_PROC]);
|
continue;
|
}
|
/*
|
* check eos task
|
* if this task is invalid while eos flag is set, we will
|
* flush display queue then push the eos frame to info that
|
* all frames have decoded.
|
*/
|
if (task_dec->flags.eos &&
|
(!task_dec->valid || task_dec->output < 0)) {
|
mpp_dec_push_display(mpp, task_dec->flags);
|
/*
|
* Use -1 as invalid buffer slot index.
|
* Reason: the last task maybe is a empty task with eos flag
|
* only but this task may go through vproc process also. We need
|
* create a buffer slot index for it.
|
*/
|
mpp_dec_put_frame(mpp, -1, task_dec->flags);
|
|
hal_task_hnd_set_status(task, TASK_IDLE);
|
task = NULL;
|
mpp_dec_notify(dec, notify_flag);
|
mpp_clock_pause(dec->clocks[DEC_HAL_PROC]);
|
continue;
|
}
|
|
mpp_clock_start(dec->clocks[DEC_HW_WAIT]);
|
mpp_hal_hw_wait(dec->hal, &task_info);
|
mpp_clock_pause(dec->clocks[DEC_HW_WAIT]);
|
dec->dec_hw_run_count++;
|
|
/*
|
* when hardware decoding is done:
|
* 1. clear decoding flag (mark buffer is ready)
|
* 2. use get_display to get a new frame with buffer
|
* 3. add frame to output list
|
* repeat 2 and 3 until not frame can be output
|
*/
|
mpp_buf_slot_clr_flag(packet_slots, task_dec->input,
|
SLOT_HAL_INPUT);
|
|
hal_task_hnd_set_status(task, (dec->parser_fast_mode) ?
|
(TASK_IDLE) : (TASK_PROC_DONE));
|
|
if (dec->parser_fast_mode)
|
notify_flag |= MPP_DEC_NOTIFY_TASK_HND_VALID;
|
else
|
notify_flag |= MPP_DEC_NOTIFY_TASK_PREV_DONE;
|
|
task = NULL;
|
|
if (task_dec->output >= 0)
|
mpp_buf_slot_clr_flag(frame_slots, task_dec->output, SLOT_HAL_OUTPUT);
|
|
for (RK_U32 i = 0; i < MPP_ARRAY_ELEMS(task_dec->refer); i++) {
|
RK_S32 index = task_dec->refer[i];
|
if (index >= 0)
|
mpp_buf_slot_clr_flag(frame_slots, index, SLOT_HAL_INPUT);
|
}
|
if (task_dec->flags.eos)
|
mpp_dec_flush(dec);
|
mpp_dec_push_display(mpp, task_dec->flags);
|
|
mpp_dec_notify(dec, notify_flag);
|
mpp_clock_pause(dec->clocks[DEC_HAL_PROC]);
|
}
|
}
|
|
mpp_clock_pause(dec->clocks[DEC_HAL_TOTAL]);
|
|
mpp_assert(mpp->mTaskPutCount == mpp->mTaskGetCount);
|
mpp_dbg_info("mpp_dec_hal_thread exited\n");
|
return NULL;
|
}
|
|
void *mpp_dec_advanced_thread(void *data)
|
{
|
Mpp *mpp = (Mpp*)data;
|
MppDecImpl *dec = (MppDecImpl *)mpp->mDec;
|
MppBufSlots frame_slots = dec->frame_slots;
|
MppBufSlots packet_slots = dec->packet_slots;
|
MppThread *thd_dec = dec->thread_parser;
|
DecTask task; /* decoder task */
|
DecTask *pTask = &task;
|
dec_task_init(pTask);
|
HalDecTask *task_dec = &pTask->info.dec;
|
MppPort input = mpp->mMppInPort;
|
MppPort output = mpp->mMppOutPort;
|
MppTask mpp_task = NULL;
|
MPP_RET ret = MPP_OK;
|
MppFrame frame = NULL;
|
MppPacket packet = NULL;
|
|
while (1) {
|
{
|
AutoMutex autolock(thd_dec->mutex());
|
if (MPP_THREAD_RUNNING != thd_dec->get_status())
|
break;
|
|
if (check_task_wait(dec, &task))
|
thd_dec->wait();
|
}
|
|
// process user control
|
if (dec->cmd_send != dec->cmd_recv) {
|
dec_dbg_detail("ctrl proc %d cmd %08x\n", dec->cmd_recv, dec->cmd);
|
sem_wait(&dec->cmd_start);
|
*dec->cmd_ret = mpp_dec_proc_cfg(dec, dec->cmd, dec->param);
|
dec->cmd_recv++;
|
dec_dbg_detail("ctrl proc %d done send %d\n", dec->cmd_recv,
|
dec->cmd_send);
|
mpp_assert(dec->cmd_send == dec->cmd_send);
|
dec->param = NULL;
|
dec->cmd = (MpiCmd)0;
|
dec->cmd_ret = NULL;
|
sem_post(&dec->cmd_done);
|
continue;
|
}
|
|
// 1. check task in
|
dec_dbg_detail("mpp_pkt_in_rdy %d\n", task.status.mpp_pkt_in_rdy);
|
if (!task.status.mpp_pkt_in_rdy) {
|
ret = mpp_port_poll(input, MPP_POLL_NON_BLOCK);
|
if (ret < 0) {
|
task.wait.dec_pkt_in = 1;
|
continue;
|
}
|
|
dec_dbg_detail("poll ready\n");
|
|
task.status.mpp_pkt_in_rdy = 1;
|
task.wait.dec_pkt_in = 0;
|
|
ret = mpp_port_dequeue(input, &mpp_task);
|
mpp_assert(ret == MPP_OK);
|
}
|
dec_dbg_detail("task in ready\n");
|
|
mpp_assert(mpp_task);
|
|
mpp_task_meta_get_packet(mpp_task, KEY_INPUT_PACKET, &packet);
|
mpp_task_meta_get_frame (mpp_task, KEY_OUTPUT_FRAME, &frame);
|
|
if (NULL == packet || NULL == frame) {
|
mpp_port_enqueue(input, mpp_task);
|
task.status.mpp_pkt_in_rdy = 0;
|
continue;
|
}
|
|
if (mpp_packet_get_buffer(packet)) {
|
/*
|
* if there is available buffer in the input packet do decoding
|
*/
|
MppBuffer input_buffer = mpp_packet_get_buffer(packet);
|
MppBuffer output_buffer = mpp_frame_get_buffer(frame);
|
|
mpp_parser_prepare(dec->parser, packet, task_dec);
|
|
/*
|
* We may find eos in prepare step and there will be no anymore vaild task generated.
|
* So here we try push eos task to hal, hal will push all frame to display then
|
* push a eos frame to tell all frame decoded
|
*/
|
if (task_dec->flags.eos && !task_dec->valid) {
|
mpp_frame_set_eos(frame, 1);
|
goto DEC_OUT;
|
}
|
|
/*
|
* look for a unused packet slot index
|
*/
|
if (task_dec->input < 0) {
|
mpp_buf_slot_get_unused(packet_slots, &task_dec->input);
|
}
|
mpp_buf_slot_set_prop(packet_slots, task_dec->input, SLOT_BUFFER, input_buffer);
|
mpp_buf_slot_set_flag(packet_slots, task_dec->input, SLOT_CODEC_READY);
|
mpp_buf_slot_set_flag(packet_slots, task_dec->input, SLOT_HAL_INPUT);
|
|
ret = mpp_parser_parse(dec->parser, task_dec);
|
if (ret != MPP_OK) {
|
mpp_err_f("something wrong with mpp_parser_parse!\n");
|
mpp_frame_set_errinfo(frame, 1); /* 0 - OK; 1 - error */
|
mpp_buf_slot_clr_flag(packet_slots, task_dec->input, SLOT_HAL_INPUT);
|
goto DEC_OUT;
|
}
|
|
if (mpp_buf_slot_is_changed(frame_slots)) {
|
size_t slot_size = mpp_buf_slot_get_size(frame_slots);
|
size_t buffer_size = mpp_buffer_get_size(output_buffer);
|
|
if (slot_size == buffer_size) {
|
mpp_buf_slot_ready(frame_slots);
|
}
|
|
if (slot_size > buffer_size) {
|
mpp_err_f("required buffer size %d is larger than input buffer size %d\n",
|
slot_size, buffer_size);
|
mpp_assert(slot_size <= buffer_size);
|
}
|
}
|
|
mpp_buf_slot_set_prop(frame_slots, task_dec->output, SLOT_BUFFER, output_buffer);
|
// update codec info
|
if (!dec->info_updated && dec->dev) {
|
MppFrame tmp = NULL;
|
|
mpp_buf_slot_get_prop(frame_slots, task_dec->output, SLOT_FRAME_PTR, &tmp);
|
update_dec_hal_info(dec, tmp);
|
dec->info_updated = 1;
|
}
|
// register genertation
|
mpp_hal_reg_gen(dec->hal, &pTask->info);
|
mpp_hal_hw_start(dec->hal, &pTask->info);
|
mpp_hal_hw_wait(dec->hal, &pTask->info);
|
|
MppFrame tmp = NULL;
|
mpp_buf_slot_get_prop(frame_slots, task_dec->output, SLOT_FRAME_PTR, &tmp);
|
mpp_frame_set_width(frame, mpp_frame_get_width(tmp));
|
mpp_frame_set_height(frame, mpp_frame_get_height(tmp));
|
mpp_frame_set_hor_stride(frame, mpp_frame_get_hor_stride(tmp));
|
mpp_frame_set_ver_stride(frame, mpp_frame_get_ver_stride(tmp));
|
mpp_frame_set_hor_stride_pixel(frame, mpp_frame_get_hor_stride_pixel(tmp));
|
mpp_frame_set_pts(frame, mpp_frame_get_pts(tmp));
|
mpp_frame_set_fmt(frame, mpp_frame_get_fmt(tmp));
|
mpp_frame_set_errinfo(frame, mpp_frame_get_errinfo(tmp));
|
|
mpp_buf_slot_clr_flag(packet_slots, task_dec->input, SLOT_HAL_INPUT);
|
mpp_buf_slot_clr_flag(frame_slots, task_dec->output, SLOT_HAL_OUTPUT);
|
} else {
|
/*
|
* else init a empty frame for output
|
*/
|
mpp_log_f("line(%d): Error! Get no buffer from input packet\n", __LINE__);
|
mpp_frame_init(&frame);
|
mpp_frame_set_errinfo(frame, 1);
|
}
|
|
/*
|
* first clear output packet
|
* then enqueue task back to input port
|
* final user will release the mpp_frame they had input
|
*/
|
DEC_OUT:
|
mpp_task_meta_set_packet(mpp_task, KEY_INPUT_PACKET, packet);
|
mpp_port_enqueue(input, mpp_task);
|
mpp_task = NULL;
|
|
// send finished task to output port
|
mpp_port_poll(output, MPP_POLL_BLOCK);
|
mpp_port_dequeue(output, &mpp_task);
|
mpp_task_meta_set_frame(mpp_task, KEY_OUTPUT_FRAME, frame);
|
|
// setup output task here
|
mpp_port_enqueue(output, mpp_task);
|
mpp_task = NULL;
|
packet = NULL;
|
frame = NULL;
|
|
dec_task_info_init(&pTask->info);
|
|
task.status.mpp_pkt_in_rdy = 0;
|
}
|
|
// clear remain task in output port
|
dec_release_task_in_port(input);
|
dec_release_task_in_port(mpp->mUsrInPort);
|
dec_release_task_in_port(mpp->mUsrOutPort);
|
|
return NULL;
|
}
|
|
static const char *timing_str[DEC_TIMING_BUTT] = {
|
"prs thread",
|
"prs wait ",
|
"prs proc ",
|
"prepare ",
|
"parse ",
|
"gen reg ",
|
"hw start ",
|
|
"hal thread",
|
"hal wait ",
|
"hal proc ",
|
"hw wait ",
|
};
|
|
MPP_RET mpp_dec_set_cfg(MppDecCfgSet *dst, MppDecCfgSet *src)
|
{
|
MppDecBaseCfg *src_base = &src->base;
|
MppDecCbCfg *src_cb = &src->cb;
|
|
if (src_base->change) {
|
MppDecBaseCfg *dst_base = &dst->base;
|
RK_U32 change = src_base->change;
|
|
if (change & MPP_DEC_CFG_CHANGE_TYPE)
|
dst_base->type = src_base->type;
|
|
if (change & MPP_DEC_CFG_CHANGE_CODING)
|
dst_base->coding = src_base->coding;
|
|
if (change & MPP_DEC_CFG_CHANGE_HW_TYPE)
|
dst_base->hw_type = src_base->hw_type;
|
|
if (change & MPP_DEC_CFG_CHANGE_BATCH_MODE)
|
dst_base->batch_mode = src_base->batch_mode;
|
|
if (change & MPP_DEC_CFG_CHANGE_OUTPUT_FORMAT)
|
dst_base->out_fmt = src_base->out_fmt;
|
|
if (change & MPP_DEC_CFG_CHANGE_FAST_OUT)
|
dst_base->fast_out = src_base->fast_out;
|
|
if (change & MPP_DEC_CFG_CHANGE_FAST_PARSE)
|
dst_base->fast_parse = src_base->fast_parse;
|
|
if (change & MPP_DEC_CFG_CHANGE_SPLIT_PARSE)
|
dst_base->split_parse = src_base->split_parse;
|
|
if (change & MPP_DEC_CFG_CHANGE_INTERNAL_PTS)
|
dst_base->internal_pts = src_base->internal_pts;
|
|
if (change & MPP_DEC_CFG_CHANGE_SORT_PTS)
|
dst_base->sort_pts = src_base->sort_pts;
|
|
if (change & MPP_DEC_CFG_CHANGE_DISABLE_ERROR)
|
dst_base->disable_error = src_base->disable_error;
|
|
if (change & MPP_DEC_CFG_CHANGE_ENABLE_VPROC)
|
dst_base->enable_vproc = src_base->enable_vproc;
|
|
if (change & MPP_DEC_CFG_CHANGE_ENABLE_FAST_PLAY)
|
dst_base->enable_fast_play = src_base->enable_fast_play;
|
|
dst_base->change = change;
|
src_base->change = 0;
|
}
|
|
if (src_cb->change) {
|
MppDecCbCfg *dst_cb = &dst->cb;
|
RK_U32 change = src_cb->change;
|
|
if (change & MPP_DEC_CB_CFG_CHANGE_PKT_RDY) {
|
dst_cb->pkt_rdy_cb = src_cb->pkt_rdy_cb;
|
dst_cb->pkt_rdy_ctx = src_cb->pkt_rdy_ctx;
|
dst_cb->pkt_rdy_cmd = src_cb->pkt_rdy_cmd;
|
}
|
|
if (change & MPP_DEC_CB_CFG_CHANGE_FRM_RDY) {
|
dst_cb->frm_rdy_cb = src_cb->frm_rdy_cb;
|
dst_cb->frm_rdy_ctx = src_cb->frm_rdy_ctx;
|
dst_cb->frm_rdy_cmd = src_cb->frm_rdy_cmd;
|
}
|
|
dst_cb->change = change;
|
src_cb->change = 0;
|
}
|
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_dec_callback_hal_to_parser(const char *caller, void *ctx,
|
RK_S32 cmd, void *param)
|
{
|
MppDecImpl *p = (MppDecImpl *)ctx;
|
MPP_RET ret = MPP_OK;
|
(void) caller;
|
|
mpp_assert(cmd == DEC_PARSER_CALLBACK);
|
|
if (p->parser)
|
ret = mpp_parser_callback(p->parser, param);
|
|
return ret;
|
}
|
|
MPP_RET mpp_dec_init(MppDec *dec, MppDecInitCfg *cfg)
|
{
|
RK_S32 i;
|
MPP_RET ret;
|
MppCodingType coding;
|
MppBufSlots frame_slots = NULL;
|
MppBufSlots packet_slots = NULL;
|
HalTaskGroup tasks = NULL;
|
Parser parser = NULL;
|
MppHal hal = NULL;
|
Mpp *mpp = (Mpp *)cfg->mpp;
|
MppDecImpl *p = NULL;
|
MppDecCfgSet *dec_cfg = NULL;
|
RK_U32 hal_task_count = 2;
|
RK_U32 support_fast_mode = 0;
|
|
mpp_env_get_u32("mpp_dec_debug", &mpp_dec_debug, 0);
|
dec_dbg_func("in\n");
|
|
if (NULL == dec || NULL == cfg) {
|
mpp_err_f("invalid input dec %p cfg %p\n", dec, cfg);
|
return MPP_ERR_NULL_PTR;
|
}
|
|
*dec = NULL;
|
|
p = mpp_calloc(MppDecImpl, 1);
|
if (NULL == p) {
|
mpp_err_f("failed to malloc context\n");
|
return MPP_ERR_MALLOC;
|
}
|
|
p->mpp = mpp;
|
coding = cfg->coding;
|
dec_cfg = &p->cfg;
|
|
mpp_assert(cfg->cfg);
|
mpp_dec_cfg_set_default(dec_cfg);
|
mpp_dec_set_cfg(dec_cfg, cfg->cfg);
|
mpp_dec_update_cfg(p);
|
|
p->dec_cb.callBack = mpp_dec_callback_hal_to_parser;
|
p->dec_cb.ctx = p;
|
p->dec_cb.cmd = DEC_PARSER_CALLBACK;
|
|
do {
|
ret = mpp_buf_slot_init(&frame_slots);
|
if (ret) {
|
mpp_err_f("could not init frame buffer slot\n");
|
break;
|
}
|
|
ret = mpp_buf_slot_init(&packet_slots);
|
if (ret) {
|
mpp_err_f("could not init packet buffer slot\n");
|
break;
|
}
|
|
MppHalCfg hal_cfg = {
|
MPP_CTX_DEC,
|
coding,
|
frame_slots,
|
packet_slots,
|
dec_cfg,
|
&p->dec_cb,
|
NULL,
|
NULL,
|
0,
|
};
|
|
ret = mpp_hal_init(&hal, &hal_cfg);
|
if (ret) {
|
mpp_err_f("could not init hal\n");
|
break;
|
}
|
|
support_fast_mode = hal_cfg.support_fast_mode;
|
|
if (dec_cfg->base.fast_parse && support_fast_mode) {
|
hal_task_count = 3;
|
} else {
|
dec_cfg->base.fast_parse = 0;
|
p->parser_fast_mode = 0;
|
}
|
dec_cfg->status.hal_support_fast_mode = support_fast_mode;
|
dec_cfg->status.hal_task_count = hal_task_count;
|
|
ret = hal_task_group_init(&tasks, TASK_BUTT, hal_task_count, sizeof(HalDecTask));
|
if (ret) {
|
mpp_err_f("hal_task_group_init failed ret %d\n", ret);
|
break;
|
}
|
|
mpp_buf_slot_setup(packet_slots, hal_task_count);
|
|
p->hw_info = hal_cfg.hw_info;
|
p->dev = hal_cfg.dev;
|
/* check fbc cap after hardware info is valid */
|
mpp_dec_check_fbc_cap(p);
|
|
ParserCfg parser_cfg = {
|
coding,
|
frame_slots,
|
packet_slots,
|
dec_cfg,
|
p->hw_info,
|
};
|
|
ret = mpp_parser_init(&parser, &parser_cfg);
|
if (ret) {
|
mpp_err_f("could not init parser\n");
|
break;
|
}
|
|
ret = hal_info_init(&p->hal_info, MPP_CTX_DEC, coding);
|
if (ret) {
|
mpp_err_f("could not init hal info\n");
|
break;
|
}
|
|
p->coding = coding;
|
p->parser = parser;
|
p->hal = hal;
|
p->tasks = tasks;
|
p->frame_slots = frame_slots;
|
p->packet_slots = packet_slots;
|
|
p->statistics_en = (mpp_dec_debug & MPP_DEC_DBG_TIMING) ? 1 : 0;
|
|
for (i = 0; i < DEC_TIMING_BUTT; i++) {
|
p->clocks[i] = mpp_clock_get(timing_str[i]);
|
mpp_assert(p->clocks[i]);
|
mpp_clock_enable(p->clocks[i], p->statistics_en);
|
}
|
|
p->cmd_lock = new Mutex();
|
sem_init(&p->parser_reset, 0, 0);
|
sem_init(&p->hal_reset, 0, 0);
|
|
// init timestamp for record and sort pts
|
mpp_spinlock_init(&p->ts_lock);
|
INIT_LIST_HEAD(&p->ts_link);
|
p->ts_pool = mpp_mem_pool_init(sizeof(MppPktTs));
|
if (!p->ts_pool) {
|
mpp_err_f("malloc ts pool failed!\n");
|
break;
|
}
|
|
*dec = p;
|
dec_dbg_func("%p out\n", p);
|
return MPP_OK;
|
} while (0);
|
|
mpp_dec_deinit(p);
|
return MPP_NOK;
|
}
|
|
MPP_RET mpp_dec_deinit(MppDec ctx)
|
{
|
RK_S32 i;
|
MppDecImpl *dec = (MppDecImpl *)ctx;
|
|
dec_dbg_func("%p in\n", dec);
|
if (NULL == dec) {
|
mpp_err_f("found NULL input\n");
|
return MPP_ERR_NULL_PTR;
|
}
|
|
if (dec->statistics_en) {
|
mpp_log("%p work %lu wait %lu\n", dec,
|
dec->parser_work_count, dec->parser_wait_count);
|
|
for (i = 0; i < DEC_TIMING_BUTT; i++) {
|
MppClock timer = dec->clocks[i];
|
RK_S32 base = (i < DEC_HAL_TOTAL) ? (DEC_PRS_TOTAL) : (DEC_HAL_TOTAL);
|
RK_S64 time = mpp_clock_get_sum(timer);
|
RK_S64 total = mpp_clock_get_sum(dec->clocks[base]);
|
|
if (!time || !total)
|
continue;
|
|
mpp_log("%p %s - %6.2f %-12lld avg %-12lld\n", dec,
|
mpp_clock_get_name(timer), time * 100.0 / total, time,
|
time / mpp_clock_get_count(timer));
|
}
|
}
|
|
for (i = 0; i < DEC_TIMING_BUTT; i++) {
|
mpp_clock_put(dec->clocks[i]);
|
dec->clocks[i] = NULL;
|
}
|
|
if (dec->hal_info) {
|
hal_info_deinit(dec->hal_info);
|
dec->hal_info = NULL;
|
}
|
|
if (dec->parser) {
|
mpp_parser_deinit(dec->parser);
|
dec->parser = NULL;
|
}
|
|
if (dec->tasks) {
|
hal_task_group_deinit(dec->tasks);
|
dec->tasks = NULL;
|
}
|
|
if (dec->hal) {
|
mpp_hal_deinit(dec->hal);
|
dec->hal = NULL;
|
}
|
|
if (dec->vproc) {
|
dec_vproc_deinit(dec->vproc);
|
dec->vproc = NULL;
|
}
|
|
if (dec->frame_slots) {
|
mpp_buf_slot_deinit(dec->frame_slots);
|
dec->frame_slots = NULL;
|
}
|
|
if (dec->packet_slots) {
|
mpp_buf_slot_deinit(dec->packet_slots);
|
dec->packet_slots = NULL;
|
}
|
|
if (dec->cmd_lock) {
|
delete dec->cmd_lock;
|
dec->cmd_lock = NULL;
|
}
|
|
sem_destroy(&dec->parser_reset);
|
sem_destroy(&dec->hal_reset);
|
if (dec->ts_pool) {
|
mpp_mem_pool_deinit(dec->ts_pool);
|
dec->ts_pool = NULL;
|
}
|
|
mpp_free(dec);
|
dec_dbg_func("%p out\n", dec);
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_dec_start(MppDec ctx)
|
{
|
MPP_RET ret = MPP_OK;
|
MppDecImpl *dec = (MppDecImpl *)ctx;
|
|
dec_dbg_func("%p in\n", dec);
|
|
if (dec->coding != MPP_VIDEO_CodingMJPEG) {
|
dec->thread_parser = new MppThread(mpp_dec_parser_thread,
|
dec->mpp, "mpp_dec_parser");
|
dec->thread_hal = new MppThread(mpp_dec_hal_thread,
|
dec->mpp, "mpp_dec_hal");
|
|
dec->thread_parser->start();
|
dec->thread_hal->start();
|
} else {
|
dec->thread_parser = new MppThread(mpp_dec_advanced_thread,
|
dec->mpp, "mpp_dec_parser");
|
dec->thread_parser->start();
|
}
|
|
dec_dbg_func("%p out\n", dec);
|
return ret;
|
}
|
|
MPP_RET mpp_dec_stop(MppDec ctx)
|
{
|
MPP_RET ret = MPP_OK;
|
MppDecImpl *dec = (MppDecImpl *)ctx;
|
|
dec_dbg_func("%p in\n", dec);
|
|
if (dec->thread_parser)
|
dec->thread_parser->stop();
|
|
if (dec->thread_hal)
|
dec->thread_hal->stop();
|
|
if (dec->thread_parser) {
|
delete dec->thread_parser;
|
dec->thread_parser = NULL;
|
}
|
if (dec->thread_hal) {
|
delete dec->thread_hal;
|
dec->thread_hal = NULL;
|
}
|
|
dec_dbg_func("%p out\n", dec);
|
return ret;
|
}
|
|
MPP_RET mpp_dec_reset(MppDec ctx)
|
{
|
MppDecImpl *dec = (MppDecImpl *)ctx;
|
|
dec_dbg_func("%p in\n", dec);
|
if (NULL == dec) {
|
mpp_err_f("found NULL input dec %p\n", dec);
|
return MPP_ERR_NULL_PTR;
|
}
|
|
MppThread *parser = dec->thread_parser;
|
|
if (dec->coding != MPP_VIDEO_CodingMJPEG) {
|
// set reset flag
|
parser->lock(THREAD_CONTROL);
|
dec->reset_flag = 1;
|
// signal parser thread to reset
|
mpp_dec_notify(dec, MPP_DEC_RESET);
|
parser->unlock(THREAD_CONTROL);
|
sem_wait(&dec->parser_reset);
|
}
|
|
dec->dec_in_pkt_count = 0;
|
dec->dec_hw_run_count = 0;
|
dec->dec_out_frame_count = 0;
|
dec->info_updated = 0;
|
|
dec_dbg_func("%p out\n", dec);
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_dec_flush(MppDec ctx)
|
{
|
MppDecImpl *dec = (MppDecImpl *)ctx;
|
|
dec_dbg_func("%p in\n", dec);
|
if (NULL == dec) {
|
mpp_err_f("found NULL input dec %p\n", dec);
|
return MPP_ERR_NULL_PTR;
|
}
|
|
mpp_parser_flush(dec->parser);
|
mpp_hal_flush(dec->hal);
|
|
dec_dbg_func("%p out\n", dec);
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_dec_notify(MppDec ctx, RK_U32 flag)
|
{
|
MppDecImpl *dec = (MppDecImpl *)ctx;
|
MppThread *thd_dec = dec->thread_parser;
|
RK_U32 notify = 0;
|
|
dec_dbg_func("%p in flag %08x\n", dec, flag);
|
|
thd_dec->lock();
|
if (flag == MPP_DEC_CONTROL) {
|
dec->parser_notify_flag |= flag;
|
notify = 1;
|
} else {
|
RK_U32 old_flag = dec->parser_notify_flag;
|
|
dec->parser_notify_flag |= flag;
|
if ((old_flag != dec->parser_notify_flag) &&
|
(dec->parser_notify_flag & dec->parser_wait_flag))
|
notify = 1;
|
}
|
|
if (notify) {
|
dec_dbg_notify("%p status %08x notify control signal\n", dec,
|
dec->parser_wait_flag, dec->parser_notify_flag);
|
thd_dec->signal();
|
}
|
thd_dec->unlock();
|
dec_dbg_func("%p out\n", dec);
|
return MPP_OK;
|
}
|
|
MPP_RET mpp_dec_callback(MppDec ctx, MppDecEvent event, void *arg)
|
{
|
MppDecImpl *dec = (MppDecImpl *)ctx;
|
MppDecCbCfg *cb = &dec->cfg.cb;
|
Mpp *mpp = (Mpp *)dec->mpp;
|
MPP_RET ret = MPP_OK;
|
|
switch (event) {
|
case MPP_DEC_EVENT_ON_PKT_RELEASE : {
|
if (cb->pkt_rdy_cb)
|
ret = cb->pkt_rdy_cb(cb->pkt_rdy_ctx, mpp->mCtx, cb->pkt_rdy_cmd, arg);
|
} break;
|
case MPP_DEC_EVENT_ON_FRM_READY : {
|
if (cb->frm_rdy_cb)
|
ret = cb->frm_rdy_cb(cb->frm_rdy_ctx, mpp->mCtx, cb->frm_rdy_cmd, arg);
|
} break;
|
default : {
|
} break;
|
}
|
|
return ret;
|
}
|
|
MPP_RET mpp_dec_control(MppDec ctx, MpiCmd cmd, void *param)
|
{
|
MPP_RET ret = MPP_OK;
|
MppDecImpl *dec = (MppDecImpl *)ctx;
|
|
dec_dbg_func("%p in %08x %p\n", dec, cmd, param);
|
if (NULL == dec) {
|
mpp_err_f("found NULL input dec %p\n", dec);
|
return MPP_ERR_NULL_PTR;
|
}
|
|
AutoMutex auto_lock(dec->cmd_lock);
|
dec->cmd = cmd;
|
dec->param = param;
|
dec->cmd_ret = &ret;
|
dec->cmd_send++;
|
dec_dbg_detail("detail: %p control cmd %08x param %p start\n", dec, cmd, param);
|
mpp_dec_notify(ctx, MPP_DEC_CONTROL);
|
sem_post(&dec->cmd_start);
|
sem_wait(&dec->cmd_done);
|
dec_dbg_detail("detail: %p control cmd %08x param %p finish\n", dec, cmd, param);
|
|
dec_dbg_func("%p out\n", dec);
|
return ret;
|
}
|
|
MPP_RET mpp_dec_set_cfg_by_cmd(MppDecCfgSet *set, MpiCmd cmd, void *param)
|
{
|
MppDecBaseCfg *cfg = &set->base;
|
MPP_RET ret = MPP_OK;
|
|
switch (cmd) {
|
case MPP_DEC_SET_PRESENT_TIME_ORDER : {
|
cfg->sort_pts = (param) ? (*((RK_U32 *)param)) : (1);
|
cfg->change |= MPP_DEC_CFG_CHANGE_SORT_PTS;
|
dec_dbg_func("sort time order %d\n", cfg->sort_pts);
|
} break;
|
case MPP_DEC_SET_PARSER_SPLIT_MODE : {
|
cfg->split_parse = (param) ? (*((RK_U32 *)param)) : (0);
|
cfg->change |= MPP_DEC_CFG_CHANGE_SPLIT_PARSE;
|
dec_dbg_func("split parse mode %d\n", cfg->split_parse);
|
} break;
|
case MPP_DEC_SET_PARSER_FAST_MODE : {
|
cfg->fast_parse = (param) ? (*((RK_U32 *)param)) : (0);
|
cfg->change |= MPP_DEC_CFG_CHANGE_FAST_PARSE;
|
dec_dbg_func("fast parse mode %d\n", cfg->fast_parse);
|
} break;
|
case MPP_DEC_SET_OUTPUT_FORMAT : {
|
cfg->out_fmt = (param) ? (*((MppFrameFormat *)param)) : (MPP_FMT_YUV420SP);
|
cfg->change |= MPP_DEC_CFG_CHANGE_OUTPUT_FORMAT;
|
} break;
|
case MPP_DEC_SET_DISABLE_ERROR: {
|
cfg->disable_error = (param) ? (*((RK_U32 *)param)) : (1);
|
cfg->change |= MPP_DEC_CFG_CHANGE_DISABLE_ERROR;
|
dec_dbg_func("disable error %d\n", cfg->disable_error);
|
} break;
|
case MPP_DEC_SET_IMMEDIATE_OUT : {
|
cfg->fast_out = (param) ? (*((RK_U32 *)param)) : (0);
|
cfg->change |= MPP_DEC_CFG_CHANGE_FAST_OUT;
|
dec_dbg_func("fast output mode %d\n", cfg->fast_out);
|
} break;
|
case MPP_DEC_SET_ENABLE_DEINTERLACE: {
|
cfg->enable_vproc = (param) ? (*((RK_U32 *)param)) : (1);
|
cfg->change |= MPP_DEC_CFG_CHANGE_ENABLE_VPROC;
|
dec_dbg_func("enable dec_vproc %d\n", cfg->enable_vproc);
|
} break;
|
case MPP_DEC_SET_ENABLE_FAST_PLAY : {
|
cfg->enable_fast_play = (param) ? (*((RK_U32 *)param)) : (0);
|
cfg->change |= MPP_DEC_CFG_CHANGE_ENABLE_FAST_PLAY;
|
dec_dbg_func("disable idr immediately output %d\n", cfg->enable_fast_play);
|
} break;
|
default : {
|
mpp_err_f("unsupported cfg update cmd %x\n", cmd);
|
ret = MPP_NOK;
|
} break;
|
}
|
|
return ret;
|
}
|