Initial version of libomxil-vc4 for RPI3
[platform/adaptation/broadcom/libomxil-vc4.git] / host_applications / vmcs / test_apps / mmalcam / viewfinder.c
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, &param_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, &param_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, &param_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, &param.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, &param_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(&param, &param_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, &param.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, &param_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, &param_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, &param_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