diff options
Diffstat (limited to 'encoder/ih264e_encode.c')
-rwxr-xr-x | encoder/ih264e_encode.c | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/encoder/ih264e_encode.c b/encoder/ih264e_encode.c new file mode 100755 index 0000000..ffc6fb7 --- /dev/null +++ b/encoder/ih264e_encode.c @@ -0,0 +1,580 @@ +/****************************************************************************** + * + * 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; + + /* 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; +} |