From 38fc8aada822a87b331974e3ba61da03588a5257 Mon Sep 17 00:00:00 2001 From: "sayon.chen" Date: Tue, 20 Apr 2021 19:18:45 +0800 Subject: [PATCH] [mpp_dec]: Support no thread decode 1. Use cmd MPP_SET_DISABLE_THREAD to disable thread, cmd set must be after mpp_create before mpp_init. 2. When thread disabled put_packet & get_frame MUST not be used. 3. Separate normal flow and no thread flow. Change-Id: Icf189d312ff39aacf6ca0afe7f345ff4d1cbfb67 Signed-off-by: sayon.chen --- mpp/codec/CMakeLists.txt | 2 + mpp/codec/inc/mpp_dec.h | 2 + mpp/codec/inc/mpp_dec_impl.h | 113 ++- mpp/codec/inc/mpp_dec_no_thread.h | 32 + mpp/codec/inc/mpp_dec_normal.h | 32 + mpp/codec/mpp_dec.cpp | 1291 ++--------------------------- mpp/codec/mpp_dec_debug.h | 40 + mpp/codec/mpp_dec_no_thread.cpp | 343 ++++++++ mpp/codec/mpp_dec_normal.cpp | 1179 ++++++++++++++++++++++++++ mpp/inc/mpp.h | 4 + mpp/mpi.cpp | 42 +- mpp/mpp.cpp | 71 +- test/CMakeLists.txt | 3 + test/mpi_dec_nt_test.c | 602 ++++++++++++++ 14 files changed, 2490 insertions(+), 1266 deletions(-) create mode 100644 mpp/codec/inc/mpp_dec_no_thread.h create mode 100644 mpp/codec/inc/mpp_dec_normal.h create mode 100644 mpp/codec/mpp_dec_debug.h create mode 100644 mpp/codec/mpp_dec_no_thread.cpp create mode 100644 mpp/codec/mpp_dec_normal.cpp create mode 100644 test/mpi_dec_nt_test.c diff --git a/mpp/codec/CMakeLists.txt b/mpp/codec/CMakeLists.txt index 2850dfab..bef1b96e 100644 --- a/mpp/codec/CMakeLists.txt +++ b/mpp/codec/CMakeLists.txt @@ -7,6 +7,8 @@ add_library(mpp_codec STATIC mpp_enc_impl.cpp mpp_enc_v2.cpp enc_impl.cpp + mpp_dec_no_thread.cpp + mpp_dec_normal.cpp mpp_dec.cpp mpp_parser.cpp ) diff --git a/mpp/codec/inc/mpp_dec.h b/mpp/codec/inc/mpp_dec.h index 96230049..8942ea63 100644 --- a/mpp/codec/inc/mpp_dec.h +++ b/mpp/codec/inc/mpp_dec.h @@ -58,6 +58,8 @@ MPP_RET mpp_dec_callback(MppDec ctx, MppDecEvent event, void *arg); MPP_RET mpp_dec_set_cfg_by_cmd(MppDecCfgSet *set, MpiCmd cmd, void *param); MPP_RET mpp_dec_set_cfg(MppDecCfgSet *dst, MppDecCfgSet *src); +MPP_RET mpp_dec_decode(MppDec ctx, MppPacket packet, MppFrame *frame); + #ifdef __cplusplus } #endif diff --git a/mpp/codec/inc/mpp_dec_impl.h b/mpp/codec/inc/mpp_dec_impl.h index 5bae793d..5e5e7cef 100644 --- a/mpp/codec/inc/mpp_dec_impl.h +++ b/mpp/codec/inc/mpp_dec_impl.h @@ -46,9 +46,30 @@ typedef enum MppDecTimingType_e { DEC_TIMING_BUTT, } MppDecTimingType; -typedef struct MppDecImpl_t { + +typedef enum MppDecMode_e { + MPP_DEC_MODE_DEFAULT, + MPP_DEC_MODE_NO_THREAD, + + MPP_DEC_MODE_BUTT, +} MppDecMode; + +typedef struct MppDecImpl_t MppDecImpl; + +typedef struct MppDecModeApi_t { + MPP_RET (*start)(MppDecImpl *dec); + MPP_RET (*stop)(MppDecImpl *dec); + MPP_RET (*reset)(MppDecImpl *dec); + MPP_RET (*notify)(MppDecImpl *dec, RK_U32 flag); + MPP_RET (*control)(MppDecImpl *dec, MpiCmd cmd, void *param); +} MppDecModeApi; + +struct MppDecImpl_t { MppCodingType coding; + MppDecMode mode; + MppDecModeApi *api; + Parser parser; MppHal hal; @@ -72,7 +93,7 @@ typedef struct MppDecImpl_t { MppDecCfgSet cfg; /* control process */ - Mutex *cmd_lock; + MppMutexCond *cmd_lock; RK_U32 cmd_send; RK_U32 cmd_recv; MpiCmd cmd; @@ -127,12 +148,98 @@ typedef struct MppDecImpl_t { MppMemPool ts_pool; struct list_head ts_link; spinlock_t ts_lock; -} MppDecImpl; + void *task_single; +}; + +/* 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; + + MppBuffer hal_pkt_buf_in; + RK_S32 hal_pkt_idx_in; + MppBuffer hal_frm_buf_out; +} DecTask; #ifdef __cplusplus extern "C" { #endif +MPP_RET dec_task_info_init(HalTaskInfo *task); +void dec_task_init(DecTask *task); + +MPP_RET mpp_dec_proc_cfg(MppDecImpl *dec, MpiCmd cmd, void *param); + +MPP_RET update_dec_hal_info(MppDecImpl *dec, MppFrame frame); +void mpp_dec_put_frame(Mpp *mpp, RK_S32 index, HalDecTaskFlag flags); +void mpp_dec_push_display(Mpp *mpp, HalDecTaskFlag flags); #ifdef __cplusplus } diff --git a/mpp/codec/inc/mpp_dec_no_thread.h b/mpp/codec/inc/mpp_dec_no_thread.h new file mode 100644 index 00000000..c32760b8 --- /dev/null +++ b/mpp/codec/inc/mpp_dec_no_thread.h @@ -0,0 +1,32 @@ +/* + * Copyright 2022 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. + */ + +#ifndef __MPP_DEC_NO_THREAD_H__ +#define __MPP_DEC_NO_THREAD_H__ + +#include "mpp_dec_impl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern MppDecModeApi dec_api_no_thread; + +#ifdef __cplusplus +} +#endif + +#endif /*__MPP_DEC_NO_THREAD_H__*/ diff --git a/mpp/codec/inc/mpp_dec_normal.h b/mpp/codec/inc/mpp_dec_normal.h new file mode 100644 index 00000000..112e5509 --- /dev/null +++ b/mpp/codec/inc/mpp_dec_normal.h @@ -0,0 +1,32 @@ +/* + * Copyright 2022 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. + */ + +#ifndef __MPP_DEC_NORMAL_H__ +#define __MPP_DEC_NORMAL_H__ + +#include "mpp_dec_impl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern MppDecModeApi dec_api_normal; + +#ifdef __cplusplus +} +#endif + +#endif /*__MPP_DEC_NORMAL_H__*/ diff --git a/mpp/codec/mpp_dec.cpp b/mpp/codec/mpp_dec.cpp index 44769f6b..79b5d003 100644 --- a/mpp/codec/mpp_dec.cpp +++ b/mpp/codec/mpp_dec.cpp @@ -19,124 +19,20 @@ #include #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_debug.h" #include "mpp_dec_vproc.h" #include "mpp_dec_cb_param.h" +#include "mpp_dec_normal.h" +#include "mpp_dec_no_thread.h" -static RK_U32 mpp_dec_debug = 0; +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) +MPP_RET dec_task_info_init(HalTaskInfo *task) { HalDecTask *p = &task->dec; @@ -152,7 +48,7 @@ static MPP_RET dec_task_info_init(HalTaskInfo *task) return MPP_OK; } -static void dec_task_init(DecTask *task) +void dec_task_init(DecTask *task) { task->hnd = NULL; task->status.val = 0; @@ -163,203 +59,7 @@ static void dec_task_init(DecTask *task) 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) +static MPP_RET mpp_dec_update_cfg(MppDecImpl *p) { MppDecCfgSet *cfg = &p->cfg; MppDecBaseCfg *base = &cfg->base; @@ -379,7 +79,7 @@ MPP_RET mpp_dec_update_cfg(MppDecImpl *p) return MPP_OK; } -MPP_RET mpp_dec_check_fbc_cap(MppDecImpl *p) +static MPP_RET mpp_dec_check_fbc_cap(MppDecImpl *p) { MppDecBaseCfg *base = &p->cfg.base; @@ -495,7 +195,7 @@ RET: } /* Overall mpp_dec output frame function */ -static void mpp_dec_put_frame(Mpp *mpp, RK_S32 index, HalDecTaskFlag flags) +void mpp_dec_put_frame(Mpp *mpp, RK_S32 index, HalDecTaskFlag flags) { MppDecImpl *dec = (MppDecImpl *)mpp->mDec; MppBufSlots slots = dec->frame_slots; @@ -687,7 +387,7 @@ static void mpp_dec_put_frame(Mpp *mpp, RK_S32 index, HalDecTaskFlag flags) } } -static void mpp_dec_push_display(Mpp *mpp, HalDecTaskFlag flags) +void mpp_dec_push_display(Mpp *mpp, HalDecTaskFlag flags) { RK_S32 index = -1; MppDecImpl *dec = (MppDecImpl *)mpp->mDec; @@ -705,8 +405,8 @@ static void mpp_dec_push_display(Mpp *mpp, HalDecTaskFlag flags) * nothing to do with frames being output. */ tmp.info_change = 0; - - dec->thread_hal->lock(THREAD_OUTPUT); + if (dec->thread_hal) + 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)) @@ -715,53 +415,11 @@ static void mpp_dec_push_display(Mpp *mpp, HalDecTaskFlag flags) mpp_dec_put_frame(mpp, index, tmp); mpp_buf_slot_clr_flag(frame_slots, index, SLOT_QUEUE_USE); } - dec->thread_hal->unlock(THREAD_OUTPUT); + if (dec->thread_hal) + 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) +MPP_RET update_dec_hal_info(MppDecImpl *dec, MppFrame frame) { HalInfo hal_info = dec->hal_info; MppDevInfoCfg data[DEC_INFO_BUTT]; @@ -789,810 +447,10 @@ static MPP_RET update_dec_hal_info(MppDecImpl *dec, MppFrame frame) 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_frame_set_buf_size(frame, mpp_frame_get_buf_size(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 MppDecModeApi *dec_api[] = { + &dec_api_normal, + &dec_api_no_thread, +}; static const char *timing_str[DEC_TIMING_BUTT] = { "prs thread", @@ -1663,6 +521,9 @@ MPP_RET mpp_dec_set_cfg(MppDecCfgSet *dst, MppDecCfgSet *src) if (change & MPP_DEC_CFG_CHANGE_ENABLE_THUMBNAIL) dst_base->enable_thumbnail = src_base->enable_thumbnail; + if (change & MPP_DEC_CFG_CHANGE_DISABLE_THREAD) + dst_base->disable_thread = src_base->disable_thread; + dst_base->change = change; src_base->change = 0; } @@ -1722,6 +583,7 @@ MPP_RET mpp_dec_init(MppDec *dec, MppDecInitCfg *cfg) 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) { @@ -1840,12 +702,25 @@ MPP_RET mpp_dec_init(MppDec *dec, MppDecInitCfg *cfg) mpp_clock_enable(p->clocks[i], p->statistics_en); } - p->cmd_lock = new Mutex(); + p->cmd_lock = new MppMutexCond(); sem_init(&p->parser_reset, 0, 0); sem_init(&p->hal_reset, 0, 0); sem_init(&p->cmd_start, 0, 0); sem_init(&p->cmd_done, 0, 0); + if (p->cfg.base.disable_thread) { + DecTask *task = mpp_calloc(DecTask, 1); + + mpp_assert(task); + + p->task_single = task; + dec_task_info_init(&task->info); + + p->mode = MPP_DEC_MODE_NO_THREAD; + } + + p->api = dec_api[p->mode]; + // init timestamp for record and sort pts mpp_spinlock_init(&p->ts_lock); INIT_LIST_HEAD(&p->ts_link); @@ -1949,6 +824,7 @@ MPP_RET mpp_dec_deinit(MppDec ctx) dec->ts_pool = NULL; } + MPP_FREE(dec->task_single); mpp_free(dec); dec_dbg_func("%p out\n", dec); return MPP_OK; @@ -1956,26 +832,15 @@ MPP_RET mpp_dec_deinit(MppDec ctx) MPP_RET mpp_dec_start(MppDec ctx) { - MPP_RET ret = MPP_OK; MppDecImpl *dec = (MppDecImpl *)ctx; + MPP_RET ret = MPP_OK; 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"); + if (dec->api && dec->api->start) + ret = dec->api->start(dec); - 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); + dec_dbg_func("%p out ret %d\n", dec, ret); return ret; } @@ -1996,6 +861,7 @@ MPP_RET mpp_dec_stop(MppDec ctx) delete dec->thread_parser; dec->thread_parser = NULL; } + if (dec->thread_hal) { delete dec->thread_hal; dec->thread_hal = NULL; @@ -2008,32 +874,21 @@ MPP_RET mpp_dec_stop(MppDec ctx) MPP_RET mpp_dec_reset(MppDec ctx) { MppDecImpl *dec = (MppDecImpl *)ctx; + MPP_RET ret = MPP_OK; - 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; + dec_dbg_func("%p in\n", dec); - 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); - } + if (dec->api && dec->api->reset) + ret = dec->api->reset(dec); - 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 ret %d\n", dec, ret); - dec_dbg_func("%p out\n", dec); - return MPP_OK; + return ret; } MPP_RET mpp_dec_flush(MppDec ctx) @@ -2056,35 +911,19 @@ MPP_RET mpp_dec_flush(MppDec ctx) MPP_RET mpp_dec_notify(MppDec ctx, RK_U32 flag) { MppDecImpl *dec = (MppDecImpl *)ctx; - MppThread *thd_dec = dec->thread_parser; - RK_U32 notify = 0; + MPP_RET ret = MPP_OK; 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 (dec->vproc && (flag & MPP_DEC_NOTIFY_BUFFER_MATCH)) dec_vproc_signal(dec->vproc); - 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; + if (dec->api && dec->api->notify) + ret = dec->api->notify(dec, flag); + + dec_dbg_func("%p out ret %d\n", dec, ret); + + return ret; } MPP_RET mpp_dec_callback(MppDec ctx, MppDecEvent event, void *arg) @@ -2115,24 +954,20 @@ 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_func("%p in %08x %p\n", dec, cmd, param); 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); + if (dec->api && dec->api->control) + ret = dec->api->control(dec, cmd, param); + + dec_dbg_detail("detail: %p control cmd %08x param %p ret %d\n", dec, cmd, param, ret); + dec_dbg_func("%p out ret %d\n", dec, ret); + return ret; } diff --git a/mpp/codec/mpp_dec_debug.h b/mpp/codec/mpp_dec_debug.h new file mode 100644 index 00000000..0820dabe --- /dev/null +++ b/mpp/codec/mpp_dec_debug.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022 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. + */ + +#ifndef __MPP_DEC_DEBUG_H__ +#define __MPP_DEC_DEBUG_H__ + +#include "mpp_debug.h" + +#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__) + +extern RK_U32 mpp_dec_debug; + +#endif /* __MPP_DEC_DEBUG_H__ */ diff --git a/mpp/codec/mpp_dec_no_thread.cpp b/mpp/codec/mpp_dec_no_thread.cpp new file mode 100644 index 00000000..2ae396f2 --- /dev/null +++ b/mpp/codec/mpp_dec_no_thread.cpp @@ -0,0 +1,343 @@ +/* + * Copyright 2022 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_nt" + +#include + +#include "mpp_buffer_impl.h" + +#include "mpp_dec_debug.h" +#include "mpp_dec_vproc.h" +#include "mpp_dec_no_thread.h" + +MPP_RET mpp_dec_decode(MppDec ctx, MppPacket packet, MppFrame *frame) +{ + MppDecImpl *dec = (MppDecImpl *)ctx; + Mpp *mpp = (Mpp *)dec->mpp; + DecTask *task = (DecTask *)dec->task_single; + MppBufSlots frame_slots = dec->frame_slots; + MppBufSlots packet_slots = dec->packet_slots; + HalDecTask *task_dec = &task->info.dec; + MppMutexCond *cmd_lock = dec->cmd_lock; + MppBuffer hal_buf_in = NULL; + MppBuffer hal_buf_out = NULL; + size_t stream_size = 0; + RK_S32 output = 0; + + AutoMutex auto_lock(cmd_lock->mutex()); + + if (dec->mpp_pkt_in == NULL && !task->status.curr_task_rdy) + dec->mpp_pkt_in = packet; + + if (!task->status.curr_task_rdy) { + if (dec->mpp_pkt_in == NULL) + return MPP_OK; + + mpp_parser_prepare(dec->parser, dec->mpp_pkt_in, task_dec); + + if (0 == mpp_packet_get_length(dec->mpp_pkt_in)) { + // mpp_packet_deinit(&dec->mpp_pkt_in); + dec->mpp_pkt_in = NULL; + } + + if (!task_dec->valid) { + if (task_dec->flags.eos) { + mpp_dec_flush(dec); + 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); + } + return MPP_OK; + } + } + + task->status.curr_task_rdy = task_dec->valid; + + // 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); + } + + /* + * 5. malloc hardware buffer for the packet slot index + */ + task->hal_pkt_idx_in = task_dec->input; + stream_size = mpp_packet_get_size(task_dec->input_packet); + + mpp_buf_slot_get_prop(packet_slots, task->hal_pkt_idx_in, 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->hal_pkt_idx_in, 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->hal_pkt_buf_in = hal_buf_in; + if (!task->status.dec_pkt_copy_rdy) { + void *dst = mpp_buffer_get_ptr(task->hal_pkt_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; + } + + 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_flush(dec); + mpp_dec_push_display(mpp, task_dec->flags); + } + + 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_flush(dec); + mpp_dec_push_display(mpp, task_dec->flags); + mpp_dec_put_frame(mpp, task_dec->output, task_dec->flags); + task_dec->flags.eos = eos; + task->status.info_task_gen_rdy = 1; + return MPP_OK; + } + dec->info_updated = 0; + } + + task->wait.info_change = mpp_buf_slot_is_changed(frame_slots); + if (task->wait.info_change) { + return MPP_OK; + } 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) + cmd_lock->wait(); + } + dec_dbg_detail("detail: %p check frame group count pass\n", dec); + + 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); + } + if (!dec->info_updated && dec->dev) { + MppFrame slot_frm = NULL; + + mpp_buf_slot_get_prop(frame_slots, output, SLOT_FRAME_PTR, &slot_frm); + update_dec_hal_info(dec, slot_frm); + dec->info_updated = 1; + } + + task->hal_frm_buf_out = hal_buf_out; + task->wait.dec_pic_match = (NULL == hal_buf_out); + if (task->wait.dec_pic_match) + return MPP_NOK; + + mpp_hal_reg_gen(dec->hal, &task->info); + mpp_hal_hw_start(dec->hal, &task->info); + mpp_hal_hw_wait(dec->hal, &task->info); + 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); + + 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); + + (void)frame; + + 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); + return MPP_OK; +} + +MPP_RET mpp_dec_reset_no_thread(MppDecImpl *dec) +{ + DecTask *task = (DecTask *)dec->task_single; + MppBufSlots frame_slots = dec->frame_slots; + MppMutexCond *cmd_lock = dec->cmd_lock; + HalDecTask *task_dec = &task->info.dec; + RK_S32 index; + + AutoMutex auto_lock(cmd_lock->mutex()); + + 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"); + } + + // wait hal thread reset ready + if (task->wait.info_change) { + mpp_log("reset at info change status\n"); + mpp_buf_slot_reset(frame_slots, task_dec->output); + } + + if (task->status.task_parsed_rdy) { + mpp_log("task no send to hal que must clr current frame hal status\n"); + 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; + } + + 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 (task->status.dec_pkt_copy_rdy) { + mpp_buf_slot_clr_flag(dec->packet_slots, task_dec->input, SLOT_HAL_INPUT); + task->status.dec_pkt_copy_rdy = 0; + task_dec->input = -1; + } + + task->status.task_parsed_rdy = 0; + dec_task_init(task); + + dec_dbg_reset("reset: parser reset all done\n"); + + dec->dec_in_pkt_count = 0; + dec->dec_hw_run_count = 0; + dec->dec_out_frame_count = 0; + dec->info_updated = 0; + + cmd_lock->signal(); + + return MPP_OK; +} + +MPP_RET mpp_dec_notify_no_thread(MppDecImpl *dec, RK_U32 flag) +{ + // Only notify buffer group control + if (flag == (MPP_DEC_NOTIFY_BUFFER_VALID | MPP_DEC_NOTIFY_BUFFER_MATCH)) { + dec->cmd_lock->signal(); + return MPP_OK; + } + + return MPP_OK; +} + +MPP_RET mpp_dec_control_no_thread(MppDecImpl *dec, MpiCmd cmd, void *param) +{ + // cmd_lock is used to sync all async operations + AutoMutex auto_lock(dec->cmd_lock->mutex()); + + dec->cmd_send++; + return mpp_dec_proc_cfg(dec, cmd, param); +} + +MppDecModeApi dec_api_no_thread = { + NULL, + NULL, + mpp_dec_reset_no_thread, + mpp_dec_notify_no_thread, + mpp_dec_control_no_thread, +}; diff --git a/mpp/codec/mpp_dec_normal.cpp b/mpp/codec/mpp_dec_normal.cpp new file mode 100644 index 00000000..783cfc20 --- /dev/null +++ b/mpp/codec/mpp_dec_normal.cpp @@ -0,0 +1,1179 @@ +/* + * 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 + +#include "mpp_buffer_impl.h" + +#include "mpp_dec_debug.h" +#include "mpp_dec_vproc.h" +#include "mpp_dec_normal.h" + +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; +} + +/* + * 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); + + mpp_assert(hal); + + 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; +} + +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 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_frame_set_buf_size(frame, mpp_frame_get_buf_size(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; +} + +MPP_RET mpp_dec_start_normal(MppDecImpl *dec) +{ + if (dec->coding != MPP_VIDEO_CodingMJPEG) { + dec->thread_parser = new MppThread(mpp_dec_parser_thread, + dec->mpp, "mpp_dec_parser"); + dec->thread_parser->start(); + dec->thread_hal = new MppThread(mpp_dec_hal_thread, + dec->mpp, "mpp_dec_hal"); + + dec->thread_hal->start(); + } else { + dec->thread_parser = new MppThread(mpp_dec_advanced_thread, + dec->mpp, "mpp_dec_parser"); + dec->thread_parser->start(); + } + + return MPP_OK; +} + +MPP_RET mpp_dec_reset_normal(MppDecImpl *dec) +{ + 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; + + return MPP_OK; +} + +MPP_RET mpp_dec_notify_normal(MppDecImpl *dec, RK_U32 flag) +{ + MppThread *thd_dec = dec->thread_parser; + RK_U32 notify = 0; + + 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(); + + return MPP_OK; +} + +MPP_RET mpp_dec_control_normal(MppDecImpl *dec, MpiCmd cmd, void *param) +{ + MPP_RET ret = MPP_OK; + AutoMutex auto_lock(dec->cmd_lock->mutex()); + + dec->cmd = cmd; + dec->param = param; + dec->cmd_ret = &ret; + dec->cmd_send++; + + dec_dbg_detail("detail: %p control cmd %08x param %p start disable_thread %d \n", + dec, cmd, param, dec->cfg.base.disable_thread); + + mpp_dec_notify_normal(dec, MPP_DEC_CONTROL); + sem_post(&dec->cmd_start); + sem_wait(&dec->cmd_done); + + return ret; +} + +MppDecModeApi dec_api_normal = { + mpp_dec_start_normal, + NULL, + mpp_dec_reset_normal, + mpp_dec_notify_normal, + mpp_dec_control_normal, +}; diff --git a/mpp/inc/mpp.h b/mpp/inc/mpp.h index 5159ddb9..d2ef84bf 100644 --- a/mpp/inc/mpp.h +++ b/mpp/inc/mpp.h @@ -115,6 +115,7 @@ public: MPP_RET put_packet(MppPacket packet); MPP_RET get_frame(MppFrame *frame); + MPP_RET get_frame_noblock(MppFrame *frame); MPP_RET put_frame(MppFrame frame); MPP_RET get_packet(MppPacket *packet); @@ -123,6 +124,8 @@ public: MPP_RET dequeue(MppPortType type, MppTask *task); MPP_RET enqueue(MppPortType type, MppTask task); + MPP_RET decode(MppPacket packet, MppFrame *frame); + MPP_RET reset(); MPP_RET control(MpiCmd cmd, MppParam param); @@ -188,6 +191,7 @@ public: RK_U32 mEncAyncIo; RK_U32 mEncAyncProc; MppIoMode mIoMode; + RK_U32 mDisableThread; /* dump info for debug */ MppDump mDump; diff --git a/mpp/mpi.cpp b/mpp/mpi.cpp index 9e86544c..ccbdbe73 100644 --- a/mpp/mpi.cpp +++ b/mpp/mpi.cpp @@ -103,47 +103,23 @@ static MPP_RET mpi_decode(MppCtx ctx, MppPacket packet, MppFrame *frame) MpiImpl *p = (MpiImpl *)ctx; mpi_dbg_func("enter ctx %p packet %p frame %p\n", ctx, packet, frame); + do { - RK_U32 packet_done = 0; - Mpp *mpp = p->ctx; ret = check_mpp_ctx(p); if (ret) break; - if (NULL == frame || NULL == packet) { - mpp_err_f("found NULL input packet %p frame %p\n", packet, frame); - ret = MPP_ERR_NULL_PTR; - break; - } + /* + * NOTE: packet and frame could be NULL + * If packet is NULL then it is equal to get_frame + * If frame is NULL then it is equal to put_packet + */ + if (frame) + *frame = NULL; - *frame = NULL; - - do { - /* - * If there is frame to return get the frame first - * But if the output mode is block then we need to send packet first - */ - if (!mpp->mOutputTimeout || packet_done) { - ret = mpp->get_frame(frame); - if (ret || *frame) - break; - } - - /* when packet is send do one more get frame here */ - if (packet_done) - break; - - /* - * then send input stream with timeout mode - */ - ret = mpp->put_packet(packet); - if (MPP_OK == ret) - packet_done = 1; - } while (1); + ret = p->ctx->decode(packet, frame); } while (0); - mpp_assert(0 == mpp_packet_get_length(packet)); - mpi_dbg_func("leave ctx %p ret %d\n", ctx, ret); return ret; } diff --git a/mpp/mpp.cpp b/mpp/mpp.cpp index dfba5969..82c4baf2 100644 --- a/mpp/mpp.cpp +++ b/mpp/mpp.cpp @@ -41,8 +41,6 @@ #define MPP_TEST_FRAME_SIZE SZ_1M #define MPP_TEST_PACKET_SIZE SZ_512K -typedef MPP_RET (*QueueFunc)(Mpp* mpp, MppPortType type, MppTask *task); - static void mpp_notify_by_buffer_group(void *arg, void *group) { Mpp *mpp = (Mpp *)arg; @@ -103,6 +101,7 @@ Mpp::Mpp(MppCtx ctx) mEncAyncIo(0), mEncAyncProc(0), mIoMode(MPP_IO_MODE_DEFAULT), + mDisableThread(0), mDump(NULL), mType(MPP_CTX_BUTT), mCoding(MPP_VIDEO_CodingUnused), @@ -172,6 +171,9 @@ MPP_RET Mpp::init(MppCtxType type, MppCodingType coding) mMppInPort = mpp_task_queue_get_port(mInputTaskQueue, MPP_PORT_OUTPUT); mMppOutPort = mpp_task_queue_get_port(mOutputTaskQueue, MPP_PORT_INPUT); + mDecInitcfg.base.disable_thread = mDisableThread; + mDecInitcfg.base.change |= MPP_DEC_CFG_CHANGE_DISABLE_THREAD; + MppDecInitCfg cfg = { coding, this, @@ -316,6 +318,7 @@ void Mpp::clear() mpp_buffer_group_put(mPacketGroup); mPacketGroup = NULL; } + if (mFrameGroup && !mExternalFrameGroup) { mpp_buffer_group_put(mFrameGroup); mFrameGroup = NULL; @@ -354,6 +357,11 @@ MPP_RET Mpp::put_packet(MppPacket packet) MppTask task_dequeue = NULL; RK_U32 pkt_copy = 0; + if (mDisableThread) { + mpp_err_f("no thread decoding case MUST use mpi_decode interface\n"); + return ret; + } + if (mExtraPacket) { MppPacket extra = mExtraPacket; @@ -504,6 +512,61 @@ MPP_RET Mpp::get_frame(MppFrame *frame) return MPP_OK; } +MPP_RET Mpp::get_frame_noblock(MppFrame *frame) +{ + MppFrame first = NULL; + + if (!mInitDone) + return MPP_ERR_INIT; + + mFrmOut->lock(); + if (mFrmOut->list_size()) { + mFrmOut->del_at_head(&first, sizeof(frame)); + mFrameGetCount++; + } + mFrmOut->unlock(); + *frame = first; + + return MPP_OK; +} + +MPP_RET Mpp::decode(MppPacket packet, MppFrame *frame) +{ + RK_U32 packet_done = 0; + MPP_RET ret = MPP_NOK; + + if (!mDec) + return MPP_NOK; + + do { + /* + * If there is frame to return get the frame first + * But if the output mode is block then we need to send packet first + */ + if (!mOutputTimeout || packet_done) { + ret = get_frame_noblock(frame); + if (ret || *frame) + break; + } + + /* when packet is send do one more get frame here */ + if (packet_done) + break; + + ret = mpp_dec_decode(mDec, packet, frame); + if (!ret) + packet_done = 1; + + if (!mOutputTimeout || packet_done) { + ret = get_frame_noblock(frame); + if (ret || *frame) + break; + } + } while (1); + + return ret; +} + MPP_RET Mpp::put_frame(MppFrame frame) { if (!mInitDone) @@ -979,6 +1042,10 @@ MPP_RET Mpp::control_mpp(MpiCmd cmd, MppParam param) mpp_log("deprecated block control, use timeout control instead\n"); } break; + case MPP_SET_DISABLE_THREAD: { + mDisableThread = 1; + } break; + case MPP_SET_INPUT_TIMEOUT: case MPP_SET_OUTPUT_TIMEOUT: { MppPollType timeout = (param) ? *((MppPollType *)param) : MPP_POLL_NON_BLOCK; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 01441a74..b5cc594f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -30,6 +30,9 @@ add_mpp_test(mpi_dec c) # mpi decoder multi-thread input / output unit test add_mpp_test(mpi_dec_mt c) +# mpi decoder no-thread input / output unit test +add_mpp_test(mpi_dec_nt c) + # mpi encoder unit test add_mpp_test(mpi_enc c) diff --git a/test/mpi_dec_nt_test.c b/test/mpi_dec_nt_test.c new file mode 100644 index 00000000..4cf4efa9 --- /dev/null +++ b/test/mpi_dec_nt_test.c @@ -0,0 +1,602 @@ +/* + * Copyright 2022 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. + */ + +#if defined(_WIN32) +#include "vld.h" +#endif + +#define MODULE_TAG "mpi_dec_nt_test" + +#include +#include "rk_mpi.h" + +#include "mpp_mem.h" +#include "mpp_env.h" +#include "mpp_time.h" +#include "mpp_common.h" +#include "mpi_dec_utils.h" + +typedef struct { + MpiDecTestCmd *cmd; + MppCtx ctx; + MppApi *mpi; + RK_U32 quiet; + + /* end of stream flag when set quit the loop */ + RK_U32 loop_end; + + /* input and output */ + MppBufferGroup frm_grp; + MppPacket packet; + MppFrame frame; + + FILE *fp_output; + RK_S32 frame_count; + RK_S32 frame_num; + + RK_S64 first_pkt; + RK_S64 first_frm; + + size_t max_usage; + float frame_rate; + RK_S64 elapsed_time; + RK_S64 delay; + FILE *fp_verify; + FrmCrc checkcrc; +} MpiDecLoopData; + +static int dec_loop(MpiDecLoopData *data) +{ + RK_U32 pkt_done = 0; + RK_U32 pkt_eos = 0; + MPP_RET ret = MPP_OK; + MpiDecTestCmd *cmd = data->cmd; + MppCtx ctx = data->ctx; + MppApi *mpi = data->mpi; + MppPacket packet = data->packet; + FileBufSlot *slot = NULL; + RK_U32 quiet = data->quiet; + FrmCrc *checkcrc = &data->checkcrc; + + // when packet size is valid read the input binary file + ret = reader_read(cmd->reader, &slot); + + mpp_assert(ret == MPP_OK); + mpp_assert(slot); + + pkt_eos = slot->eos; + + if (pkt_eos) { + if (data->frame_num < 0 || data->frame_num > data->frame_count) { + mpp_log_q(quiet, "%p loop again\n", ctx); + reader_rewind(cmd->reader); + pkt_eos = 0; + } else { + mpp_log_q(quiet, "%p found last packet\n", ctx); + data->loop_end = 1; + } + } + + mpp_packet_set_data(packet, slot->data); + mpp_packet_set_size(packet, slot->size); + mpp_packet_set_pos(packet, slot->data); + mpp_packet_set_length(packet, slot->size); + // setup eos flag + if (pkt_eos) + mpp_packet_set_eos(packet); + + do { + RK_U32 frm_eos = 0; + RK_S32 get_frm = 0; + MppFrame frame = NULL; + + // send the packet first if packet is not done + ret = mpi->decode(ctx, packet, &frame); + if (ret) + mpp_err("decode failed ret %d\n", ret); + + // then get all available frame and release + if (frame) { + if (mpp_frame_get_info_change(frame)) { + RK_U32 width = mpp_frame_get_width(frame); + RK_U32 height = mpp_frame_get_height(frame); + RK_U32 hor_stride = mpp_frame_get_hor_stride(frame); + RK_U32 ver_stride = mpp_frame_get_ver_stride(frame); + RK_U32 buf_size = mpp_frame_get_buf_size(frame); + + mpp_log_q(quiet, "%p decode_get_frame get info changed found\n", ctx); + mpp_log_q(quiet, "%p decoder require buffer w:h [%d:%d] stride [%d:%d] buf_size %d", + ctx, width, height, hor_stride, ver_stride, buf_size); + + /* + * NOTE: We can choose decoder's buffer mode here. + * There are three mode that decoder can support: + * + * Mode 1: Pure internal mode + * In the mode user will NOT call MPP_DEC_SET_EXT_BUF_GROUP + * control to decoder. Only call MPP_DEC_SET_INFO_CHANGE_READY + * to let decoder go on. Then decoder will use create buffer + * internally and user need to release each frame they get. + * + * Advantage: + * Easy to use and get a demo quickly + * Disadvantage: + * 1. The buffer from decoder may not be return before + * decoder is close. So memroy leak or crash may happen. + * 2. The decoder memory usage can not be control. Decoder + * is on a free-to-run status and consume all memory it can + * get. + * 3. Difficult to implement zero-copy display path. + * + * Mode 2: Half internal mode + * This is the mode current test code using. User need to + * create MppBufferGroup according to the returned info + * change MppFrame. User can use mpp_buffer_group_limit_config + * function to limit decoder memory usage. + * + * Advantage: + * 1. Easy to use + * 2. User can release MppBufferGroup after decoder is closed. + * So memory can stay longer safely. + * 3. Can limit the memory usage by mpp_buffer_group_limit_config + * Disadvantage: + * 1. The buffer limitation is still not accurate. Memory usage + * is 100% fixed. + * 2. Also difficult to implement zero-copy display path. + * + * Mode 3: Pure external mode + * In this mode use need to create empty MppBufferGroup and + * import memory from external allocator by file handle. + * On Android surfaceflinger will create buffer. Then + * mediaserver get the file handle from surfaceflinger and + * commit to decoder's MppBufferGroup. + * + * Advantage: + * 1. Most efficient way for zero-copy display + * Disadvantage: + * 1. Difficult to learn and use. + * 2. Player work flow may limit this usage. + * 3. May need a external parser to get the correct buffer + * size for the external allocator. + * + * The required buffer size caculation: + * hor_stride * ver_stride * 3 / 2 for pixel data + * hor_stride * ver_stride / 2 for extra info + * Total hor_stride * ver_stride * 2 will be enough. + * + * For H.264/H.265 20+ buffers will be enough. + * For other codec 10 buffers will be enough. + */ + + if (NULL == data->frm_grp) { + /* If buffer group is not set create one and limit it */ + ret = mpp_buffer_group_get_internal(&data->frm_grp, MPP_BUFFER_TYPE_ION); + if (ret) { + mpp_err("%p get mpp buffer group failed ret %d\n", ctx, ret); + break; + } + + /* Set buffer to mpp decoder */ + ret = mpi->control(ctx, MPP_DEC_SET_EXT_BUF_GROUP, data->frm_grp); + if (ret) { + mpp_err("%p set buffer group failed ret %d\n", ctx, ret); + break; + } + } else { + /* If old buffer group exist clear it */ + ret = mpp_buffer_group_clear(data->frm_grp); + if (ret) { + mpp_err("%p clear buffer group failed ret %d\n", ctx, ret); + break; + } + } + + /* Use limit config to limit buffer count to 24 with buf_size */ + ret = mpp_buffer_group_limit_config(data->frm_grp, buf_size, 24); + if (ret) { + mpp_err("%p limit buffer group failed ret %d\n", ctx, ret); + break; + } + + /* + * All buffer group config done. Set info change ready to let + * decoder continue decoding + */ + ret = mpi->control(ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL); + if (ret) { + mpp_err("%p info change ready failed ret %d\n", ctx, ret); + break; + } + } else { + char log_buf[256]; + RK_S32 log_size = sizeof(log_buf) - 1; + RK_S32 log_len = 0; + RK_U32 err_info = mpp_frame_get_errinfo(frame); + RK_U32 discard = mpp_frame_get_discard(frame); + + if (!data->first_frm) + data->first_frm = mpp_time(); + + log_len += snprintf(log_buf + log_len, log_size - log_len, + "decode get frame %d", data->frame_count); + + if (mpp_frame_has_meta(frame)) { + MppMeta meta = mpp_frame_get_meta(frame); + RK_S32 temporal_id = 0; + + mpp_meta_get_s32(meta, KEY_TEMPORAL_ID, &temporal_id); + + log_len += snprintf(log_buf + log_len, log_size - log_len, + " tid %d", temporal_id); + } + + if (err_info || discard) { + log_len += snprintf(log_buf + log_len, log_size - log_len, + " err %x discard %x", err_info, discard); + } + mpp_log_q(quiet, "%p %s\n", ctx, log_buf); + + data->frame_count++; + if (data->fp_output && !err_info) + dump_mpp_frame_to_file(frame, data->fp_output); + + if (data->fp_verify) { + calc_frm_crc(frame, checkcrc); + write_frm_crc(data->fp_verify, checkcrc); + } + + fps_calc_inc(cmd->fps); + } + frm_eos = mpp_frame_get_eos(frame); + mpp_frame_deinit(&frame); + get_frm = 1; + } + + // try get runtime frame memory usage + if (data->frm_grp) { + size_t usage = mpp_buffer_group_usage(data->frm_grp); + if (usage > data->max_usage) + data->max_usage = usage; + } + + // when get one output frame check the output frame count limit + if (get_frm) { + if (data->frame_num > 0) { + // when get enough frame quit + if (data->frame_count >= data->frame_num) { + data->loop_end = 1; + break; + } + } else { + // when get last frame quit + if (frm_eos) { + mpp_log_q(quiet, "%p found last packet\n", ctx); + data->loop_end = 1; + break; + } + } + } + + if (packet) { + if (mpp_packet_get_length(packet)) { + msleep(1); + continue; + } + + if (!data->first_pkt) + data->first_pkt = mpp_time(); + + packet = NULL; + pkt_done = 1; + } + + mpp_assert(pkt_done); + + // if last packet is send but last frame is not found continue + if (pkt_eos && !frm_eos) { + msleep(1); + continue; + } + + if (pkt_done) + break; + + /* + * why sleep here: + * mpi->decode_put_packet will failed when packet in internal queue is + * full,waiting the package is consumed .Usually hardware decode one + * frame which resolution is 1080p needs 2 ms,so here we sleep 1ms + * * is enough. + */ + msleep(1); + } while (1); + + return ret; +} + +void *thread_decode(void *arg) +{ + MpiDecLoopData *data = (MpiDecLoopData *)arg; + RK_S64 t_s, t_e; + + memset(&data->checkcrc, 0, sizeof(data->checkcrc)); + data->checkcrc.luma.sum = mpp_malloc(RK_ULONG, 512); + data->checkcrc.chroma.sum = mpp_malloc(RK_ULONG, 512); + + t_s = mpp_time(); + + while (!data->loop_end) + dec_loop(data); + + t_e = mpp_time(); + data->elapsed_time = t_e - t_s; + data->frame_count = data->frame_count; + data->frame_rate = (float)data->frame_count * 1000000 / data->elapsed_time; + data->delay = data->first_frm - data->first_pkt; + + mpp_log("decode %d frames time %lld ms delay %3d ms fps %3.2f\n", + data->frame_count, (RK_S64)(data->elapsed_time / 1000), + (RK_S32)(data->delay / 1000), data->frame_rate); + + MPP_FREE(data->checkcrc.luma.sum); + MPP_FREE(data->checkcrc.chroma.sum); + + return NULL; +} + +int dec_nt_decode(MpiDecTestCmd *cmd) +{ + // base flow context + MppCtx ctx = NULL; + MppApi *mpi = NULL; + + // input / output + MppPacket packet = NULL; + MppFrame frame = NULL; + + // paramter for resource malloc + RK_U32 width = cmd->width; + RK_U32 height = cmd->height; + MppCodingType type = cmd->type; + + // config for runtime mode + MppDecCfg cfg = NULL; + RK_U32 need_split = 1; + + // resources + MppBuffer frm_buf = NULL; + pthread_t thd; + pthread_attr_t attr; + MpiDecLoopData data; + MPP_RET ret = MPP_OK; + + mpp_log("mpi_dec_test start\n"); + memset(&data, 0, sizeof(data)); + pthread_attr_init(&attr); + + cmd->simple = (cmd->type != MPP_VIDEO_CodingMJPEG) ? (1) : (0); + + if (cmd->have_output) { + data.fp_output = fopen(cmd->file_output, "w+b"); + if (NULL == data.fp_output) { + mpp_err("failed to open output file %s\n", cmd->file_output); + goto MPP_TEST_OUT; + } + } + + if (cmd->file_slt) { + data.fp_verify = fopen(cmd->file_slt, "wt"); + if (!data.fp_verify) + mpp_err("failed to open verify file %s\n", cmd->file_slt); + } + + if (cmd->simple) { + ret = mpp_packet_init(&packet, NULL, 0); + mpp_err_f("mpp_packet_init get %p\n", packet); + if (ret) { + mpp_err("mpp_packet_init failed\n"); + goto MPP_TEST_OUT; + } + } else { + RK_U32 hor_stride = MPP_ALIGN(width, 16); + RK_U32 ver_stride = MPP_ALIGN(height, 16); + + ret = mpp_buffer_group_get_internal(&data.frm_grp, MPP_BUFFER_TYPE_ION); + if (ret) { + mpp_err("failed to get buffer group for input frame ret %d\n", ret); + goto MPP_TEST_OUT; + } + + ret = mpp_frame_init(&frame); /* output frame */ + if (ret) { + mpp_err("mpp_frame_init failed\n"); + goto MPP_TEST_OUT; + } + + /* + * NOTE: For jpeg could have YUV420 and YUV422 the buffer should be + * larger for output. And the buffer dimension should align to 16. + * YUV420 buffer is 3/2 times of w*h. + * YUV422 buffer is 2 times of w*h. + * So create larger buffer with 2 times w*h. + */ + ret = mpp_buffer_get(data.frm_grp, &frm_buf, hor_stride * ver_stride * 4); + if (ret) { + mpp_err("failed to get buffer for input frame ret %d\n", ret); + goto MPP_TEST_OUT; + } + + mpp_frame_set_buffer(frame, frm_buf); + } + + // decoder demo + ret = mpp_create(&ctx, &mpi); + if (ret) { + mpp_err("mpp_create failed\n"); + goto MPP_TEST_OUT; + } + + mpp_log("%p mpi_dec_test decoder test start w %d h %d type %d\n", + ctx, width, height, type); + + ret = mpi->control(ctx, MPP_SET_DISABLE_THREAD, NULL); + + ret = mpp_init(ctx, MPP_CTX_DEC, type); + if (ret) { + mpp_err("%p mpp_init failed\n", ctx); + goto MPP_TEST_OUT; + } + + mpp_dec_cfg_init(&cfg); + + /* get default config from decoder context */ + ret = mpi->control(ctx, MPP_DEC_GET_CFG, cfg); + if (ret) { + mpp_err("%p failed to get decoder cfg ret %d\n", ctx, ret); + goto MPP_TEST_OUT; + } + + /* + * split_parse is to enable mpp internal frame spliter when the input + * packet is not aplited into frames. + */ + ret = mpp_dec_cfg_set_u32(cfg, "base:split_parse", need_split); + if (ret) { + mpp_err("%p failed to set split_parse ret %d\n", ctx, ret); + goto MPP_TEST_OUT; + } + + ret = mpi->control(ctx, MPP_DEC_SET_CFG, cfg); + if (ret) { + mpp_err("%p failed to set cfg %p ret %d\n", ctx, cfg, ret); + goto MPP_TEST_OUT; + } + + data.cmd = cmd; + data.ctx = ctx; + data.mpi = mpi; + data.loop_end = 0; + data.packet = packet; + data.frame = frame; + data.frame_count = 0; + data.frame_num = cmd->frame_num; + data.quiet = cmd->quiet; + + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + ret = pthread_create(&thd, &attr, thread_decode, &data); + if (ret) { + mpp_err("failed to create thread for input ret %d\n", ret); + goto MPP_TEST_OUT; + } + + if (cmd->frame_num < 0) { + // wait for input then quit decoding + mpp_log("*******************************************\n"); + mpp_log("**** Press Enter to stop loop decoding ****\n"); + mpp_log("*******************************************\n"); + + getc(stdin); + data.loop_end = 1; + } + + pthread_join(thd, NULL); + + cmd->max_usage = data.max_usage; + + ret = mpi->reset(ctx); + if (ret) { + mpp_err("%p mpi->reset failed\n", ctx); + goto MPP_TEST_OUT; + } + +MPP_TEST_OUT: + if (data.packet) { + mpp_packet_deinit(&data.packet); + data.packet = NULL; + } + + if (frame) { + mpp_frame_deinit(&frame); + frame = NULL; + } + + if (ctx) { + mpp_destroy(ctx); + ctx = NULL; + } + + if (!cmd->simple) { + if (frm_buf) { + mpp_buffer_put(frm_buf); + frm_buf = NULL; + } + } + + if (data.frm_grp) { + mpp_buffer_group_put(data.frm_grp); + data.frm_grp = NULL; + } + + if (data.fp_output) { + fclose(data.fp_output); + data.fp_output = NULL; + } + + if (data.fp_verify) { + fclose(data.fp_verify); + data.fp_verify = NULL; + } + + if (cfg) { + mpp_dec_cfg_deinit(cfg); + cfg = NULL; + } + + pthread_attr_destroy(&attr); + + return ret; +} + +int main(int argc, char **argv) +{ + RK_S32 ret = 0; + MpiDecTestCmd cmd_ctx; + MpiDecTestCmd* cmd = &cmd_ctx; + + memset((void*)cmd, 0, sizeof(*cmd)); + cmd->format = MPP_FMT_BUTT; + cmd->pkt_size = MPI_DEC_STREAM_SIZE; + + // parse the cmd option + ret = mpi_dec_test_cmd_init(cmd, argc, argv); + if (ret) + goto RET; + + mpi_dec_test_cmd_options(cmd); + + ret = dec_nt_decode(cmd); + if (MPP_OK == ret) + mpp_log("test success max memory %.2f MB\n", cmd->max_usage / (float)(1 << 20)); + else + mpp_err("test failed ret %d\n", ret); + +RET: + mpi_dec_test_cmd_deinit(cmd); + + return ret; +} +