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 <memory.h> |
28 | #include <stdio.h> |
29 | #include <stdlib.h> |
30 | #include <string.h> |
31 | #include <fcntl.h> |
32 | #include <stdarg.h> |
33 | |
34 | #include "mmalcam.h" |
35 | |
36 | #include "interface/mmal/mmal.h" |
37 | #include "interface/mmal/mmal_logging.h" |
38 | #include "interface/mmal/util/mmal_util.h" |
39 | #include "interface/mmal/util/mmal_default_components.h" |
40 | |
41 | #define USE_CONTAINER 0 |
42 | |
43 | #if USE_CONTAINER |
44 | #include "containers/containers.h" |
45 | #include "containers/core/containers_utils.h" // FIXME |
46 | #include "containers/containers_codecs.h" |
47 | #endif |
48 | |
49 | /** Number of buffers we want to use for video render. Video render needs at least 2. */ |
50 | #define VIDEO_OUTPUT_BUFFERS_NUM 3 |
51 | |
52 | /** After this many packets, the container (if any) will be closed and we |
53 | * start discarding encoded packets. |
54 | */ |
55 | #define MAX_PACKET_COUNT 150 |
56 | |
57 | /** Initialise a parameter structure */ |
58 | #define INIT_PARAMETER(PARAM, PARAM_ID) \ |
59 | do { \ |
60 | memset(&(PARAM), 0, sizeof(PARAM)); \ |
61 | (PARAM).hdr.id = PARAM_ID; \ |
62 | (PARAM).hdr.size = sizeof(PARAM); \ |
63 | } while (0) |
64 | |
65 | /* Utility functions to manipulate containers */ |
66 | #if USE_CONTAINER |
67 | static VC_CONTAINER_T *test_container_open(const char *uri, MMAL_ES_FORMAT_T* format, MMAL_STATUS_T *status); |
68 | static MMAL_STATUS_T test_container_write(VC_CONTAINER_T *container, MMAL_BUFFER_HEADER_T *buffer); |
69 | static VC_CONTAINER_FOURCC_T test_container_encoding_to_codec(uint32_t encoding); |
70 | #endif |
71 | |
72 | /* Utility function to create and setup the camera viewfinder component */ |
73 | static MMAL_COMPONENT_T *test_camera_create(MMALCAM_BEHAVIOUR_T *behaviour, MMAL_STATUS_T *status); |
74 | static MMAL_BOOL_T mmalcam_next_effect(MMAL_COMPONENT_T *camera); |
75 | static MMAL_BOOL_T mmalcam_next_rotation(MMAL_COMPONENT_T *camera); |
76 | static MMAL_BOOL_T mmalcam_next_zoom(MMAL_COMPONENT_T *camera); |
77 | static MMAL_BOOL_T mmalcam_next_focus(MMAL_COMPONENT_T *camera); |
78 | static MMAL_BOOL_T mmalcam_reset_focus(MMAL_COMPONENT_T *camera, MMAL_PARAM_FOCUS_T focus_setting); |
79 | static MMAL_BOOL_T mmalcam_next_drc(MMAL_COMPONENT_T *camera); |
80 | static MMAL_BOOL_T mmalcam_next_hdr(MMAL_COMPONENT_T *camera); |
81 | static MMAL_BOOL_T mmalcam_next_colour_param(MMAL_COMPONENT_T *camera, uint32_t id, int min, int max, const char *param_name); |
82 | |
83 | /* Utility function to create and setup the video render component */ |
84 | static MMAL_COMPONENT_T *test_video_render_create(MMALCAM_BEHAVIOUR_T *behaviour, MMAL_STATUS_T *status); |
85 | |
86 | /* Utility function to create and setup the video encoder component */ |
87 | static MMAL_COMPONENT_T *test_video_encoder_create(MMALCAM_BEHAVIOUR_T *behaviour, MMAL_STATUS_T *status); |
88 | |
89 | /*****************************************************************************/ |
90 | |
91 | typedef enum { |
92 | MMAL_CAM_BUFFER_READY = 1 << 0, |
93 | MMAL_CAM_AUTOFOCUS_COMPLETE = 1 << 1, |
94 | MMAL_CAM_ANY_EVENT = 0x7FFFFFFF |
95 | } MMAL_CAM_EVENT_T; |
96 | |
97 | static VCOS_EVENT_FLAGS_T events; |
98 | VCOS_LOG_CAT_T mmalcam_log_category; |
99 | static MMAL_BOOL_T zero_copy; |
100 | static MMAL_BOOL_T tunneling; |
101 | |
102 | static MMAL_BOOL_T enable_zero_copy(void) |
103 | { |
104 | return zero_copy; |
105 | } |
106 | |
107 | static MMAL_BOOL_T enable_tunneling(void) |
108 | { |
109 | return tunneling; |
110 | } |
111 | |
112 | /* Buffer header callbacks */ |
113 | static void control_bh_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
114 | { |
115 | LOG_DEBUG("control_bh_cb %p,%p (cmd=0x%08x)" , port, buffer, buffer->cmd); |
116 | if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED) |
117 | { |
118 | MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data; |
119 | |
120 | vcos_assert(buffer->length >= sizeof(MMAL_EVENT_PARAMETER_CHANGED_T)); |
121 | vcos_assert(buffer->length == param->hdr.size); |
122 | switch (param->hdr.id) |
123 | { |
124 | case MMAL_PARAMETER_FOCUS_STATUS: |
125 | vcos_assert(param->hdr.size == sizeof(MMAL_PARAMETER_FOCUS_STATUS_T)); |
126 | { |
127 | MMAL_PARAMETER_FOCUS_STATUS_T *focus_status = (MMAL_PARAMETER_FOCUS_STATUS_T *)param; |
128 | LOG_INFO("Focus status: %d" , focus_status->status); |
129 | vcos_event_flags_set(&events, MMAL_CAM_AUTOFOCUS_COMPLETE, VCOS_OR); |
130 | } |
131 | break; |
132 | case MMAL_PARAMETER_CAMERA_NUM: |
133 | vcos_assert(param->hdr.size == sizeof(MMAL_PARAMETER_UINT32_T)); |
134 | { |
135 | MMAL_PARAMETER_UINT32_T *camera_num = (MMAL_PARAMETER_UINT32_T *)param; |
136 | LOG_INFO("Camera number: %d" , camera_num->value); |
137 | } |
138 | break; |
139 | default: |
140 | LOG_ERROR("Unexpected changed event for parameter 0x%08x" , param->hdr.id); |
141 | } |
142 | } |
143 | else |
144 | { |
145 | LOG_ERROR("Unexpected event, 0x%08x" , buffer->cmd); |
146 | } |
147 | mmal_buffer_header_release(buffer); |
148 | } |
149 | |
150 | static void generic_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
151 | { |
152 | if (buffer->cmd != 0) |
153 | { |
154 | LOG_INFO("%s callback: event %u not supported" , port->name, buffer->cmd); |
155 | mmal_buffer_header_release(buffer); |
156 | } |
157 | else |
158 | { |
159 | MMAL_QUEUE_T *queue = (MMAL_QUEUE_T *)port->userdata; |
160 | |
161 | LOG_DEBUG("%s callback" , port->name); |
162 | mmal_queue_put(queue, buffer); |
163 | } |
164 | |
165 | vcos_event_flags_set(&events, MMAL_CAM_BUFFER_READY, VCOS_OR); |
166 | } |
167 | |
168 | static void generic_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
169 | { |
170 | if (buffer->cmd != 0) |
171 | { |
172 | LOG_INFO("%s callback: event %u not supported" , port->name, buffer->cmd); |
173 | } |
174 | |
175 | mmal_buffer_header_release(buffer); |
176 | vcos_event_flags_set(&events, MMAL_CAM_BUFFER_READY, VCOS_OR); |
177 | } |
178 | |
179 | static MMAL_STATUS_T setup_output_port(MMAL_PORT_T *output_port, MMAL_QUEUE_T **p_queue, MMAL_POOL_T **p_pool) |
180 | { |
181 | MMAL_STATUS_T status = MMAL_ENOMEM; |
182 | MMAL_QUEUE_T *queue = NULL; |
183 | MMAL_POOL_T *pool = NULL; |
184 | |
185 | /* Create a queue for frames filled by the output port. |
186 | * The main loop will pass these on to the input port. */ |
187 | queue = mmal_queue_create(); |
188 | if (!queue) |
189 | { |
190 | LOG_ERROR("failed to create queue for %s" , output_port->name); |
191 | goto error; |
192 | } |
193 | |
194 | /* Create pool of buffer headers for the output port to consume */ |
195 | pool = mmal_port_pool_create(output_port, output_port->buffer_num, output_port->buffer_size); |
196 | if (!pool) |
197 | { |
198 | LOG_ERROR("failed to create pool for %s" , output_port->name); |
199 | goto error; |
200 | } |
201 | |
202 | output_port->userdata = (void *)queue; |
203 | |
204 | status = mmal_port_enable(output_port, generic_output_port_cb); |
205 | if (status != MMAL_SUCCESS) |
206 | { |
207 | LOG_ERROR("failed to enable %s" , output_port->name); |
208 | goto error; |
209 | } |
210 | |
211 | *p_queue = queue; |
212 | *p_pool = pool; |
213 | |
214 | return MMAL_SUCCESS; |
215 | |
216 | error: |
217 | if (queue) |
218 | mmal_queue_destroy(queue); |
219 | if (pool) |
220 | mmal_pool_destroy(pool); |
221 | |
222 | return status; |
223 | } |
224 | |
225 | static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_QUEUE_T **p_queue, MMAL_POOL_T **p_pool) |
226 | { |
227 | MMAL_STATUS_T status; |
228 | |
229 | status = mmal_format_full_copy(input_port->format, output_port->format); |
230 | if (status != MMAL_SUCCESS) |
231 | return status; |
232 | |
233 | status = mmal_port_format_commit(input_port); |
234 | if (status != MMAL_SUCCESS) |
235 | return status; |
236 | |
237 | if (enable_tunneling()) |
238 | { |
239 | status = mmal_port_connect(output_port, input_port); |
240 | if (status != MMAL_SUCCESS) |
241 | return status; |
242 | |
243 | status = mmal_port_enable(output_port, NULL); |
244 | if (status != MMAL_SUCCESS) |
245 | mmal_port_disconnect(output_port); |
246 | |
247 | return status; |
248 | } |
249 | |
250 | /* Non-tunneling setup */ |
251 | input_port->buffer_size = input_port->buffer_size_recommended; |
252 | if (input_port->buffer_size < input_port->buffer_size_min) |
253 | input_port->buffer_size = input_port->buffer_size_min; |
254 | input_port->buffer_num = input_port->buffer_num_recommended; |
255 | if (input_port->buffer_num < input_port->buffer_num_min) |
256 | input_port->buffer_num = input_port->buffer_num_min; |
257 | output_port->buffer_size = output_port->buffer_size_recommended; |
258 | if (output_port->buffer_size < output_port->buffer_size_min) |
259 | output_port->buffer_size = output_port->buffer_size_min; |
260 | output_port->buffer_num = output_port->buffer_num_recommended; |
261 | if (output_port->buffer_num < output_port->buffer_num_min) |
262 | output_port->buffer_num = output_port->buffer_num_min; |
263 | |
264 | input_port->buffer_num = output_port->buffer_num = |
265 | MMAL_MAX(input_port->buffer_num, output_port->buffer_num); |
266 | input_port->buffer_size = output_port->buffer_size = |
267 | MMAL_MAX(input_port->buffer_size, output_port->buffer_size); |
268 | |
269 | status = setup_output_port(output_port, p_queue, p_pool); |
270 | if (status != MMAL_SUCCESS) |
271 | goto error; |
272 | |
273 | status = mmal_port_enable(input_port, generic_input_port_cb); |
274 | if (status != MMAL_SUCCESS) |
275 | goto error; |
276 | |
277 | return status; |
278 | |
279 | error: |
280 | if (input_port->is_enabled) |
281 | mmal_port_disable(input_port); |
282 | if (output_port->is_enabled) |
283 | mmal_port_disable(output_port); |
284 | if (*p_pool) |
285 | mmal_pool_destroy(*p_pool); |
286 | if (*p_queue) |
287 | mmal_queue_destroy(*p_queue); |
288 | |
289 | return status; |
290 | } |
291 | |
292 | static MMAL_STATUS_T send_buffer_from_queue(MMAL_PORT_T *port, MMAL_QUEUE_T *queue) |
293 | { |
294 | MMAL_STATUS_T status = MMAL_SUCCESS; |
295 | MMAL_BUFFER_HEADER_T *buffer; |
296 | |
297 | if (!queue) |
298 | return MMAL_SUCCESS; |
299 | |
300 | buffer = mmal_queue_get(queue); |
301 | |
302 | if (buffer) |
303 | { |
304 | status = mmal_port_send_buffer(port, buffer); |
305 | |
306 | if (status != MMAL_SUCCESS) |
307 | { |
308 | mmal_queue_put_back(queue, buffer); |
309 | LOG_DEBUG("%s send failed (%i)" , port->name, status); |
310 | } |
311 | } |
312 | |
313 | return status; |
314 | } |
315 | |
316 | static MMAL_STATUS_T fill_port_from_pool(MMAL_PORT_T *port, MMAL_POOL_T *pool) |
317 | { |
318 | MMAL_STATUS_T status = MMAL_SUCCESS; |
319 | MMAL_QUEUE_T *queue; |
320 | |
321 | if (!pool) |
322 | return MMAL_SUCCESS; |
323 | |
324 | queue = pool->queue; |
325 | while (status == MMAL_SUCCESS && mmal_queue_length(queue) > 0) |
326 | status = send_buffer_from_queue(port, queue); |
327 | |
328 | return status; |
329 | } |
330 | |
331 | static void disable_port(MMAL_PORT_T *port) |
332 | { |
333 | if (port && port->is_enabled) |
334 | mmal_port_disable(port); |
335 | } |
336 | |
337 | |
338 | static int parse_vformat(const char* vformat, uint32_t *out_width, |
339 | uint32_t *out_height, uint32_t *out_encoding) |
340 | { |
341 | char vcodec[8]; |
342 | uint32_t width, height, encoding; |
343 | |
344 | // coverity[secure_coding] Scanning integer values, and a string where the length is safe given vcodec declaration |
345 | if (sscanf(vformat, "%4ux%4u:%7s" , &width, &height, vcodec) != 3) |
346 | { |
347 | fprintf(stderr, "Error, malformed or unsupported video format: %s\n" , vformat); |
348 | return -1; |
349 | } |
350 | |
351 | if (!vcos_strncasecmp(vcodec, "h263" , 4)) |
352 | { |
353 | encoding = MMAL_ENCODING_H263; |
354 | /* Special case, H263 supports a limited set of resolutions */ |
355 | if (!((width == 128 && height == 96) || |
356 | (width == 176 && height == 144) || |
357 | (width == 352 && height == 288) || |
358 | (width == 704 && height == 576) || |
359 | (width == 1408 && height == 1152))) |
360 | { |
361 | fprintf(stderr, |
362 | "Error, only 128x96, 176x144, 352x288, 704x576 and 1408x1152 are supported for H263\n" ); |
363 | return -1; |
364 | } |
365 | } |
366 | else if (!vcos_strncasecmp(vcodec, "mp4v" , 4)) |
367 | encoding = MMAL_ENCODING_MP4V; |
368 | else if (!vcos_strncasecmp(vcodec, "h264" , 4)) |
369 | encoding = MMAL_ENCODING_H264; |
370 | else if (!vcos_strncasecmp(vcodec, "jpeg" , 4)) |
371 | encoding = MMAL_ENCODING_JPEG; |
372 | else |
373 | { |
374 | fprintf(stderr, "Error, unknown video encoding: %s\n" , vcodec); |
375 | return -1; |
376 | } |
377 | |
378 | if (out_width) |
379 | *out_width = width; |
380 | if (out_height) |
381 | *out_height = height; |
382 | if (out_encoding) |
383 | *out_encoding = encoding; |
384 | LOG_DEBUG("Video format: w:%d h:%d codec:%4.4s" , width, height, (const char *)&encoding); |
385 | |
386 | return 0; |
387 | } |
388 | |
389 | /*****************************************************************************/ |
390 | int test_mmal_start_camcorder(volatile int *stop, MMALCAM_BEHAVIOUR_T *behaviour) |
391 | { |
392 | MMAL_STATUS_T status = MMAL_SUCCESS; |
393 | MMAL_POOL_T *pool_viewfinder = 0, *pool_encoder_in = 0, *pool_encoder_out = 0; |
394 | MMAL_QUEUE_T *queue_viewfinder = 0, *queue_encoder_in = 0, *queue_encoder_out = 0; |
395 | MMAL_COMPONENT_T *camera = 0, *encoder = 0, *render = 0; |
396 | MMAL_PORT_T *viewfinder_port = 0, *video_port = 0, *still_port = 0; |
397 | MMAL_PORT_T *render_port = 0, *encoder_input = 0, *encoder_output = 0; |
398 | uint32_t ms_per_change, last_change_ms, set_focus_delay_ms; |
399 | int packet_count = 0; |
400 | #if USE_CONTAINER |
401 | VC_CONTAINER_T *container = 0; |
402 | #endif |
403 | FILE *output = NULL; |
404 | |
405 | if(vcos_event_flags_create(&events, "MMALCam" ) != VCOS_SUCCESS) |
406 | { |
407 | behaviour->init_result = MMALCAM_INIT_ERROR_EVENT_FLAGS; |
408 | goto error; |
409 | } |
410 | |
411 | zero_copy = behaviour->zero_copy; |
412 | tunneling = behaviour->tunneling; |
413 | |
414 | /* Create and setup camera viewfinder component */ |
415 | camera = test_camera_create(behaviour, &status); |
416 | if(!camera) |
417 | { |
418 | behaviour->init_result = MMALCAM_INIT_ERROR_CAMERA; |
419 | goto error; |
420 | } |
421 | viewfinder_port = camera->output[0]; |
422 | video_port = camera->output[1]; |
423 | still_port = camera->output[2]; |
424 | |
425 | /* Create and setup video render component */ |
426 | render = test_video_render_create(behaviour, &status); |
427 | if(!render) |
428 | { |
429 | behaviour->init_result = MMALCAM_INIT_ERROR_RENDER; |
430 | goto error; |
431 | } |
432 | render_port = render->input[0]; |
433 | |
434 | status = connect_ports(viewfinder_port, render_port, &queue_viewfinder, &pool_viewfinder); |
435 | if (status != MMAL_SUCCESS) |
436 | { |
437 | behaviour->init_result = MMALCAM_INIT_ERROR_VIEWFINDER; |
438 | goto error; |
439 | } |
440 | |
441 | if (behaviour->uri) |
442 | { |
443 | MMAL_PARAMETER_BOOLEAN_T camera_capture = |
444 | {{MMAL_PARAMETER_CAPTURE, sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 1}; |
445 | |
446 | /* Create and setup video encoder component */ |
447 | encoder = test_video_encoder_create(behaviour, &status); |
448 | if(!encoder) |
449 | { |
450 | behaviour->init_result = MMALCAM_INIT_ERROR_ENCODER; |
451 | goto error; |
452 | } |
453 | encoder_input = encoder->input[0]; |
454 | encoder_output = encoder->output[0]; |
455 | |
456 | if (encoder_output->format->encoding == MMAL_ENCODING_JPEG) |
457 | video_port = still_port; |
458 | |
459 | status = connect_ports(video_port, encoder_input, &queue_encoder_in, &pool_encoder_in); |
460 | if (status != MMAL_SUCCESS) |
461 | { |
462 | behaviour->init_result = MMALCAM_INIT_ERROR_ENCODER_IN; |
463 | goto error; |
464 | } |
465 | |
466 | status = setup_output_port(encoder_output, &queue_encoder_out, &pool_encoder_out); |
467 | if (status != MMAL_SUCCESS) |
468 | { |
469 | behaviour->init_result = MMALCAM_INIT_ERROR_ENCODER_OUT; |
470 | goto error; |
471 | } |
472 | |
473 | status = mmal_port_parameter_set(video_port, &camera_capture.hdr); |
474 | if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) |
475 | { |
476 | behaviour->init_result = MMALCAM_INIT_ERROR_CAMERA_CAPTURE; |
477 | goto error; |
478 | } |
479 | |
480 | #if USE_CONTAINER |
481 | container = test_container_open(behaviour->uri, encoder_output->format, &status); |
482 | if (!container) |
483 | { |
484 | /* Notify user, carry on discarding encoded output buffers */ |
485 | fprintf(stderr, "Error (%i) opening container: %s\n" , status, behaviour->uri); |
486 | } |
487 | #else |
488 | |
489 | output = fopen(behaviour->uri, "wb" ); |
490 | if(!output) |
491 | { |
492 | /* Notify user, carry on discarding encoded output buffers */ |
493 | fprintf(stderr, "Error opening output file: %s\n" , behaviour->uri); |
494 | } |
495 | #endif |
496 | } |
497 | |
498 | /* Initialisation now complete */ |
499 | behaviour->init_result = MMALCAM_INIT_SUCCESS; |
500 | vcos_semaphore_post(&behaviour->init_sem); |
501 | |
502 | ms_per_change = behaviour->seconds_per_change * 1000; |
503 | last_change_ms = vcos_get_ms(); |
504 | set_focus_delay_ms = 1000; |
505 | |
506 | while(1) |
507 | { |
508 | MMAL_BUFFER_HEADER_T *buffer; |
509 | VCOS_UNSIGNED set; |
510 | |
511 | vcos_event_flags_get(&events, MMAL_CAM_ANY_EVENT, VCOS_OR_CONSUME, VCOS_TICKS_TO_MS(2), &set); |
512 | if(*stop) break; |
513 | |
514 | if (behaviour->focus_test != MMAL_PARAM_FOCUS_MAX) |
515 | { |
516 | if (set & MMAL_CAM_AUTOFOCUS_COMPLETE || |
517 | (set_focus_delay_ms && (vcos_get_ms() - last_change_ms) >= set_focus_delay_ms)) |
518 | { |
519 | set_focus_delay_ms = 0; |
520 | mmalcam_reset_focus(camera, behaviour->focus_test); |
521 | } |
522 | } |
523 | |
524 | /* Send empty buffers to the output ports */ |
525 | status = fill_port_from_pool(viewfinder_port, pool_viewfinder); |
526 | if (status != MMAL_SUCCESS) |
527 | break; |
528 | status = fill_port_from_pool(video_port, pool_encoder_in); |
529 | if (status != MMAL_SUCCESS) |
530 | break; |
531 | status = fill_port_from_pool(encoder_output, pool_encoder_out); |
532 | if (status != MMAL_SUCCESS) |
533 | break; |
534 | |
535 | /* Process filled output buffers */ |
536 | status = send_buffer_from_queue(render_port, queue_viewfinder); |
537 | if (status != MMAL_SUCCESS) |
538 | break; |
539 | status = send_buffer_from_queue(encoder_input, queue_encoder_in); |
540 | if (status != MMAL_SUCCESS) |
541 | break; |
542 | |
543 | /* Process output buffers from encoder */ |
544 | if (queue_encoder_out) |
545 | { |
546 | buffer = mmal_queue_get(queue_encoder_out); |
547 | if (buffer) |
548 | { |
549 | if (output |
550 | #if USE_CONTAINER |
551 | || container |
552 | #endif |
553 | ) |
554 | { |
555 | mmal_buffer_header_mem_lock(buffer); |
556 | #if USE_CONTAINER |
557 | test_container_write(container, buffer); |
558 | #else |
559 | LOG_ERROR("Write %d bytes of data from %p" , buffer->length, buffer->data); |
560 | fwrite(buffer->data, 1, buffer->length, output); |
561 | #endif |
562 | mmal_buffer_header_mem_unlock(buffer); |
563 | packet_count++; |
564 | if (packet_count > MAX_PACKET_COUNT) |
565 | { |
566 | #if USE_CONTAINER |
567 | vc_container_close(container); |
568 | container = 0; |
569 | #else |
570 | fclose(output); |
571 | #endif |
572 | output = NULL; |
573 | fprintf(stderr, "All packets written\n" ); |
574 | } |
575 | } |
576 | mmal_buffer_header_release(buffer); |
577 | } |
578 | } |
579 | |
580 | /* Change a camera parameter if requested */ |
581 | if (ms_per_change != 0) |
582 | { |
583 | if((vcos_get_ms() - last_change_ms) >= ms_per_change) |
584 | { |
585 | last_change_ms = vcos_get_ms(); |
586 | switch (behaviour->change) |
587 | { |
588 | case MMALCAM_CHANGE_IMAGE_EFFECT: |
589 | if (!mmalcam_next_effect(camera)) |
590 | *stop = 1; |
591 | break; |
592 | case MMALCAM_CHANGE_ROTATION: |
593 | if (!mmalcam_next_rotation(camera)) |
594 | *stop = 1; |
595 | break; |
596 | case MMALCAM_CHANGE_ZOOM: |
597 | if (!mmalcam_next_zoom(camera)) |
598 | *stop = 1; |
599 | break; |
600 | case MMALCAM_CHANGE_FOCUS: |
601 | if (!mmalcam_next_focus(camera)) |
602 | *stop = 1; |
603 | break; |
604 | case MMALCAM_CHANGE_DRC: |
605 | if (!mmalcam_next_drc(camera)) |
606 | *stop = 1; |
607 | break; |
608 | case MMALCAM_CHANGE_HDR: |
609 | if (!mmalcam_next_hdr(camera)) |
610 | *stop = 1; |
611 | break; |
612 | case MMALCAM_CHANGE_CONTRAST: |
613 | if (!mmalcam_next_colour_param(camera, MMAL_PARAMETER_CONTRAST, -100, 100, "contrast" )) |
614 | *stop = 1; |
615 | break; |
616 | case MMALCAM_CHANGE_BRIGHTNESS: |
617 | if (!mmalcam_next_colour_param(camera, MMAL_PARAMETER_BRIGHTNESS, 0, 100, "brightness" )) |
618 | *stop = 1; |
619 | break; |
620 | case MMALCAM_CHANGE_SATURATION: |
621 | if (!mmalcam_next_colour_param(camera, MMAL_PARAMETER_SATURATION, -100, 100, "saturation" )) |
622 | *stop = 1; |
623 | break; |
624 | case MMALCAM_CHANGE_SHARPNESS: |
625 | if (!mmalcam_next_colour_param(camera, MMAL_PARAMETER_SHARPNESS, -100, 100, "sharpness" )) |
626 | *stop = 1; |
627 | break; |
628 | default: |
629 | LOG_ERROR("Unexpected change behaviour: %d" , behaviour->change); |
630 | break; |
631 | } |
632 | } |
633 | } |
634 | } |
635 | |
636 | /* Disable ports */ |
637 | disable_port(viewfinder_port); |
638 | disable_port(render_port); |
639 | disable_port(video_port); |
640 | disable_port(encoder_input); |
641 | disable_port(encoder_output); |
642 | |
643 | /* Disable components */ |
644 | mmal_component_disable(render); |
645 | if (encoder) |
646 | mmal_component_disable(encoder); |
647 | mmal_component_disable(camera); |
648 | |
649 | INIT_PARAMETER(behaviour->render_stats, MMAL_PARAMETER_STATISTICS); |
650 | mmal_port_parameter_get(render_port, &behaviour->render_stats.hdr); |
651 | if (encoder) |
652 | { |
653 | INIT_PARAMETER(behaviour->encoder_stats, MMAL_PARAMETER_STATISTICS); |
654 | mmal_port_parameter_get(encoder_output, &behaviour->encoder_stats.hdr); |
655 | } |
656 | |
657 | error: |
658 | /* The pools need to be destroyed first since they are owned by the components */ |
659 | if(pool_viewfinder) |
660 | mmal_port_pool_destroy(viewfinder_port, pool_viewfinder); |
661 | if(pool_encoder_in) |
662 | mmal_port_pool_destroy(video_port, pool_encoder_in); |
663 | if(pool_encoder_out) |
664 | mmal_port_pool_destroy(encoder_output, pool_encoder_out); |
665 | |
666 | if(render) |
667 | mmal_component_destroy(render); |
668 | if(encoder) |
669 | mmal_component_destroy(encoder); |
670 | if(camera) |
671 | mmal_component_destroy(camera); |
672 | |
673 | if(queue_viewfinder) |
674 | mmal_queue_destroy(queue_viewfinder); |
675 | if(queue_encoder_in) |
676 | mmal_queue_destroy(queue_encoder_in); |
677 | if(queue_encoder_out) |
678 | mmal_queue_destroy(queue_encoder_out); |
679 | |
680 | #if USE_CONTAINER |
681 | if(container) |
682 | vc_container_close(container); |
683 | #endif |
684 | if(output) |
685 | fclose(output); |
686 | |
687 | vcos_event_flags_delete(&events); |
688 | |
689 | if (packet_count) |
690 | printf("Packet count: %d\n" , packet_count); |
691 | |
692 | if (behaviour->init_result != MMALCAM_INIT_SUCCESS) |
693 | vcos_semaphore_post(&behaviour->init_sem); |
694 | |
695 | return (int)status; |
696 | } |
697 | |
698 | /*****************************************************************************/ |
699 | static MMAL_COMPONENT_T *test_camera_create(MMALCAM_BEHAVIOUR_T *behaviour, MMAL_STATUS_T *status) |
700 | { |
701 | MMAL_COMPONENT_T *camera = 0; |
702 | MMAL_ES_FORMAT_T *format; |
703 | MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request = |
704 | {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)}, 0, 1}; |
705 | MMAL_PORT_T *viewfinder_port = NULL, *video_port = NULL, *still_port = NULL; |
706 | uint32_t width, height; |
707 | MMAL_PARAMETER_INT32_T camera_num = |
708 | {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)},0}; |
709 | |
710 | /* Create the component */ |
711 | *status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); |
712 | if(*status != MMAL_SUCCESS) |
713 | { |
714 | LOG_ERROR("couldn't create camera" ); |
715 | goto error; |
716 | } |
717 | if(!camera->output_num) |
718 | { |
719 | LOG_ERROR("camera doesn't have output ports" ); |
720 | *status = MMAL_EINVAL; |
721 | goto error; |
722 | } |
723 | |
724 | viewfinder_port = camera->output[0]; |
725 | video_port = camera->output[1]; |
726 | still_port = camera->output[2]; |
727 | |
728 | change_event_request.change_id = MMAL_PARAMETER_FOCUS_STATUS; |
729 | *status = mmal_port_parameter_set(camera->control, &change_event_request.hdr); |
730 | if (*status != MMAL_SUCCESS && *status != MMAL_ENOSYS) |
731 | { |
732 | LOG_ERROR("No focus status change events" ); |
733 | } |
734 | camera_num.value = behaviour->camera_num; |
735 | *status = mmal_port_parameter_set(camera->control, &camera_num.hdr); |
736 | if (*status != MMAL_SUCCESS && *status != MMAL_ENOSYS) |
737 | { |
738 | LOG_ERROR("No camera number change events" ); |
739 | } |
740 | if (enable_zero_copy()) |
741 | { |
742 | MMAL_PARAMETER_BOOLEAN_T param_zc = |
743 | {{MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 1}; |
744 | *status = mmal_port_parameter_set(viewfinder_port, ¶m_zc.hdr); |
745 | if( *status != MMAL_SUCCESS && *status != MMAL_ENOSYS ) |
746 | { |
747 | LOG_ERROR("failed to set zero copy on camera output" ); |
748 | goto error; |
749 | } |
750 | LOG_INFO("enabled zero copy on camera" ); |
751 | *status = mmal_port_parameter_set(video_port, ¶m_zc.hdr); |
752 | if( *status != MMAL_SUCCESS && *status != MMAL_ENOSYS ) |
753 | { |
754 | LOG_ERROR("failed to set zero copy on camera output" ); |
755 | goto error; |
756 | } |
757 | *status = mmal_port_parameter_set(still_port, ¶m_zc.hdr); |
758 | if( *status != MMAL_SUCCESS && *status != MMAL_ENOSYS ) |
759 | { |
760 | LOG_ERROR("failed to set zero copy on camera output" ); |
761 | goto error; |
762 | } |
763 | } |
764 | |
765 | if ( behaviour->change == MMALCAM_CHANGE_HDR ) |
766 | { |
767 | MMAL_PARAMETER_ALGORITHM_CONTROL_T algo_ctrl = {{MMAL_PARAMETER_ALGORITHM_CONTROL, sizeof(MMAL_PARAMETER_ALGORITHM_CONTROL_T)}, |
768 | MMAL_PARAMETER_ALGORITHM_CONTROL_ALGORITHMS_HIGH_DYNAMIC_RANGE, 1 }; |
769 | mmal_port_parameter_set(camera->control, &algo_ctrl.hdr); |
770 | } |
771 | |
772 | *status = mmal_port_enable(camera->control, control_bh_cb); |
773 | if (*status) |
774 | { |
775 | LOG_ERROR("control port couldn't be enabled: %d" , *status); |
776 | goto error; |
777 | } |
778 | |
779 | /* Set camera viewfinder port format */ |
780 | if (parse_vformat(behaviour->vformat, &width, &height, NULL)) |
781 | { |
782 | *status = MMAL_EINVAL; |
783 | goto error; |
784 | } |
785 | |
786 | /* Default to integer frame rate in numerator */ |
787 | if (!behaviour->frame_rate.den) |
788 | behaviour->frame_rate.den = 1; |
789 | |
790 | { |
791 | MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = {{MMAL_PARAMETER_CAMERA_CONFIG,sizeof(cam_config)}, |
792 | .max_stills_w = width, |
793 | .max_stills_h = height, |
794 | .stills_yuv422 = 0, |
795 | .one_shot_stills = 0, |
796 | .max_preview_video_w = width, |
797 | .max_preview_video_h = height, |
798 | .num_preview_video_frames = 3, |
799 | .stills_capture_circular_buffer_height = 0, |
800 | .fast_preview_resume = 0, |
801 | /* No way of using fast resume in Android, as preview |
802 | * automatically stops on capture. |
803 | */ |
804 | .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC |
805 | }; |
806 | |
807 | mmal_port_parameter_set(camera->control, &cam_config.hdr); |
808 | } |
809 | |
810 | /* Set up the viewfinder port format */ |
811 | format = viewfinder_port->format; |
812 | if (behaviour->opaque) |
813 | format->encoding = MMAL_ENCODING_OPAQUE; |
814 | else |
815 | format->encoding = MMAL_ENCODING_I420; |
816 | |
817 | format->es->video.width = width; |
818 | format->es->video.height = height; |
819 | format->es->video.crop.x = 0; |
820 | format->es->video.crop.y = 0; |
821 | format->es->video.crop.width = width; |
822 | format->es->video.crop.height = height; |
823 | format->es->video.frame_rate = behaviour->frame_rate; |
824 | |
825 | *status = mmal_port_format_commit(viewfinder_port); |
826 | if(*status) |
827 | { |
828 | LOG_ERROR("camera viewfinder format couldn't be set" ); |
829 | goto error; |
830 | } |
831 | |
832 | /* Set the same format on the video (for encoder) port */ |
833 | mmal_format_full_copy(video_port->format, format); |
834 | *status = mmal_port_format_commit(video_port); |
835 | if(*status) |
836 | { |
837 | LOG_ERROR("camera video format couldn't be set" ); |
838 | goto error; |
839 | } |
840 | |
841 | /* Ensure there are enough buffers to avoid dropping frames */ |
842 | if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) |
843 | video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; |
844 | |
845 | /* Set the same format on the still (for encoder) port */ |
846 | mmal_format_full_copy(still_port->format, format); |
847 | *status = mmal_port_format_commit(still_port); |
848 | if(*status) |
849 | { |
850 | LOG_ERROR("camera still format couldn't be set" ); |
851 | goto error; |
852 | } |
853 | |
854 | /* Ensure there are enough buffers to avoid dropping frames */ |
855 | if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) |
856 | still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; |
857 | |
858 | /* Enable component */ |
859 | *status = mmal_component_enable(camera); |
860 | if(*status) |
861 | { |
862 | LOG_ERROR("camera component couldn't be enabled" ); |
863 | goto error; |
864 | } |
865 | |
866 | return camera; |
867 | |
868 | error: |
869 | if(camera) mmal_component_destroy(camera); |
870 | return 0; |
871 | } |
872 | |
873 | /*****************************************************************************/ |
874 | static MMAL_BOOL_T mmalcam_next_effect(MMAL_COMPONENT_T *camera) |
875 | { |
876 | static const MMAL_PARAM_IMAGEFX_T effects[] = { |
877 | MMAL_PARAM_IMAGEFX_NONE, |
878 | MMAL_PARAM_IMAGEFX_NEGATIVE, |
879 | MMAL_PARAM_IMAGEFX_SOLARIZE |
880 | }; |
881 | static unsigned int index; |
882 | MMAL_PARAMETER_IMAGEFX_T image_fx = {{ MMAL_PARAMETER_IMAGE_EFFECT, sizeof(image_fx)},0}; |
883 | MMAL_PARAMETER_IMAGEFX_T image_fx_check = {{ MMAL_PARAMETER_IMAGE_EFFECT, sizeof(image_fx)},0}; |
884 | MMAL_STATUS_T result; |
885 | |
886 | index++; |
887 | if(index >= countof(effects)) |
888 | index = 0; |
889 | image_fx.value = effects[index]; |
890 | result = mmal_port_parameter_set(camera->control, &image_fx.hdr); |
891 | if (result != MMAL_SUCCESS) |
892 | { |
893 | LOG_ERROR("Failed to set image effect, %d" , result); |
894 | return MMAL_FALSE; |
895 | } |
896 | result = mmal_port_parameter_get(camera->control, &image_fx_check.hdr); |
897 | if (result != MMAL_SUCCESS) |
898 | { |
899 | LOG_ERROR("Failed to retrieve image effect, %d" , result); |
900 | return MMAL_FALSE; |
901 | } |
902 | if (memcmp(&image_fx, &image_fx_check, sizeof(image_fx)) != 0) |
903 | { |
904 | LOG_ERROR("Image effect set (%d) was not retrieved (%d)" , image_fx.value, image_fx_check.value); |
905 | return MMAL_FALSE; |
906 | } |
907 | return MMAL_TRUE; |
908 | } |
909 | |
910 | /*****************************************************************************/ |
911 | static MMAL_BOOL_T mmalcam_next_rotation(MMAL_COMPONENT_T *camera) |
912 | { |
913 | static MMAL_PARAMETER_UINT32_T rotate = {{MMAL_PARAMETER_ROTATION,sizeof(rotate)},0}; |
914 | MMAL_PARAMETER_UINT32_T rotate_check = {{MMAL_PARAMETER_ROTATION,sizeof(rotate_check)},0}; |
915 | MMAL_STATUS_T result; |
916 | |
917 | rotate.value += 90; |
918 | if(rotate.value == 360) |
919 | rotate.value = 0; |
920 | result = mmal_port_parameter_set(camera->output[0], &rotate.hdr); |
921 | if (result != MMAL_SUCCESS) |
922 | { |
923 | LOG_ERROR("Failed to set rotation, %d" , result); |
924 | return MMAL_FALSE; |
925 | } |
926 | result = mmal_port_parameter_get(camera->output[0], &rotate_check.hdr); |
927 | if (result != MMAL_SUCCESS) |
928 | { |
929 | LOG_ERROR("Failed to retrieve rotation, %d" , result); |
930 | return MMAL_FALSE; |
931 | } |
932 | if (memcmp(&rotate, &rotate_check, sizeof(rotate)) != 0) |
933 | { |
934 | LOG_ERROR("Rotation set (%d) was not retrieved (%d)" , rotate.value, rotate_check.value); |
935 | return MMAL_FALSE; |
936 | } |
937 | return MMAL_TRUE; |
938 | } |
939 | |
940 | /*****************************************************************************/ |
941 | static MMAL_BOOL_T mmalcam_next_zoom(MMAL_COMPONENT_T *camera) |
942 | { |
943 | static MMAL_PARAMETER_SCALEFACTOR_T scale = {{MMAL_PARAMETER_ZOOM,sizeof(scale)},1<<16,1<<16}; |
944 | static int32_t dirn = 1 << 14; |
945 | MMAL_PARAMETER_SCALEFACTOR_T scale_check = {{MMAL_PARAMETER_ZOOM,sizeof(scale_check)},0,0}; |
946 | MMAL_STATUS_T result; |
947 | |
948 | scale.scale_x += dirn; |
949 | scale.scale_y += dirn; |
950 | if (scale.scale_x >= 4<<16 || scale.scale_x <= 1<<16) |
951 | dirn = -dirn; |
952 | result = mmal_port_parameter_set(camera->control, &scale.hdr); |
953 | if (result != MMAL_SUCCESS) |
954 | { |
955 | LOG_ERROR("Failed to set scale, %d" , result); |
956 | return MMAL_FALSE; |
957 | } |
958 | result = mmal_port_parameter_get(camera->control, &scale_check.hdr); |
959 | if (result != MMAL_SUCCESS) |
960 | { |
961 | LOG_ERROR("Failed to retrieve scale, %d" , result); |
962 | return MMAL_FALSE; |
963 | } |
964 | if (memcmp(&scale, &scale_check, sizeof(scale)) != 0) |
965 | { |
966 | LOG_ERROR("Scale set (%d,%d) was not retrieved (%d,%d)" , |
967 | scale.scale_x, scale.scale_y, scale_check.scale_x, scale_check.scale_y); |
968 | return MMAL_FALSE; |
969 | } |
970 | return MMAL_TRUE; |
971 | } |
972 | |
973 | /*****************************************************************************/ |
974 | static MMAL_BOOL_T mmalcam_next_focus(MMAL_COMPONENT_T *camera) |
975 | { |
976 | static const MMAL_PARAM_FOCUS_T focus_setting[] = { |
977 | MMAL_PARAM_FOCUS_AUTO, |
978 | MMAL_PARAM_FOCUS_AUTO_MACRO, |
979 | MMAL_PARAM_FOCUS_CAF, |
980 | MMAL_PARAM_FOCUS_FIXED_INFINITY, |
981 | MMAL_PARAM_FOCUS_FIXED_HYPERFOCAL, |
982 | MMAL_PARAM_FOCUS_FIXED_MACRO, |
983 | MMAL_PARAM_FOCUS_EDOF, |
984 | }; |
985 | static unsigned int index; |
986 | static MMAL_PARAMETER_FOCUS_T focus = {{MMAL_PARAMETER_FOCUS,sizeof(focus)},0}; |
987 | static MMAL_PARAMETER_FOCUS_T focus_check = {{MMAL_PARAMETER_FOCUS,sizeof(focus)},0}; |
988 | MMAL_STATUS_T result; |
989 | |
990 | index++; |
991 | if(index >= countof(focus_setting)) |
992 | index = MMAL_FALSE; |
993 | focus.value = focus_setting[index]; |
994 | result = mmal_port_parameter_set(camera->control, &focus.hdr); |
995 | if (result != MMAL_SUCCESS) |
996 | { |
997 | LOG_ERROR("Failed to set focus to %d" , focus.value); |
998 | /* As this depends on the camera module, do not fail */ |
999 | return MMAL_TRUE; |
1000 | } |
1001 | result = mmal_port_parameter_get(camera->control, &focus_check.hdr); |
1002 | if (result != MMAL_SUCCESS) |
1003 | { |
1004 | LOG_ERROR("Failed to retrieve focus, %d" , result); |
1005 | return MMAL_FALSE; |
1006 | } |
1007 | /* Focus setting is asynchronous, so the value read back may not match what was set */ |
1008 | return MMAL_TRUE; |
1009 | } |
1010 | |
1011 | /*****************************************************************************/ |
1012 | static MMAL_BOOL_T mmalcam_reset_focus(MMAL_COMPONENT_T *camera, MMAL_PARAM_FOCUS_T focus_setting) |
1013 | { |
1014 | MMAL_PARAMETER_FOCUS_T focus = {{MMAL_PARAMETER_FOCUS, sizeof(focus)},MMAL_PARAM_FOCUS_FIXED_HYPERFOCAL}; |
1015 | MMAL_STATUS_T result; |
1016 | |
1017 | result = mmal_port_parameter_set(camera->control, &focus.hdr); |
1018 | if (result != MMAL_SUCCESS) |
1019 | { |
1020 | LOG_ERROR("Failed to set focus to HYPERFOCAL, result %d" , result); |
1021 | return MMAL_FALSE; |
1022 | } |
1023 | focus.value = focus_setting; |
1024 | result = mmal_port_parameter_set(camera->control, &focus.hdr); |
1025 | if (result != MMAL_SUCCESS) |
1026 | { |
1027 | LOG_ERROR("Failed to set focus to %d, result %d" , focus_setting, result); |
1028 | return MMAL_FALSE; |
1029 | } |
1030 | return MMAL_TRUE; |
1031 | } |
1032 | |
1033 | /*****************************************************************************/ |
1034 | static MMAL_BOOL_T mmalcam_next_drc(MMAL_COMPONENT_T *camera) |
1035 | { |
1036 | static const MMAL_PARAMETER_DRC_STRENGTH_T drc_setting[] = { |
1037 | MMAL_PARAMETER_DRC_STRENGTH_OFF, |
1038 | MMAL_PARAMETER_DRC_STRENGTH_LOW, |
1039 | MMAL_PARAMETER_DRC_STRENGTH_MEDIUM, |
1040 | MMAL_PARAMETER_DRC_STRENGTH_HIGH |
1041 | }; |
1042 | static unsigned int index; |
1043 | MMAL_STATUS_T result; |
1044 | MMAL_PARAMETER_DRC_T drc = {{MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION,sizeof(drc)},0}; |
1045 | MMAL_PARAMETER_DRC_T drc_check = {{MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION,sizeof(drc_check)},0}; |
1046 | |
1047 | index++; |
1048 | if(index >= countof(drc_setting)) |
1049 | index = 0; |
1050 | drc.strength = drc_setting[index]; |
1051 | |
1052 | result = mmal_port_parameter_set(camera->control, &drc.hdr); |
1053 | if (result != MMAL_SUCCESS) |
1054 | { |
1055 | LOG_ERROR("Failed to set drc, %d" , result); |
1056 | return MMAL_FALSE; |
1057 | } |
1058 | result = mmal_port_parameter_get(camera->control, &drc_check.hdr); |
1059 | if (result != MMAL_SUCCESS) |
1060 | { |
1061 | LOG_ERROR("Failed to retrieve drc, %d" , result); |
1062 | return MMAL_FALSE; |
1063 | } |
1064 | if (memcmp(&drc, &drc_check, sizeof(drc)) != 0) |
1065 | { |
1066 | LOG_ERROR("DRC set (%d) was not retrieved (%d)" , drc.strength, drc_check.strength); |
1067 | return MMAL_FALSE; |
1068 | } |
1069 | return MMAL_TRUE; |
1070 | } |
1071 | |
1072 | /*****************************************************************************/ |
1073 | static MMAL_BOOL_T mmalcam_next_hdr(MMAL_COMPONENT_T *camera) |
1074 | { |
1075 | static const MMAL_BOOL_T hdr_setting[] = { |
1076 | MMAL_FALSE, |
1077 | MMAL_TRUE, |
1078 | }; |
1079 | static unsigned int index; |
1080 | MMAL_STATUS_T result; |
1081 | MMAL_PARAMETER_BOOLEAN_T hdr = {{MMAL_PARAMETER_HIGH_DYNAMIC_RANGE,sizeof(hdr)},0}; |
1082 | MMAL_PARAMETER_BOOLEAN_T hdr_check = {{MMAL_PARAMETER_HIGH_DYNAMIC_RANGE,sizeof(hdr_check)},0}; |
1083 | |
1084 | index++; |
1085 | if(index >= countof(hdr_setting)) |
1086 | index = 0; |
1087 | hdr.enable = hdr_setting[index]; |
1088 | |
1089 | result = mmal_port_parameter_set(camera->control, &hdr.hdr); |
1090 | if (result != MMAL_SUCCESS) |
1091 | { |
1092 | LOG_ERROR("Failed to set hdr, %d" , result); |
1093 | return MMAL_FALSE; |
1094 | } |
1095 | result = mmal_port_parameter_get(camera->control, &hdr_check.hdr); |
1096 | if (result != MMAL_SUCCESS) |
1097 | { |
1098 | LOG_ERROR("Failed to retrieve hdr, %d" , result); |
1099 | return MMAL_FALSE; |
1100 | } |
1101 | if (memcmp(&hdr, &hdr_check, sizeof(hdr)) != 0) |
1102 | { |
1103 | LOG_ERROR("HDR set (%d) was not retrieved (%d)" , hdr.enable, hdr_check.enable); |
1104 | return MMAL_FALSE; |
1105 | } |
1106 | return MMAL_TRUE; |
1107 | } |
1108 | |
1109 | /*****************************************************************************/ |
1110 | /* Contrast, brightness, saturation, and sharpness all take the same format, |
1111 | * but need different parameter IDs, and brightness is 0-100, not -100 to 100. |
1112 | */ |
1113 | static MMAL_BOOL_T mmalcam_next_colour_param(MMAL_COMPONENT_T *camera, uint32_t id, int min, int max, const char *param_name) |
1114 | { |
1115 | static MMAL_PARAMETER_RATIONAL_T param = {{MMAL_PARAMETER_GROUP_CAMERA,sizeof(param)},{0,100}}; |
1116 | MMAL_PARAMETER_RATIONAL_T param_check = {{MMAL_PARAMETER_GROUP_CAMERA,sizeof(param_check)},{0,100}}; |
1117 | MMAL_STATUS_T result; |
1118 | param.hdr.id = id; |
1119 | param_check.hdr.id = id; |
1120 | |
1121 | param.value.num += 20; |
1122 | if(param.value.num < min || param.value.num > max) |
1123 | param.value.num = min; |
1124 | result = mmal_port_parameter_set(camera->control, ¶m.hdr); |
1125 | if (result != MMAL_SUCCESS) |
1126 | { |
1127 | LOG_ERROR("Failed to set %s, %d" , param_name, result); |
1128 | return MMAL_FALSE; |
1129 | } |
1130 | result = mmal_port_parameter_get(camera->control, ¶m_check.hdr); |
1131 | if (result != MMAL_SUCCESS) |
1132 | { |
1133 | LOG_ERROR("Failed to retrieve %s, %d" , param_name, result); |
1134 | return MMAL_FALSE; |
1135 | } |
1136 | if (memcmp(¶m, ¶m_check, sizeof(param)) != 0) |
1137 | { |
1138 | LOG_ERROR("%s set (%d/%d) was not retrieved (%d/%d)" , param_name, |
1139 | param.value.num, param.value.den, |
1140 | param_check.value.num, param_check.value.den); |
1141 | return MMAL_FALSE; |
1142 | } |
1143 | return MMAL_TRUE; |
1144 | } |
1145 | |
1146 | |
1147 | /*****************************************************************************/ |
1148 | static MMAL_COMPONENT_T *test_video_render_create(MMALCAM_BEHAVIOUR_T *behaviour, MMAL_STATUS_T *status) |
1149 | { |
1150 | MMAL_COMPONENT_T *render = 0; |
1151 | MMAL_PORT_T *render_port = NULL; |
1152 | |
1153 | *status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &render); |
1154 | if(*status != MMAL_SUCCESS) |
1155 | { |
1156 | LOG_ERROR("couldn't create video render" ); |
1157 | goto error; |
1158 | } |
1159 | if(!render->input_num) |
1160 | { |
1161 | LOG_ERROR("video render doesn't have input ports" ); |
1162 | *status = MMAL_EINVAL; |
1163 | goto error; |
1164 | } |
1165 | |
1166 | render_port = render->input[0]; |
1167 | |
1168 | /* Give higher priority to the overlay layer */ |
1169 | MMAL_DISPLAYREGION_T param; |
1170 | param.hdr.id = MMAL_PARAMETER_DISPLAYREGION; |
1171 | param.hdr.size = sizeof(MMAL_DISPLAYREGION_T); |
1172 | param.set = MMAL_DISPLAY_SET_LAYER; |
1173 | param.layer = behaviour->layer; |
1174 | if (behaviour->display_area.width && behaviour->display_area.height) |
1175 | { |
1176 | param.set |= MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN; |
1177 | param.fullscreen = 0; |
1178 | param.dest_rect = behaviour->display_area; |
1179 | } |
1180 | *status = mmal_port_parameter_set( render_port, ¶m.hdr ); |
1181 | if (*status != MMAL_SUCCESS && *status != MMAL_ENOSYS) |
1182 | { |
1183 | LOG_ERROR("could not set video render display properties (%u)" , *status); |
1184 | goto error; |
1185 | } |
1186 | |
1187 | if (enable_zero_copy()) |
1188 | { |
1189 | MMAL_PARAMETER_BOOLEAN_T param_zc = |
1190 | {{MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 1}; |
1191 | *status = mmal_port_parameter_set(render_port, ¶m_zc.hdr); |
1192 | if (*status != MMAL_SUCCESS && *status != MMAL_ENOSYS) |
1193 | { |
1194 | LOG_ERROR("failed to set zero copy on render input" ); |
1195 | goto error; |
1196 | } |
1197 | LOG_INFO("enabled zero copy on render" ); |
1198 | } |
1199 | |
1200 | if (behaviour->opaque) |
1201 | { |
1202 | render_port->format->encoding = MMAL_ENCODING_OPAQUE; |
1203 | } |
1204 | |
1205 | /* Enable component */ |
1206 | *status = mmal_component_enable(render); |
1207 | if(*status) |
1208 | { |
1209 | LOG_ERROR("video render component couldn't be enabled (%u)" , *status); |
1210 | goto error; |
1211 | } |
1212 | |
1213 | return render; |
1214 | |
1215 | error: |
1216 | if(render) mmal_component_destroy(render); |
1217 | return 0; |
1218 | } |
1219 | |
1220 | /*****************************************************************************/ |
1221 | static MMAL_COMPONENT_T *test_video_encoder_create(MMALCAM_BEHAVIOUR_T *behaviour, MMAL_STATUS_T *status) |
1222 | { |
1223 | MMAL_COMPONENT_T *encoder = 0; |
1224 | MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; |
1225 | const char *component_name = MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER; |
1226 | uint32_t encoding; |
1227 | |
1228 | /* Set the port format */ |
1229 | if (parse_vformat(behaviour->vformat, 0, 0, &encoding)) |
1230 | { |
1231 | *status = MMAL_EINVAL; |
1232 | goto error; |
1233 | } |
1234 | |
1235 | if (encoding == MMAL_ENCODING_JPEG) |
1236 | component_name = MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER; |
1237 | |
1238 | *status = mmal_component_create(component_name, &encoder); |
1239 | if(*status != MMAL_SUCCESS) |
1240 | { |
1241 | LOG_ERROR("couldn't create video encoder" ); |
1242 | goto error; |
1243 | } |
1244 | if(!encoder->input_num || !encoder->output_num) |
1245 | { |
1246 | LOG_ERROR("video encoder doesn't have input/output ports" ); |
1247 | *status = MMAL_EINVAL; |
1248 | goto error; |
1249 | } |
1250 | |
1251 | encoder_input = encoder->input[0]; |
1252 | encoder_output = encoder->output[0]; |
1253 | |
1254 | mmal_format_copy(encoder_output->format, encoder_input->format); |
1255 | encoder_output->format->encoding = encoding; |
1256 | encoder_output->format->bitrate = behaviour->bit_rate; |
1257 | *status = mmal_port_format_commit(encoder_output); |
1258 | if(*status != MMAL_SUCCESS) |
1259 | { |
1260 | LOG_ERROR("format not set on video encoder output port" ); |
1261 | goto error; |
1262 | } |
1263 | encoder_output->buffer_size = encoder_output->buffer_size_recommended; |
1264 | if (encoder_output->buffer_size < encoder_output->buffer_size_min) |
1265 | encoder_output->buffer_size = encoder_output->buffer_size_min; |
1266 | encoder_output->buffer_num = encoder_output->buffer_num_recommended; |
1267 | if (encoder_output->buffer_num < encoder_output->buffer_num_min) |
1268 | encoder_output->buffer_num = encoder_output->buffer_num_min; |
1269 | |
1270 | if (enable_zero_copy()) |
1271 | { |
1272 | MMAL_PARAMETER_BOOLEAN_T param_zc = |
1273 | {{MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 1}; |
1274 | *status = mmal_port_parameter_set(encoder_output, ¶m_zc.hdr); |
1275 | if (*status != MMAL_SUCCESS && *status != MMAL_ENOSYS) |
1276 | { |
1277 | LOG_ERROR("failed to set zero copy on encoder output" ); |
1278 | goto error; |
1279 | } |
1280 | *status = mmal_port_parameter_set(encoder_input, ¶m_zc.hdr); |
1281 | if (*status != MMAL_SUCCESS && *status != MMAL_ENOSYS) |
1282 | { |
1283 | LOG_ERROR("failed to set zero copy on encoder input" ); |
1284 | goto error; |
1285 | } |
1286 | LOG_INFO("enabled zero copy on encoder" ); |
1287 | } |
1288 | |
1289 | if (behaviour->opaque) |
1290 | { |
1291 | encoder_input->format->encoding = MMAL_ENCODING_OPAQUE; |
1292 | } |
1293 | |
1294 | /* Enable component */ |
1295 | *status = mmal_component_enable(encoder); |
1296 | if(*status) |
1297 | { |
1298 | LOG_ERROR("video encoder component couldn't be enabled" ); |
1299 | goto error; |
1300 | } |
1301 | |
1302 | return encoder; |
1303 | |
1304 | error: |
1305 | if(encoder) mmal_component_destroy(encoder); |
1306 | return 0; |
1307 | } |
1308 | |
1309 | #if USE_CONTAINER |
1310 | /*****************************************************************************/ |
1311 | static MMAL_STATUS_T test_container_to_mmal_status(VC_CONTAINER_STATUS_T status) |
1312 | { |
1313 | switch (status) |
1314 | { |
1315 | case VC_CONTAINER_SUCCESS: |
1316 | case VC_CONTAINER_ERROR_NOT_READY: |
1317 | return MMAL_SUCCESS; |
1318 | case VC_CONTAINER_ERROR_LIMIT_REACHED: |
1319 | case VC_CONTAINER_ERROR_OUT_OF_SPACE: |
1320 | return MMAL_ENOSPC; |
1321 | case VC_CONTAINER_ERROR_URI_NOT_FOUND: |
1322 | return MMAL_ENOENT; |
1323 | default: |
1324 | return MMAL_ENXIO; |
1325 | } |
1326 | } |
1327 | |
1328 | /*****************************************************************************/ |
1329 | static VC_CONTAINER_T *test_container_open(const char *uri, MMAL_ES_FORMAT_T *format, MMAL_STATUS_T *p_status) |
1330 | { |
1331 | VC_CONTAINER_T *container = 0; |
1332 | VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FAILED; |
1333 | VC_CONTAINER_ES_FORMAT_T *container_format = 0; |
1334 | |
1335 | /* Open container */ |
1336 | container = vc_container_open_writer(uri, &status, 0, 0); |
1337 | if(status != VC_CONTAINER_SUCCESS) |
1338 | { |
1339 | LOG_ERROR("error opening uri %s (%i)" , uri, status); |
1340 | goto error; |
1341 | } |
1342 | |
1343 | /* Set format from MMAL port format */ |
1344 | container_format = vc_container_format_create(0); |
1345 | if (!container_format) |
1346 | { |
1347 | status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; |
1348 | LOG_ERROR("error (%i)" , status); |
1349 | goto error; |
1350 | } |
1351 | |
1352 | switch (format->type) |
1353 | { |
1354 | case MMAL_ES_TYPE_VIDEO: |
1355 | container_format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; |
1356 | break; |
1357 | default: |
1358 | status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
1359 | LOG_ERROR("unsupported elementary stream format error (%i)" , status); |
1360 | goto error; |
1361 | } |
1362 | |
1363 | container_format->codec = test_container_encoding_to_codec(format->encoding); |
1364 | if(format->encoding == MMAL_ENCODING_H264) |
1365 | container_format->codec_variant = VC_FOURCC('a','v','c','C'); |
1366 | container_format->type->video.width = format->es->video.width; |
1367 | container_format->type->video.height = format->es->video.height; |
1368 | container_format->type->video.frame_rate_num = format->es->video.frame_rate.num; |
1369 | container_format->type->video.frame_rate_den = format->es->video.frame_rate.den; |
1370 | container_format->type->video.par_num = format->es->video.par.num; |
1371 | container_format->type->video.par_den = format->es->video.par.den; |
1372 | container_format->bitrate = format->bitrate; |
1373 | container_format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; |
1374 | |
1375 | container_format->extradata_size = 0; |
1376 | |
1377 | status = vc_container_control(container, VC_CONTAINER_CONTROL_TRACK_ADD, container_format); |
1378 | if(status != VC_CONTAINER_SUCCESS) |
1379 | { |
1380 | LOG_ERROR("error adding track (%i)" , status); |
1381 | goto error; |
1382 | } |
1383 | |
1384 | vc_container_control(container, VC_CONTAINER_CONTROL_TRACK_ADD_DONE); |
1385 | |
1386 | end: |
1387 | if (container_format) |
1388 | vc_container_format_delete(container_format); |
1389 | if (p_status) *p_status = test_container_to_mmal_status(status); |
1390 | return container; |
1391 | error: |
1392 | if (container) |
1393 | { |
1394 | vc_container_close(container); |
1395 | container = 0; |
1396 | } |
1397 | goto end; |
1398 | } |
1399 | |
1400 | /*****************************************************************************/ |
1401 | static MMAL_STATUS_T test_container_write(VC_CONTAINER_T *container, MMAL_BUFFER_HEADER_T *buffer) |
1402 | { |
1403 | VC_CONTAINER_PACKET_T packet; |
1404 | VC_CONTAINER_STATUS_T status; |
1405 | memset(&packet, 0, sizeof(packet)); |
1406 | static int first_fragment = 1; |
1407 | |
1408 | #if 0 |
1409 | if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG) |
1410 | buffer->length = 0; /* Discard codec config data arriving in buffers */ |
1411 | #endif |
1412 | |
1413 | if (buffer->length == 0) |
1414 | return MMAL_SUCCESS; |
1415 | |
1416 | packet.track = 0; |
1417 | packet.pts = buffer->pts == MMAL_TIME_UNKNOWN ? VC_CONTAINER_TIME_UNKNOWN : buffer->pts; |
1418 | packet.dts = buffer->dts == MMAL_TIME_UNKNOWN ? VC_CONTAINER_TIME_UNKNOWN : buffer->dts; |
1419 | if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME) |
1420 | packet.flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; |
1421 | if(first_fragment || (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_START)) |
1422 | { |
1423 | packet.flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; |
1424 | first_fragment = 0; |
1425 | } |
1426 | |
1427 | if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) |
1428 | { |
1429 | packet.flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; |
1430 | first_fragment = 1; /* Next buffer will be the first fragment */ |
1431 | } |
1432 | |
1433 | packet.size = packet.buffer_size = buffer->length; |
1434 | if ((packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME) == VC_CONTAINER_PACKET_FLAG_FRAME) |
1435 | packet.frame_size = packet.size; |
1436 | |
1437 | vcos_assert(buffer->offset == 0); |
1438 | |
1439 | packet.data = buffer->data; |
1440 | |
1441 | LOG_DEBUG("writing packet: track %i, size %i/%i, pts %" PRId64", flags %x%s" , |
1442 | packet.track, packet.size, packet.frame_size, packet.pts, |
1443 | packet.flags, (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "" ); |
1444 | |
1445 | status = vc_container_write(container, &packet); |
1446 | |
1447 | return test_container_to_mmal_status(status); |
1448 | } |
1449 | |
1450 | /*****************************************************************************/ |
1451 | static struct { |
1452 | VC_CONTAINER_FOURCC_T codec; |
1453 | uint32_t encoding; |
1454 | } codec_to_encoding_table[] = |
1455 | { |
1456 | {VC_CONTAINER_CODEC_H263, MMAL_ENCODING_H263}, |
1457 | {VC_CONTAINER_CODEC_H264, MMAL_ENCODING_H264}, |
1458 | {VC_CONTAINER_CODEC_MP4V, MMAL_ENCODING_MP4V}, |
1459 | {VC_CONTAINER_CODEC_MP2V, MMAL_ENCODING_MP2V}, |
1460 | {VC_CONTAINER_CODEC_MP1V, MMAL_ENCODING_MP1V}, |
1461 | {VC_CONTAINER_CODEC_WMV3, MMAL_ENCODING_WMV3}, |
1462 | {VC_CONTAINER_CODEC_WMV2, MMAL_ENCODING_WMV2}, |
1463 | {VC_CONTAINER_CODEC_WMV1, MMAL_ENCODING_WMV1}, |
1464 | {VC_CONTAINER_CODEC_WVC1, MMAL_ENCODING_WVC1}, |
1465 | {VC_CONTAINER_CODEC_VP6, MMAL_ENCODING_VP6}, |
1466 | {VC_CONTAINER_CODEC_VP7, MMAL_ENCODING_VP7}, |
1467 | {VC_CONTAINER_CODEC_VP8, MMAL_ENCODING_VP8}, |
1468 | {VC_CONTAINER_CODEC_THEORA, MMAL_ENCODING_THEORA}, |
1469 | {VC_CONTAINER_CODEC_UNKNOWN, MMAL_ENCODING_UNKNOWN} |
1470 | }; |
1471 | |
1472 | static VC_CONTAINER_FOURCC_T test_container_encoding_to_codec(uint32_t encoding) |
1473 | { |
1474 | unsigned int i; |
1475 | for(i = 0; codec_to_encoding_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) |
1476 | if(codec_to_encoding_table[i].encoding == encoding) break; |
1477 | return codec_to_encoding_table[i].codec; |
1478 | } |
1479 | #endif |
1480 | |