1 /*****************************************************************************
2 * mmal.c: MMAL-based decoder plugin for Raspberry Pi
3 *****************************************************************************
4 * Copyright © 2014 jusst technologies GmbH
7 * Authors: Dennis Hamester <dennis.hamester@gmail.com>
8 * Julian Scheel <julian@jusst.de>
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.
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.
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 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_codec.h>
32 #include <vlc_threads.h>
35 #include <interface/mmal/mmal.h>
36 #include <interface/mmal/util/mmal_util.h>
37 #include <interface/mmal/util/mmal_default_components.h>
39 /* This value must match the define in video_output/mmal.c
40 * Think twice before changing this. Incorrect values cause havoc.
42 #define NUM_ACTUAL_OPAQUE_BUFFERS 40
44 #define NUM_EXTRA_BUFFERS 10
45 #define NUM_OPAQUE_BUFFERS 20
47 #define MIN_NUM_BUFFERS_IN_TRANSIT 2
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.")
53 static int OpenDecoder(decoder_t *dec);
54 static void CloseDecoder(decoder_t *dec);
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)
65 struct decoder_sys_t {
67 MMAL_COMPONENT_T *component;
69 MMAL_POOL_T *input_pool;
71 MMAL_POOL_T *output_pool;
72 MMAL_ES_FORMAT_T *output_format;
73 MMAL_QUEUE_T *decoded_pictures;
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);
82 /* VLC decoder callback */
83 static picture_t *decode(decoder_t *dec, block_t **block);
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);
90 static int OpenDecoder(decoder_t *dec)
92 int ret = VLC_SUCCESS;
94 MMAL_PARAMETER_BOOLEAN_T error_concealment;
95 MMAL_PARAMETER_UINT32_T extra_buffers;
98 if (dec->fmt_in.i_cat != VIDEO_ES)
101 if (dec->fmt_in.i_codec != VLC_CODEC_MPGV &&
102 dec->fmt_in.i_codec != VLC_CODEC_H264)
105 sys = calloc(1, sizeof(decoder_sys_t));
111 dec->b_need_packetized = true;
113 sys->opaque = var_InheritBool(dec, MMAL_ZEROCOPY_NAME);
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));
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));
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;
138 sys->input->format->encoding = MMAL_ENCODING_H264;
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;
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));
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));
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));
170 sys->input->buffer_size = sys->input->buffer_size_recommended;
171 sys->input->buffer_num = __MAX(sys->input->buffer_num_recommended, 80);
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));
181 sys->output = sys->component->output[0];
182 sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
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));
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));
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));
213 sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
214 sys->decoded_pictures = mmal_queue_create();
217 dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
218 dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
220 dec->fmt_out.i_codec = VLC_CODEC_I420;
221 dec->fmt_out.video.i_chroma = VLC_CODEC_I420;
224 dec->fmt_out.i_cat = VIDEO_ES;
225 dec->pf_decode_video = decode;
227 vlc_mutex_init_recursive(&sys->mutex);
230 if (ret != VLC_SUCCESS)
236 static void CloseDecoder(decoder_t *dec)
238 decoder_sys_t *sys = dec->p_sys;
239 MMAL_BUFFER_HEADER_T *buffer;
244 if (sys->component && sys->component->control->is_enabled)
245 mmal_port_disable(sys->component->control);
247 if (sys->input && sys->input->is_enabled)
248 mmal_port_disable(sys->input);
250 if (sys->output && sys->output->is_enabled)
251 mmal_port_disable(sys->output);
253 if (sys->component && sys->component->is_enabled)
254 mmal_component_disable(sys->component);
257 mmal_pool_destroy(sys->input_pool);
259 if (sys->output_format)
260 mmal_format_free(sys->output_format);
262 /* Free pictures which are decoded but have not yet been sent
264 while (buffer = mmal_queue_get(sys->decoded_pictures)) {
265 picture_t *pic = (picture_t *)buffer->user_data;
266 picture_Release(pic);
268 buffer->user_data = NULL;
269 buffer->alloc_size = 0;
271 mmal_buffer_header_release(buffer);
274 if (sys->decoded_pictures)
275 mmal_queue_destroy(sys->decoded_pictures);
277 if (sys->output_pool)
278 mmal_pool_destroy(sys->output_pool);
281 mmal_component_release(sys->component);
288 static int change_output_format(decoder_t *dec)
290 decoder_sys_t *sys = dec->p_sys;
291 MMAL_STATUS_T status;
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));
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));
313 sys->output->buffer_num = NUM_ACTUAL_OPAQUE_BUFFERS;
314 pool_size = sys->output->buffer_num_recommended + NUM_EXTRA_BUFFERS;
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;
321 sys->output->buffer_size = sys->output->buffer_size_recommended;
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));
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);
335 dec->i_extra_picture_buffers = sys->output_pool->headers_num;
337 if (dec->fmt_in.i_codec == VLC_CODEC_H264)
338 dec->i_extra_picture_buffers -= 16;
340 dec->i_extra_picture_buffers -= 4;
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);
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;
360 mmal_format_free(sys->output_format);
361 sys->output_format = NULL;
366 static int send_output_buffer(decoder_t *dec)
368 decoder_sys_t *sys = dec->p_sys;
369 MMAL_BUFFER_HEADER_T *buffer;
371 MMAL_STATUS_T status;
374 buffer = mmal_queue_get(sys->output_pool->queue);
376 msg_Warn(dec, "Failed to get new buffer");
381 picture = decoder_NewPicture(dec);
383 msg_Warn(dec, "Failed to get new picture");
384 mmal_buffer_header_release(buffer);
389 mmal_buffer_header_reset(buffer);
390 buffer->user_data = picture;
392 buffer->alloc_size = sys->output->buffer_size;
393 buffer->data = picture->p[0].p_pixels;
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);
409 static void fill_output_port(decoder_t *dec)
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;
421 if (buffers_to_send > buffers_available)
422 buffers_to_send = buffers_available;
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);
429 for (i = 0; i < buffers_to_send; ++i)
430 if (send_output_buffer(dec) < 0)
434 static picture_t *decode(decoder_t *dec, block_t **pblock)
436 decoder_sys_t *sys = dec->p_sys;
438 MMAL_BUFFER_HEADER_T *buffer;
441 MMAL_STATUS_T status;
442 picture_t *ret = NULL;
445 * Configure output port if necessary
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);
455 * Send output buffers
457 if (sys->output_pool) {
458 vlc_mutex_lock(&sys->mutex);
459 buffer = mmal_queue_get(sys->decoded_pictures);
461 ret = (picture_t *)buffer->user_data;
462 ret->date = buffer->pts;
464 buffer->user_data = NULL;
465 buffer->alloc_size = 0;
467 mmal_buffer_header_release(buffer);
470 fill_output_port(dec);
471 vlc_mutex_unlock(&sys->mutex);
490 if (block->i_flags & BLOCK_FLAG_CORRUPTED)
491 flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED;
493 if (block->i_flags & BLOCK_FLAG_DISCONTINUITY)
494 flags |= MMAL_BUFFER_HEADER_FLAG_DISCONTINUITY;
496 vlc_mutex_lock(&sys->mutex);
497 while (block->i_buffer > 0) {
498 buffer = mmal_queue_timedwait(sys->input_pool->queue, 2);
500 msg_Err(dec, "Failed to retrieve buffer header for input data");
503 mmal_buffer_header_reset(buffer);
505 buffer->pts = block->i_pts;
506 buffer->dts = block->i_dts;
507 buffer->alloc_size = sys->input->buffer_size;
509 len = block->i_buffer;
510 if (len > buffer->alloc_size)
511 len = buffer->alloc_size;
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;
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));
528 vlc_mutex_unlock(&sys->mutex);
534 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
536 decoder_t *dec = (decoder_t *)port->userdata;
537 MMAL_STATUS_T status;
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));
545 mmal_buffer_header_release(buffer);
548 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
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;
555 mmal_buffer_header_release(buffer);
556 vlc_mutex_lock(&sys->mutex);
558 block_Release(block);
559 vlc_mutex_unlock(&sys->mutex);
562 static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
564 decoder_t *dec = (decoder_t *)port->userdata;
565 decoder_sys_t *sys = dec->p_sys;
567 MMAL_EVENT_FORMAT_CHANGED_T *fmt;
568 MMAL_ES_FORMAT_T *format;
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);
576 picture = (picture_t *)buffer->user_data;
577 decoder_DeletePicture(dec, picture);
578 buffer->user_data = NULL;
579 buffer->alloc_size = 0;
581 mmal_buffer_header_release(buffer);
583 } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
584 fmt = mmal_event_format_changed_get(buffer);
586 format = mmal_format_alloc();
587 mmal_format_full_copy(format, fmt->format);
590 format->encoding = MMAL_ENCODING_OPAQUE;
592 sys->output_format = format;
594 mmal_buffer_header_release(buffer);
596 mmal_buffer_header_release(buffer);
598 vlc_mutex_unlock(&sys->mutex);