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 | |
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 | |
42 | VCOS_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 | |
56 | static unsigned int video_render_num; |
57 | |
58 | /** Structure describing a mmal component */ |
59 | typedef 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 */ |
70 | struct 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 | |
98 | typedef 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 | |
113 | static MMAL_COMPONENT_T *mmalplay_component_create(MMALPLAY_T *ctx, const char *name, MMAL_STATUS_T *status); |
114 | static 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 */ |
117 | static 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 */ |
120 | static 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 */ |
123 | static MMAL_STATUS_T mmalplay_setup_video_decoder(MMALPLAY_T *ctx, MMAL_COMPONENT_T *); |
124 | /* Utility function to setup the splitter component */ |
125 | static MMAL_STATUS_T mmalplay_setup_splitter(MMALPLAY_T *ctx, MMAL_COMPONENT_T *); |
126 | /* Utility function to setup the video converter component */ |
127 | static MMAL_STATUS_T mmalplay_setup_video_converter(MMALPLAY_T *ctx, MMAL_COMPONENT_T *); |
128 | /* Utility function to setup the video render component */ |
129 | static MMAL_STATUS_T mmalplay_setup_video_render(MMALPLAY_T *ctx, MMAL_COMPONENT_T *); |
130 | /* Utility function to setup the video scheduler component */ |
131 | static MMAL_STATUS_T mmalplay_setup_video_scheduler(MMALPLAY_T *ctx, MMAL_COMPONENT_T *); |
132 | /* Utility function to setup the audio decoder component */ |
133 | static MMAL_STATUS_T mmalplay_setup_audio_decoder(MMALPLAY_T *ctx, MMAL_COMPONENT_T *); |
134 | /* Utility function to setup the audio render component */ |
135 | static MMAL_STATUS_T mmalplay_setup_audio_render(MMALPLAY_T *ctx, MMAL_COMPONENT_T *); |
136 | |
137 | static 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. */ |
142 | static 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. */ |
164 | static 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 | /*****************************************************************************/ |
171 | static 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 | |
201 | static 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 | |
235 | static 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 | |
322 | static 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 | */ |
353 | MMALPLAY_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 | */ |
429 | MMAL_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 | */ |
525 | void 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 | */ |
534 | void 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 | /*****************************************************************************/ |
585 | static 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 | /*****************************************************************************/ |
686 | static 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 | /*****************************************************************************/ |
757 | static 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], ¶m_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], ¶m_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 | /*****************************************************************************/ |
797 | static 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], ¶m_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], ¶m_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 | /*****************************************************************************/ |
844 | static 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], ¶m_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], ¶m_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 | /*****************************************************************************/ |
881 | static 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], ¶m.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], ¶m_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 | /*****************************************************************************/ |
938 | static 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, ¶m->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 | /*****************************************************************************/ |
1029 | static 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, ¶m->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 | /*****************************************************************************/ |
1072 | static 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 | /*****************************************************************************/ |
1111 | static 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 | /*****************************************************************************/ |
1158 | static 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 | /*****************************************************************************/ |
1195 | static 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 | |