1 | /* |
2 | Copyright (c) 2012, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the following conditions are met: |
7 | * Redistributions of source code must retain the above copyright |
8 | notice, this list of conditions and the following disclaimer. |
9 | * Redistributions in binary form must reproduce the above copyright |
10 | notice, this list of conditions and the following disclaimer in the |
11 | documentation and/or other materials provided with the distribution. |
12 | * Neither the name of the copyright holder nor the |
13 | names of its contributors may be used to endorse or promote products |
14 | derived from this software without specific prior written permission. |
15 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | #include "interface/mmal/mmal.h" |
28 | #include "mmal_encodings.h" |
29 | #include "mmal_util.h" |
30 | #include "mmal_logging.h" |
31 | #include <string.h> |
32 | #include <stdio.h> |
33 | |
34 | #define STATUS_TO_STR(x) { MMAL_##x, #x } |
35 | |
36 | static struct { |
37 | MMAL_STATUS_T status; |
38 | const char *str; |
39 | } status_to_string_map[] = |
40 | { |
41 | STATUS_TO_STR(SUCCESS), |
42 | STATUS_TO_STR(ENOMEM), |
43 | STATUS_TO_STR(ENOSPC), |
44 | STATUS_TO_STR(EINVAL), |
45 | STATUS_TO_STR(ENOSYS), |
46 | STATUS_TO_STR(ENOENT), |
47 | STATUS_TO_STR(ENXIO), |
48 | STATUS_TO_STR(EIO), |
49 | STATUS_TO_STR(ESPIPE), |
50 | STATUS_TO_STR(ECORRUPT), |
51 | STATUS_TO_STR(ENOTREADY), |
52 | STATUS_TO_STR(ECONFIG), |
53 | {0, 0} |
54 | }; |
55 | |
56 | const char *mmal_status_to_string(MMAL_STATUS_T status) |
57 | { |
58 | unsigned i; |
59 | |
60 | for (i=0; status_to_string_map[i].str; i++) |
61 | if (status_to_string_map[i].status == status) |
62 | break; |
63 | |
64 | return status_to_string_map[i].str ? status_to_string_map[i].str : "UNKNOWN" ; |
65 | } |
66 | |
67 | static struct { |
68 | uint32_t encoding; |
69 | uint32_t pitch_num; |
70 | uint32_t pitch_den; |
71 | uint32_t alignment; |
72 | } pixel_pitch[] = |
73 | { |
74 | {MMAL_ENCODING_I420, 1, 1, 1}, |
75 | {MMAL_ENCODING_YV12, 1, 1, 1}, |
76 | {MMAL_ENCODING_I422, 1, 1, 1}, |
77 | {MMAL_ENCODING_NV21, 1, 1, 1}, |
78 | {MMAL_ENCODING_NV12, 1, 1, 1}, |
79 | {MMAL_ENCODING_ARGB, 4, 1, 1}, |
80 | {MMAL_ENCODING_RGBA, 4, 1, 1}, |
81 | {MMAL_ENCODING_RGB32, 4, 1, 1}, |
82 | {MMAL_ENCODING_ABGR, 4, 1, 1}, |
83 | {MMAL_ENCODING_BGRA, 4, 1, 1}, |
84 | {MMAL_ENCODING_BGR32, 4, 1, 1}, |
85 | {MMAL_ENCODING_RGB16, 2, 1, 1}, |
86 | {MMAL_ENCODING_RGB24, 3, 1, 1}, |
87 | {MMAL_ENCODING_BGR16, 2, 1, 1}, |
88 | {MMAL_ENCODING_BGR24, 3, 1, 1}, |
89 | {MMAL_ENCODING_I420_16, 2, 1, 1}, |
90 | {MMAL_ENCODING_I420_10, 2, 1, 1}, |
91 | {MMAL_ENCODING_I420_S, 1, 1, 1}, |
92 | |
93 | {MMAL_ENCODING_I420_SLICE, 1, 1, 1}, |
94 | {MMAL_ENCODING_I422_SLICE, 1, 1, 1}, |
95 | {MMAL_ENCODING_ARGB_SLICE, 4, 1, 1}, |
96 | {MMAL_ENCODING_RGBA_SLICE, 4, 1, 1}, |
97 | {MMAL_ENCODING_RGB32_SLICE, 4, 1, 1}, |
98 | {MMAL_ENCODING_ABGR_SLICE, 4, 1, 1}, |
99 | {MMAL_ENCODING_BGRA_SLICE, 4, 1, 1}, |
100 | {MMAL_ENCODING_BGR32_SLICE, 4, 1, 1}, |
101 | {MMAL_ENCODING_RGB16_SLICE, 2, 1, 1}, |
102 | {MMAL_ENCODING_RGB24_SLICE, 3, 1, 1}, |
103 | {MMAL_ENCODING_BGR16_SLICE, 2, 1, 1}, |
104 | {MMAL_ENCODING_BGR24_SLICE, 3, 1, 1}, |
105 | |
106 | {MMAL_ENCODING_YUYV, 2, 1, 1}, |
107 | {MMAL_ENCODING_YVYU, 2, 1, 1}, |
108 | {MMAL_ENCODING_UYVY, 2, 1, 1}, |
109 | {MMAL_ENCODING_VYUY, 2, 1, 1}, |
110 | |
111 | // Bayer formats, the resulting alignment must also be a multiple of 16. |
112 | // Camplus padded to a multiple of 32, so let's copy that. |
113 | {MMAL_ENCODING_BAYER_SBGGR8, 1, 1, 32}, |
114 | {MMAL_ENCODING_BAYER_SGBRG8, 1, 1, 32}, |
115 | {MMAL_ENCODING_BAYER_SGRBG8, 1, 1, 32}, |
116 | {MMAL_ENCODING_BAYER_SRGGB8, 1, 1, 32}, |
117 | {MMAL_ENCODING_BAYER_SBGGR10DPCM8, 1, 1, 32}, |
118 | {MMAL_ENCODING_BAYER_SGBRG10DPCM8, 1, 1, 32}, |
119 | {MMAL_ENCODING_BAYER_SGRBG10DPCM8, 1, 1, 32}, |
120 | {MMAL_ENCODING_BAYER_SRGGB10DPCM8, 1, 1, 32}, |
121 | {MMAL_ENCODING_BAYER_SBGGR10P, 10,8, 32}, |
122 | {MMAL_ENCODING_BAYER_SGRBG10P, 10,8, 32}, |
123 | {MMAL_ENCODING_BAYER_SGBRG10P, 10,8, 32}, |
124 | {MMAL_ENCODING_BAYER_SRGGB10P, 10,8, 32}, |
125 | {MMAL_ENCODING_BAYER_SBGGR12P, 12,8, 32}, |
126 | {MMAL_ENCODING_BAYER_SGRBG12P, 12,8, 32}, |
127 | {MMAL_ENCODING_BAYER_SGBRG12P, 12,8, 32}, |
128 | {MMAL_ENCODING_BAYER_SRGGB12P, 12,8, 32}, |
129 | {MMAL_ENCODING_BAYER_SBGGR16, 2, 1, 32}, |
130 | {MMAL_ENCODING_BAYER_SGBRG16, 2, 1, 32}, |
131 | {MMAL_ENCODING_BAYER_SGRBG16, 2, 1, 32}, |
132 | {MMAL_ENCODING_BAYER_SRGGB16, 2, 1, 32}, |
133 | |
134 | /* {MMAL_ENCODING_YUVUV128, 1, 1}, That's a special case which must not be included */ |
135 | /* {MMAL_ENCODING_YUVUV64_16, 1, 1}, That's a special case which must not be included */ |
136 | /* {MMAL_ENCODING_YUVUV64_10, 1, 1}, That's a special case which must not be included */ |
137 | /* {MMAL_ENCODING_YUV10_COL, 1, 1}, That's a special case which must not be included */ |
138 | {MMAL_ENCODING_UNKNOWN, 0, 0} |
139 | }; |
140 | |
141 | static struct { |
142 | uint32_t encoding; |
143 | uint32_t sliced_encoding; |
144 | } slice_equivalents[] = |
145 | { |
146 | { MMAL_ENCODING_I420, MMAL_ENCODING_I420_SLICE }, |
147 | { MMAL_ENCODING_I422, MMAL_ENCODING_I422_SLICE }, |
148 | { MMAL_ENCODING_ARGB, MMAL_ENCODING_ARGB_SLICE }, |
149 | { MMAL_ENCODING_RGBA, MMAL_ENCODING_RGBA_SLICE }, |
150 | { MMAL_ENCODING_RGB32, MMAL_ENCODING_RGB32_SLICE }, |
151 | { MMAL_ENCODING_ABGR, MMAL_ENCODING_ABGR_SLICE }, |
152 | { MMAL_ENCODING_BGRA, MMAL_ENCODING_BGRA_SLICE }, |
153 | { MMAL_ENCODING_BGR32, MMAL_ENCODING_BGR32_SLICE }, |
154 | { MMAL_ENCODING_RGB16, MMAL_ENCODING_RGB16_SLICE }, |
155 | { MMAL_ENCODING_RGB24, MMAL_ENCODING_RGB24_SLICE }, |
156 | { MMAL_ENCODING_BGR16, MMAL_ENCODING_BGR16_SLICE }, |
157 | { MMAL_ENCODING_BGR24, MMAL_ENCODING_BGR24_SLICE }, |
158 | { MMAL_ENCODING_UNKNOWN, MMAL_ENCODING_UNKNOWN }, |
159 | }; |
160 | |
161 | |
162 | uint32_t mmal_encoding_stride_to_width(uint32_t encoding, uint32_t stride) |
163 | { |
164 | unsigned int i; |
165 | |
166 | for(i = 0; pixel_pitch[i].encoding != MMAL_ENCODING_UNKNOWN; i++) |
167 | if(pixel_pitch[i].encoding == encoding) break; |
168 | |
169 | if(pixel_pitch[i].encoding == MMAL_ENCODING_UNKNOWN) |
170 | return 0; |
171 | |
172 | return pixel_pitch[i].pitch_den * stride / pixel_pitch[i].pitch_num; |
173 | } |
174 | |
175 | uint32_t mmal_encoding_width_to_stride(uint32_t encoding, uint32_t width) |
176 | { |
177 | unsigned int i; |
178 | |
179 | for(i = 0; pixel_pitch[i].encoding != MMAL_ENCODING_UNKNOWN; i++) |
180 | if(pixel_pitch[i].encoding == encoding) break; |
181 | |
182 | if(pixel_pitch[i].encoding == MMAL_ENCODING_UNKNOWN) |
183 | return 0; |
184 | |
185 | return VCOS_ALIGN_UP(pixel_pitch[i].pitch_num * width / pixel_pitch[i].pitch_den, pixel_pitch[i].alignment); |
186 | } |
187 | |
188 | uint32_t mmal_encoding_get_slice_variant(uint32_t encoding) |
189 | { |
190 | unsigned int i; |
191 | |
192 | for(i = 0; slice_equivalents[i].encoding != MMAL_ENCODING_UNKNOWN; i++) |
193 | if(slice_equivalents[i].encoding == encoding) break; |
194 | |
195 | return slice_equivalents[i].sliced_encoding; |
196 | } |
197 | |
198 | const char* mmal_port_type_to_string(MMAL_PORT_TYPE_T type) |
199 | { |
200 | const char *str; |
201 | |
202 | switch (type) |
203 | { |
204 | case MMAL_PORT_TYPE_INPUT: str = "in" ; break; |
205 | case MMAL_PORT_TYPE_OUTPUT: str = "out" ; break; |
206 | case MMAL_PORT_TYPE_CLOCK: str = "clk" ; break; |
207 | case MMAL_PORT_TYPE_CONTROL: str = "ctr" ; break; |
208 | default: str = "invalid" ; break; |
209 | } |
210 | |
211 | return str; |
212 | } |
213 | |
214 | MMAL_PARAMETER_HEADER_T *mmal_port_parameter_alloc_get(MMAL_PORT_T *port, |
215 | uint32_t id, uint32_t size, MMAL_STATUS_T *p_status) |
216 | { |
217 | MMAL_PARAMETER_HEADER_T *param = NULL; |
218 | MMAL_STATUS_T status = MMAL_ENOSYS; |
219 | |
220 | if (size < sizeof(MMAL_PARAMETER_HEADER_T)) |
221 | size = sizeof(MMAL_PARAMETER_HEADER_T); |
222 | |
223 | if ((param = vcos_calloc(1, size, "mmal_port_param_get" )) == NULL) |
224 | { |
225 | status = MMAL_ENOMEM; |
226 | goto error; |
227 | } |
228 | |
229 | param->id = id; |
230 | param->size = size; |
231 | |
232 | if ((status = mmal_port_parameter_get(port, param)) == MMAL_ENOSPC) |
233 | { |
234 | /* We need to reallocate to get enough space for all parameter data */ |
235 | size = param->size; |
236 | vcos_free(param); |
237 | if ((param = vcos_calloc(1, size, "mmal_port_param_get" )) == NULL) |
238 | { |
239 | status = MMAL_ENOMEM; |
240 | goto error; |
241 | } |
242 | |
243 | /* Now retrieve it again */ |
244 | param->id = id; |
245 | param->size = size; |
246 | status = mmal_port_parameter_get(port, param); |
247 | } |
248 | |
249 | if (status != MMAL_SUCCESS) |
250 | goto error; |
251 | |
252 | end: |
253 | if (p_status) *p_status = status; |
254 | return param; |
255 | error: |
256 | if (param) vcos_free(param); |
257 | param = NULL; |
258 | goto end; |
259 | } |
260 | |
261 | void mmal_port_parameter_free(MMAL_PARAMETER_HEADER_T *param) |
262 | { |
263 | vcos_free(param); |
264 | } |
265 | |
266 | /** Copy buffer header metadata from source to dest |
267 | */ |
268 | void (MMAL_BUFFER_HEADER_T *dest, const MMAL_BUFFER_HEADER_T *src) |
269 | { |
270 | dest->cmd = src->cmd; |
271 | dest->offset = src->offset; |
272 | dest->length = src->length; |
273 | dest->flags = src->flags; |
274 | dest->pts = src->pts; |
275 | dest->dts = src->dts; |
276 | *dest->type = *src->type; |
277 | } |
278 | |
279 | /** Create a pool of MMAL_BUFFER_HEADER_T */ |
280 | MMAL_POOL_T *mmal_port_pool_create(MMAL_PORT_T *port, unsigned int , uint32_t payload_size) |
281 | { |
282 | if (!port || !port->priv) |
283 | return NULL; |
284 | |
285 | LOG_TRACE("%s(%i:%i) port %p, headers %u, size %i" , port->component->name, |
286 | (int)port->type, (int)port->index, port, headers, (int)payload_size); |
287 | |
288 | /* Create a pool and ask the port for some memory */ |
289 | return mmal_pool_create_with_allocator(headers, payload_size, (void *)port, |
290 | (mmal_pool_allocator_alloc_t)mmal_port_payload_alloc, |
291 | (mmal_pool_allocator_free_t)mmal_port_payload_free); |
292 | } |
293 | |
294 | /** Destroy a pool of MMAL_BUFFER_HEADER_T */ |
295 | void mmal_port_pool_destroy(MMAL_PORT_T *port, MMAL_POOL_T *pool) |
296 | { |
297 | if (!port || !port->priv || !pool) |
298 | return; |
299 | |
300 | LOG_TRACE("%s(%i:%i) port %p, pool %p" , port->component->name, |
301 | (int)port->type, (int)port->index, port, pool); |
302 | |
303 | if (!vcos_verify(!port->is_enabled)) |
304 | { |
305 | LOG_ERROR("port %p, pool %p destroyed while port enabled" , port, pool); |
306 | mmal_port_disable(port); |
307 | } |
308 | |
309 | mmal_pool_destroy(pool); |
310 | } |
311 | |
312 | /*****************************************************************************/ |
313 | void mmal_log_dump_port(MMAL_PORT_T *port) |
314 | { |
315 | if (!port) |
316 | return; |
317 | |
318 | LOG_DEBUG("%s(%p)" , port->name, port); |
319 | |
320 | mmal_log_dump_format(port->format); |
321 | |
322 | LOG_DEBUG(" buffers num: %i(opt %i, min %i), size: %i(opt %i, min: %i), align: %i" , |
323 | port->buffer_num, port->buffer_num_recommended, port->buffer_num_min, |
324 | port->buffer_size, port->buffer_size_recommended, port->buffer_size_min, |
325 | port->buffer_alignment_min); |
326 | } |
327 | |
328 | /*****************************************************************************/ |
329 | void mmal_log_dump_format(MMAL_ES_FORMAT_T *format) |
330 | { |
331 | const char *name_type; |
332 | |
333 | if (!format) |
334 | return; |
335 | |
336 | switch(format->type) |
337 | { |
338 | case MMAL_ES_TYPE_AUDIO: name_type = "audio" ; break; |
339 | case MMAL_ES_TYPE_VIDEO: name_type = "video" ; break; |
340 | case MMAL_ES_TYPE_SUBPICTURE: name_type = "subpicture" ; break; |
341 | default: name_type = "unknown" ; break; |
342 | } |
343 | |
344 | LOG_DEBUG("type: %s, fourcc: %4.4s" , name_type, (char *)&format->encoding); |
345 | LOG_DEBUG(" bitrate: %i, framed: %i" , format->bitrate, |
346 | !!(format->flags & MMAL_ES_FORMAT_FLAG_FRAMED)); |
347 | LOG_DEBUG(" extra data: %i, %p" , format->extradata_size, format->extradata); |
348 | switch(format->type) |
349 | { |
350 | case MMAL_ES_TYPE_AUDIO: |
351 | LOG_DEBUG(" samplerate: %i, channels: %i, bps: %i, block align: %i" , |
352 | format->es->audio.sample_rate, format->es->audio.channels, |
353 | format->es->audio.bits_per_sample, format->es->audio.block_align); |
354 | break; |
355 | |
356 | case MMAL_ES_TYPE_VIDEO: |
357 | LOG_DEBUG(" width: %i, height: %i, (%i,%i,%i,%i)" , |
358 | format->es->video.width, format->es->video.height, |
359 | format->es->video.crop.x, format->es->video.crop.y, |
360 | format->es->video.crop.width, format->es->video.crop.height); |
361 | LOG_DEBUG(" pixel aspect ratio: %i/%i, frame rate: %i/%i" , |
362 | format->es->video.par.num, format->es->video.par.den, |
363 | format->es->video.frame_rate.num, format->es->video.frame_rate.den); |
364 | break; |
365 | |
366 | case MMAL_ES_TYPE_SUBPICTURE: |
367 | break; |
368 | |
369 | default: break; |
370 | } |
371 | } |
372 | |
373 | MMAL_PORT_T *mmal_util_get_port(MMAL_COMPONENT_T *comp, MMAL_PORT_TYPE_T type, unsigned index) |
374 | { |
375 | unsigned num; |
376 | MMAL_PORT_T **list; |
377 | |
378 | switch (type) |
379 | { |
380 | case MMAL_PORT_TYPE_INPUT: |
381 | num = comp->input_num; |
382 | list = comp->input; |
383 | break; |
384 | |
385 | case MMAL_PORT_TYPE_OUTPUT: |
386 | num = comp->output_num; |
387 | list = comp->output; |
388 | break; |
389 | |
390 | case MMAL_PORT_TYPE_CLOCK: |
391 | num = comp->clock_num; |
392 | list = comp->clock; |
393 | break; |
394 | |
395 | case MMAL_PORT_TYPE_CONTROL: |
396 | num = 1; |
397 | list = &comp->control; |
398 | break; |
399 | |
400 | default: |
401 | vcos_assert(0); |
402 | return NULL; |
403 | } |
404 | if (index < num) |
405 | /* coverity[ptr_arith] num is 1 here */ |
406 | return list[index]; |
407 | else |
408 | return NULL; |
409 | } |
410 | |
411 | char *mmal_4cc_to_string(char *buf, size_t len, uint32_t fourcc) |
412 | { |
413 | char *src = (char*)&fourcc; |
414 | vcos_assert(len >= 5); |
415 | if (len < 5) |
416 | { |
417 | buf[0] = '\0'; |
418 | } |
419 | else if (fourcc) |
420 | { |
421 | memcpy(buf, src, 4); |
422 | buf[4] = '\0'; |
423 | } |
424 | else |
425 | { |
426 | snprintf(buf, len, "<0>" ); |
427 | } |
428 | return buf; |
429 | } |
430 | |
431 | #define MAX_ENCODINGS_NUM 25 |
432 | typedef struct { |
433 | MMAL_PARAMETER_HEADER_T ; |
434 | MMAL_FOURCC_T encodings[MAX_ENCODINGS_NUM]; |
435 | } MMAL_SUPPORTED_ENCODINGS_T; |
436 | |
437 | |
438 | int mmal_util_rgb_order_fixed(MMAL_PORT_T *port) |
439 | { |
440 | int new_fw = 0; |
441 | MMAL_STATUS_T ret; |
442 | //Firmware support of RGB24 vs BGR24 colour ordering from camera |
443 | //and video splitter components has been corrected as of June 2016. |
444 | //New firmwares always report MMAL_ENCODING_RGB24 before BGR24, and |
445 | //that is the format we want. |
446 | //Old firmware reported BGR24 first, and also returned an error on |
447 | //the still port on querying MMAL_PARAMETER_SUPPORTED_ENCODINGS. |
448 | |
449 | MMAL_SUPPORTED_ENCODINGS_T sup_encodings = {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(sup_encodings)}, {0}}; |
450 | ret = mmal_port_parameter_get(port, &sup_encodings.header); |
451 | if (ret == MMAL_SUCCESS || ret == MMAL_ENOSPC) |
452 | { |
453 | //Allow ENOSPC error and hope that the desired formats are in the first |
454 | //MAX_ENCODINGS_NUM entries. |
455 | int i; |
456 | int num_encodings = (sup_encodings.header.size - sizeof(sup_encodings.header)) / |
457 | sizeof(sup_encodings.encodings[0]); |
458 | if(num_encodings > MAX_ENCODINGS_NUM) |
459 | num_encodings = MAX_ENCODINGS_NUM; |
460 | for (i=0; i<num_encodings; i++) |
461 | { |
462 | if (sup_encodings.encodings[i] == MMAL_ENCODING_BGR24) |
463 | { |
464 | //Found BGR24 first - old firmware. |
465 | break; |
466 | } |
467 | if (sup_encodings.encodings[i] == MMAL_ENCODING_RGB24) |
468 | { |
469 | //Found RGB24 first - new firmware, so use RGB24. |
470 | new_fw = 1; |
471 | } |
472 | } |
473 | } |
474 | return new_fw; |
475 | } |
476 | |