]> git.sesse.net Git - vlc/blob - modules/codec/mmal.c
smf: reorder code, no functional changes
[vlc] / modules / codec / mmal.c
1 /*****************************************************************************
2  * mmal.c: MMAL-based decoder plugin for Raspberry Pi
3  *****************************************************************************
4  * Copyright © 2014 jusst technologies GmbH
5  * $Id$
6  *
7  * Authors: Dennis Hamester <dennis.hamester@gmail.com>
8  *          Julian Scheel <julian@jusst.de>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_codec.h>
32 #include <vlc_threads.h>
33
34 #include <bcm_host.h>
35 #include <interface/mmal/mmal.h>
36 #include <interface/mmal/util/mmal_util.h>
37 #include <interface/mmal/util/mmal_default_components.h>
38
39 /* This value must match the define in video_output/mmal.c
40  * Think twice before changing this. Incorrect values cause havoc.
41  */
42 #define NUM_ACTUAL_OPAQUE_BUFFERS 40
43
44 #define NUM_EXTRA_BUFFERS 10
45 #define NUM_OPAQUE_BUFFERS 20
46
47 #define MIN_NUM_BUFFERS_IN_TRANSIT 2
48
49 #define MMAL_ZEROCOPY_NAME "mmal-zerocopy"
50 #define MMAL_ZEROCOPY_TEXT N_("Decode frames directly into RPI VideoCore instead of host memory.")
51 #define MMAL_ZEROCOPY_LONGTEXT N_("Decode frames directly into RPI VideoCore instead of host memory. This option must only be used with the MMAL video output plugin.")
52
53 static int OpenDecoder(decoder_t *dec);
54 static void CloseDecoder(decoder_t *dec);
55
56 vlc_module_begin()
57     set_shortname(N_("MMAL decoder"))
58     set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
59     set_capability("decoder", 90)
60     add_shortcut("mmal_decoder")
61     add_bool(MMAL_ZEROCOPY_NAME, true, MMAL_ZEROCOPY_TEXT, MMAL_ZEROCOPY_LONGTEXT, false)
62     set_callbacks(OpenDecoder, CloseDecoder)
63 vlc_module_end()
64
65 struct decoder_sys_t {
66     bool opaque;
67     MMAL_COMPONENT_T *component;
68     MMAL_PORT_T *input;
69     MMAL_POOL_T *input_pool;
70     MMAL_PORT_T *output;
71     MMAL_POOL_T *output_pool;
72     MMAL_ES_FORMAT_T *output_format;
73     MMAL_QUEUE_T *decoded_pictures;
74     vlc_mutex_t mutex;
75 };
76
77 /* Utilities */
78 static int change_output_format(decoder_t *dec);
79 static int send_output_buffer(decoder_t *dec);
80 static void fill_output_port(decoder_t *dec);
81
82 /* VLC decoder callback */
83 static picture_t *decode(decoder_t *dec, block_t **block);
84
85 /* MMAL callbacks */
86 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
87 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
88 static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
89
90 static int OpenDecoder(decoder_t *dec)
91 {
92     int ret = VLC_SUCCESS;
93     decoder_sys_t *sys;
94     MMAL_PARAMETER_BOOLEAN_T error_concealment;
95     MMAL_PARAMETER_UINT32_T extra_buffers;
96     MMAL_STATUS_T status;
97
98     if (dec->fmt_in.i_cat != VIDEO_ES)
99         return VLC_EGENERIC;
100
101     if (dec->fmt_in.i_codec != VLC_CODEC_MPGV &&
102             dec->fmt_in.i_codec != VLC_CODEC_H264)
103         return VLC_EGENERIC;
104
105     sys = calloc(1, sizeof(decoder_sys_t));
106     if (!sys) {
107         ret = VLC_ENOMEM;
108         goto out;
109     }
110     dec->p_sys = sys;
111     dec->b_need_packetized = true;
112
113     sys->opaque = var_InheritBool(dec, MMAL_ZEROCOPY_NAME);
114     bcm_host_init();
115
116     status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
117     if (status != MMAL_SUCCESS) {
118         msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
119                 MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
120         ret = VLC_EGENERIC;
121         goto out;
122     }
123
124     sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
125     status = mmal_port_enable(sys->component->control, control_port_cb);
126     if (status != MMAL_SUCCESS) {
127         msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
128                 sys->component->control->name, status, mmal_status_to_string(status));
129         ret = VLC_EGENERIC;
130         goto out;
131     }
132
133     sys->input = sys->component->input[0];
134     sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
135     if (dec->fmt_in.i_codec == VLC_CODEC_MPGV)
136         sys->input->format->encoding = MMAL_ENCODING_MP2V;
137     else
138         sys->input->format->encoding = MMAL_ENCODING_H264;
139
140     if (dec->fmt_in.i_codec == VLC_CODEC_H264) {
141         if (dec->fmt_in.i_extra > 0) {
142             status = mmal_format_extradata_alloc(sys->input->format,
143                     dec->fmt_in.i_extra);
144             if (status == MMAL_SUCCESS) {
145                 memcpy(sys->input->format->extradata, dec->fmt_in.p_extra,
146                         dec->fmt_in.i_extra);
147                 sys->input->format->extradata_size = dec->fmt_in.i_extra;
148             } else {
149                 msg_Err(dec, "Failed to allocate extra format data on input port %s (status=%"PRIx32" %s)",
150                         sys->input->name, status, mmal_status_to_string(status));
151             }
152         } else {
153             error_concealment.hdr.id = MMAL_PARAMETER_VIDEO_DECODE_ERROR_CONCEALMENT;
154             error_concealment.hdr.size = sizeof(MMAL_PARAMETER_BOOLEAN_T);
155             error_concealment.enable = MMAL_FALSE;
156             status = mmal_port_parameter_set(sys->input, &error_concealment.hdr);
157             if (status != MMAL_SUCCESS)
158                 msg_Err(dec, "Failed to disable error concealment (status=%"PRIx32" %s)",
159                         status, mmal_status_to_string(status));
160         }
161     }
162
163     status = mmal_port_format_commit(sys->input);
164     if (status != MMAL_SUCCESS) {
165         msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
166                 sys->input->name, status, mmal_status_to_string(status));
167         ret = VLC_EGENERIC;
168         goto out;
169     }
170     sys->input->buffer_size = sys->input->buffer_size_recommended;
171     sys->input->buffer_num = __MAX(sys->input->buffer_num_recommended, 80);
172
173     status = mmal_port_enable(sys->input, input_port_cb);
174     if (status != MMAL_SUCCESS) {
175         msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
176                 sys->input->name, status, mmal_status_to_string(status));
177         ret = VLC_EGENERIC;
178         goto out;
179     }
180
181     sys->output = sys->component->output[0];
182     sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
183
184     if (sys->opaque) {
185         extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS;
186         extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T);
187         extra_buffers.value = NUM_ACTUAL_OPAQUE_BUFFERS - NUM_OPAQUE_BUFFERS;
188         status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
189         if (status != MMAL_SUCCESS) {
190             msg_Err(dec, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
191                     status, mmal_status_to_string(status));
192             ret = VLC_EGENERIC;
193             goto out;
194         }
195     }
196
197     status = mmal_port_enable(sys->output, output_port_cb);
198     if (status != MMAL_SUCCESS) {
199         msg_Err(dec, "Failed to enable output port %s (status=%"PRIx32" %s)",
200                 sys->output->name, status, mmal_status_to_string(status));
201         ret = VLC_EGENERIC;
202         goto out;
203     }
204
205     status = mmal_component_enable(sys->component);
206     if (status != MMAL_SUCCESS) {
207         msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
208                 sys->component->name, status, mmal_status_to_string(status));
209         ret = VLC_EGENERIC;
210         goto out;
211     }
212
213     sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
214     sys->decoded_pictures = mmal_queue_create();
215
216     if (sys->opaque) {
217         dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
218         dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
219     } else {
220         dec->fmt_out.i_codec = VLC_CODEC_I420;
221         dec->fmt_out.video.i_chroma = VLC_CODEC_I420;
222     }
223
224     dec->fmt_out.i_cat = VIDEO_ES;
225     dec->pf_decode_video = decode;
226
227     vlc_mutex_init_recursive(&sys->mutex);
228
229 out:
230     if (ret != VLC_SUCCESS)
231         CloseDecoder(dec);
232
233     return ret;
234 }
235
236 static void CloseDecoder(decoder_t *dec)
237 {
238     decoder_sys_t *sys = dec->p_sys;
239     MMAL_BUFFER_HEADER_T *buffer;
240
241     if (!sys)
242         return;
243
244     if (sys->component && sys->component->control->is_enabled)
245         mmal_port_disable(sys->component->control);
246
247     if (sys->input && sys->input->is_enabled)
248         mmal_port_disable(sys->input);
249
250     if (sys->output && sys->output->is_enabled)
251         mmal_port_disable(sys->output);
252
253     if (sys->component && sys->component->is_enabled)
254         mmal_component_disable(sys->component);
255
256     if (sys->input_pool)
257         mmal_pool_destroy(sys->input_pool);
258
259     if (sys->output_format)
260         mmal_format_free(sys->output_format);
261
262     /* Free pictures which are decoded but have not yet been sent
263      * out to the core */
264     while (buffer = mmal_queue_get(sys->decoded_pictures)) {
265         picture_t *pic = (picture_t *)buffer->user_data;
266         picture_Release(pic);
267
268         buffer->user_data = NULL;
269         buffer->alloc_size = 0;
270         buffer->data = NULL;
271         mmal_buffer_header_release(buffer);
272     }
273
274     if (sys->decoded_pictures)
275         mmal_queue_destroy(sys->decoded_pictures);
276
277     if (sys->output_pool)
278         mmal_pool_destroy(sys->output_pool);
279
280     if (sys->component)
281         mmal_component_release(sys->component);
282
283     free(sys);
284
285     bcm_host_deinit();
286 }
287
288 static int change_output_format(decoder_t *dec)
289 {
290     decoder_sys_t *sys = dec->p_sys;
291     MMAL_STATUS_T status;
292     int pool_size;
293     int ret = 0;
294
295     status = mmal_port_disable(sys->output);
296     if (status != MMAL_SUCCESS) {
297         msg_Err(dec, "Failed to disable output port (status=%"PRIx32" %s)",
298                 status, mmal_status_to_string(status));
299         ret = -1;
300         goto out;
301     }
302
303     mmal_format_full_copy(sys->output->format, sys->output_format);
304     status = mmal_port_format_commit(sys->output);
305     if (status != MMAL_SUCCESS) {
306         msg_Err(dec, "Failed to commit output format (status=%"PRIx32" %s)",
307                 status, mmal_status_to_string(status));
308         ret = -1;
309         goto out;
310     }
311
312     if (sys->opaque) {
313         sys->output->buffer_num = NUM_ACTUAL_OPAQUE_BUFFERS;
314         pool_size = sys->output->buffer_num_recommended + NUM_EXTRA_BUFFERS;
315     } else {
316         sys->output->buffer_num = __MAX(sys->output->buffer_num_recommended,
317                 MIN_NUM_BUFFERS_IN_TRANSIT);
318         pool_size = sys->output->buffer_num + NUM_EXTRA_BUFFERS;
319     }
320
321     sys->output->buffer_size = sys->output->buffer_size_recommended;
322
323     status = mmal_port_enable(sys->output, output_port_cb);
324     if (status != MMAL_SUCCESS) {
325         msg_Err(dec, "Failed to enable output port (status=%"PRIx32" %s)",
326                 status, mmal_status_to_string(status));
327         ret = -1;
328         goto out;
329     }
330
331     if (!sys->output_pool) {
332         sys->output_pool = mmal_pool_create(pool_size, 0);
333         msg_Dbg(dec, "Created output pool with %d pictures", sys->output_pool->headers_num);
334
335         dec->i_extra_picture_buffers = sys->output_pool->headers_num;
336
337         if (dec->fmt_in.i_codec == VLC_CODEC_H264)
338             dec->i_extra_picture_buffers -= 16;
339         else
340             dec->i_extra_picture_buffers -= 4;
341
342         dec->i_extra_picture_buffers = __MAX(dec->i_extra_picture_buffers,
343                 MIN_NUM_BUFFERS_IN_TRANSIT + 1);
344         msg_Dbg(dec, "Request %d extra pictures", dec->i_extra_picture_buffers);
345     }
346
347     dec->fmt_out.video.i_width = sys->output->format->es->video.width;
348     dec->fmt_out.video.i_height = sys->output->format->es->video.height;
349     dec->fmt_out.video.i_x_offset = sys->output->format->es->video.crop.x;
350     dec->fmt_out.video.i_y_offset = sys->output->format->es->video.crop.y;
351     dec->fmt_out.video.i_visible_width = sys->output->format->es->video.crop.width;
352     dec->fmt_out.video.i_visible_height = sys->output->format->es->video.crop.height;
353     dec->fmt_out.video.i_sar_num = sys->output->format->es->video.par.num;
354     dec->fmt_out.video.i_sar_den = sys->output->format->es->video.par.den;
355     dec->fmt_out.video.i_frame_rate = sys->output->format->es->video.frame_rate.num;
356     dec->fmt_out.video.i_frame_rate_base = sys->output->format->es->video.frame_rate.den;
357
358
359 out:
360     mmal_format_free(sys->output_format);
361     sys->output_format = NULL;
362
363     return ret;
364 }
365
366 static int send_output_buffer(decoder_t *dec)
367 {
368     decoder_sys_t *sys = dec->p_sys;
369     MMAL_BUFFER_HEADER_T *buffer;
370     picture_t *picture;
371     MMAL_STATUS_T status;
372     int ret = 0;
373
374     buffer = mmal_queue_get(sys->output_pool->queue);
375     if (!buffer) {
376         msg_Warn(dec, "Failed to get new buffer");
377         ret = -1;
378         goto out;
379     }
380
381     picture = decoder_NewPicture(dec);
382     if (!picture) {
383         msg_Warn(dec, "Failed to get new picture");
384         mmal_buffer_header_release(buffer);
385         ret = -1;
386         goto out;
387     }
388
389     mmal_buffer_header_reset(buffer);
390     buffer->user_data = picture;
391     buffer->cmd = 0;
392     buffer->alloc_size = sys->output->buffer_size;
393     buffer->data = picture->p[0].p_pixels;
394
395     status = mmal_port_send_buffer(sys->output, buffer);
396     if (status != MMAL_SUCCESS) {
397         msg_Err(dec, "Failed to send buffer to output port (status=%"PRIx32" %s)",
398                 status, mmal_status_to_string(status));
399         mmal_buffer_header_release(buffer);
400         decoder_DeletePicture(dec, picture);
401         ret = -1;
402         goto out;
403     }
404
405 out:
406     return ret;
407 }
408
409 static void fill_output_port(decoder_t *dec)
410 {
411     decoder_sys_t *sys = dec->p_sys;
412     /* allow at least 2 buffers in transit */
413     unsigned max_buffers_in_transit = __MAX(sys->output->buffer_num_recommended,
414             MIN_NUM_BUFFERS_IN_TRANSIT);
415     unsigned buffers_available = mmal_queue_length(sys->output_pool->queue);
416     unsigned buffers_in_transit = sys->output_pool->headers_num - buffers_available -
417         mmal_queue_length(sys->decoded_pictures);
418     unsigned buffers_to_send = max_buffers_in_transit - buffers_in_transit;
419     unsigned i;
420
421     if (buffers_to_send > buffers_available)
422         buffers_to_send = buffers_available;
423
424 #ifndef NDEBUG
425     msg_Dbg(dec, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)",
426                     buffers_to_send, buffers_available, buffers_in_transit,
427                     sys->output->buffer_num);
428 #endif
429     for (i = 0; i < buffers_to_send; ++i)
430         if (send_output_buffer(dec) < 0)
431             break;
432 }
433
434 static picture_t *decode(decoder_t *dec, block_t **pblock)
435 {
436     decoder_sys_t *sys = dec->p_sys;
437     block_t *block;
438     MMAL_BUFFER_HEADER_T *buffer;
439     uint32_t len;
440     uint32_t flags = 0;
441     MMAL_STATUS_T status;
442     picture_t *ret = NULL;
443
444     /*
445      * Configure output port if necessary
446      */
447     if (sys->output_format) {
448         vlc_mutex_lock(&sys->mutex);
449         if (change_output_format(dec) < 0)
450             msg_Err(dec, "Failed to change output port format");
451         vlc_mutex_unlock(&sys->mutex);
452     }
453
454     /*
455      * Send output buffers
456      */
457     if (sys->output_pool) {
458         vlc_mutex_lock(&sys->mutex);
459         buffer = mmal_queue_get(sys->decoded_pictures);
460         if (buffer) {
461             ret = (picture_t *)buffer->user_data;
462             ret->date = buffer->pts;
463
464             buffer->user_data = NULL;
465             buffer->alloc_size = 0;
466             buffer->data = NULL;
467             mmal_buffer_header_release(buffer);
468         }
469
470         fill_output_port(dec);
471         vlc_mutex_unlock(&sys->mutex);
472     }
473
474     if (ret)
475         goto out;
476
477     /*
478      * Process input
479      */
480     if (!pblock)
481         goto out;
482
483     block = *pblock;
484
485     if (!block)
486         goto out;
487
488     *pblock = NULL;
489
490     if (block->i_flags & BLOCK_FLAG_CORRUPTED)
491         flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED;
492
493     if (block->i_flags & BLOCK_FLAG_DISCONTINUITY)
494         flags |= MMAL_BUFFER_HEADER_FLAG_DISCONTINUITY;
495
496     vlc_mutex_lock(&sys->mutex);
497     while (block->i_buffer > 0) {
498         buffer = mmal_queue_timedwait(sys->input_pool->queue, 2);
499         if (!buffer) {
500             msg_Err(dec, "Failed to retrieve buffer header for input data");
501             break;
502         }
503         mmal_buffer_header_reset(buffer);
504         buffer->cmd = 0;
505         buffer->pts = block->i_pts;
506         buffer->dts = block->i_dts;
507         buffer->alloc_size = sys->input->buffer_size;
508
509         len = block->i_buffer;
510         if (len > buffer->alloc_size)
511             len = buffer->alloc_size;
512
513         buffer->data = block->p_buffer;
514         block->p_buffer += len;
515         block->i_buffer -= len;
516         buffer->length = len;
517         if (block->i_buffer == 0)
518             buffer->user_data = block;
519         buffer->flags = flags;
520
521         status = mmal_port_send_buffer(sys->input, buffer);
522         if (status != MMAL_SUCCESS) {
523             msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)",
524                     status, mmal_status_to_string(status));
525             break;
526         }
527     }
528     vlc_mutex_unlock(&sys->mutex);
529
530 out:
531     return ret;
532 }
533
534 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
535 {
536     decoder_t *dec = (decoder_t *)port->userdata;
537     MMAL_STATUS_T status;
538
539     if (buffer->cmd == MMAL_EVENT_ERROR) {
540         status = *(uint32_t *)buffer->data;
541         msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
542                 mmal_status_to_string(status));
543     }
544
545     mmal_buffer_header_release(buffer);
546 }
547
548 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
549 {
550     block_t *block = (block_t *)buffer->user_data;
551     decoder_t *dec = (decoder_t *)port->userdata;
552     decoder_sys_t *sys = dec->p_sys;
553     buffer->user_data = NULL;
554
555     mmal_buffer_header_release(buffer);
556     vlc_mutex_lock(&sys->mutex);
557     if (block)
558         block_Release(block);
559     vlc_mutex_unlock(&sys->mutex);
560 }
561
562 static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
563 {
564     decoder_t *dec = (decoder_t *)port->userdata;
565     decoder_sys_t *sys = dec->p_sys;
566     picture_t *picture;
567     MMAL_EVENT_FORMAT_CHANGED_T *fmt;
568     MMAL_ES_FORMAT_T *format;
569
570     vlc_mutex_lock(&sys->mutex);
571     if (buffer->cmd == 0) {
572         if (buffer->length > 0) {
573             mmal_queue_put(sys->decoded_pictures, buffer);
574             fill_output_port(dec);
575         } else {
576             picture = (picture_t *)buffer->user_data;
577             decoder_DeletePicture(dec, picture);
578             buffer->user_data = NULL;
579             buffer->alloc_size = 0;
580             buffer->data = NULL;
581             mmal_buffer_header_release(buffer);
582         }
583     } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
584         fmt = mmal_event_format_changed_get(buffer);
585
586         format = mmal_format_alloc();
587         mmal_format_full_copy(format, fmt->format);
588
589         if (sys->opaque)
590             format->encoding = MMAL_ENCODING_OPAQUE;
591
592         sys->output_format = format;
593
594         mmal_buffer_header_release(buffer);
595     } else {
596         mmal_buffer_header_release(buffer);
597     }
598     vlc_mutex_unlock(&sys->mutex);
599 }