[vlc-devel] [PATCH 6/6] Add mmal-based deinterlace filter
Julian Scheel
julian at jusst.de
Thu Jun 19 11:22:21 CEST 2014
Add a video_filter module which uses the Broadcom Multi Media Abstraction
Layer (mmal) to deinterlace video in hardware.
As of now it is only working in opaque mode, due to a firmware limitation.
Once this is fixed it can be easily extended to support I420 encoding and use
it as simple chroma filter.
Signed-off-by: Julian Scheel <julian at jusst.de>
---
configure.ac | 27 ++
modules/video_filter/Modules.am | 7 +
modules/video_filter/deinterlace/mmal.c | 474 ++++++++++++++++++++++++++++++++
3 files changed, 508 insertions(+)
create mode 100644 modules/video_filter/deinterlace/mmal.c
diff --git a/configure.ac b/configure.ac
index 2611a11..781dd4d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2174,6 +2174,33 @@ if test "${enable_mmal_codec}" != "no"; then
fi
dnl
+dnl MMAL deinterlace filter plugin
+dnl
+AC_ARG_ENABLE(mmal-deinterlace,
+ AS_HELP_STRING([--enable-mmal-deinterlace],
+ [Multi-Media Abstraction Layer (MMAL) based deinterlace plugin for Raspberry Pi (default enable)]))
+if test "${enable_mmal_deinterlace}" != "no"; then
+ VLC_SAVE_FLAGS
+ LDFLAGS="${LDFLAGS} -L/opt/vc/lib"
+ CPPFLAGS="${CPPFLAGS} -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vcos/linux"
+ AC_CHECK_HEADERS(interface/mmal/mmal.h,
+ [ AC_CHECK_LIB(bcm_host, bcm_host_init, [
+ VLC_ADD_PLUGIN([mmal_deinterlace])
+ VLC_ADD_LDFLAGS([mmal_deinterlace],[ -L/opt/vc/lib ])
+ VLC_ADD_CFLAGS([mmal_deinterlace],[ -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vcos/linux ])
+ VLC_ADD_LIBS([mmal_deinterlace],[ -lbcm_host -lmmal ]) ], [
+ AS_IF([test "${enable_mmal_deinterlace}" = "yes"],
+ [ AC_MSG_ERROR([Cannot find bcm library...]) ],
+ [ AC_MSG_WARN([Cannot find bcm library...]) ])
+ ],
+ [])
+ ] , [ AS_IF([test "${enable_mmal_deinterlace}" = "yes"],
+ [ AC_MSG_ERROR([Cannot find development headers for mmal...]) ],
+ [ AC_MSG_WARN([Cannot find development headers for mmal...]) ]) ])
+ VLC_RESTORE_FLAGS
+fi
+
+dnl
dnl CrystalHD codec plugin
dnl
AC_ARG_ENABLE(crystalhd,
diff --git a/modules/video_filter/Modules.am b/modules/video_filter/Modules.am
index 3bb8cdb..42e10e8 100644
--- a/modules/video_filter/Modules.am
+++ b/modules/video_filter/Modules.am
@@ -31,6 +31,13 @@ libdeinterlace_plugin_la_CFLAGS += -DCAN_COMPILE_ARM
endif
video_filter_LTLIBRARIES += libdeinterlace_plugin.la
+libmmal_deinterlace_plugin_la_SOURCES = \
+ deinterlace/mmal.c
+libmmal_deinterlace_plugin_la_CFLAGS = $(AM_CFLAGS) $(CFLAGS_mmal_deinterlace)
+libmmal_deinterlace_plugin_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAGS_mmal_deinterlace)
+libmmal_deinterlace_plugin_la_LIBADD = $(LIBS_mmal_deinterlace)
+video_filter_LTLIBRARIES += libmmal_deinterlace_plugin.la
+
libdynamicoverlay_plugin_la_SOURCES = \
dynamicoverlay/dynamicoverlay_buffer.c \
dynamicoverlay/dynamicoverlay_queue.c \
diff --git a/modules/video_filter/deinterlace/mmal.c b/modules/video_filter/deinterlace/mmal.c
new file mode 100644
index 0000000..65eb39c
--- /dev/null
+++ b/modules/video_filter/deinterlace/mmal.c
@@ -0,0 +1,474 @@
+/*****************************************************************************
+ * mmal.c: MMAL-based deinterlace plugin for Raspberry Pi
+ *****************************************************************************
+ * Copyright © 2014 jusst technologies GmbH
+ * $Id$
+ *
+ * Authors: Julian Scheel <julian at jusst.de>
+ * Dennis Hamester <dennis.hamester at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_filter.h>
+
+#include <bcm_host.h>
+#include <interface/mmal/mmal.h>
+#include <interface/mmal/util/mmal_util.h>
+#include <interface/mmal/util/mmal_default_components.h>
+
+#define NUM_ACTUAL_OPAQUE_BUFFERS 22
+#define NUM_EXTRA_BUFFERS 5
+#define MIN_NUM_BUFFERS_IN_TRANSIT 2
+
+#define MAX_ALLOWED_FPS 30
+
+static int Open(filter_t *filter);
+static void Close(filter_t *filter);
+
+vlc_module_begin()
+ set_shortname(N_("MMAL deinterlace"))
+ set_description(N_("MMAL-based deinterlace filter plugin"))
+ set_capability("video filter2", 10)
+ set_category(CAT_VIDEO)
+ set_subcategory(SUBCAT_VIDEO_VFILTER)
+ set_callbacks(Open, Close)
+ add_shortcut("deinterlace")
+vlc_module_end()
+
+struct filter_sys_t {
+ MMAL_COMPONENT_T *component;
+ MMAL_PORT_T *input;
+ MMAL_POOL_T *input_pool;
+ MMAL_PORT_T *output;
+ MMAL_POOL_T *output_pool;
+
+ video_format_t fmt_out;
+
+ MMAL_QUEUE_T *filtered_pictures;
+ vlc_mutex_t mutex;
+};
+
+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
+static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
+static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
+static picture_t *deinterlace(filter_t *filter, picture_t *picture);
+
+#define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx"
+
+static int Open(filter_t *filter)
+{
+ int32_t frame_duration = 1000000 /
+ ((double)filter->fmt_in.video.i_frame_rate /
+ filter->fmt_in.video.i_frame_rate_base);
+ MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
+ { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
+ MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
+ 2,
+ { 3, frame_duration }
+ };
+ MMAL_PARAMETER_UINT32_T extra_buffers;
+
+ int ret = VLC_SUCCESS;
+ MMAL_STATUS_T status;
+ filter_sys_t *sys;
+
+ msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d!", frame_duration);
+
+ if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
+ return VLC_EGENERIC;
+
+ if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
+ return VLC_EGENERIC;
+
+ sys = calloc(1, sizeof(filter_sys_t));
+ if (!sys)
+ return VLC_ENOMEM;
+ filter->p_sys = sys;
+
+ bcm_host_init();
+
+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
+ ret = VLC_EGENERIC;
+ goto out;
+ }
+
+ status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
+ ret = VLC_EGENERIC;
+ goto out;
+ }
+
+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
+ status = mmal_port_enable(sys->component->control, control_port_cb);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
+ sys->component->control->name, status, mmal_status_to_string(status));
+ ret = VLC_EGENERIC;
+ goto out;
+ }
+
+ sys->input = sys->component->input[0];
+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
+ if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE)
+ sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
+ sys->input->format->es->video.width = filter->fmt_in.video.i_width;
+ sys->input->format->es->video.height = filter->fmt_in.video.i_height;
+ sys->input->format->es->video.crop.x = 0;
+ sys->input->format->es->video.crop.y = 0;
+ sys->input->format->es->video.crop.width = filter->fmt_in.video.i_width;
+ sys->input->format->es->video.crop.height = filter->fmt_in.video.i_height;
+ sys->input->format->es->video.par.num = filter->fmt_in.video.i_sar_num;
+ sys->input->format->es->video.par.den = filter->fmt_in.video.i_sar_den;
+
+ es_format_Copy(&filter->fmt_out, &filter->fmt_in);
+
+ status = mmal_port_format_commit(sys->input);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
+ sys->input->name, status, mmal_status_to_string(status));
+ ret = VLC_EGENERIC;
+ goto out;
+ }
+ sys->input->buffer_size = sys->input->buffer_size_recommended;
+
+ sys->input->buffer_num = NUM_ACTUAL_OPAQUE_BUFFERS;
+ status = mmal_port_enable(sys->input, input_port_cb);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
+ sys->input->name, status, mmal_status_to_string(status));
+ ret = VLC_EGENERIC;
+ goto out;
+ }
+
+ sys->output = sys->component->output[0];
+ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
+ mmal_format_full_copy(sys->output->format, sys->input->format);
+
+#if 1
+ if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
+ extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS;
+ extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T);
+ extra_buffers.value = NUM_EXTRA_BUFFERS;
+ status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
+ status, mmal_status_to_string(status));
+ ret = VLC_EGENERIC;
+ goto out;
+ }
+ }
+#endif
+
+ status = mmal_port_format_commit(sys->output);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
+ sys->input->name, status, mmal_status_to_string(status));
+ ret = VLC_EGENERIC;
+ goto out;
+ }
+
+ sys->output->buffer_num = NUM_ACTUAL_OPAQUE_BUFFERS;
+ status = mmal_port_enable(sys->output, output_port_cb);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
+ sys->output->name, status, mmal_status_to_string(status));
+ ret = VLC_EGENERIC;
+ goto out;
+ }
+
+ status = mmal_component_enable(sys->component);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
+ sys->component->name, status, mmal_status_to_string(status));
+ ret = VLC_EGENERIC;
+ goto out;
+ }
+
+ sys->output_pool = mmal_pool_create(sys->output->buffer_num_recommended, 0);
+ sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
+ sys->filtered_pictures = mmal_queue_create();
+
+ filter->pf_video_filter = deinterlace;
+ vlc_mutex_init_recursive(&sys->mutex);
+
+out:
+ if (ret != VLC_SUCCESS)
+ Close(filter);
+
+ return ret;
+}
+
+static void Close(filter_t *filter)
+{
+ filter_sys_t *sys = filter->p_sys;
+ MMAL_BUFFER_HEADER_T *buffer;
+
+ if (!sys)
+ return;
+
+ if (sys->component && sys->component->control->is_enabled)
+ mmal_port_disable(sys->component->control);
+
+ if (sys->input && sys->input->is_enabled)
+ mmal_port_disable(sys->input);
+
+ if (sys->output && sys->output->is_enabled)
+ mmal_port_disable(sys->output);
+
+ if (sys->component && sys->component->is_enabled)
+ mmal_component_disable(sys->component);
+
+ if (sys->input_pool)
+ mmal_pool_destroy(sys->input_pool);
+
+ while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
+ picture_t *pic = (picture_t *)buffer->user_data;
+ picture_Release(pic);
+
+ buffer->user_data = NULL;
+ buffer->alloc_size = 0;
+ buffer->data = NULL;
+ mmal_buffer_header_release(buffer);
+ }
+
+ if (sys->filtered_pictures)
+ mmal_queue_destroy(sys->filtered_pictures);
+
+ if (sys->output_pool)
+ mmal_pool_destroy(sys->output_pool);
+
+ if (sys->component)
+ mmal_component_release(sys->component);
+
+ free(sys);
+
+ bcm_host_deinit();
+}
+
+static int send_output_buffer(filter_t *filter)
+{
+ filter_sys_t *sys = filter->p_sys;
+ MMAL_BUFFER_HEADER_T *buffer;
+ MMAL_STATUS_T status;
+ picture_t *picture;
+ int ret = 0;
+
+ buffer = mmal_queue_get(sys->output_pool->queue);
+ if (!buffer) {
+ msg_Warn(filter, "Failed to get new buffer");
+ ret = -1;
+ goto out;
+ }
+
+ picture = filter_NewPicture(filter);
+ if (!picture) {
+ msg_Warn(filter, "Failed to get new picture");
+ mmal_buffer_header_release(buffer);
+ ret = -1;
+ goto out;
+ }
+
+ mmal_buffer_header_reset(buffer);
+ buffer->user_data = picture;
+ buffer->cmd = 0;
+ buffer->alloc_size = sys->output->buffer_size;
+ buffer->data = picture->p[0].p_pixels;
+
+ status = mmal_port_send_buffer(sys->output, buffer);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)",
+ status, mmal_status_to_string(status));
+ mmal_buffer_header_release(buffer);
+ filter_DeletePicture(filter, picture);
+ ret = -1;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static void fill_output_port(filter_t *filter)
+{
+ filter_sys_t *sys = filter->p_sys;
+ /* allow at least 2 buffers in transit */
+ unsigned max_buffers_in_transit = __MAX(sys->output->buffer_num_recommended,
+ MIN_NUM_BUFFERS_IN_TRANSIT);
+ unsigned buffers_available = mmal_queue_length(sys->output_pool->queue);
+ unsigned buffers_in_transit = sys->output_pool->headers_num - buffers_available -
+ mmal_queue_length(sys->filtered_pictures);
+ unsigned buffers_to_send = max_buffers_in_transit - buffers_in_transit;
+ unsigned i;
+
+ if (buffers_to_send > buffers_available)
+ buffers_to_send = buffers_available;
+
+#ifndef NDEBUG
+ msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)",
+ buffers_to_send, buffers_available, buffers_in_transit,
+ sys->output->buffer_num);
+#endif
+ for (i = 0; i < buffers_to_send; ++i) {
+ if (send_output_buffer(filter) < 0)
+ break;
+ }
+}
+
+static picture_t *deinterlace(filter_t *filter, picture_t *picture)
+{
+ filter_sys_t *sys = filter->p_sys;
+ MMAL_BUFFER_HEADER_T *buffer;
+ picture_t *out_picture = NULL;
+ picture_t *ret = NULL;
+ MMAL_STATUS_T status;
+
+ /*
+ * Send output buffers
+ */
+ if (sys->output_pool) {
+ int i = 0;
+ vlc_mutex_lock(&sys->mutex);
+ while((buffer = mmal_queue_get(sys->filtered_pictures))) {
+ i++;
+ if (!out_picture) {
+ out_picture = (picture_t *)buffer->user_data;
+ ret = out_picture;
+ } else {
+ out_picture->p_next = (picture_t *)buffer->user_data;
+ out_picture = out_picture->p_next;
+ }
+ out_picture->date = buffer->pts;
+
+ buffer->user_data = NULL;
+ buffer->alloc_size = 0;
+ buffer->data = NULL;
+ mmal_buffer_header_release(buffer);
+ }
+ if (out_picture)
+ out_picture->p_next = NULL;
+ fill_output_port(filter);
+ vlc_mutex_unlock(&sys->mutex);
+ }
+
+ /*
+ * Process input
+ */
+ if (!picture)
+ goto out;
+
+ /*
+ * Do not deinterlace if framerate is > 30fps. This is unlikely to work
+ * well in any case.
+ */
+ if (picture) {
+ double fps = (double)picture->format.i_frame_rate / picture->format.i_frame_rate_base;
+ if (fps > MAX_ALLOWED_FPS)
+ return picture;
+ }
+
+ vlc_mutex_lock(&sys->mutex);
+ buffer = mmal_queue_timedwait(sys->input_pool->queue, 2);
+ if (!buffer) {
+ msg_Err(filter, "Failed to retrieve buffer header for input picture");
+ goto out;
+ }
+
+ mmal_buffer_header_reset(buffer);
+ buffer->user_data = picture;
+ buffer->pts = picture->date;
+ buffer->cmd = 0;
+ buffer->alloc_size = sys->input->buffer_size;
+ buffer->length = sys->input->buffer_size;
+ buffer->data = picture->p[0].p_pixels;
+
+ status = mmal_port_send_buffer(sys->input, buffer);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to send buffer to input port (status=%"PRIx32" %s)",
+ status, mmal_status_to_string(status));
+ goto out;
+ }
+ vlc_mutex_unlock(&sys->mutex);
+
+out:
+ return ret;
+}
+
+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ filter_t *filter = (filter_t *)port->userdata;
+ MMAL_STATUS_T status;
+
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
+ status = *(uint32_t *)buffer->data;
+ msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
+ mmal_status_to_string(status));
+ }
+
+ mmal_buffer_header_release(buffer);
+}
+
+static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ picture_t *picture = (picture_t *)buffer->user_data;
+ filter_t *filter = (filter_t *)port->userdata;
+ filter_sys_t *sys = filter->p_sys;
+
+ buffer->user_data = NULL;
+ mmal_buffer_header_release(buffer);
+ vlc_mutex_lock(&sys->mutex);
+ if (picture)
+ picture_Release(picture);
+ vlc_mutex_unlock(&sys->mutex);
+}
+
+static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ filter_t *filter = (filter_t *)port->userdata;
+ filter_sys_t *sys = filter->p_sys;
+ picture_t *picture;
+
+ vlc_mutex_lock(&sys->mutex);
+ if (buffer->cmd == 0) {
+ if (buffer->length > 0) {
+ mmal_queue_put(sys->filtered_pictures, buffer);
+ fill_output_port(filter);
+ } else {
+ picture = (picture_t *)buffer->user_data;
+ filter_DeletePicture(filter, picture);
+ buffer->user_data = NULL;
+ buffer->alloc_size = 0;
+ buffer->data = NULL;
+ mmal_buffer_header_release(buffer);
+ }
+ } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
+ msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled");
+ mmal_buffer_header_release(buffer);
+ } else {
+ mmal_buffer_header_release(buffer);
+ }
+ vlc_mutex_unlock(&sys->mutex);
+}
--
2.0.0
More information about the vlc-devel
mailing list