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