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
;
69 vlc_cond_t buffer_cond
;
72 int output_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
;
91 memset(&picture_res
, 0, sizeof(picture_resource_t
));
92 sys
->pictures
= calloc(sys
->output
->buffer_num
, sizeof(picture_t
*));
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
) {
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
,
111 if (!sys
->pictures
[i
]) {
112 free(picture_res
.p_sys
);
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");
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
;
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
)
158 if (filter
->fmt_out
.video
.i_chroma
!= VLC_CODEC_MMAL_OPAQUE
)
161 sys
= calloc(1, sizeof(filter_sys_t
));
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
));
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
));
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
));
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
));
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
));
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
));
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
));
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
));
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
));
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
);
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
)
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
);
289 if (ret
!= VLC_SUCCESS
)
295 static void Close(filter_t
*filter
)
297 filter_sys_t
*sys
= filter
->p_sys
;
298 MMAL_BUFFER_HEADER_T
*buffer
;
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;
322 mmal_buffer_header_release(buffer
);
325 if (sys
->filtered_pictures
)
326 mmal_queue_destroy(sys
->filtered_pictures
);
329 mmal_pool_destroy(sys
->input_pool
);
331 if (sys
->output_pool
)
332 mmal_pool_destroy(sys
->output_pool
);
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
);
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
;
356 picture
= picture_pool_Get(sys
->picture_pool
);
358 msg_Warn(filter
, "Failed to get new picture");
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
;
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
);
377 sys
->output_in_transit
++;
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
;
393 if (buffers_to_send
> buffers_available
)
394 buffers_to_send
= buffers_available
;
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
);
401 for (i
= 0; i
< buffers_to_send
; ++i
) {
402 if (send_output_buffer(filter
) < 0)
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
) {
420 vlc_mutex_lock(&sys
->mutex
);
421 while((buffer
= mmal_queue_get(sys
->filtered_pictures
))) {
424 out_picture
= (picture_t
*)buffer
->user_data
;
427 out_picture
->p_next
= (picture_t
*)buffer
->user_data
;
428 out_picture
= out_picture
->p_next
;
430 out_picture
->date
= buffer
->pts
;
433 out_picture
->p_next
= NULL
;
434 fill_output_port(filter
);
435 vlc_mutex_unlock(&sys
->mutex
);
444 vlc_mutex_lock(&sys
->mutex
);
445 buffer
= mmal_queue_timedwait(sys
->input_pool
->queue
, 2);
447 msg_Err(filter
, "Failed to retrieve buffer header for input picture");
451 mmal_buffer_header_reset(buffer
);
452 buffer
->user_data
= picture
;
453 buffer
->pts
= picture
->date
;
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
++;
467 vlc_mutex_unlock(&sys
->mutex
);
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
));
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
);
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
;
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
);
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
);
551 mmal_buffer_header_release(buffer
);
553 vlc_mutex_unlock(&sys
->mutex
);