| /****************************************************************************** |
| * |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * 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. |
| * |
| ***************************************************************************** |
| * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore |
| */ |
| |
| /** |
| ****************************************************************************** |
| * @file |
| * ih264e_encode.c |
| * |
| * @brief |
| * This file contains functions for encoding the input yuv frame in synchronous |
| * api mode |
| * |
| * @author |
| * ittiam |
| * |
| * List of Functions |
| * - ih264e_join_threads() |
| * - ih264e_wait_for_thread() |
| * - ih264e_encode() |
| * |
| ****************************************************************************** |
| */ |
| |
| /*****************************************************************************/ |
| /* File Includes */ |
| /*****************************************************************************/ |
| |
| /* System Include files */ |
| #include <stdio.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| /* User Include files */ |
| #include "ih264e_config.h" |
| #include "ih264_typedefs.h" |
| #include "iv2.h" |
| #include "ive2.h" |
| #include "ih264e.h" |
| #include "ithread.h" |
| #include "ih264_defs.h" |
| #include "ih264_macros.h" |
| #include "ih264_debug.h" |
| #include "ih264_structs.h" |
| #include "ih264_platform_macros.h" |
| #include "ih264_error.h" |
| #include "ime_distortion_metrics.h" |
| #include "ime_structs.h" |
| #include "ih264_defs.h" |
| #include "ih264_error.h" |
| #include "ih264_structs.h" |
| #include "ih264_trans_quant_itrans_iquant.h" |
| #include "ih264_inter_pred_filters.h" |
| #include "ih264_mem_fns.h" |
| #include "ih264_padding.h" |
| #include "ih264_intra_pred_filters.h" |
| #include "ih264_deblk_edge_filters.h" |
| #include "ih264_list.h" |
| #include "ih264e_error.h" |
| #include "ih264e_defs.h" |
| #include "ih264_padding.h" |
| #include "ih264e_bitstream.h" |
| #include "irc_mem_req_and_acq.h" |
| #include "irc_cntrl_param.h" |
| #include "irc_frame_info_collector.h" |
| #include "ih264e_rate_control.h" |
| #include "ih264e_time_stamp.h" |
| #include "ih264e_structs.h" |
| #include "ih264e_master.h" |
| #include "ih264e_process.h" |
| #include "ih264_buf_mgr.h" |
| #include "ih264_dpb_mgr.h" |
| #include "ih264e_utils.h" |
| #include "ih264e_fmt_conv.h" |
| #include "ih264e_config.h" |
| #include "ih264e_statistics.h" |
| #include "ih264e_trace.h" |
| #include "ih264e_debug.h" |
| #ifdef LOGO_EN |
| #include "ih264e_ittiam_logo.h" |
| #endif |
| |
| /*****************************************************************************/ |
| /* Function Definitions */ |
| /*****************************************************************************/ |
| |
| /** |
| ****************************************************************************** |
| * |
| * @brief |
| * This function joins all the spawned threads after successful completion of |
| * their tasks |
| * |
| * @par Description |
| * |
| * @param[in] ps_codec |
| * pointer to codec context |
| * |
| * @returns none |
| * |
| ****************************************************************************** |
| */ |
| void ih264e_join_threads(codec_t *ps_codec) |
| { |
| /* temp var */ |
| WORD32 i = 0; |
| WORD32 ret = 0; |
| |
| /* join spawned threads */ |
| while (i < ps_codec->i4_proc_thread_cnt) |
| { |
| if (ps_codec->ai4_process_thread_created[i]) |
| { |
| ret = ithread_join(ps_codec->apv_proc_thread_handle[i], NULL); |
| if (ret != 0) |
| { |
| printf("pthread Join Failed"); |
| assert(0); |
| } |
| ps_codec->ai4_process_thread_created[i] = 0; |
| i++; |
| } |
| } |
| |
| ps_codec->i4_proc_thread_cnt = 0; |
| } |
| |
| /** |
| ****************************************************************************** |
| * |
| * @brief This function puts the current thread to sleep for a duration |
| * of sleep_us |
| * |
| * @par Description |
| * ithread_yield() method causes the calling thread to yield execution to another |
| * thread that is ready to run on the current processor. The operating system |
| * selects the thread to yield to. ithread_usleep blocks the current thread for |
| * the specified number of milliseconds. In other words, yield just says, |
| * end my timeslice prematurely, look around for other threads to run. If there |
| * is nothing better than me, continue. Sleep says I don't want to run for x |
| * milliseconds. Even if no other thread wants to run, don't make me run. |
| * |
| * @param[in] sleep_us |
| * thread sleep duration |
| * |
| * @returns error_status |
| * |
| ****************************************************************************** |
| */ |
| IH264E_ERROR_T ih264e_wait_for_thread(UWORD32 sleep_us) |
| { |
| /* yield thread */ |
| ithread_yield(); |
| |
| /* put thread to sleep */ |
| ithread_usleep(sleep_us); |
| |
| return IH264E_SUCCESS; |
| } |
| |
| /** |
| ****************************************************************************** |
| * |
| * @brief |
| * Encodes in synchronous api mode |
| * |
| * @par Description |
| * This routine processes input yuv, encodes it and outputs bitstream and recon |
| * |
| * @param[in] ps_codec_obj |
| * Pointer to codec object at API level |
| * |
| * @param[in] pv_api_ip |
| * Pointer to input argument structure |
| * |
| * @param[out] pv_api_op |
| * Pointer to output argument structure |
| * |
| * @returns Status |
| * |
| ****************************************************************************** |
| */ |
| WORD32 ih264e_encode(iv_obj_t *ps_codec_obj, void *pv_api_ip, void *pv_api_op) |
| { |
| /* error status */ |
| IH264E_ERROR_T error_status = IH264E_SUCCESS; |
| |
| /* codec ctxt */ |
| codec_t *ps_codec = (codec_t *)ps_codec_obj->pv_codec_handle; |
| |
| /* input frame to encode */ |
| ih264e_video_encode_ip_t *ps_video_encode_ip = pv_api_ip; |
| |
| /* output buffer to write stream */ |
| ih264e_video_encode_op_t *ps_video_encode_op = pv_api_op; |
| |
| /* i/o structures */ |
| inp_buf_t s_inp_buf; |
| out_buf_t s_out_buf; |
| |
| /* temp var */ |
| WORD32 ctxt_sel = 0, i; |
| |
| /********************************************************************/ |
| /* BEGIN INIT */ |
| /********************************************************************/ |
| /* reset output structure */ |
| ps_video_encode_op->s_ive_op.u4_error_code = IV_SUCCESS; |
| ps_video_encode_op->s_ive_op.output_present = 0; |
| ps_video_encode_op->s_ive_op.dump_recon = 0; |
| ps_video_encode_op->s_ive_op.u4_encoded_frame_type = IV_NA_FRAME; |
| |
| /* copy input info. to internal structure */ |
| s_inp_buf.s_raw_buf = ps_video_encode_ip->s_ive_ip.s_inp_buf; |
| s_inp_buf.u4_timestamp_low = ps_video_encode_ip->s_ive_ip.u4_timestamp_low; |
| s_inp_buf.u4_timestamp_high = ps_video_encode_ip->s_ive_ip.u4_timestamp_high; |
| s_inp_buf.u4_is_last = ps_video_encode_ip->s_ive_ip.u4_is_last; |
| s_inp_buf.pv_mb_info = ps_video_encode_ip->s_ive_ip.pv_mb_info; |
| s_inp_buf.u4_mb_info_type = ps_video_encode_ip->s_ive_ip.u4_mb_info_type; |
| s_inp_buf.pv_pic_info = ps_video_encode_ip->s_ive_ip.pv_pic_info; |
| s_inp_buf.u4_pic_info_type = ps_video_encode_ip->s_ive_ip.u4_pic_info_type; |
| |
| /* copy output info. to internal structure */ |
| s_out_buf.s_bits_buf = ps_video_encode_ip->s_ive_ip.s_out_buf; |
| s_out_buf.u4_is_last = ps_video_encode_ip->s_ive_ip.u4_is_last; |
| s_out_buf.u4_timestamp_low = ps_video_encode_ip->s_ive_ip.u4_timestamp_low; |
| s_out_buf.u4_timestamp_high = ps_video_encode_ip->s_ive_ip.u4_timestamp_high; |
| |
| /* api call cnt */ |
| ps_codec->i4_encode_api_call_cnt += 1; |
| |
| /* curr pic cnt */ |
| ps_codec->i4_pic_cnt += 1; |
| |
| /* codec context selector */ |
| ctxt_sel = ps_codec->i4_encode_api_call_cnt & 1; |
| |
| /* reset status flags */ |
| ps_codec->ai4_pic_cnt[ctxt_sel] = -1; |
| ps_codec->s_rate_control.post_encode_skip[ctxt_sel] = 0; |
| ps_codec->s_rate_control.pre_encode_skip[ctxt_sel] = 0; |
| |
| /* pass output buffer to codec */ |
| ps_codec->as_out_buf[ctxt_sel] = s_out_buf; |
| |
| /* initialize codec ctxt with default params for the first encode api call */ |
| if (ps_codec->i4_encode_api_call_cnt == 0) |
| { |
| ih264e_codec_init(ps_codec); |
| } |
| |
| /* parse configuration params */ |
| for (i = 0; i < MAX_ACTIVE_CONFIG_PARAMS; i++) |
| { |
| cfg_params_t *ps_cfg = &ps_codec->as_cfg[i]; |
| |
| if (1 == ps_cfg->u4_is_valid) |
| { |
| if ( ((ps_cfg->u4_timestamp_high == s_inp_buf.u4_timestamp_high) && |
| (ps_cfg->u4_timestamp_low == s_inp_buf.u4_timestamp_low)) || |
| ((WORD32)ps_cfg->u4_timestamp_high == -1) || |
| ((WORD32)ps_cfg->u4_timestamp_low == -1) ) |
| { |
| error_status |= ih264e_codec_update_config(ps_codec, ps_cfg); |
| SET_ERROR_ON_RETURN(error_status, |
| IVE_UNSUPPORTEDPARAM, |
| ps_video_encode_op->s_ive_op.u4_error_code, |
| IV_FAIL); |
| |
| ps_cfg->u4_is_valid = 0; |
| } |
| } |
| } |
| |
| /****************************************************************** |
| * INSERT LOGO |
| *****************************************************************/ |
| #ifdef LOGO_EN |
| if (s_inp_buf.s_raw_buf.apv_bufs[0] != NULL && |
| ps_codec->i4_header_mode != 1) |
| { |
| ih264e_insert_logo(s_inp_buf.s_raw_buf.apv_bufs[0], |
| s_inp_buf.s_raw_buf.apv_bufs[1], |
| s_inp_buf.s_raw_buf.apv_bufs[2], |
| s_inp_buf.s_raw_buf.au4_strd[0], |
| 0, |
| 0, |
| ps_codec->s_cfg.e_inp_color_fmt, |
| ps_codec->s_cfg.u4_disp_wd, |
| ps_codec->s_cfg.u4_disp_ht); |
| } |
| #endif /*LOGO_EN*/ |
| |
| if (ps_codec->i4_encode_api_call_cnt == 0) |
| { |
| /********************************************************************/ |
| /* number of mv/ref bank buffers used by the codec, */ |
| /* 1 to handle curr frame */ |
| /* 1 to store information of ref frame */ |
| /* 1 more additional because of the codec employs 2 ctxt sets */ |
| /* to assist asynchronous API */ |
| /********************************************************************/ |
| |
| /* initialize mv bank buffer manager */ |
| error_status |= ih264e_mv_buf_mgr_add_bufs(ps_codec); |
| SET_ERROR_ON_RETURN(error_status, |
| IVE_FATALERROR, |
| ps_video_encode_op->s_ive_op.u4_error_code, |
| IV_FAIL); |
| |
| /* initialize ref bank buffer manager */ |
| error_status |= ih264e_pic_buf_mgr_add_bufs(ps_codec); |
| SET_ERROR_ON_RETURN(error_status, |
| IVE_FATALERROR, |
| ps_video_encode_op->s_ive_op.u4_error_code, |
| IV_FAIL); |
| |
| /* for the first frame, generate header when not requested explicitly */ |
| if (ps_codec->i4_header_mode == 0 && |
| ps_codec->u4_header_generated == 0) |
| { |
| ps_codec->i4_gen_header = 1; |
| } |
| } |
| |
| /* generate header and return when encoder is operated in header mode */ |
| if (ps_codec->i4_header_mode == 1) |
| { |
| /* whenever the header is generated, this implies a start of sequence |
| * and a sequence needs to be started with IDR |
| */ |
| ps_codec->force_curr_frame_type = IV_IDR_FRAME; |
| |
| /* generate header */ |
| error_status |= ih264e_generate_sps_pps(ps_codec); |
| |
| /* api call cnt */ |
| ps_codec->i4_encode_api_call_cnt --; |
| |
| /* curr pic cnt */ |
| ps_codec->i4_pic_cnt --; |
| |
| /* header mode tag is not sticky */ |
| ps_codec->i4_header_mode = 0; |
| ps_codec->i4_gen_header = 0; |
| |
| /* send the input to app */ |
| ps_video_encode_op->s_ive_op.s_inp_buf = s_inp_buf.s_raw_buf; |
| |
| /* send the output to app */ |
| ps_video_encode_op->s_ive_op.output_present = 1; |
| ps_video_encode_op->s_ive_op.dump_recon = 0; |
| ps_video_encode_op->s_ive_op.s_out_buf = ps_codec->as_out_buf[ctxt_sel].s_bits_buf; |
| |
| /* error status */ |
| SET_ERROR_ON_RETURN(error_status, |
| IVE_FATALERROR, |
| ps_video_encode_op->s_ive_op.u4_error_code, |
| IV_FAIL); |
| |
| /* indicates that header has been generated previously */ |
| ps_codec->u4_header_generated = 1; |
| |
| return IV_SUCCESS; |
| } |
| |
| |
| if (s_inp_buf.s_raw_buf.apv_bufs[0] != NULL) |
| { |
| /* array giving pic cnt that is being processed in curr context set */ |
| ps_codec->ai4_pic_cnt[ctxt_sel] = ps_codec->i4_pic_cnt; |
| |
| /* initialize all relevant process ctxts */ |
| error_status |= ih264e_pic_init(ps_codec, &s_inp_buf); |
| SET_ERROR_ON_RETURN(error_status, |
| IVE_FATALERROR, |
| ps_video_encode_op->s_ive_op.u4_error_code, |
| IV_FAIL); |
| |
| if (ps_codec->s_rate_control.pre_encode_skip[ctxt_sel] == 0) |
| { |
| /* proc ctxt base idx */ |
| WORD32 proc_ctxt_select = ctxt_sel * MAX_PROCESS_THREADS; |
| |
| /* proc ctxt */ |
| process_ctxt_t *ps_proc = &ps_codec->as_process[proc_ctxt_select]; |
| |
| WORD32 ret = 0; |
| |
| /* number of addl. threads to be created */ |
| WORD32 num_thread_cnt = ps_codec->s_cfg.u4_num_cores - 1; |
| |
| for (i = 0; i < num_thread_cnt; i++) |
| { |
| ret = ithread_create(ps_codec->apv_proc_thread_handle[i], |
| NULL, |
| (void*)ih264e_process_thread, |
| &ps_codec->as_process[i + 1]); |
| if (ret != 0) |
| { |
| printf("pthread Create Failed"); |
| assert(0); |
| } |
| |
| ps_codec->ai4_process_thread_created[i] = 1; |
| |
| ps_codec->i4_proc_thread_cnt++; |
| } |
| |
| |
| /* launch job */ |
| ih264e_process_thread(ps_proc); |
| |
| /* Join threads at the end of encoding a frame */ |
| ih264e_join_threads(ps_codec); |
| |
| ih264_list_reset(ps_codec->pv_proc_jobq); |
| |
| ih264_list_reset(ps_codec->pv_entropy_jobq); |
| } |
| } |
| |
| if (-1 != ps_codec->ai4_pic_cnt[ctxt_sel]) |
| { |
| /* proc ctxt base idx */ |
| WORD32 proc_ctxt_select = ctxt_sel * MAX_PROCESS_THREADS; |
| |
| /* proc ctxt */ |
| process_ctxt_t *ps_proc = &ps_codec->as_process[proc_ctxt_select]; |
| |
| /* receive output back from codec */ |
| s_out_buf = ps_codec->as_out_buf[ctxt_sel]; |
| |
| /* send the output to app */ |
| ps_video_encode_op->s_ive_op.output_present = 1; |
| ps_video_encode_op->s_ive_op.dump_recon = 1; |
| ps_video_encode_op->s_ive_op.s_out_buf = s_out_buf.s_bits_buf; |
| ps_video_encode_op->s_ive_op.u4_error_code = IV_SUCCESS; |
| |
| /* receive input back from codec */ |
| s_inp_buf = ps_proc->s_inp_buf; |
| |
| /* send the input to app */ |
| ps_video_encode_op->s_ive_op.s_inp_buf = s_inp_buf.s_raw_buf; |
| |
| if (ps_codec->s_cfg.u4_enable_recon && |
| ps_codec->s_rate_control.pre_encode_skip[ctxt_sel] == 0) |
| { |
| /* error status */ |
| IH264_ERROR_T ret = IH264_SUCCESS; |
| |
| /* recon buffer */ |
| rec_buf_t *ps_rec_buf = &ps_codec->as_rec_buf[ctxt_sel]; |
| |
| ps_video_encode_op->s_ive_op.s_recon_buf = ps_video_encode_ip->s_ive_ip.s_recon_buf; |
| |
| /* copy/convert the recon buffer and return */ |
| ih264e_fmt_conv(ps_codec, &ps_rec_buf->s_pic_buf, |
| ps_video_encode_ip->s_ive_ip.s_recon_buf.apv_bufs[0], |
| ps_video_encode_ip->s_ive_ip.s_recon_buf.apv_bufs[1], |
| ps_video_encode_ip->s_ive_ip.s_recon_buf.apv_bufs[2], |
| ps_video_encode_ip->s_ive_ip.s_recon_buf.au4_wd[0], |
| ps_video_encode_ip->s_ive_ip.s_recon_buf.au4_wd[1], |
| 0, |
| ps_codec->s_cfg.u4_disp_ht); |
| |
| ret = ih264_buf_mgr_release(ps_codec->pv_ref_buf_mgr, ps_rec_buf->s_pic_buf.i4_buf_id, BUF_MGR_IO); |
| if (IH264_SUCCESS != ret) |
| { |
| SET_ERROR_ON_RETURN((IH264E_ERROR_T)ret, |
| IVE_FATALERROR, |
| ps_video_encode_op->s_ive_op.u4_error_code, |
| IV_FAIL); |
| } |
| } |
| |
| /* release buffers from ref list */ |
| if (ps_codec->s_rate_control.post_encode_skip[ctxt_sel] == 1) |
| { |
| /* pic info */ |
| pic_buf_t *ps_cur_pic; |
| |
| /* mv info */ |
| mv_buf_t *ps_cur_mv_buf; |
| |
| /* error status */ |
| IH264_ERROR_T ret = IH264_SUCCESS; |
| |
| /* Decrement coded pic count */ |
| ps_codec->i4_coded_pic_cnt--; |
| |
| /* loop through to get the min pic cnt among the list of pics stored in ref list */ |
| /* since the skipped frame may not be on reference list, we may not have an MV bank |
| * hence free only if we have allocated */ |
| for (i = 0; i < ps_codec->i4_ref_buf_cnt; i++) |
| { |
| if (ps_codec->i4_pic_cnt == ps_codec->as_ref_set[i].i4_pic_cnt) |
| { |
| ps_codec->as_ref_set[i].i4_pic_cnt = -1; |
| ps_codec->as_ref_set[i].i4_poc = -1; |
| |
| ps_cur_pic = ps_codec->as_ref_set[i].ps_pic_buf; |
| |
| ps_cur_mv_buf = ps_codec->as_ref_set[i].ps_mv_buf; |
| |
| /* release this frame from reference list */ |
| ret = ih264_buf_mgr_release(ps_codec->pv_mv_buf_mgr, ps_cur_mv_buf->i4_buf_id , BUF_MGR_REF); |
| SET_ERROR_ON_RETURN((IH264E_ERROR_T)ret, |
| IVE_FATALERROR, |
| ps_video_encode_op->s_ive_op.u4_error_code, |
| IV_FAIL); |
| |
| ret = ih264_buf_mgr_release(ps_codec->pv_ref_buf_mgr, ps_cur_pic->i4_buf_id , BUF_MGR_REF); |
| SET_ERROR_ON_RETURN((IH264E_ERROR_T)ret, |
| IVE_FATALERROR, |
| ps_video_encode_op->s_ive_op.u4_error_code, |
| IV_FAIL); |
| break; |
| } |
| } |
| } |
| |
| if ((ps_codec->s_rate_control.post_encode_skip[ctxt_sel] == 1) || |
| (ps_codec->s_rate_control.pre_encode_skip[ctxt_sel] == 1)) |
| { |
| ps_video_encode_op->s_ive_op.dump_recon = 0; |
| } |
| else |
| { |
| /* set output pic type */ |
| if (ps_codec->i4_slice_type == PSLICE) |
| { |
| ps_video_encode_op->s_ive_op.u4_encoded_frame_type = IV_P_FRAME; |
| } |
| else if (ps_codec->i4_slice_type == ISLICE && ps_codec->u4_is_idr != 1) |
| { |
| ps_video_encode_op->s_ive_op.u4_encoded_frame_type = IV_I_FRAME; |
| } |
| else |
| { |
| ps_video_encode_op->s_ive_op.u4_encoded_frame_type = IV_IDR_FRAME; |
| } |
| } |
| |
| /* loop through to get the error status */ |
| for (i = 0; i < (WORD32)ps_codec->s_cfg.u4_num_cores; i++) |
| { |
| error_status |= ps_codec->as_process[ctxt_sel + i].i4_error_code; |
| } |
| SET_ERROR_ON_RETURN(error_status, |
| IVE_FATALERROR, |
| ps_video_encode_op->s_ive_op.u4_error_code, |
| IV_FAIL); |
| } |
| |
| if (1 == s_inp_buf.u4_is_last) |
| { |
| ps_video_encode_op->s_ive_op.output_present = 0; |
| ps_video_encode_op->s_ive_op.dump_recon = 0; |
| } |
| |
| return IV_SUCCESS; |
| } |