1 /*****************************************************************************
2 * mmal.c: MMAL-based deinterlace plugin for Raspberry Pi
3 *****************************************************************************
4 * Copyright © 2014 jusst technologies GmbH
7 * Authors: Julian Scheel <julian@jusst.de>
8 * Dennis Hamester <dennis.hamester@gmail.com>
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_picture_pool.h>
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_filter.h>
34 #include "mmal_picture.h"
37 #include <interface/mmal/mmal.h>
38 #include <interface/mmal/util/mmal_util.h>
39 #include <interface/mmal/util/mmal_default_components.h>
41 #define NUM_EXTRA_BUFFERS 2
42 #define MIN_NUM_BUFFERS_IN_TRANSIT 2
44 static int Open(filter_t *filter);
45 static void Close(filter_t *filter);
48 set_shortname(N_("MMAL deinterlace"))
49 set_description(N_("MMAL-based deinterlace filter plugin"))
50 set_capability("video filter2", 10)
51 set_category(CAT_VIDEO)
52 set_subcategory(SUBCAT_VIDEO_VFILTER)
53 set_callbacks(Open, Close)
54 add_shortcut("deinterlace")
58 MMAL_COMPONENT_T *component;
60 MMAL_POOL_T *input_pool;
62 MMAL_POOL_T *output_pool;
64 picture_pool_t *picture_pool;
67 MMAL_QUEUE_T *filtered_pictures;
71 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
72 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
73 static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
74 static picture_t *deinterlace(filter_t *filter, picture_t *picture);
76 #define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx"
78 static int create_picture_pool(filter_t *filter)
80 picture_pool_configuration_t picture_pool_cfg;
81 filter_sys_t *sys = filter->p_sys;
82 picture_resource_t picture_res;
85 memset(&picture_res, 0, sizeof(picture_resource_t));
86 sys->pictures = calloc(sys->output->buffer_num, sizeof(picture_t *));
92 for (unsigned i = 0; i < sys->output->buffer_num; i++) {
93 picture_res.p_sys = calloc(1, sizeof(picture_sys_t));
94 if (!picture_res.p_sys) {
99 picture_res.p_sys->owner = (vlc_object_t *)filter;
100 picture_res.p_sys->queue = sys->output_pool->queue;
102 sys->pictures[i] = picture_NewFromResource(&filter->fmt_out.video,
104 if (!sys->pictures[i]) {
105 free(picture_res.p_sys);
111 memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t));
112 picture_pool_cfg.picture_count = sys->output->buffer_num;
113 picture_pool_cfg.picture = sys->pictures;
114 picture_pool_cfg.lock = mmal_picture_lock;
115 picture_pool_cfg.unlock = mmal_picture_unlock;
117 sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
118 if (!sys->picture_pool) {
119 msg_Err(filter, "Failed to create picture pool");
128 static int Open(filter_t *filter)
130 int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
131 (int64_t)1000000 * filter->fmt_in.video.i_frame_rate_base /
132 filter->fmt_in.video.i_frame_rate : 0;
134 MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
135 { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
136 MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
138 { 3, frame_duration }
140 MMAL_PARAMETER_UINT32_T extra_buffers;
142 int ret = VLC_SUCCESS;
143 MMAL_STATUS_T status;
146 msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d!", frame_duration);
148 if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
151 if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
154 sys = calloc(1, sizeof(filter_sys_t));
161 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
162 if (status != MMAL_SUCCESS) {
163 msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
164 MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
169 status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
170 if (status != MMAL_SUCCESS) {
171 msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
172 MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
177 sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
178 status = mmal_port_enable(sys->component->control, control_port_cb);
179 if (status != MMAL_SUCCESS) {
180 msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
181 sys->component->control->name, status, mmal_status_to_string(status));
186 sys->input = sys->component->input[0];
187 sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
188 if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE)
189 sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
190 sys->input->format->es->video.width = filter->fmt_in.video.i_width;
191 sys->input->format->es->video.height = filter->fmt_in.video.i_height;
192 sys->input->format->es->video.crop.x = 0;
193 sys->input->format->es->video.crop.y = 0;
194 sys->input->format->es->video.crop.width = filter->fmt_in.video.i_width;
195 sys->input->format->es->video.crop.height = filter->fmt_in.video.i_height;
196 sys->input->format->es->video.par.num = filter->fmt_in.video.i_sar_num;
197 sys->input->format->es->video.par.den = filter->fmt_in.video.i_sar_den;
199 es_format_Copy(&filter->fmt_out, &filter->fmt_in);
200 filter->fmt_out.video.i_frame_rate *= 2;
202 status = mmal_port_format_commit(sys->input);
203 if (status != MMAL_SUCCESS) {
204 msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
205 sys->input->name, status, mmal_status_to_string(status));
209 sys->input->buffer_size = sys->input->buffer_size_recommended;
211 sys->input->buffer_num = sys->input->buffer_num_recommended;
212 status = mmal_port_enable(sys->input, input_port_cb);
213 if (status != MMAL_SUCCESS) {
214 msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
215 sys->input->name, status, mmal_status_to_string(status));
220 sys->output = sys->component->output[0];
221 sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
222 mmal_format_full_copy(sys->output->format, sys->input->format);
224 if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
225 extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS;
226 extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T);
227 extra_buffers.value = NUM_EXTRA_BUFFERS;
228 status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
229 if (status != MMAL_SUCCESS) {
230 msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
231 status, mmal_status_to_string(status));
237 status = mmal_port_format_commit(sys->output);
238 if (status != MMAL_SUCCESS) {
239 msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
240 sys->input->name, status, mmal_status_to_string(status));
245 sys->output->buffer_num = sys->output->buffer_num_recommended;
246 status = mmal_port_enable(sys->output, output_port_cb);
247 if (status != MMAL_SUCCESS) {
248 msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
249 sys->output->name, status, mmal_status_to_string(status));
254 status = mmal_component_enable(sys->component);
255 if (status != MMAL_SUCCESS) {
256 msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
257 sys->component->name, status, mmal_status_to_string(status));
262 sys->output_pool = mmal_pool_create(sys->output->buffer_num,
263 sys->output->buffer_size);
264 if (!sys->output_pool) {
265 msg_Err(filter, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
266 sys->output->buffer_num, sys->output->buffer_size);
269 sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
270 sys->filtered_pictures = mmal_queue_create();
272 ret = create_picture_pool(filter);
273 if (ret != VLC_SUCCESS)
276 filter->pf_video_filter = deinterlace;
277 vlc_mutex_init_recursive(&sys->mutex);
280 if (ret != VLC_SUCCESS)
286 static void Close(filter_t *filter)
288 filter_sys_t *sys = filter->p_sys;
289 MMAL_BUFFER_HEADER_T *buffer;
294 if (sys->component && sys->component->control->is_enabled)
295 mmal_port_disable(sys->component->control);
297 if (sys->input && sys->input->is_enabled)
298 mmal_port_disable(sys->input);
300 if (sys->output && sys->output->is_enabled)
301 mmal_port_disable(sys->output);
303 if (sys->component && sys->component->is_enabled)
304 mmal_component_disable(sys->component);
306 while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
307 picture_t *pic = (picture_t *)buffer->user_data;
308 picture_Release(pic);
310 buffer->user_data = NULL;
311 buffer->alloc_size = 0;
313 mmal_buffer_header_release(buffer);
316 if (sys->filtered_pictures)
317 mmal_queue_destroy(sys->filtered_pictures);
320 mmal_pool_destroy(sys->input_pool);
322 if (sys->output_pool)
323 mmal_pool_destroy(sys->output_pool);
326 mmal_component_release(sys->component);
328 if (sys->picture_pool)
329 picture_pool_Release(sys->picture_pool);
331 vlc_mutex_destroy(&sys->mutex);
338 static int send_output_buffer(filter_t *filter)
340 filter_sys_t *sys = filter->p_sys;
341 MMAL_BUFFER_HEADER_T *buffer;
342 MMAL_STATUS_T status;
346 picture = picture_pool_Get(sys->picture_pool);
348 msg_Warn(filter, "Failed to get new picture");
352 picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate;
353 picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base;
355 buffer = picture->p_sys->buffer;
357 buffer->alloc_size = sys->output->buffer_size;
359 status = mmal_port_send_buffer(sys->output, buffer);
360 if (status != MMAL_SUCCESS) {
361 msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)",
362 status, mmal_status_to_string(status));
363 picture_Release(picture);
372 static void fill_output_port(filter_t *filter)
374 filter_sys_t *sys = filter->p_sys;
375 /* allow at least 2 buffers in transit */
376 unsigned max_buffers_in_transit = __MAX(sys->output->buffer_num,
377 MIN_NUM_BUFFERS_IN_TRANSIT);
378 unsigned buffers_available = mmal_queue_length(sys->output_pool->queue);
379 unsigned buffers_in_transit = sys->output_pool->headers_num - buffers_available -
380 mmal_queue_length(sys->filtered_pictures);
381 unsigned buffers_to_send = max_buffers_in_transit - buffers_in_transit;
384 if (buffers_to_send > buffers_available)
385 buffers_to_send = buffers_available;
388 msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)",
389 buffers_to_send, buffers_available, buffers_in_transit,
390 sys->output->buffer_num);
392 for (i = 0; i < buffers_to_send; ++i) {
393 if (send_output_buffer(filter) < 0)
398 static picture_t *deinterlace(filter_t *filter, picture_t *picture)
400 filter_sys_t *sys = filter->p_sys;
401 MMAL_BUFFER_HEADER_T *buffer;
402 picture_t *out_picture = NULL;
403 picture_t *ret = NULL;
404 MMAL_STATUS_T status;
407 * Send output buffers
409 if (sys->output_pool) {
411 vlc_mutex_lock(&sys->mutex);
412 while((buffer = mmal_queue_get(sys->filtered_pictures))) {
415 out_picture = (picture_t *)buffer->user_data;
418 out_picture->p_next = (picture_t *)buffer->user_data;
419 out_picture = out_picture->p_next;
421 out_picture->date = buffer->pts;
424 out_picture->p_next = NULL;
425 fill_output_port(filter);
426 vlc_mutex_unlock(&sys->mutex);
435 vlc_mutex_lock(&sys->mutex);
436 buffer = mmal_queue_timedwait(sys->input_pool->queue, 2);
438 msg_Err(filter, "Failed to retrieve buffer header for input picture");
442 mmal_buffer_header_reset(buffer);
443 buffer->user_data = picture;
444 buffer->pts = picture->date;
446 buffer->alloc_size = sys->input->buffer_size;
447 buffer->length = sys->input->buffer_size;
448 buffer->data = picture->p[0].p_pixels;
450 status = mmal_port_send_buffer(sys->input, buffer);
451 if (status != MMAL_SUCCESS) {
452 msg_Err(filter, "Failed to send buffer to input port (status=%"PRIx32" %s)",
453 status, mmal_status_to_string(status));
457 vlc_mutex_unlock(&sys->mutex);
461 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
463 filter_t *filter = (filter_t *)port->userdata;
464 MMAL_STATUS_T status;
466 if (buffer->cmd == MMAL_EVENT_ERROR) {
467 status = *(uint32_t *)buffer->data;
468 msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
469 mmal_status_to_string(status));
472 mmal_buffer_header_release(buffer);
475 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
477 picture_t *picture = (picture_t *)buffer->user_data;
478 filter_t *filter = (filter_t *)port->userdata;
480 buffer->user_data = NULL;
481 mmal_buffer_header_release(buffer);
483 picture_Release(picture);
486 static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
488 filter_t *filter = (filter_t *)port->userdata;
489 filter_sys_t *sys = filter->p_sys;
492 if (buffer->cmd == 0) {
493 if (buffer->length > 0) {
494 mmal_queue_put(sys->filtered_pictures, buffer);
495 fill_output_port(filter);
497 picture = (picture_t *)buffer->user_data;
498 picture_Release(picture);
499 buffer->user_data = NULL;
501 } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
502 msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled");
503 mmal_buffer_header_release(buffer);
505 mmal_buffer_header_release(buffer);