1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "GoldfishHevcHelper.h"
18 
19 #define LOG_TAG "GoldfishHevcHelper"
20 #include <log/log.h>
21 
22 #include "ihevc_typedefs.h"
23 #include "ihevcd_cxa.h"
24 
25 #define DEBUG 0
26 #if DEBUG
27 #define DDD(...) ALOGD(__VA_ARGS__)
28 #else
29 #define DDD(...) ((void)0)
30 #endif
31 
32 
33 #include <Codec2Mapper.h>
34 
35 #define ivdec_api_function ihevcd_cxa_api_function
36 #define ivdext_create_ip_t ihevcd_cxa_create_ip_t
37 #define ivdext_create_op_t ihevcd_cxa_create_op_t
38 #define ivdext_delete_ip_t ihevcd_cxa_delete_ip_t
39 #define ivdext_delete_op_t ihevcd_cxa_delete_op_t
40 #define ivdext_ctl_set_num_cores_ip_t ihevcd_cxa_ctl_set_num_cores_ip_t
41 #define ivdext_ctl_set_num_cores_op_t ihevcd_cxa_ctl_set_num_cores_op_t
42 #define ivdext_ctl_get_vui_params_ip_t ihevcd_cxa_ctl_get_vui_params_ip_t
43 #define ivdext_ctl_get_vui_params_op_t ihevcd_cxa_ctl_get_vui_params_op_t
44 #define ALIGN128(x) ((((x) + 127) >> 7) << 7)
45 #define MAX_NUM_CORES 4
46 #define IVDEXT_CMD_CTL_SET_NUM_CORES                                           \
47     (IVD_CONTROL_API_COMMAND_TYPE_T) IHEVCD_CXA_CMD_CTL_SET_NUM_CORES
48 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
49 
50 namespace android {
51 
ivd_aligned_malloc(void * ctxt,WORD32 alignment,WORD32 size)52 static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
53     (void) ctxt;
54     return memalign(alignment, size);
55 }
56 
ivd_aligned_free(void * ctxt,void * mem)57 static void ivd_aligned_free(void *ctxt, void *mem) {
58     (void) ctxt;
59     free(mem);
60 }
61 
62 
GoldfishHevcHelper(int w,int h)63 GoldfishHevcHelper::GoldfishHevcHelper(int w, int h):mWidth(w),mHeight(h) { createDecoder(); }
64 
~GoldfishHevcHelper()65 GoldfishHevcHelper::~GoldfishHevcHelper() {
66     destroyDecoder();
67 }
68 
createDecoder()69 void GoldfishHevcHelper::createDecoder() {
70     ivdext_create_ip_t s_create_ip = {};
71     ivdext_create_op_t s_create_op = {};
72 
73     s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
74     s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
75     s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
76     s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorformat;
77     s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc;
78     s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free;
79     s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = nullptr;
80     s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t);
81     IV_API_CALL_STATUS_T status =
82         ivdec_api_function(mDecHandle, &s_create_ip, &s_create_op);
83     if (status != IV_SUCCESS) {
84         ALOGE("error in %s: 0x%x", __func__,
85               s_create_op.s_ivd_create_op_t.u4_error_code);
86         return;
87     }
88     mDecHandle = (iv_obj_t *)s_create_op.s_ivd_create_op_t.pv_handle;
89     mDecHandle->pv_fxns = (void *)ivdec_api_function;
90     mDecHandle->u4_size = sizeof(iv_obj_t);
91 
92     mStride = ALIGN128(mWidth);
93 
94     setNumCores();
95 }
96 
destroyDecoder()97 void GoldfishHevcHelper::destroyDecoder() {
98     if (mDecHandle) {
99         ivdext_delete_ip_t s_delete_ip = {};
100         ivdext_delete_op_t s_delete_op = {};
101 
102         s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
103         s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;
104         s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t);
105         IV_API_CALL_STATUS_T status =
106             ivdec_api_function(mDecHandle, &s_delete_ip, &s_delete_op);
107         if (status != IV_SUCCESS) {
108             ALOGE("error in %s: 0x%x", __func__,
109                   s_delete_op.s_ivd_delete_op_t.u4_error_code);
110         }
111         mDecHandle = nullptr;
112     }
113 }
114 
setNumCores()115 void GoldfishHevcHelper::setNumCores() {
116     ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip = {};
117     ivdext_ctl_set_num_cores_op_t s_set_num_cores_op = {};
118 
119     s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
120     s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
121     s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
122     s_set_num_cores_ip.u4_num_cores = mNumCores;
123     s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
124     IV_API_CALL_STATUS_T status = ivdec_api_function(
125         mDecHandle, &s_set_num_cores_ip, &s_set_num_cores_op);
126     if (IV_SUCCESS != status) {
127         DDD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code);
128     }
129 }
130 
resetDecoder()131 void GoldfishHevcHelper::resetDecoder() {
132     ivd_ctl_reset_ip_t s_reset_ip = {};
133     ivd_ctl_reset_op_t s_reset_op = {};
134 
135     s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
136     s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
137     s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
138     s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t);
139     IV_API_CALL_STATUS_T status =
140         ivdec_api_function(mDecHandle, &s_reset_ip, &s_reset_op);
141     if (IV_SUCCESS != status) {
142         ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code);
143     }
144     setNumCores();
145 }
146 
setParams(size_t stride,IVD_VIDEO_DECODE_MODE_T dec_mode)147 void GoldfishHevcHelper::setParams(size_t stride,
148                                    IVD_VIDEO_DECODE_MODE_T dec_mode) {
149     ihevcd_cxa_ctl_set_config_ip_t s_hevcd_set_dyn_params_ip = {};
150     ihevcd_cxa_ctl_set_config_op_t s_hevcd_set_dyn_params_op = {};
151     ivd_ctl_set_config_ip_t *ps_set_dyn_params_ip =
152         &s_hevcd_set_dyn_params_ip.s_ivd_ctl_set_config_ip_t;
153     ivd_ctl_set_config_op_t *ps_set_dyn_params_op =
154         &s_hevcd_set_dyn_params_op.s_ivd_ctl_set_config_op_t;
155 
156     ps_set_dyn_params_ip->u4_size = sizeof(ihevcd_cxa_ctl_set_config_ip_t);
157     ps_set_dyn_params_ip->e_cmd = IVD_CMD_VIDEO_CTL;
158     ps_set_dyn_params_ip->e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
159     ps_set_dyn_params_ip->u4_disp_wd = (UWORD32)stride;
160     ps_set_dyn_params_ip->e_frm_skip_mode = IVD_SKIP_NONE;
161     ps_set_dyn_params_ip->e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
162     ps_set_dyn_params_ip->e_vid_dec_mode = dec_mode;
163     ps_set_dyn_params_op->u4_size = sizeof(ihevcd_cxa_ctl_set_config_op_t);
164     IV_API_CALL_STATUS_T status = ivdec_api_function(
165         mDecHandle, ps_set_dyn_params_ip, ps_set_dyn_params_op);
166     if (status != IV_SUCCESS) {
167         ALOGE("error in %s: 0x%x", __func__,
168               ps_set_dyn_params_op->u4_error_code);
169     }
170 }
171 
isVpsFrame(const uint8_t * frame,int inSize)172 bool GoldfishHevcHelper::isVpsFrame(const uint8_t* frame, int inSize) {
173     if (inSize < 5) return false;
174     if (frame[0] == 0 && frame[1] == 0 && frame[2] == 0 && frame[3] == 1) {
175         const bool forbiddenBitIsInvalid = 0x80 & frame[4];
176         if (forbiddenBitIsInvalid) {
177             return false;
178         }
179         // nalu type is the lower 6 bits after shiftting to right 1 bit
180         uint8_t naluType = 0x3f & (frame[4] >> 1);
181         if (naluType == 32
182             || naluType == 33
183             || naluType == 34
184                 ) return true;
185         else return false;
186     } else {
187         return false;
188     }
189 }
190 
decodeHeader(const uint8_t * frame,int inSize,bool & helperstatus)191 bool GoldfishHevcHelper::decodeHeader(const uint8_t *frame, int inSize,
192                                       bool &helperstatus) {
193     helperstatus = true;
194     // should we check the header for vps/sps/pps frame ? otherwise
195     // there is no point calling decoder
196     if (!isVpsFrame(frame, inSize)) {
197         DDD("could not find valid vps frame");
198         return false;
199     } else {
200         DDD("found valid vps frame");
201     }
202 
203     ihevcd_cxa_video_decode_ip_t s_hevcd_decode_ip = {};
204     ihevcd_cxa_video_decode_op_t s_hevcd_decode_op = {};
205     ivd_video_decode_ip_t *ps_decode_ip =
206         &s_hevcd_decode_ip.s_ivd_video_decode_ip_t;
207     ivd_video_decode_op_t *ps_decode_op =
208         &s_hevcd_decode_op.s_ivd_video_decode_op_t;
209 
210     // setup input/output arguments to decoder
211     setDecodeArgs(ps_decode_ip, ps_decode_op, frame, mStride,
212             0, // offset
213             inSize, // size
214             0 // time-stamp, does not matter
215             );
216 
217     setParams(mStride, IVD_DECODE_HEADER);
218 
219     // now kick off the decoding
220     IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
221     if (status != IV_SUCCESS) {
222         ALOGE("failed to call decoder function for header\n");
223         ALOGE("error in %s: 0x%x", __func__,
224               ps_decode_op->u4_error_code);
225         helperstatus = false;
226         return false;
227     }
228 
229     if (IVD_RES_CHANGED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
230         DDD("resolution changed, reset decoder");
231         resetDecoder();
232         setParams(mStride, IVD_DECODE_HEADER);
233         ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
234     }
235 
236     // get the w/h and update
237     if (0 < ps_decode_op->u4_pic_wd && 0 < ps_decode_op->u4_pic_ht) {
238         DDD("success decode w/h %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
239         DDD("existing w/h %d %d", mWidth, mHeight);
240         if (ps_decode_op->u4_pic_wd != mWidth ||  ps_decode_op->u4_pic_ht != mHeight) {
241             mWidth = ps_decode_op->u4_pic_wd;
242             mHeight = ps_decode_op->u4_pic_ht;
243             return true;
244         } else {
245             DDD("success decode w/h, but they are the same %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
246         }
247     } else {
248         ALOGE("could not decode w/h");
249     }
250 
251     // get output delay
252     if (ps_decode_op->i4_reorder_depth >= 0) {
253         if (mOutputDelay != ps_decode_op->i4_reorder_depth) {
254             mOutputDelay = ps_decode_op->i4_reorder_depth;
255             DDD("New Output delay %d ", mOutputDelay);
256         } else {
257             DDD("same Output delay %d ", mOutputDelay);
258         }
259     }
260 
261     return false;
262 }
263 
setDecodeArgs(ivd_video_decode_ip_t * ps_decode_ip,ivd_video_decode_op_t * ps_decode_op,const uint8_t * inBuffer,uint32_t displayStride,size_t inOffset,size_t inSize,uint32_t tsMarker)264 bool GoldfishHevcHelper::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
265                                        ivd_video_decode_op_t *ps_decode_op,
266                                        const uint8_t *inBuffer,
267                                        uint32_t displayStride, size_t inOffset,
268                                        size_t inSize, uint32_t tsMarker) {
269     uint32_t displayHeight = mHeight;
270     size_t lumaSize = displayStride * displayHeight;
271     size_t chromaSize = lumaSize >> 2;
272 
273     if (mStride != displayStride) {
274         mStride = displayStride;
275     }
276 
277     // force decoder to always decode header and get dimensions,
278     // hope this will be quick and cheap
279     setParams(mStride, IVD_DECODE_HEADER);
280 
281     ps_decode_ip->u4_size = sizeof(ihevcd_cxa_video_decode_ip_t);
282     ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
283     if (inBuffer) {
284         ps_decode_ip->u4_ts = tsMarker;
285         ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer) + inOffset;
286         ps_decode_ip->u4_num_Bytes = inSize;
287     } else {
288         ps_decode_ip->u4_ts = 0;
289         ps_decode_ip->pv_stream_buffer = nullptr;
290         ps_decode_ip->u4_num_Bytes = 0;
291     }
292     DDD("setting pv_stream_buffer 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
293             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[0],
294             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[1],
295             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[2],
296             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[3],
297             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[4],
298             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[5],
299             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[6],
300             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[7]
301             );
302     DDD("input bytes %d", ps_decode_ip->u4_num_Bytes);
303 
304     ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
305     ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
306     ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
307     {
308         ps_decode_ip->s_out_buffer.pu1_bufs[0] = nullptr;
309         ps_decode_ip->s_out_buffer.pu1_bufs[1] = nullptr;
310         ps_decode_ip->s_out_buffer.pu1_bufs[2] = nullptr;
311     }
312     ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
313     ps_decode_op->u4_size = sizeof(ihevcd_cxa_video_decode_op_t);
314     ps_decode_op->u4_output_present = 0;
315 
316     return true;
317 }
318 
319 } // namespace android
320