1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/** \file
29 * MMAL test application which plays back video files
30 * Note: this is test code. Do not use this in your app. It *will* change or even be removed without notice.
31 */
32
33#include <memory.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <fcntl.h>
38#include <stdarg.h>
39
40#include "mmalplay.h"
41
42VCOS_LOG_CAT_T mmalplay_log_category;
43
44#include "interface/mmal/mmal_logging.h"
45#include "interface/mmal/util/mmal_util.h"
46#include "interface/mmal/util/mmal_connection.h"
47#include "interface/mmal/util/mmal_util_params.h"
48#include "interface/mmal/util/mmal_default_components.h"
49
50#define MMALPLAY_STILL_IMAGE_PAUSE 2000
51
52#define MMALPLAY_MAX_STRING 128
53#define MMALPLAY_MAX_CONNECTIONS 16
54#define MMALPLAY_MAX_RENDERERS 3
55
56static unsigned int video_render_num;
57
58/** Structure describing a mmal component */
59typedef struct {
60 struct MMALPLAY_T *ctx;
61 MMAL_COMPONENT_T *comp;
62
63 /* Used for debug / statistics */
64 char name[MMALPLAY_MAX_STRING];
65 int64_t time_setup;
66 int64_t time_cleanup;
67} MMALPLAY_COMPONENT_T;
68
69/** Context for a mmalplay playback instance */
70struct MMALPLAY_T {
71 const char *uri;
72 VCOS_SEMAPHORE_T event;
73
74 unsigned int component_num;
75 MMALPLAY_COMPONENT_T component[MMALPLAY_MAX_CONNECTIONS*2];
76
77 unsigned int connection_num;
78 MMAL_CONNECTION_T *connection[MMALPLAY_MAX_CONNECTIONS];
79
80 MMAL_STATUS_T status;
81 unsigned int stop;
82
83 MMAL_BOOL_T is_still_image;
84 MMAL_PORT_T *reader_video;
85 MMAL_PORT_T *reader_audio;
86 MMAL_PORT_T *video_out_port;
87 MMAL_PORT_T *converter_out_port;
88 MMAL_PORT_T *audio_clock;
89 MMAL_PORT_T *video_clock;
90
91 MMALPLAY_OPTIONS_T options;
92
93 /* Used for debug / statistics */
94 int64_t time_playback;
95 unsigned int decoded_frames;
96};
97
98typedef struct MMALPLAY_CONNECTIONS_T {
99 unsigned int num;
100
101 struct {
102 MMAL_PORT_T *in;
103 MMAL_PORT_T *out;
104 uint32_t flags;
105 } connection[MMALPLAY_MAX_CONNECTIONS+1];
106
107} MMALPLAY_CONNECTIONS_T;
108#define MMALPLAY_CONNECTION_IN(cx) (cx)->connection[(cx)->num].in
109#define MMALPLAY_CONNECTION_OUT(cx) (cx)->connection[(cx)->num].out
110#define MMALPLAY_CONNECTION_ADD(cx) if (++(cx)->num > MMALPLAY_MAX_CONNECTIONS) goto error
111#define MMALPLAY_CONNECTION_SET(cx, f) do { (cx)->connection[(cx)->num].flags |= (f); } while(0)
112
113static MMAL_COMPONENT_T *mmalplay_component_create(MMALPLAY_T *ctx, const char *name, MMAL_STATUS_T *status);
114static MMAL_STATUS_T mmalplay_connection_create(MMALPLAY_T *ctx, MMAL_PORT_T *out, MMAL_PORT_T *in, uint32_t flags);
115
116/* Utility function to setup the container reader component */
117static MMAL_STATUS_T mmalplay_setup_container_reader(MMALPLAY_T *, MMAL_COMPONENT_T *,
118 const char *uri);
119/* Utility function to setup the container writer component */
120static MMAL_STATUS_T mmalplay_setup_container_writer(MMALPLAY_T *, MMAL_COMPONENT_T *,
121 const char *uri);
122/* Utility function to setup the video decoder component */
123static MMAL_STATUS_T mmalplay_setup_video_decoder(MMALPLAY_T *ctx, MMAL_COMPONENT_T *);
124/* Utility function to setup the splitter component */
125static MMAL_STATUS_T mmalplay_setup_splitter(MMALPLAY_T *ctx, MMAL_COMPONENT_T *);
126/* Utility function to setup the video converter component */
127static MMAL_STATUS_T mmalplay_setup_video_converter(MMALPLAY_T *ctx, MMAL_COMPONENT_T *);
128/* Utility function to setup the video render component */
129static MMAL_STATUS_T mmalplay_setup_video_render(MMALPLAY_T *ctx, MMAL_COMPONENT_T *);
130/* Utility function to setup the video scheduler component */
131static MMAL_STATUS_T mmalplay_setup_video_scheduler(MMALPLAY_T *ctx, MMAL_COMPONENT_T *);
132/* Utility function to setup the audio decoder component */
133static MMAL_STATUS_T mmalplay_setup_audio_decoder(MMALPLAY_T *ctx, MMAL_COMPONENT_T *);
134/* Utility function to setup the audio render component */
135static MMAL_STATUS_T mmalplay_setup_audio_render(MMALPLAY_T *ctx, MMAL_COMPONENT_T *);
136
137static void log_format(MMAL_ES_FORMAT_T *format, MMAL_PORT_T *port);
138
139/*****************************************************************************/
140
141/** Callback from a control port. Error and EOS events stop playback. */
142static void mmalplay_bh_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
143{
144 MMALPLAY_T *ctx = (MMALPLAY_T *)port->userdata;
145 LOG_TRACE("%s(%p),%p,%4.4s", port->name, port, buffer, (char *)&buffer->cmd);
146
147 if (buffer->cmd == MMAL_EVENT_ERROR || buffer->cmd == MMAL_EVENT_EOS)
148 {
149 if (buffer->cmd == MMAL_EVENT_ERROR)
150 {
151 LOG_INFO("error event from %s: %s", port->name,
152 mmal_status_to_string(*(MMAL_STATUS_T*)buffer->data));
153 ctx->status = *(MMAL_STATUS_T *)buffer->data;
154 }
155 else if (buffer->cmd == MMAL_EVENT_EOS)
156 LOG_INFO("%s: EOS received", port->name);
157 mmalplay_stop(ctx);
158 }
159
160 mmal_buffer_header_release(buffer);
161}
162
163/** Callback from the connection. Buffer is available. */
164static void mmalplay_connection_cb(MMAL_CONNECTION_T *connection)
165{
166 MMALPLAY_T *ctx = (MMALPLAY_T *)connection->user_data;
167 vcos_semaphore_post(&ctx->event);
168}
169
170/*****************************************************************************/
171static MMAL_STATUS_T mmalplay_event_handle(MMAL_CONNECTION_T *connection, MMAL_PORT_T *port,
172 MMAL_BUFFER_HEADER_T *buffer)
173{
174 MMAL_STATUS_T status = MMAL_SUCCESS;
175
176 LOG_INFO("%s(%p) received event %4.4s (%i bytes)", port->name, port,
177 (char *)&buffer->cmd, (int)buffer->length);
178
179 if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED && port->type == MMAL_PORT_TYPE_OUTPUT)
180 {
181 MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(buffer);
182 if (event)
183 {
184 LOG_INFO("----------Port format changed----------");
185 log_format(port->format, port);
186 LOG_INFO("-----------------to---------------------");
187 log_format(event->format, 0);
188 LOG_INFO(" buffers num (opt %i, min %i), size (opt %i, min: %i)",
189 event->buffer_num_recommended, event->buffer_num_min,
190 event->buffer_size_recommended, event->buffer_size_min);
191 LOG_INFO("----------------------------------------");
192 }
193
194 status = mmal_connection_event_format_changed(connection, buffer);
195 }
196
197 mmal_buffer_header_release(buffer);
198 return status;
199}
200
201static MMAL_STATUS_T mmalplay_pipeline_audio_create(MMALPLAY_T *ctx, MMAL_PORT_T *source,
202 MMALPLAY_CONNECTIONS_T *connections)
203{
204 MMAL_STATUS_T status;
205 MMAL_COMPONENT_T *component;
206 unsigned int save = connections->num;
207
208 if (!source || ctx->options.disable_audio)
209 return MMAL_EINVAL;
210
211 MMALPLAY_CONNECTION_OUT(connections) = source;
212
213 /* Create and setup audio decoder component */
214 component = mmalplay_component_create(ctx, MMAL_COMPONENT_DEFAULT_AUDIO_DECODER, &status);
215 if (!component)
216 goto error;
217 MMALPLAY_CONNECTION_IN(connections) = component->input[0];
218 MMALPLAY_CONNECTION_ADD(connections);
219 MMALPLAY_CONNECTION_OUT(connections) = component->output[0];
220
221 /* Create and setup audio render component */
222 component = mmalplay_component_create(ctx, MMAL_COMPONENT_DEFAULT_AUDIO_RENDERER, &status);
223 if (!component)
224 goto error;
225 MMALPLAY_CONNECTION_IN(connections) = component->input[0];
226 MMALPLAY_CONNECTION_ADD(connections);
227
228 return MMAL_SUCCESS;
229
230 error:
231 connections->num = save;
232 return status == MMAL_SUCCESS ? MMAL_ENOSPC : status;
233}
234
235static MMAL_STATUS_T mmalplay_pipeline_video_create(MMALPLAY_T *ctx, MMAL_PORT_T *source,
236 MMALPLAY_CONNECTIONS_T *connections)
237{
238 MMAL_STATUS_T status;
239 MMAL_COMPONENT_T *component, *previous = NULL;
240 unsigned int i, save = connections->num;
241
242 if (!source || ctx->options.disable_video)
243 return MMAL_EINVAL;
244
245 MMALPLAY_CONNECTION_OUT(connections) = source;
246
247 if (ctx->options.output_uri)
248 {
249 /* Create and setup container writer component */
250 component = mmalplay_component_create(ctx, MMAL_COMPONENT_DEFAULT_CONTAINER_WRITER, &status);
251 if (!component)
252 goto error;
253 MMALPLAY_CONNECTION_IN(connections) = component->input[0];
254 MMALPLAY_CONNECTION_ADD(connections);
255 return MMAL_SUCCESS;
256 }
257
258 /* Create and setup video decoder component */
259 if (!ctx->options.disable_video_decode)
260 {
261 component = previous = mmalplay_component_create(ctx, MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &status);
262 if (!component)
263 goto error;
264 MMALPLAY_CONNECTION_IN(connections) = component->input[0];
265 MMALPLAY_CONNECTION_ADD(connections);
266 MMALPLAY_CONNECTION_OUT(connections) = component->output[0];
267 }
268 else ctx->video_out_port = source;
269
270 if (ctx->options.enable_scheduling)
271 {
272 /* Create a scheduler component */
273 component = previous = mmalplay_component_create(ctx, MMAL_COMPONENT_DEFAULT_SCHEDULER, &status);
274 if (!component)
275 goto error;
276 MMALPLAY_CONNECTION_IN(connections) = component->input[0];
277 MMALPLAY_CONNECTION_ADD(connections);
278 MMALPLAY_CONNECTION_OUT(connections) = component->output[0];
279 }
280
281 /* Create and setup video converter component */
282 if (ctx->options.render_format ||
283 (ctx->options.render_rect.width && ctx->options.render_rect.height))
284 {
285 component = previous = mmalplay_component_create(ctx, MMAL_COMPONENT_DEFAULT_VIDEO_CONVERTER, &status);
286 if (!component)
287 goto error;
288 MMALPLAY_CONNECTION_IN(connections) = component->input[0];
289 MMALPLAY_CONNECTION_ADD(connections);
290 MMALPLAY_CONNECTION_OUT(connections) = component->output[0];
291 }
292
293 if (ctx->options.output_num > 1)
294 {
295 /* Create and setup splitter component */
296 component = previous = mmalplay_component_create(ctx, MMAL_COMPONENT_DEFAULT_SPLITTER, &status);
297 if (!component || component->output_num < ctx->options.output_num)
298 goto error;
299 MMALPLAY_CONNECTION_IN(connections) = component->input[0];
300 MMALPLAY_CONNECTION_ADD(connections);
301 MMALPLAY_CONNECTION_OUT(connections) = component->output[0];
302 }
303
304 /* Create and setup video render components */
305 for (i = 0; i < ctx->options.output_num; i++)
306 {
307 component = mmalplay_component_create(ctx, MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &status);
308 if (!component)
309 goto error;
310 MMALPLAY_CONNECTION_OUT(connections) = previous ? previous->output[i] : source;
311 MMALPLAY_CONNECTION_IN(connections) = component->input[0];
312 MMALPLAY_CONNECTION_ADD(connections);
313 }
314
315 return MMAL_SUCCESS;
316
317 error:
318 connections->num = save;
319 return status == MMAL_SUCCESS ? MMAL_ENOSPC : status;
320}
321
322static MMAL_STATUS_T mmalplay_pipeline_clock_create(MMALPLAY_T *ctx, MMALPLAY_CONNECTIONS_T *connections)
323{
324 MMAL_STATUS_T status = MMAL_EINVAL;
325
326 if (!ctx->options.enable_scheduling)
327 return MMAL_SUCCESS;
328
329 if (!ctx->audio_clock || !ctx->video_clock)
330 {
331 LOG_ERROR("clock port(s) not present %p %p", ctx->audio_clock, ctx->video_clock);
332 return MMAL_SUCCESS;
333 }
334
335 /* Connect audio clock to video clock */
336 MMALPLAY_CONNECTION_SET(connections, MMAL_CONNECTION_FLAG_TUNNELLING);
337 MMALPLAY_CONNECTION_OUT(connections) = ctx->audio_clock;
338 MMALPLAY_CONNECTION_IN(connections) = ctx->video_clock;
339 MMALPLAY_CONNECTION_ADD(connections);
340
341 /* Set audio clock as master */
342 status = mmal_port_parameter_set_boolean(ctx->audio_clock, MMAL_PARAMETER_CLOCK_REFERENCE, MMAL_TRUE);
343 if (status != MMAL_SUCCESS)
344 LOG_ERROR("failed to set clock reference");
345
346 error:
347 return status;
348}
349
350/** Create an instance of mmalplay.
351 * Note: this is test code. Do not use it in your app. It *will* change or even be removed without notice.
352 */
353MMALPLAY_T *mmalplay_create(const char *uri, MMALPLAY_OPTIONS_T *opts, MMAL_STATUS_T *pstatus)
354{
355 MMAL_STATUS_T status = MMAL_SUCCESS, status_audio, status_video, status_clock;
356 MMALPLAY_T *ctx;
357 MMAL_COMPONENT_T *component;
358 MMALPLAY_CONNECTIONS_T connections;
359 unsigned int i;
360
361 LOG_TRACE("%s", uri);
362
363 if (pstatus) *pstatus = MMAL_ENOMEM;
364
365 /* Allocate and initialise context */
366 ctx = malloc(sizeof(MMALPLAY_T));
367 if (!ctx)
368 return NULL;
369 memset(ctx, 0, sizeof(*ctx));
370 memset(&connections, 0, sizeof(connections));
371 if (vcos_semaphore_create(&ctx->event, "MMALTest", 1) != VCOS_SUCCESS)
372 {
373 free(ctx);
374 return NULL;
375 }
376
377 ctx->uri = uri;
378 if (opts)
379 ctx->options = *opts;
380
381 ctx->options.output_num = MMAL_MAX(ctx->options.output_num, 1);
382 ctx->options.output_num = MMAL_MIN(ctx->options.output_num, MMALPLAY_MAX_RENDERERS);
383 connections.num = 0;
384
385 /* Create and setup the container reader component */
386 component = mmalplay_component_create(ctx, MMAL_COMPONENT_DEFAULT_CONTAINER_READER, &status);
387 if (!component)
388 goto error;
389
390 status_video = mmalplay_pipeline_video_create(ctx, ctx->reader_video, &connections);
391 status_audio = mmalplay_pipeline_audio_create(ctx, ctx->reader_audio, &connections);
392 status_clock = mmalplay_pipeline_clock_create(ctx, &connections);
393 if (status_video != MMAL_SUCCESS && status_audio != MMAL_SUCCESS && status_clock != MMAL_SUCCESS)
394 {
395 status = status_video;
396 goto error;
397 }
398
399 /* Create our connections */
400 for (i = 0; i < connections.num; i++)
401 {
402 status = mmalplay_connection_create(ctx, connections.connection[i].out, connections.connection[i].in,
403 connections.connection[i].flags);
404 if (status != MMAL_SUCCESS)
405 goto error;
406 }
407
408 /* Enable our connections */
409 for (i = 0; i < ctx->connection_num; i++)
410 {
411 status = mmal_connection_enable(ctx->connection[i]);
412 if (status != MMAL_SUCCESS)
413 goto error;
414 }
415
416 if (pstatus) *pstatus = MMAL_SUCCESS;
417 return ctx;
418
419 error:
420 mmalplay_destroy(ctx);
421 if (status == MMAL_SUCCESS) status = MMAL_ENOSPC;
422 if (pstatus) *pstatus = status;
423 return NULL;
424}
425
426/** Start playback on an instance of mmalplay.
427 * Note: this is test code. Do not use it in your app. It *will* change or even be removed without notice.
428 */
429MMAL_STATUS_T mmalplay_play(MMALPLAY_T *ctx)
430{
431 MMAL_STATUS_T status = MMAL_SUCCESS;
432 unsigned int i;
433
434 LOG_TRACE("%p, %s", ctx, ctx->uri);
435
436 ctx->time_playback = vcos_getmicrosecs();
437
438 /* Start the clocks */
439 if (ctx->video_clock)
440 mmal_port_parameter_set_boolean(ctx->video_clock, MMAL_PARAMETER_CLOCK_ACTIVE, MMAL_TRUE);
441 if (ctx->audio_clock)
442 mmal_port_parameter_set_boolean(ctx->audio_clock, MMAL_PARAMETER_CLOCK_ACTIVE, MMAL_TRUE);
443
444 while(1)
445 {
446 MMAL_BUFFER_HEADER_T *buffer;
447
448 vcos_semaphore_wait(&ctx->event);
449 if (ctx->stop || ctx->status != MMAL_SUCCESS)
450 {
451 status = ctx->status;
452 break;
453 }
454
455 /* Loop through all the connections */
456 for (i = 0; i < ctx->connection_num; i++)
457 {
458 MMAL_CONNECTION_T *connection = ctx->connection[i];
459
460 if (connection->flags & MMAL_CONNECTION_FLAG_TUNNELLING)
461 continue; /* Nothing else to do in tunnelling mode */
462
463 /* Send any queued buffer to the next component */
464 buffer = mmal_queue_get(connection->queue);
465 while (buffer)
466 {
467 if (buffer->cmd)
468 {
469 status = mmalplay_event_handle(connection, connection->out, buffer);
470 if (status != MMAL_SUCCESS)
471 goto error;
472 buffer = mmal_queue_get(connection->queue);
473 continue;
474 }
475
476 /* Code specific to handling of the video output port */
477 if (connection->out == ctx->video_out_port)
478 {
479 if (buffer->length)
480 ctx->decoded_frames++;
481
482 if (ctx->options.stepping)
483 getchar();
484 }
485
486 status = mmal_port_send_buffer(connection->in, buffer);
487 if (status != MMAL_SUCCESS)
488 {
489 LOG_ERROR("mmal_port_send_buffer failed (%i)", status);
490 mmal_queue_put_back(connection->queue, buffer);
491 goto error;
492 }
493 buffer = mmal_queue_get(connection->queue);
494 }
495
496 /* Send empty buffers to the output port of the connection */
497 buffer = connection->pool ? mmal_queue_get(connection->pool->queue) : NULL;
498 while (buffer)
499 {
500 status = mmal_port_send_buffer(connection->out, buffer);
501 if (status != MMAL_SUCCESS)
502 {
503 LOG_ERROR("mmal_port_send_buffer failed (%i)", status);
504 mmal_queue_put_back(connection->pool->queue, buffer);
505 goto error;
506 }
507 buffer = mmal_queue_get(connection->pool->queue);
508 }
509 }
510 }
511
512 error:
513 ctx->time_playback = vcos_getmicrosecs() - ctx->time_playback;
514
515 /* For still images we want to pause a bit once they are displayed */
516 if (ctx->is_still_image && status == MMAL_SUCCESS)
517 vcos_sleep(MMALPLAY_STILL_IMAGE_PAUSE);
518
519 return status;
520}
521
522/** Stop the playback on an instance of mmalplay.
523 * Note: this is test code. Do not use it in your app. It *will* change or even be removed without notice.
524 */
525void mmalplay_stop(MMALPLAY_T *ctx)
526{
527 ctx->stop = 1;
528 vcos_semaphore_post(&ctx->event);
529}
530
531/** Destroys an instance of mmalplay.
532 * Note: this is test code. Do not use it in your app. It *will* change or even be removed without notice.
533 */
534void mmalplay_destroy(MMALPLAY_T *ctx)
535{
536 unsigned int i;
537
538 LOG_TRACE("%p, %s", ctx, ctx->uri);
539
540 /* Disable connections */
541 for (i = ctx->connection_num; i; i--)
542 mmal_connection_disable(ctx->connection[i-1]);
543
544 LOG_INFO("--- statistics ---");
545 LOG_INFO("decoded %i frames in %.2fs (%.2ffps)", (int)ctx->decoded_frames,
546 ctx->time_playback / 1000000.0, ctx->decoded_frames * 1000000.0 / ctx->time_playback);
547
548 for (i = 0; i < ctx->connection_num; i++)
549 {
550 LOG_INFO("%s", ctx->connection[i]->name);
551 LOG_INFO("- setup time: %ims",
552 (int)(ctx->connection[i]->time_setup / 1000));
553 LOG_INFO("- enable time: %ims, disable time: %ims",
554 (int)(ctx->connection[i]->time_enable / 1000),
555 (int)(ctx->connection[i]->time_disable / 1000));
556 }
557
558 /* Destroy connections */
559 for (i = ctx->connection_num; i; i--)
560 mmal_connection_destroy(ctx->connection[i-1]);
561
562 /* Destroy components */
563 for (i = ctx->component_num; i; i--)
564 {
565 ctx->component[i-1].time_cleanup = vcos_getmicrosecs();
566 mmal_component_destroy(ctx->component[i-1].comp);
567 ctx->component[i-1].time_cleanup = vcos_getmicrosecs() - ctx->component[i-1].time_cleanup;
568 }
569
570 vcos_semaphore_delete(&ctx->event);
571
572 for (i = 0; i < ctx->component_num; i++)
573 {
574 LOG_INFO("%s:", ctx->component[i].name);
575 LOG_INFO("- setup time: %ims, cleanup time: %ims",
576 (int)(ctx->component[i].time_setup / 1000),
577 (int)(ctx->component[i].time_cleanup / 1000));
578 }
579 LOG_INFO("-----------------");
580
581 free(ctx);
582}
583
584/*****************************************************************************/
585static MMAL_COMPONENT_T *mmalplay_component_create(MMALPLAY_T *ctx,
586 const char *name, MMAL_STATUS_T *status)
587{
588 MMALPLAY_COMPONENT_T *component = &ctx->component[ctx->component_num];
589 const char *component_name = name;
590
591 LOG_TRACE("%p, %s", ctx, name);
592
593 if (ctx->component_num >= MMAL_COUNTOF(ctx->component))
594 {
595 *status = MMAL_ENOSPC;
596 return NULL;
597 }
598
599 /* Decide whether we want an image decoder instead of a video_decoder */
600 if (ctx->is_still_image &&
601 !strcmp(name, MMAL_COMPONENT_DEFAULT_VIDEO_DECODER) && !ctx->options.component_video_decoder)
602 ctx->options.component_video_decoder = MMAL_COMPONENT_DEFAULT_IMAGE_DECODER;
603
604 /* Override name if requested by the user */
605 if (!strcmp(name, MMAL_COMPONENT_DEFAULT_VIDEO_DECODER) && ctx->options.component_video_decoder)
606 component_name = ctx->options.component_video_decoder;
607 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_SPLITTER) && ctx->options.component_splitter)
608 component_name = ctx->options.component_splitter;
609 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER) && ctx->options.component_video_render)
610 component_name = ctx->options.component_video_render;
611 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_VIDEO_CONVERTER) && ctx->options.component_video_converter)
612 component_name = ctx->options.component_video_converter;
613 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_SCHEDULER) && ctx->options.component_video_scheduler)
614 component_name = ctx->options.component_video_scheduler;
615 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_AUDIO_DECODER) && ctx->options.component_audio_decoder)
616 component_name = ctx->options.component_audio_decoder;
617 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_AUDIO_RENDERER) && ctx->options.component_audio_render)
618 component_name = ctx->options.component_audio_render;
619 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_CONTAINER_READER) && ctx->options.component_container_reader)
620 component_name = ctx->options.component_container_reader;
621
622 component->comp = NULL;
623 vcos_safe_strcpy(component->name, component_name, sizeof(component->name), 0);
624 component->time_setup = vcos_getmicrosecs();
625
626 /* Create the component */
627 *status = mmal_component_create(component_name, &component->comp);
628 if(*status != MMAL_SUCCESS)
629 {
630 LOG_ERROR("couldn't create %s", component_name);
631 goto error;
632 }
633
634 if (!strcmp(name, MMAL_COMPONENT_DEFAULT_CONTAINER_READER))
635 *status = mmalplay_setup_container_reader(ctx, component->comp, ctx->uri);
636 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_CONTAINER_WRITER))
637 *status = mmalplay_setup_container_writer(ctx, component->comp, ctx->options.output_uri);
638 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_VIDEO_DECODER))
639 *status = mmalplay_setup_video_decoder(ctx, component->comp);
640 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_SPLITTER))
641 *status = mmalplay_setup_splitter(ctx, component->comp);
642 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_VIDEO_CONVERTER))
643 *status = mmalplay_setup_video_converter(ctx, component->comp);
644 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER))
645 *status = mmalplay_setup_video_render(ctx, component->comp);
646 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_SCHEDULER))
647 *status = mmalplay_setup_video_scheduler(ctx, component->comp);
648 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_AUDIO_DECODER))
649 *status = mmalplay_setup_audio_decoder(ctx, component->comp);
650 else if (!strcmp(name, MMAL_COMPONENT_DEFAULT_AUDIO_RENDERER))
651 *status = mmalplay_setup_audio_render(ctx, component->comp);
652
653 if (*status != MMAL_SUCCESS)
654 goto error;
655
656 /* Enable component */
657 *status = mmal_component_enable(component->comp);
658 if(*status)
659 {
660 LOG_ERROR("%s couldn't be enabled", component_name);
661 goto error;
662 }
663
664 /* Enable control port. The callback specified here is the function which
665 * will be called when an empty buffer header comes back to the port. */
666 component->comp->control->userdata = (void *)ctx;
667 *status = mmal_port_enable(component->comp->control, mmalplay_bh_control_cb);
668 if (*status)
669 {
670 LOG_ERROR("control port couldn't be enabled");
671 goto error;
672 }
673
674 component->time_setup = vcos_getmicrosecs() - component->time_setup;
675 ctx->component_num++;
676 return component->comp;
677
678 error:
679 component->time_setup = vcos_getmicrosecs() - component->time_setup;
680 if (component->comp)
681 mmal_component_destroy(component->comp);
682 return NULL;
683}
684
685/*****************************************************************************/
686static MMAL_STATUS_T mmalplay_connection_create(MMALPLAY_T *ctx, MMAL_PORT_T *out, MMAL_PORT_T *in, uint32_t flags)
687{
688 MMAL_CONNECTION_T **connection = &ctx->connection[ctx->connection_num];
689 uint32_t encoding_override = MMAL_ENCODING_UNKNOWN;
690 MMAL_RECT_T *rect_override = NULL;
691 MMAL_BOOL_T format_override = MMAL_FALSE;
692 MMAL_STATUS_T status;
693
694 if (ctx->connection_num >= MMAL_COUNTOF(ctx->connection))
695 return MMAL_ENOMEM;
696
697 /* Globally enable tunnelling if requested by the user */
698 flags |= ctx->options.tunnelling ? MMAL_CONNECTION_FLAG_TUNNELLING : 0;
699
700 /* Apply any override to the format specified by the user */
701 if (out == ctx->video_out_port)
702 {
703 encoding_override = ctx->options.output_format;
704 rect_override = &ctx->options.output_rect;
705 }
706 else if (out == ctx->converter_out_port)
707 {
708 encoding_override = ctx->options.render_format;
709 rect_override = &ctx->options.render_rect;
710 }
711
712 if (encoding_override != MMAL_ENCODING_UNKNOWN &&
713 encoding_override != out->format->encoding)
714 {
715 out->format->encoding = encoding_override;
716 format_override = MMAL_TRUE;
717 }
718
719 if (rect_override && rect_override->width && rect_override->height)
720 {
721 out->format->es->video.crop = *rect_override;
722 out->format->es->video.width = rect_override->width;
723 out->format->es->video.height = rect_override->height;
724 format_override = MMAL_TRUE;
725 }
726
727 if (format_override)
728 {
729 status = mmal_port_format_commit(out);
730 if (status != MMAL_SUCCESS)
731 {
732 LOG_ERROR("cannot override format on output port %s", out->name);
733 return status;
734 }
735 }
736
737 /* Create the actual connection */
738 status = mmal_connection_create(connection, out, in, flags);
739 if (status != MMAL_SUCCESS)
740 {
741 LOG_ERROR("cannot create connection");
742 return status;
743 }
744
745 /* Set our connection callback */
746 (*connection)->callback = mmalplay_connection_cb;
747 (*connection)->user_data = (void *)ctx;
748
749 log_format(out->format, out);
750 log_format(in->format, in);
751
752 ctx->connection_num++;
753 return MMAL_SUCCESS;
754}
755
756/*****************************************************************************/
757static MMAL_STATUS_T mmalplay_setup_video_decoder(MMALPLAY_T *ctx, MMAL_COMPONENT_T *decoder)
758{
759 MMAL_PARAMETER_BOOLEAN_T param_zc =
760 {{MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 1};
761 MMAL_STATUS_T status = MMAL_EINVAL;
762
763 if(!decoder->input_num || !decoder->output_num)
764 {
765 LOG_ERROR("%s doesn't have input/output ports", decoder->name);
766 goto error;
767 }
768
769 /* Enable Zero Copy if requested. This needs to be sent before enabling the port. */
770 if (!ctx->options.copy_input)
771 {
772 status = mmal_port_parameter_set(decoder->input[0], &param_zc.hdr);
773 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
774 {
775 LOG_ERROR("failed to set zero copy on %s", decoder->input[0]->name);
776 goto error;
777 }
778 }
779 if (!ctx->options.copy_output)
780 {
781 status = mmal_port_parameter_set(decoder->output[0], &param_zc.hdr);
782 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
783 {
784 LOG_ERROR("failed to set zero copy on %s", decoder->output[0]->name);
785 goto error;
786 }
787 }
788
789 ctx->video_out_port = decoder->output[0];
790 status = MMAL_SUCCESS;
791
792 error:
793 return status;
794}
795
796/*****************************************************************************/
797static MMAL_STATUS_T mmalplay_setup_splitter(MMALPLAY_T *ctx, MMAL_COMPONENT_T *splitter)
798{
799 MMAL_STATUS_T status = MMAL_EINVAL;
800
801 if(!splitter->input_num || !splitter->output_num)
802 {
803 LOG_ERROR("%s doesn't have input ports", splitter->name);
804 goto error;
805 }
806 if(splitter->output_num < ctx->options.output_num)
807 {
808 LOG_ERROR("%s doesn't have enough output ports (%u/%u)", splitter->name,
809 (unsigned int)splitter->output_num, ctx->options.output_num);
810 goto error;
811 }
812
813 /* Enable Zero Copy if requested. This needs to be sent before enabling the port. */
814 if (!ctx->options.copy_output)
815 {
816 MMAL_PARAMETER_BOOLEAN_T param_zc =
817 {{MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 1};
818 unsigned int i;
819
820 status = mmal_port_parameter_set(splitter->input[0], &param_zc.hdr);
821 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
822 {
823 LOG_ERROR("failed to set zero copy on %s", splitter->input[0]->name);
824 goto error;
825 }
826 for (i = 0; i < splitter->output_num; i++)
827 {
828 status = mmal_port_parameter_set(splitter->output[i], &param_zc.hdr);
829 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
830 {
831 LOG_ERROR("failed to set zero copy on %s", splitter->output[i]->name);
832 goto error;
833 }
834 }
835 }
836
837 status = MMAL_SUCCESS;
838
839 error:
840 return status;
841}
842
843/*****************************************************************************/
844static MMAL_STATUS_T mmalplay_setup_video_converter(MMALPLAY_T *ctx, MMAL_COMPONENT_T *converter)
845{
846 MMAL_PARAMETER_BOOLEAN_T param_zc =
847 {{MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 1};
848 MMAL_STATUS_T status = MMAL_EINVAL;
849
850 if(!converter->input_num || !converter->output_num)
851 {
852 LOG_ERROR("%s doesn't have input/output ports", converter->name);
853 goto error;
854 }
855
856 /* Enable Zero Copy if requested. This needs to be sent before enabling the port. */
857 if (!ctx->options.copy_output)
858 {
859 status = mmal_port_parameter_set(converter->input[0], &param_zc.hdr);
860 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
861 {
862 LOG_ERROR("failed to set zero copy on %s", converter->input[0]->name);
863 goto error;
864 }
865 status = mmal_port_parameter_set(converter->output[0], &param_zc.hdr);
866 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
867 {
868 LOG_ERROR("failed to set zero copy on %s", converter->output[0]->name);
869 goto error;
870 }
871 }
872
873 ctx->converter_out_port = converter->output[0];
874 status = MMAL_SUCCESS;
875
876 error:
877 return status;
878}
879
880/*****************************************************************************/
881static MMAL_STATUS_T mmalplay_setup_video_render(MMALPLAY_T *ctx, MMAL_COMPONENT_T *render)
882{
883 MMAL_STATUS_T status = MMAL_EINVAL;
884 unsigned int render_num;
885
886 if(!render->input_num)
887 {
888 LOG_ERROR("%s doesn't have input ports", render->name);
889 goto error;
890 }
891
892 render_num = video_render_num++;
893
894 /* Give higher priority to the overlay layer */
895 MMAL_DISPLAYREGION_T param;
896 param.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
897 param.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
898 param.set = MMAL_DISPLAY_SET_LAYER|MMAL_DISPLAY_SET_NUM;
899 param.layer = ctx->options.render_layer + 2; /* Offset of two to put it above the Android UI layer */
900 param.display_num = ctx->options.video_destination;
901 if (ctx->options.window)
902 {
903 param.dest_rect.x = 0;
904 param.dest_rect.width = 512;
905 param.dest_rect.height = 256;
906 param.dest_rect.y = param.dest_rect.height * render_num;
907 param.mode = MMAL_DISPLAY_MODE_LETTERBOX;
908 param.fullscreen = 0;
909 param.set |= MMAL_DISPLAY_SET_DEST_RECT|MMAL_DISPLAY_SET_MODE|MMAL_DISPLAY_SET_FULLSCREEN;
910 }
911 status = mmal_port_parameter_set( render->input[0], &param.hdr );
912 if(status != MMAL_SUCCESS && status != MMAL_ENOSYS)
913 {
914 LOG_ERROR("could not set video render layer on %s", render->input[0]->name);
915 goto error;
916 }
917
918 /* Enable Zero Copy if requested. This needs to be sent before enabling the port. */
919 if (!ctx->options.copy_output)
920 {
921 MMAL_PARAMETER_BOOLEAN_T param_zc =
922 {{MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 1};
923 status = mmal_port_parameter_set(render->input[0], &param_zc.hdr);
924 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
925 {
926 LOG_ERROR("failed to set zero copy on %s", render->input[0]->name);
927 goto error;
928 }
929 }
930
931 status = MMAL_SUCCESS;
932
933 error:
934 return status;
935}
936
937/*****************************************************************************/
938static MMAL_STATUS_T mmalplay_setup_container_reader(MMALPLAY_T *ctx,
939 MMAL_COMPONENT_T *reader, const char *uri)
940{
941 MMAL_PARAMETER_SEEK_T seek = {{MMAL_PARAMETER_SEEK, sizeof(MMAL_PARAMETER_SEEK_T)},0,0};
942 MMAL_PARAMETER_STRING_T *param = 0;
943 unsigned int param_size, track_audio, track_video;
944 MMAL_STATUS_T status = MMAL_EINVAL;
945 uint32_t encoding;
946 size_t uri_len;
947
948 if(!reader->output_num)
949 {
950 LOG_ERROR("%s doesn't have output ports", reader->name);
951 goto error;
952 }
953
954 /* Open the given URI */
955 uri_len = strlen(uri);
956 param_size = sizeof(MMAL_PARAMETER_STRING_T) + uri_len;
957 param = malloc(param_size);
958 if(!param)
959 {
960 LOG_ERROR("out of memory");
961 status = MMAL_ENOMEM;
962 goto error;
963 }
964 memset(param, 0, param_size);
965 param->hdr.id = MMAL_PARAMETER_URI;
966 param->hdr.size = param_size;
967 vcos_safe_strcpy(param->str, uri, uri_len + 1, 0);
968 status = mmal_port_parameter_set(reader->control, &param->hdr);
969 if(status != MMAL_SUCCESS && status != MMAL_ENOSYS)
970 {
971 LOG_ERROR("%s could not open file %s", reader->name, uri);
972 goto error;
973 }
974 status = MMAL_SUCCESS;
975
976 /* Look for a video track */
977 for(track_video = 0; track_video < reader->output_num; track_video++)
978 if(reader->output[track_video]->format->type == MMAL_ES_TYPE_VIDEO) break;
979 if(track_video != reader->output_num)
980 {
981 ctx->reader_video = reader->output[track_video];
982 /* Try to detect still images */
983 encoding = ctx->reader_video->format->encoding;
984 if (encoding == MMAL_ENCODING_JPEG ||
985 encoding == MMAL_ENCODING_GIF ||
986 encoding == MMAL_ENCODING_PNG ||
987 encoding == MMAL_ENCODING_PPM ||
988 encoding == MMAL_ENCODING_TGA ||
989 encoding == MMAL_ENCODING_BMP)
990 ctx->is_still_image = 1;
991 }
992
993 /* Look for an audio track */
994 for(track_audio = 0; track_audio < reader->output_num; track_audio++)
995 if(reader->output[track_audio]->format->type == MMAL_ES_TYPE_AUDIO) break;
996 if(track_audio != reader->output_num)
997 ctx->reader_audio = reader->output[track_audio];
998
999 if(track_video == reader->output_num &&
1000 track_audio == reader->output_num)
1001 {
1002 LOG_ERROR("no track to decode");
1003 status = MMAL_EINVAL;
1004 goto error;
1005 }
1006
1007 LOG_INFO("----Reader input port format-----");
1008 if(track_video != reader->output_num)
1009 log_format(reader->output[track_video]->format, 0);
1010 if(track_audio != reader->output_num)
1011 log_format(reader->output[track_audio]->format, 0);
1012
1013 if(ctx->options.seeking)
1014 {
1015 LOG_DEBUG("seek to %fs", ctx->options.seeking);
1016 seek.offset = ctx->options.seeking * INT64_C(1000000);
1017 status = mmal_port_parameter_set(reader->control, &seek.hdr);
1018 if(status != MMAL_SUCCESS)
1019 LOG_ERROR("failed to seek to %fs", ctx->options.seeking);
1020 }
1021
1022 error:
1023 if(param)
1024 free(param);
1025 return status;
1026}
1027
1028/*****************************************************************************/
1029static MMAL_STATUS_T mmalplay_setup_container_writer(MMALPLAY_T *ctx,
1030 MMAL_COMPONENT_T *writer, const char *uri)
1031{
1032 MMAL_PARAMETER_URI_T *param = 0;
1033 unsigned int param_size;
1034 MMAL_STATUS_T status = MMAL_EINVAL;
1035 size_t uri_len;
1036 MMAL_PARAM_UNUSED(ctx);
1037
1038 if(!writer->input_num)
1039 {
1040 LOG_ERROR("%s doesn't have input ports", writer->name);
1041 goto error;
1042 }
1043
1044 /* Open the given URI */
1045 uri_len = strlen(uri);
1046 param_size = sizeof(MMAL_PARAMETER_HEADER_T) + uri_len;
1047 param = malloc(param_size);
1048 if(!param)
1049 {
1050 LOG_ERROR("out of memory");
1051 status = MMAL_ENOMEM;
1052 goto error;
1053 }
1054 memset(param, 0, param_size);
1055 param->hdr.id = MMAL_PARAMETER_URI;
1056 param->hdr.size = param_size;
1057 vcos_safe_strcpy(param->uri, uri, uri_len + 1, 0);
1058 status = mmal_port_parameter_set(writer->control, &param->hdr);
1059 if(status != MMAL_SUCCESS)
1060 {
1061 LOG_ERROR("could not open file %s", uri);
1062 goto error;
1063 }
1064
1065 error:
1066 if(param)
1067 free(param);
1068 return status;
1069}
1070
1071/*****************************************************************************/
1072static MMAL_STATUS_T mmalplay_setup_video_scheduler(MMALPLAY_T *ctx, MMAL_COMPONENT_T *scheduler)
1073{
1074 MMAL_STATUS_T status = MMAL_EINVAL;
1075
1076 if (!scheduler->input_num || !scheduler->output_num || !scheduler->clock_num)
1077 {
1078 LOG_ERROR("%s doesn't have input/output/clock ports", scheduler->name);
1079 goto error;
1080 }
1081
1082 /* Enable Zero Copy if requested. This needs to be sent before enabling the port. */
1083 if (!ctx->options.copy_output)
1084 {
1085 status = mmal_port_parameter_set_boolean(scheduler->input[0],
1086 MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE);
1087 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
1088 {
1089 LOG_ERROR("failed to set zero copy on %s", scheduler->input[0]->name);
1090 goto error;
1091 }
1092 status = mmal_port_parameter_set_boolean(scheduler->output[0],
1093 MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE);
1094 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
1095 {
1096 LOG_ERROR("failed to set zero copy on %s", scheduler->output[0]->name);
1097 goto error;
1098 }
1099 }
1100
1101 /* Save a copy of the clock port to connect to the audio clock port */
1102 ctx->video_clock = scheduler->clock[0];
1103
1104 status = MMAL_SUCCESS;
1105
1106 error:
1107 return status;
1108}
1109
1110/*****************************************************************************/
1111static MMAL_STATUS_T mmalplay_setup_audio_decoder(MMALPLAY_T *ctx, MMAL_COMPONENT_T *decoder)
1112{
1113 MMAL_STATUS_T status = MMAL_EINVAL;
1114
1115 if (!decoder->input_num || !decoder->output_num)
1116 {
1117 LOG_ERROR("%s doesn't have input/output ports", decoder->name);
1118 goto error;
1119 }
1120
1121 if (ctx->options.audio_passthrough)
1122 {
1123 status = mmal_port_parameter_set_boolean(decoder->control,
1124 MMAL_PARAMETER_AUDIO_PASSTHROUGH, MMAL_TRUE);
1125 if (status != MMAL_SUCCESS)
1126 LOG_INFO("could not set audio passthrough mode");
1127 }
1128
1129 /* Enable Zero Copy if requested. This needs to be sent before enabling the port. */
1130 if (!ctx->options.copy_input)
1131 {
1132 status = mmal_port_parameter_set_boolean(decoder->input[0],
1133 MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE);
1134 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
1135 {
1136 LOG_ERROR("failed to set zero copy on %s", decoder->input[0]->name);
1137 goto error;
1138 }
1139 }
1140 if (!ctx->options.copy_output)
1141 {
1142 status = mmal_port_parameter_set_boolean(decoder->output[0],
1143 MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE);
1144 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
1145 {
1146 LOG_ERROR("failed to set zero copy on %s", decoder->output[0]->name);
1147 goto error;
1148 }
1149 }
1150
1151 status = MMAL_SUCCESS;
1152
1153 error:
1154 return status;
1155}
1156
1157/*****************************************************************************/
1158static MMAL_STATUS_T mmalplay_setup_audio_render(MMALPLAY_T *ctx, MMAL_COMPONENT_T *render)
1159{
1160 MMAL_STATUS_T status = MMAL_EINVAL;
1161
1162 /* Set the audio destination - not all audio render components support this */
1163 if (ctx->options.audio_destination)
1164 {
1165 status = mmal_port_parameter_set_string(render->control,
1166 MMAL_PARAMETER_AUDIO_DESTINATION, ctx->options.audio_destination);
1167 if (status != MMAL_SUCCESS)
1168 LOG_INFO("could not set audio destination");
1169 }
1170
1171 /* Enable Zero Copy if requested. This needs to be sent before enabling the port. */
1172 if (!ctx->options.copy_output)
1173 {
1174 status = mmal_port_parameter_set_boolean(render->input[0],
1175 MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE);
1176 if (status != MMAL_SUCCESS && status != MMAL_ENOSYS)
1177 {
1178 LOG_ERROR("failed to set zero copy on %s", render->input[0]->name);
1179 goto error;
1180 }
1181 }
1182
1183 if (render->clock_num)
1184 ctx->audio_clock = render->clock[0];
1185 else
1186 LOG_ERROR("%s doesn't have a clock port", render->name);
1187
1188 status = MMAL_SUCCESS;
1189
1190 error:
1191 return status;
1192}
1193
1194/*****************************************************************************/
1195static void log_format(MMAL_ES_FORMAT_T *format, MMAL_PORT_T *port)
1196{
1197 const char *name_type;
1198
1199 if(port)
1200 LOG_INFO("%s:%s:%i", port->component->name,
1201 port->type == MMAL_PORT_TYPE_CONTROL ? "ctr" :
1202 port->type == MMAL_PORT_TYPE_INPUT ? "in" :
1203 port->type == MMAL_PORT_TYPE_OUTPUT ? "out" : "invalid",
1204 (int)port->index);
1205
1206 switch(format->type)
1207 {
1208 case MMAL_ES_TYPE_AUDIO: name_type = "audio"; break;
1209 case MMAL_ES_TYPE_VIDEO: name_type = "video"; break;
1210 case MMAL_ES_TYPE_SUBPICTURE: name_type = "subpicture"; break;
1211 default: name_type = "unknown"; break;
1212 }
1213
1214 LOG_INFO("type: %s, fourcc: %4.4s", name_type, (char *)&format->encoding);
1215 LOG_INFO(" bitrate: %i, framed: %i", format->bitrate,
1216 !!(format->flags & MMAL_ES_FORMAT_FLAG_FRAMED));
1217 LOG_INFO(" extra data: %i, %p", format->extradata_size, format->extradata);
1218 switch(format->type)
1219 {
1220 case MMAL_ES_TYPE_AUDIO:
1221 LOG_INFO(" samplerate: %i, channels: %i, bps: %i, block align: %i",
1222 format->es->audio.sample_rate, format->es->audio.channels,
1223 format->es->audio.bits_per_sample, format->es->audio.block_align);
1224 break;
1225
1226 case MMAL_ES_TYPE_VIDEO:
1227 LOG_INFO(" width: %i, height: %i, (%i,%i,%i,%i)",
1228 format->es->video.width, format->es->video.height,
1229 format->es->video.crop.x, format->es->video.crop.y,
1230 format->es->video.crop.width, format->es->video.crop.height);
1231 LOG_INFO(" pixel aspect ratio: %i/%i, frame rate: %i/%i",
1232 format->es->video.par.num, format->es->video.par.den,
1233 format->es->video.frame_rate.num, format->es->video.frame_rate.den);
1234 break;
1235
1236 case MMAL_ES_TYPE_SUBPICTURE:
1237 break;
1238
1239 default: break;
1240 }
1241
1242 if(!port)
1243 return;
1244
1245 LOG_INFO(" buffers num: %i(opt %i, min %i), size: %i(opt %i, min: %i), align: %i",
1246 port->buffer_num, port->buffer_num_recommended, port->buffer_num_min,
1247 port->buffer_size, port->buffer_size_recommended, port->buffer_size_min,
1248 port->buffer_alignment_min);
1249}
1250