mmal/deinterlace: Implement filter flush
[vlc.git] / modules / hw / mmal / deinterlace.c
blobd0a4f241d4669c58a083984e5eaeb885661a6586
1 /*****************************************************************************
2 * mmal.c: MMAL-based deinterlace plugin for Raspberry Pi
3 *****************************************************************************
4 * Copyright © 2014 jusst technologies GmbH
5 * $Id$
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 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
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"
36 #include <bcm_host.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);
47 vlc_module_begin()
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")
55 vlc_module_end()
57 struct filter_sys_t {
58 MMAL_COMPONENT_T *component;
59 MMAL_PORT_T *input;
60 MMAL_POOL_T *input_pool;
61 MMAL_PORT_T *output;
62 MMAL_POOL_T *output_pool;
64 picture_pool_t *picture_pool;
65 picture_t **pictures;
67 MMAL_QUEUE_T *filtered_pictures;
68 vlc_mutex_t mutex;
69 vlc_cond_t buffer_cond;
71 /* statistics */
72 int output_in_transit;
73 int input_in_transit;
76 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
77 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
78 static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
79 static picture_t *deinterlace(filter_t *filter, picture_t *picture);
80 static void flush(filter_t *filter);
82 #define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx"
84 static int create_picture_pool(filter_t *filter)
86 picture_pool_configuration_t picture_pool_cfg;
87 filter_sys_t *sys = filter->p_sys;
88 picture_resource_t picture_res;
89 int ret = 0;
91 memset(&picture_res, 0, sizeof(picture_resource_t));
92 sys->pictures = calloc(sys->output->buffer_num, sizeof(picture_t *));
93 if (!sys->pictures) {
94 ret = -ENOMEM;
95 goto out;
98 for (unsigned i = 0; i < sys->output->buffer_num; i++) {
99 picture_res.p_sys = calloc(1, sizeof(picture_sys_t));
100 if (!picture_res.p_sys) {
101 ret = -ENOMEM;
102 goto out;
105 picture_res.p_sys->owner = (vlc_object_t *)filter;
106 picture_res.p_sys->queue = sys->output_pool->queue;
107 picture_res.p_sys->mutex = &sys->mutex;
109 sys->pictures[i] = picture_NewFromResource(&filter->fmt_out.video,
110 &picture_res);
111 if (!sys->pictures[i]) {
112 free(picture_res.p_sys);
113 ret = -ENOMEM;
114 goto out;
118 memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t));
119 picture_pool_cfg.picture_count = sys->output->buffer_num;
120 picture_pool_cfg.picture = sys->pictures;
121 picture_pool_cfg.lock = mmal_picture_lock;
122 picture_pool_cfg.unlock = mmal_picture_unlock;
124 sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
125 if (!sys->picture_pool) {
126 msg_Err(filter, "Failed to create picture pool");
127 ret = -ENOMEM;
128 goto out;
131 out:
132 return ret;
135 static int Open(filter_t *filter)
137 int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
138 (int64_t)1000000 * filter->fmt_in.video.i_frame_rate_base /
139 filter->fmt_in.video.i_frame_rate : 0;
141 MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
142 { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
143 MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
145 { 3, frame_duration }
147 MMAL_PARAMETER_UINT32_T extra_buffers;
149 int ret = VLC_SUCCESS;
150 MMAL_STATUS_T status;
151 filter_sys_t *sys;
153 msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d!", frame_duration);
155 if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
156 return VLC_EGENERIC;
158 if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
159 return VLC_EGENERIC;
161 sys = calloc(1, sizeof(filter_sys_t));
162 if (!sys)
163 return VLC_ENOMEM;
164 filter->p_sys = sys;
166 bcm_host_init();
168 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
169 if (status != MMAL_SUCCESS) {
170 msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
171 MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
172 ret = VLC_EGENERIC;
173 goto out;
176 status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
177 if (status != MMAL_SUCCESS) {
178 msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
179 MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
180 ret = VLC_EGENERIC;
181 goto out;
184 sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
185 status = mmal_port_enable(sys->component->control, control_port_cb);
186 if (status != MMAL_SUCCESS) {
187 msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
188 sys->component->control->name, status, mmal_status_to_string(status));
189 ret = VLC_EGENERIC;
190 goto out;
193 sys->input = sys->component->input[0];
194 sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
195 if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE)
196 sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
197 sys->input->format->es->video.width = filter->fmt_in.video.i_width;
198 sys->input->format->es->video.height = filter->fmt_in.video.i_height;
199 sys->input->format->es->video.crop.x = 0;
200 sys->input->format->es->video.crop.y = 0;
201 sys->input->format->es->video.crop.width = filter->fmt_in.video.i_width;
202 sys->input->format->es->video.crop.height = filter->fmt_in.video.i_height;
203 sys->input->format->es->video.par.num = filter->fmt_in.video.i_sar_num;
204 sys->input->format->es->video.par.den = filter->fmt_in.video.i_sar_den;
206 es_format_Copy(&filter->fmt_out, &filter->fmt_in);
207 filter->fmt_out.video.i_frame_rate *= 2;
209 status = mmal_port_format_commit(sys->input);
210 if (status != MMAL_SUCCESS) {
211 msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
212 sys->input->name, status, mmal_status_to_string(status));
213 ret = VLC_EGENERIC;
214 goto out;
216 sys->input->buffer_size = sys->input->buffer_size_recommended;
218 sys->input->buffer_num = sys->input->buffer_num_recommended;
219 status = mmal_port_enable(sys->input, input_port_cb);
220 if (status != MMAL_SUCCESS) {
221 msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
222 sys->input->name, status, mmal_status_to_string(status));
223 ret = VLC_EGENERIC;
224 goto out;
227 sys->output = sys->component->output[0];
228 sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
229 mmal_format_full_copy(sys->output->format, sys->input->format);
231 if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
232 extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS;
233 extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T);
234 extra_buffers.value = NUM_EXTRA_BUFFERS;
235 status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
236 if (status != MMAL_SUCCESS) {
237 msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
238 status, mmal_status_to_string(status));
239 ret = VLC_EGENERIC;
240 goto out;
244 status = mmal_port_format_commit(sys->output);
245 if (status != MMAL_SUCCESS) {
246 msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
247 sys->input->name, status, mmal_status_to_string(status));
248 ret = VLC_EGENERIC;
249 goto out;
252 sys->output->buffer_num = sys->output->buffer_num_recommended;
253 status = mmal_port_enable(sys->output, output_port_cb);
254 if (status != MMAL_SUCCESS) {
255 msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
256 sys->output->name, status, mmal_status_to_string(status));
257 ret = VLC_EGENERIC;
258 goto out;
261 status = mmal_component_enable(sys->component);
262 if (status != MMAL_SUCCESS) {
263 msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
264 sys->component->name, status, mmal_status_to_string(status));
265 ret = VLC_EGENERIC;
266 goto out;
269 sys->output_pool = mmal_pool_create(sys->output->buffer_num,
270 sys->output->buffer_size);
271 if (!sys->output_pool) {
272 msg_Err(filter, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
273 sys->output->buffer_num, sys->output->buffer_size);
274 goto out;
276 sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
277 sys->filtered_pictures = mmal_queue_create();
279 ret = create_picture_pool(filter);
280 if (ret != VLC_SUCCESS)
281 goto out;
283 filter->pf_video_filter = deinterlace;
284 filter->pf_video_flush = flush;
285 vlc_mutex_init_recursive(&sys->mutex);
286 vlc_cond_init(&sys->buffer_cond);
288 out:
289 if (ret != VLC_SUCCESS)
290 Close(filter);
292 return ret;
295 static void Close(filter_t *filter)
297 filter_sys_t *sys = filter->p_sys;
298 MMAL_BUFFER_HEADER_T *buffer;
300 if (!sys)
301 return;
303 if (sys->component && sys->component->control->is_enabled)
304 mmal_port_disable(sys->component->control);
306 if (sys->input && sys->input->is_enabled)
307 mmal_port_disable(sys->input);
309 if (sys->output && sys->output->is_enabled)
310 mmal_port_disable(sys->output);
312 if (sys->component && sys->component->is_enabled)
313 mmal_component_disable(sys->component);
315 while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
316 picture_t *pic = (picture_t *)buffer->user_data;
317 picture_Release(pic);
319 buffer->user_data = NULL;
320 buffer->alloc_size = 0;
321 buffer->data = NULL;
322 mmal_buffer_header_release(buffer);
325 if (sys->filtered_pictures)
326 mmal_queue_destroy(sys->filtered_pictures);
328 if (sys->input_pool)
329 mmal_pool_destroy(sys->input_pool);
331 if (sys->output_pool)
332 mmal_pool_destroy(sys->output_pool);
334 if (sys->component)
335 mmal_component_release(sys->component);
337 if (sys->picture_pool)
338 picture_pool_Release(sys->picture_pool);
340 vlc_mutex_destroy(&sys->mutex);
341 vlc_cond_destroy(&sys->buffer_cond);
342 free(sys->pictures);
343 free(sys);
345 bcm_host_deinit();
348 static int send_output_buffer(filter_t *filter)
350 filter_sys_t *sys = filter->p_sys;
351 MMAL_BUFFER_HEADER_T *buffer;
352 MMAL_STATUS_T status;
353 picture_t *picture;
354 int ret = 0;
356 picture = picture_pool_Get(sys->picture_pool);
357 if (!picture) {
358 msg_Warn(filter, "Failed to get new picture");
359 ret = -1;
360 goto out;
362 picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate;
363 picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base;
365 buffer = picture->p_sys->buffer;
366 buffer->cmd = 0;
367 buffer->alloc_size = sys->output->buffer_size;
369 status = mmal_port_send_buffer(sys->output, buffer);
370 if (status != MMAL_SUCCESS) {
371 msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)",
372 status, mmal_status_to_string(status));
373 picture_Release(picture);
374 ret = -1;
375 goto out;
377 sys->output_in_transit++;
379 out:
380 return ret;
383 static void fill_output_port(filter_t *filter)
385 filter_sys_t *sys = filter->p_sys;
386 /* allow at least 2 buffers in transit */
387 unsigned max_buffers_in_transit = __MAX(sys->output->buffer_num,
388 MIN_NUM_BUFFERS_IN_TRANSIT);
389 unsigned buffers_available = mmal_queue_length(sys->output_pool->queue);
390 unsigned buffers_to_send = max_buffers_in_transit - sys->output_in_transit;
391 unsigned i;
393 if (buffers_to_send > buffers_available)
394 buffers_to_send = buffers_available;
396 #ifndef NDEBUG
397 msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)",
398 buffers_to_send, buffers_available, sys->output_in_transit,
399 sys->output->buffer_num);
400 #endif
401 for (i = 0; i < buffers_to_send; ++i) {
402 if (send_output_buffer(filter) < 0)
403 break;
407 static picture_t *deinterlace(filter_t *filter, picture_t *picture)
409 filter_sys_t *sys = filter->p_sys;
410 MMAL_BUFFER_HEADER_T *buffer;
411 picture_t *out_picture = NULL;
412 picture_t *ret = NULL;
413 MMAL_STATUS_T status;
416 * Send output buffers
418 if (sys->output_pool) {
419 int i = 0;
420 vlc_mutex_lock(&sys->mutex);
421 while((buffer = mmal_queue_get(sys->filtered_pictures))) {
422 i++;
423 if (!out_picture) {
424 out_picture = (picture_t *)buffer->user_data;
425 ret = out_picture;
426 } else {
427 out_picture->p_next = (picture_t *)buffer->user_data;
428 out_picture = out_picture->p_next;
430 out_picture->date = buffer->pts;
432 if (out_picture)
433 out_picture->p_next = NULL;
434 fill_output_port(filter);
435 vlc_mutex_unlock(&sys->mutex);
439 * Process input
441 if (!picture)
442 return ret;
444 vlc_mutex_lock(&sys->mutex);
445 buffer = mmal_queue_timedwait(sys->input_pool->queue, 2);
446 if (!buffer) {
447 msg_Err(filter, "Failed to retrieve buffer header for input picture");
448 goto out;
451 mmal_buffer_header_reset(buffer);
452 buffer->user_data = picture;
453 buffer->pts = picture->date;
454 buffer->cmd = 0;
455 buffer->alloc_size = sys->input->buffer_size;
456 buffer->length = sys->input->buffer_size;
457 buffer->data = picture->p[0].p_pixels;
459 status = mmal_port_send_buffer(sys->input, buffer);
460 if (status != MMAL_SUCCESS) {
461 msg_Err(filter, "Failed to send buffer to input port (status=%"PRIx32" %s)",
462 status, mmal_status_to_string(status));
464 sys->input_in_transit++;
466 out:
467 vlc_mutex_unlock(&sys->mutex);
468 return ret;
471 static void flush(filter_t *filter)
473 filter_sys_t *sys = filter->p_sys;
474 MMAL_STATUS_T status;
476 mmal_port_disable(sys->output);
477 mmal_port_disable(sys->input);
478 mmal_port_flush(sys->output);
479 mmal_port_flush(sys->input);
480 status = mmal_port_enable(sys->input, input_port_cb);
481 if (status != MMAL_SUCCESS) {
482 msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
483 sys->input->name, status, mmal_status_to_string(status));
484 return;
486 status = mmal_port_enable(sys->output, output_port_cb);
487 if (status != MMAL_SUCCESS) {
488 msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
489 sys->output->name, status, mmal_status_to_string(status));
492 msg_Dbg(filter, "flush: wait for all buffers to be returned");
493 vlc_mutex_lock(&sys->mutex);
494 while (sys->input_in_transit || sys->output_in_transit)
495 vlc_cond_wait(&sys->buffer_cond, &sys->mutex);
496 vlc_mutex_unlock(&sys->mutex);
499 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
501 filter_t *filter = (filter_t *)port->userdata;
502 MMAL_STATUS_T status;
504 if (buffer->cmd == MMAL_EVENT_ERROR) {
505 status = *(uint32_t *)buffer->data;
506 msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
507 mmal_status_to_string(status));
510 mmal_buffer_header_release(buffer);
513 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
515 picture_t *picture = (picture_t *)buffer->user_data;
516 filter_t *filter = (filter_t *)port->userdata;
517 filter_sys_t *sys = filter->p_sys;
519 buffer->user_data = NULL;
520 vlc_mutex_lock(&sys->mutex);
521 mmal_buffer_header_release(buffer);
522 if (picture)
523 picture_Release(picture);
524 sys->input_in_transit--;
525 vlc_cond_signal(&sys->buffer_cond);
526 vlc_mutex_unlock(&sys->mutex);
529 static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
531 filter_t *filter = (filter_t *)port->userdata;
532 filter_sys_t *sys = filter->p_sys;
533 picture_t *picture;
535 vlc_mutex_lock(&sys->mutex);
536 if (buffer->cmd == 0) {
537 if (buffer->length > 0) {
538 mmal_queue_put(sys->filtered_pictures, buffer);
539 fill_output_port(filter);
540 } else {
541 picture = (picture_t *)buffer->user_data;
542 picture_Release(picture);
543 buffer->user_data = NULL;
545 sys->output_in_transit--;
546 vlc_cond_signal(&sys->buffer_cond);
547 } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
548 msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled");
549 mmal_buffer_header_release(buffer);
550 } else {
551 mmal_buffer_header_release(buffer);
553 vlc_mutex_unlock(&sys->mutex);