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 "GoldfishH264Helper.h"
18 
19 #define LOG_TAG "GoldfishH264Helper"
20 #include <log/log.h>
21 
22 #define DEBUG 0
23 #if DEBUG
24 #define DDD(fmt, ...) ALOGD("%s %d:" fmt, __func__, __LINE__, ##__VA_ARGS__)
25 #else
26 #define DDD(...) ((void)0)
27 #endif
28 
29 
30 #include <Codec2Mapper.h>
31 
32 #define ivdec_api_function              ih264d_api_function
33 #define ivdext_create_ip_t              ih264d_create_ip_t
34 #define ivdext_create_op_t              ih264d_create_op_t
35 #define ivdext_delete_ip_t              ih264d_delete_ip_t
36 #define ivdext_delete_op_t              ih264d_delete_op_t
37 #define ivdext_ctl_set_num_cores_ip_t   ih264d_ctl_set_num_cores_ip_t
38 #define ivdext_ctl_set_num_cores_op_t   ih264d_ctl_set_num_cores_op_t
39 #define ivdext_ctl_get_vui_params_ip_t  ih264d_ctl_get_vui_params_ip_t
40 #define ivdext_ctl_get_vui_params_op_t  ih264d_ctl_get_vui_params_op_t
41 #define ALIGN128(x)                     ((((x) + 127) >> 7) << 7)
42 #define MAX_NUM_CORES                   4
43 #define IVDEXT_CMD_CTL_SET_NUM_CORES    \
44         (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
45 #define MIN(a, b)                       (((a) < (b)) ? (a) : (b))
46 
47 namespace android {
48 
ivd_aligned_malloc(void * ctxt,WORD32 alignment,WORD32 size)49 static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
50     (void) ctxt;
51     return memalign(alignment, size);
52 }
53 
ivd_aligned_free(void * ctxt,void * mem)54 static void ivd_aligned_free(void *ctxt, void *mem) {
55     (void) ctxt;
56     free(mem);
57 }
58 
59 
GoldfishH264Helper(int w,int h)60 GoldfishH264Helper::GoldfishH264Helper(int w, int h):mWidth(w),mHeight(h) { createDecoder(); }
61 
~GoldfishH264Helper()62 GoldfishH264Helper::~GoldfishH264Helper() {
63     destroyDecoder();
64 }
65 
createDecoder()66 void GoldfishH264Helper::createDecoder() {
67     ivdext_create_ip_t s_create_ip = {};
68     ivdext_create_op_t s_create_op = {};
69 
70     s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
71     s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
72     s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
73     s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorformat;
74     s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc;
75     s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free;
76     s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = nullptr;
77     s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t);
78     IV_API_CALL_STATUS_T status =
79         ivdec_api_function(mDecHandle, &s_create_ip, &s_create_op);
80     if (status != IV_SUCCESS) {
81         ALOGE("error in %s: 0x%x", __func__,
82               s_create_op.s_ivd_create_op_t.u4_error_code);
83         return;
84     }
85     mDecHandle = (iv_obj_t *)s_create_op.s_ivd_create_op_t.pv_handle;
86     mDecHandle->pv_fxns = (void *)ivdec_api_function;
87     mDecHandle->u4_size = sizeof(iv_obj_t);
88 
89     mStride = ALIGN128(mWidth);
90 
91     setNumCores();
92 }
93 
destroyDecoder()94 void GoldfishH264Helper::destroyDecoder() {
95     if (mDecHandle) {
96         ivdext_delete_ip_t s_delete_ip = {};
97         ivdext_delete_op_t s_delete_op = {};
98 
99         s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
100         s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;
101         s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t);
102         IV_API_CALL_STATUS_T status =
103             ivdec_api_function(mDecHandle, &s_delete_ip, &s_delete_op);
104         if (status != IV_SUCCESS) {
105             ALOGE("error in %s: 0x%x", __func__,
106                   s_delete_op.s_ivd_delete_op_t.u4_error_code);
107         }
108         mDecHandle = nullptr;
109     }
110 }
111 
setNumCores()112 void GoldfishH264Helper::setNumCores() {
113     ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip = {};
114     ivdext_ctl_set_num_cores_op_t s_set_num_cores_op = {};
115 
116     s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
117     s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
118     s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
119     s_set_num_cores_ip.u4_num_cores = mNumCores;
120     s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
121     IV_API_CALL_STATUS_T status = ivdec_api_function(
122         mDecHandle, &s_set_num_cores_ip, &s_set_num_cores_op);
123     if (IV_SUCCESS != status) {
124         DDD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code);
125     }
126 }
127 
resetDecoder()128 void GoldfishH264Helper::resetDecoder() {
129     ivd_ctl_reset_ip_t s_reset_ip = {};
130     ivd_ctl_reset_op_t s_reset_op = {};
131 
132     s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
133     s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
134     s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
135     s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t);
136     IV_API_CALL_STATUS_T status =
137         ivdec_api_function(mDecHandle, &s_reset_ip, &s_reset_op);
138     if (IV_SUCCESS != status) {
139         ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code);
140     }
141     setNumCores();
142 }
143 
setParams(size_t stride,IVD_VIDEO_DECODE_MODE_T dec_mode)144 void GoldfishH264Helper::setParams(size_t stride,
145                                    IVD_VIDEO_DECODE_MODE_T dec_mode) {
146     ih264d_ctl_set_config_ip_t s_h264d_set_dyn_params_ip = {};
147     ih264d_ctl_set_config_op_t s_h264d_set_dyn_params_op = {};
148     ivd_ctl_set_config_ip_t *ps_set_dyn_params_ip =
149         &s_h264d_set_dyn_params_ip.s_ivd_ctl_set_config_ip_t;
150     ivd_ctl_set_config_op_t *ps_set_dyn_params_op =
151         &s_h264d_set_dyn_params_op.s_ivd_ctl_set_config_op_t;
152 
153     ps_set_dyn_params_ip->u4_size = sizeof(ih264d_ctl_set_config_ip_t);
154     ps_set_dyn_params_ip->e_cmd = IVD_CMD_VIDEO_CTL;
155     ps_set_dyn_params_ip->e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
156     ps_set_dyn_params_ip->u4_disp_wd = (UWORD32) stride;
157     ps_set_dyn_params_ip->e_frm_skip_mode = IVD_SKIP_NONE;
158     ps_set_dyn_params_ip->e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
159     ps_set_dyn_params_ip->e_vid_dec_mode = dec_mode;
160     ps_set_dyn_params_op->u4_size = sizeof(ih264d_ctl_set_config_op_t);
161     IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
162                                                      &s_h264d_set_dyn_params_ip,
163                                                      &s_h264d_set_dyn_params_op);
164     if (status != IV_SUCCESS) {
165         ALOGE("error in %s: 0x%x", __func__,
166               ps_set_dyn_params_op->u4_error_code);
167     }
168 }
169 
isSpsFrame(const uint8_t * frame,int inSize)170 bool GoldfishH264Helper::isSpsFrame(const uint8_t* frame, int inSize) {
171     if (inSize < 5) return false;
172     if (frame[0] == 0 && frame[1] == 0 && frame[2] == 0 && frame[3] == 1) {
173         const bool forbiddenBitIsInvalid = 0x80 & frame[4];
174         if (forbiddenBitIsInvalid) {
175             return false;
176         }
177         // nalu type is the lower 5 bits
178         uint8_t naluType = 0x1f & frame[4];
179         if (naluType == 7
180             || naluType == 8
181                 ) return true;
182         else return false;
183     } else {
184         return false;
185     }
186 }
187 
decodeHeader(const uint8_t * frame,int inSize)188 bool GoldfishH264Helper::decodeHeader(const uint8_t *frame, int inSize) {
189     DDD("entering");
190     // should we check the header for vps/sps/pps frame ? otherwise
191     // there is no point calling decoder
192     if (!isSpsFrame(frame, inSize)) {
193         DDD("could not find valid vps frame");
194         DDD("leaving with false");
195         return false;
196     } else {
197         DDD("found valid vps frame");
198     }
199 
200     ih264d_video_decode_ip_t s_h264d_decode_ip = {};
201     ih264d_video_decode_op_t s_h264d_decode_op = {};
202     ivd_video_decode_ip_t *ps_decode_ip = &s_h264d_decode_ip.s_ivd_video_decode_ip_t;
203     ivd_video_decode_op_t *ps_decode_op = &s_h264d_decode_op.s_ivd_video_decode_op_t;
204 
205     // setup input/output arguments to decoder
206     setDecodeArgs(ps_decode_ip, ps_decode_op, frame, mStride,
207             0, // offset
208             inSize, // size
209             0 // time-stamp, does not matter
210             );
211 
212     setParams(mStride, IVD_DECODE_HEADER);
213 
214     // now kick off the decoding
215     ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
216 
217     if (IVD_RES_CHANGED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
218         DDD("resolution changed, reset decoder");
219         resetDecoder();
220         setParams(mStride, IVD_DECODE_HEADER);
221         ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
222     }
223 
224     // get the w/h and update
225     if (0 < ps_decode_op->u4_pic_wd && 0 < ps_decode_op->u4_pic_ht) {
226         DDD("success decode w/h %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
227         DDD("existing w/h %d %d", mWidth, mHeight);
228         if (ps_decode_op->u4_pic_wd != mWidth ||  ps_decode_op->u4_pic_ht != mHeight) {
229             mWidth = ps_decode_op->u4_pic_wd;
230             mHeight = ps_decode_op->u4_pic_ht;
231             DDD("leaving with true");
232             return true;
233         } else {
234             DDD("success decode w/h, but they are the same %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
235         }
236     }
237 
238     // get output delay
239     if (ps_decode_op->i4_reorder_depth >= 0) {
240         if (mOutputDelay != ps_decode_op->i4_reorder_depth) {
241             mOutputDelay = ps_decode_op->i4_reorder_depth;
242             DDD("New Output delay %d ", mOutputDelay);
243         } else {
244             DDD("same Output delay %d ", mOutputDelay);
245         }
246     }
247 
248     DDD("leaving with false");
249     return false;
250 }
251 
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)252 bool GoldfishH264Helper::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
253                                        ivd_video_decode_op_t *ps_decode_op,
254                                        const uint8_t *inBuffer,
255                                        uint32_t displayStride, size_t inOffset,
256                                        size_t inSize, uint32_t tsMarker) {
257     uint32_t displayHeight = mHeight;
258     size_t lumaSize = displayStride * displayHeight;
259     size_t chromaSize = lumaSize >> 2;
260 
261     if (mStride != displayStride) {
262         mStride = displayStride;
263     }
264 
265     // force decoder to always decode header and get dimensions,
266     // hope this will be quick and cheap
267     setParams(mStride, IVD_DECODE_HEADER);
268 
269     ps_decode_ip->u4_size = sizeof(ih264d_video_decode_ip_t);
270     ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
271     if (inBuffer) {
272         ps_decode_ip->u4_ts = tsMarker;
273         ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer) + inOffset;
274         ps_decode_ip->u4_num_Bytes = inSize;
275     } else {
276         ps_decode_ip->u4_ts = 0;
277         ps_decode_ip->pv_stream_buffer = nullptr;
278         ps_decode_ip->u4_num_Bytes = 0;
279     }
280     DDD("setting pv_stream_buffer 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
281             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[0],
282             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[1],
283             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[2],
284             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[3],
285             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[4],
286             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[5],
287             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[6],
288             ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[7]
289             );
290     DDD("input bytes %d", ps_decode_ip->u4_num_Bytes);
291 
292     ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
293     ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
294     ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
295     {
296         ps_decode_ip->s_out_buffer.pu1_bufs[0] = nullptr;
297         ps_decode_ip->s_out_buffer.pu1_bufs[1] = nullptr;
298         ps_decode_ip->s_out_buffer.pu1_bufs[2] = nullptr;
299     }
300     ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
301     ps_decode_op->u4_size = sizeof(ih264d_video_decode_op_t);
302     ps_decode_op->u4_output_present = 0;
303 
304     return true;
305 }
306 
307 } // namespace android
308