/* SPDX-License-Identifier: Apache-2.0 OR MIT */ /* * Copyright (c) 2015 Rockchip Electronics Co., Ltd. */ #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" #include "rk_hdr_meta_com.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); RK_U32 keep_notify = 0; do { if (dec->reset_flag) break; // NOTE: User control should always be processed if (notify & MPP_DEC_CONTROL) { keep_notify = 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 = keep_notify; 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); mpp_thread_lock(hal, THREAD_WORK); dec->hal_reset_post++; mpp_thread_signal(hal, THREAD_WORK); mpp_thread_unlock(hal, THREAD_WORK); 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; RK_U32 i; 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 (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_f(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); mpp_thread_lock(dec->thread_hal, THREAD_WORK); hal_task_hnd_set_status(task->hnd, TASK_PROCESSING); mpp->mTaskPutCount++; mpp_thread_signal(dec->thread_hal, THREAD_WORK); mpp_thread_unlock(dec->thread_hal, THREAD_WORK); 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); mpp_thread_lock(dec->thread_hal, 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; } } mpp_thread_unlock(dec->thread_hal, 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 function * 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_attach_dev(hal_buf_in, dec->dev); 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 *src = mpp_packet_get_data(task_dec->input_packet); size_t length = mpp_packet_get_length(task_dec->input_packet); mpp_buffer_write(hal_buf_in, 0, src, length); mpp_buffer_sync_partial_end(hal_buf_in, 0, 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_list_size(mpp->mFrmOut) > 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 | MPP_BUFFER_FLAGS_CACHABLE); } /* 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) { MppFrame mframe = NULL; mpp_buf_slot_get_prop(frame_slots, output, SLOT_FRAME_PTR, &mframe); size_t size = mpp_buf_slot_get_size(frame_slots); if (mframe && mpp_frame_get_thumbnail_en(mframe) == MPP_FRAME_THUMBNAIL_ONLY) { // only for 8K thumbnail downscale to 4K 8bit mode RK_U32 downscale_width = mpp_frame_get_width(mframe) / 2; RK_U32 downscale_height = mpp_frame_get_height(mframe) / 2; size = downscale_width * downscale_height * 3 / 2; } 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", dec, hal_buf_out); { MppFrame mframe = NULL; mpp_buf_slot_get_prop(frame_slots, output, SLOT_FRAME_PTR, &mframe); if (MPP_FRAME_FMT_IS_HDR(mpp_frame_get_fmt(mframe)) && dec->cfg->base.enable_hdr_meta) { fill_hdr_meta_to_frame(mframe, dec->coding); } } // 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_f(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) { mpp_thread_lock(parser, THREAD_WORK); if (MPP_THREAD_RUNNING != mpp_thread_get_status(parser, THREAD_WORK)) { mpp_thread_unlock(parser, THREAD_WORK); 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]); mpp_thread_wait(parser, THREAD_WORK); mpp_clock_pause(dec->clocks[DEC_PRS_WAIT]); } mpp_thread_unlock(parser, THREAD_WORK); // 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); mpp_thread_lock(parser, THREAD_CONTROL); dec->reset_flag = 0; sem_post(&dec->parser_reset); mpp_thread_unlock(parser, THREAD_CONTROL); 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; RK_U32 i; mpp_clock_start(dec->clocks[DEC_HAL_TOTAL]); while (1) { /* hal thread wait for dxva interface intput first */ mpp_thread_lock(hal, THREAD_WORK); if (MPP_THREAD_RUNNING != mpp_thread_get_status(hal, THREAD_WORK)) { mpp_thread_unlock(hal, THREAD_WORK); 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); mpp_thread_unlock(hal, THREAD_WORK); continue; } mpp_dec_notify(dec, MPP_DEC_NOTIFY_TASK_ALL_DONE); mpp_clock_start(dec->clocks[DEC_HAL_WAIT]); mpp_thread_wait(hal, THREAD_WORK); mpp_clock_pause(dec->clocks[DEC_HAL_WAIT]); mpp_thread_unlock(hal, THREAD_WORK); continue; } mpp_thread_unlock(hal, THREAD_WORK); 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 (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) { mpp_thread_lock(thd_dec, THREAD_WORK); if (MPP_THREAD_RUNNING != mpp_thread_get_status(thd_dec, THREAD_WORK)) { mpp_thread_unlock(thd_dec, THREAD_WORK); break; } if (check_task_wait(dec, &task)) mpp_thread_wait(thd_dec, THREAD_WORK); mpp_thread_unlock(thd_dec, THREAD_WORK); // 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 (!frame && packet) { MppMeta meta = mpp_packet_get_meta(packet); if (meta) { mpp_meta_get_frame(meta, KEY_OUTPUT_FRAME, &frame); if (frame) task.status.mpp_in_frm_at_pkt = 1; } } 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; } dec_dbg_detail("slot change %d\n", mpp_buf_slot_is_changed(frame_slots)); 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); dec_dbg_detail("change size required %d vs input %d\n", slot_size, buffer_size); if (slot_size == buffer_size) { mpp_buf_slot_ready(frame_slots); } mpp_assert(slot_size <= buffer_size); 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_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: if (task.status.mpp_in_frm_at_pkt) { MppList *list = mpp->mFrmOut; MppMeta meta = mpp_frame_get_meta(frame); if (meta) mpp_meta_set_packet(meta, KEY_INPUT_PACKET, packet); mpp_dbg_pts("output frame pts %lld\n", mpp_frame_get_pts(frame)); mpp_mutex_cond_lock(&list->cond_lock); mpp_list_add_at_tail(list, &frame, sizeof(frame)); mpp->mFramePutCount++; mpp_list_signal(list); mpp_mutex_cond_unlock(&list->cond_lock); mpp_port_enqueue(input, mpp_task); mpp_task = NULL; task.status.mpp_in_frm_at_pkt = 0; } else { 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); mpp_buffer_sync_ro_begin(mpp_frame_get_buffer(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 = mpp_thread_create(mpp_dec_parser_thread, dec->mpp, "mpp_dec_parser"); mpp_thread_start(dec->thread_parser); dec->thread_hal = mpp_thread_create(mpp_dec_hal_thread, dec->mpp, "mpp_dec_hal"); mpp_thread_start(dec->thread_hal); } else { dec->thread_parser = mpp_thread_create(mpp_dec_advanced_thread, dec->mpp, "mpp_dec_parser"); mpp_thread_start(dec->thread_parser); } 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 mpp_thread_lock(parser, THREAD_CONTROL); dec->reset_flag = 1; // signal parser thread to reset mpp_dec_notify(dec, MPP_DEC_RESET); mpp_thread_unlock(parser, 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; if (!thd_dec) return MPP_NOK; mpp_thread_lock(thd_dec, THREAD_WORK); 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); mpp_thread_signal(thd_dec, THREAD_WORK); } mpp_thread_unlock(thd_dec, THREAD_WORK); return MPP_OK; } MPP_RET mpp_dec_control_normal(MppDecImpl *dec, MpiCmd cmd, void *param) { MPP_RET ret = MPP_OK; mpp_mutex_cond_lock(&dec->cmd_lock); dec->cmd = cmd; dec->param = param; dec->cmd_ret = &ret; dec->cmd_send++; dec_dbg_detail("detail: %p control cmd %08x param %p start 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); mpp_mutex_cond_unlock(&dec->cmd_lock); return ret; } MppDecModeApi dec_api_normal = { mpp_dec_start_normal, NULL, mpp_dec_reset_normal, mpp_dec_notify_normal, mpp_dec_control_normal, };