[vlc-devel] [PATCH 1/4] mmal: update the code from the one actually used in Raspbian
Steve Lhomme
robux4 at ycbcr.xyz
Fri Jan 17 17:00:55 CET 2020
From: John Cox <jc at kynesim.co.uk>
Tested with basic video playback on a RPI 3B+
This is the code from the Raspbian patch ported to the 4.0 architecture.
Signed-off-by: Steve Lhomme <robux4 at ycbcr.xyz>
---
modules/hw/mmal/Makefile.am | 7 +-
modules/hw/mmal/codec.c | 784 +++++++++--------
modules/hw/mmal/deinterlace.c | 803 +++++++++--------
modules/hw/mmal/mmal_picture.c | 1023 +++++++++++++++++++++-
modules/hw/mmal/mmal_picture.h | 123 ++-
modules/hw/mmal/subpic.c | 229 +++++
modules/hw/mmal/subpic.h | 52 ++
modules/hw/mmal/vout.c | 1492 +++++++++++++++++++-------------
8 files changed, 3148 insertions(+), 1365 deletions(-)
create mode 100644 modules/hw/mmal/subpic.c
create mode 100644 modules/hw/mmal/subpic.h
diff --git a/modules/hw/mmal/Makefile.am b/modules/hw/mmal/Makefile.am
index b921f38537d..f6bb51b3ff0 100644
--- a/modules/hw/mmal/Makefile.am
+++ b/modules/hw/mmal/Makefile.am
@@ -4,13 +4,16 @@ mmaldir = $(pluginsdir)/mmal
AM_CFLAGS += $(CFLAGS_mmal)
AM_LDFLAGS += -rpath '$(mmaldir)' $(LDFLAGS_mmal)
-libmmal_vout_plugin_la_SOURCES = vout.c mmal_picture.c mmal_picture.h
+libmmal_vout_plugin_la_SOURCES = vout.c subpic.c subpic.h mmal_picture.c mmal_picture.h
libmmal_vout_plugin_la_CFLAGS = $(AM_CFLAGS)
+if ENABLE_QT
+libmmal_vout_plugin_la_CFLAGS += -DHAVE_QT
+endif
libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm
libmmal_vout_plugin_la_LIBADD = $(LIBS_mmal)
mmal_LTLIBRARIES = libmmal_vout_plugin.la
-libmmal_codec_plugin_la_SOURCES = codec.c
+libmmal_codec_plugin_la_SOURCES = codec.c mmal_picture.c mmal_picture.h
libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS)
libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
libmmal_codec_plugin_la_LIBADD = $(LIBS_mmal)
diff --git a/modules/hw/mmal/codec.c b/modules/hw/mmal/codec.c
index d09f212090c..c4de2459433 100644
--- a/modules/hw/mmal/codec.c
+++ b/modules/hw/mmal/codec.c
@@ -5,6 +5,7 @@
*
* Authors: Dennis Hamester <dennis.hamester at gmail.com>
* Julian Scheel <julian at jusst.de>
+ * John Cox <jc at kynesim.co.uk>
*
* 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
@@ -30,31 +31,46 @@
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_codec.h>
-#include <vlc_threads.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>
#include "mmal_picture.h"
+#include "subpic.h"
+
/*
* This seems to be a bit high, but reducing it causes instabilities
*/
#define NUM_EXTRA_BUFFERS 5
+//#define NUM_EXTRA_BUFFERS 10
#define NUM_DECODER_BUFFER_HEADERS 30
-#define MIN_NUM_BUFFERS_IN_TRANSIT 2
+#define CONVERTER_BUFFERS 4 // Buffers on the output of the converter
+
+#define MMAL_SLICE_HEIGHT 16
+#define MMAL_ALIGN_W 32
+#define MMAL_ALIGN_H 16
#define MMAL_OPAQUE_NAME "mmal-opaque"
#define MMAL_OPAQUE_TEXT N_("Decode frames directly into RPI VideoCore instead of host memory.")
#define MMAL_OPAQUE_LONGTEXT N_("Decode frames directly into RPI VideoCore instead of host memory. This option must only be used with the MMAL video output plugin.")
+#define MMAL_RESIZE_NAME "mmal-resize"
+#define MMAL_RESIZE_TEXT N_("Use mmal resizer rather than hvs.")
+#define MMAL_RESIZE_LONGTEXT N_("Use mmal resizer rather than isp. This uses less gpu memory than the ISP but is slower.")
+
+#define MMAL_ISP_NAME "mmal-isp"
+#define MMAL_ISP_TEXT N_("Use mmal isp rather than hvs.")
+#define MMAL_ISP_LONGTEXT N_("Use mmal isp rather than hvs. This may be faster but has no blend.")
+
static int OpenDecoder(vlc_object_t *);
static void CloseDecoder(vlc_object_t *);
vlc_module_begin()
+ set_category( CAT_INPUT )
+ set_subcategory( SUBCAT_INPUT_VCODEC )
set_shortname(N_("MMAL decoder"))
set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
set_capability("video decoder", 90)
@@ -65,224 +81,238 @@ vlc_module_end()
typedef struct
{
- bool opaque;
MMAL_COMPONENT_T *component;
MMAL_PORT_T *input;
MMAL_POOL_T *input_pool;
MMAL_PORT_T *output;
- MMAL_POOL_T *output_pool; /* only used for non-opaque mode */
+ hw_mmal_port_pool_ref_t *ppr;
MMAL_ES_FORMAT_T *output_format;
- vlc_sem_t sem;
+ MMAL_STATUS_T err_stream;
bool b_top_field_first;
bool b_progressive;
+ bool b_flushed;
+
+ vcsm_init_type_t vcsm_init_type;
+
+ // Lock to avoid pic update & allocate happenening simultainiously
+ // * We should be able to arrange life s.t. this isn't needed
+ // but while we are confused apply belt & braces
+ vlc_mutex_t pic_lock;
+
/* statistics */
- int output_in_transit;
- int input_in_transit;
bool started;
} decoder_sys_t;
-/* Utilities */
-static int change_output_format(decoder_t *dec);
-static int send_output_buffer(decoder_t *dec);
-static void fill_output_port(decoder_t *dec);
-/* VLC decoder callback */
-static int decode(decoder_t *dec, block_t *block);
-static void flush_decoder(decoder_t *dec);
+typedef struct supported_mmal_enc_s {
+ struct {
+ MMAL_PARAMETER_HEADER_T header;
+ MMAL_FOURCC_T encodings[64];
+ } supported;
+ int n;
+} supported_mmal_enc_t;
+
+#define SUPPORTED_MMAL_ENC_INIT \
+{ \
+ {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}}, \
+ -1 \
+}
-/* MMAL callbacks */
-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 supported_mmal_enc_t supported_decode_in_enc = SUPPORTED_MMAL_ENC_INIT;
-static int OpenDecoder(vlc_object_t *obj)
+static bool is_enc_supported(supported_mmal_enc_t * const support, const MMAL_FOURCC_T fcc)
{
- decoder_t *dec = (decoder_t *)obj;
- int ret = VLC_SUCCESS;
- decoder_sys_t *sys;
- MMAL_STATUS_T status;
+ int i;
- if (dec->fmt_in.i_codec != VLC_CODEC_MPGV &&
- dec->fmt_in.i_codec != VLC_CODEC_H264)
- return VLC_EGENERIC;
+ if (fcc == 0)
+ return false;
+ if (support->n == -1)
+ return true; // Unknown - say OK
+ for (i = 0; i < support->n; ++i) {
+ if (support->supported.encodings[i] == fcc)
+ return true;
+ }
+ return false;
+}
- sys = calloc(1, sizeof(decoder_sys_t));
- if (unlikely(sys == NULL))
- return VLC_ENOMEM;
- dec->p_sys = sys;
+static bool set_and_test_enc_supported(supported_mmal_enc_t * const support, MMAL_PORT_T * port, const MMAL_FOURCC_T fcc)
+{
+ if (support->n >= 0)
+ /* already done */;
+ else if (mmal_port_parameter_get(port, (MMAL_PARAMETER_HEADER_T *)&support->supported) != MMAL_SUCCESS)
+ support->n = 0;
+ else
+ support->n = (support->supported.header.size - sizeof(support->supported.header)) /
+ sizeof(support->supported.encodings[0]);
- sys->opaque = var_InheritBool(dec, MMAL_OPAQUE_NAME);
- bcm_host_init();
+ return is_enc_supported(support, fcc);
+}
- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
- if (status != MMAL_SUCCESS) {
- msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
- MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
- ret = VLC_EGENERIC;
- goto out;
+static MMAL_FOURCC_T vlc_to_mmal_es_fourcc(const unsigned int fcc)
+{
+ switch (fcc){
+ case VLC_CODEC_MJPG:
+ return MMAL_ENCODING_MJPEG;
+ case VLC_CODEC_MP1V:
+ return MMAL_ENCODING_MP1V;
+ case VLC_CODEC_MPGV:
+ case VLC_CODEC_MP2V:
+ return MMAL_ENCODING_MP2V;
+ case VLC_CODEC_H263:
+ return MMAL_ENCODING_H263;
+ case VLC_CODEC_MP4V:
+ return MMAL_ENCODING_MP4V;
+ case VLC_CODEC_H264:
+ return MMAL_ENCODING_H264;
+ case VLC_CODEC_VP6:
+ return MMAL_ENCODING_VP6;
+ case VLC_CODEC_VP8:
+ return MMAL_ENCODING_VP8;
+ case VLC_CODEC_WMV1:
+ return MMAL_ENCODING_WMV1;
+ case VLC_CODEC_WMV2:
+ return MMAL_ENCODING_WMV2;
+ case VLC_CODEC_WMV3:
+ return MMAL_ENCODING_WMV3;
+ case VLC_CODEC_VC1:
+ return MMAL_ENCODING_WVC1;
+ case VLC_CODEC_THEORA:
+ return MMAL_ENCODING_THEORA;
+ default:
+ break;
}
+ return 0;
+}
- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
- status = mmal_port_enable(sys->component->control, control_port_cb);
- if (status != MMAL_SUCCESS) {
- msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
- sys->component->control->name, status, mmal_status_to_string(status));
- ret = VLC_EGENERIC;
- goto out;
- }
+// Buffer either attached to pic or released
+static picture_t * alloc_opaque_pic(decoder_t * const dec, MMAL_BUFFER_HEADER_T * const buf)
+{
+ decoder_sys_t *const dec_sys = dec->p_sys;
- sys->input = sys->component->input[0];
- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
- if (dec->fmt_in.i_codec == VLC_CODEC_MPGV)
- sys->input->format->encoding = MMAL_ENCODING_MP2V;
- else
- sys->input->format->encoding = MMAL_ENCODING_H264;
-
- if (dec->fmt_in.i_codec == VLC_CODEC_H264) {
- if (dec->fmt_in.i_extra > 0) {
- status = mmal_format_extradata_alloc(sys->input->format,
- dec->fmt_in.i_extra);
- if (status == MMAL_SUCCESS) {
- memcpy(sys->input->format->extradata, dec->fmt_in.p_extra,
- dec->fmt_in.i_extra);
- sys->input->format->extradata_size = dec->fmt_in.i_extra;
- } else {
- msg_Err(dec, "Failed to allocate extra format data on input port %s (status=%"PRIx32" %s)",
- sys->input->name, status, mmal_status_to_string(status));
- }
- }
- }
+ vlc_mutex_lock(&dec_sys->pic_lock);
+ picture_t * const pic = decoder_NewPicture(dec);
+ vlc_mutex_unlock(&dec_sys->pic_lock);
- status = mmal_port_format_commit(sys->input);
- if (status != MMAL_SUCCESS) {
- msg_Err(dec, "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 = sys->input->buffer_num_recommended;
+ if (pic == NULL)
+ goto fail1;
- status = mmal_port_enable(sys->input, input_port_cb);
- if (status != MMAL_SUCCESS) {
- msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
- sys->input->name, status, mmal_status_to_string(status));
- ret = VLC_EGENERIC;
- goto out;
+ if (buf->length == 0) {
+ msg_Err(dec, "Empty buffer");
+ goto fail2;
}
- sys->output = sys->component->output[0];
- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
-
- if (sys->opaque) {
- MMAL_PARAMETER_UINT32_T extra_buffers = {
- { MMAL_PARAMETER_EXTRA_BUFFERS, sizeof(MMAL_PARAMETER_UINT32_T) },
- NUM_EXTRA_BUFFERS
- };
- status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
- if (status != MMAL_SUCCESS) {
- msg_Err(dec, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
- status, mmal_status_to_string(status));
- ret = VLC_EGENERIC;
- goto out;
- }
+ if ((pic->context = hw_mmal_gen_context(buf, dec_sys->ppr)) == NULL)
+ goto fail2;
- msg_Dbg(dec, "Activate zero-copy for output port");
- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
- 1
- };
+ buf_to_pic_copy_props(pic, buf);
- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
- if (status != MMAL_SUCCESS) {
- msg_Err(dec, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
- sys->output->name, status, mmal_status_to_string(status));
- goto out;
- }
- }
+ return pic;
- status = mmal_port_enable(sys->output, output_port_cb);
- if (status != MMAL_SUCCESS) {
- msg_Err(dec, "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(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
- sys->component->name, status, mmal_status_to_string(status));
- ret = VLC_EGENERIC;
- goto out;
- }
+fail2:
+ picture_Release(pic);
+fail1:
+ // Recycle rather than release to avoid buffer starvation if NewPic fails
+ hw_mmal_port_pool_ref_recycle(dec_sys->ppr, buf);
+ return NULL;
+}
- sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ decoder_t *dec = (decoder_t *)port->userdata;
+ MMAL_STATUS_T status;
- if (sys->opaque) {
- dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
- dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
- } else {
- dec->fmt_out.i_codec = VLC_CODEC_I420;
- dec->fmt_out.video.i_chroma = VLC_CODEC_I420;
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
+ status = *(uint32_t *)buffer->data;
+ decoder_sys_t * const sys = dec->p_sys;
+ sys->err_stream = status;
+ msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
+ mmal_status_to_string(status));
}
- dec->pf_decode = decode;
- dec->pf_flush = flush_decoder;
+ mmal_buffer_header_release(buffer);
+}
+
+static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ block_t * const block = (block_t *)buffer->user_data;
- vlc_sem_init(&sys->sem, 0);
+ (void)port; // Unused
-out:
- if (ret != VLC_SUCCESS)
- CloseDecoder(obj);
+ mmal_buffer_header_reset(buffer);
+ mmal_buffer_header_release(buffer);
- return ret;
+ if (block != NULL)
+ block_Release(block);
}
-static void CloseDecoder(vlc_object_t *obj)
+static void decoder_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
- decoder_t *dec = (decoder_t *)obj;
- decoder_sys_t *sys = dec->p_sys;
+ decoder_t * const dec = (decoder_t *)port->userdata;
- if (!sys)
+ if (buffer->cmd == 0 && buffer->length != 0)
+ {
+ picture_t *pic = alloc_opaque_pic(dec, buffer);
+ if (pic == NULL)
+ msg_Err(dec, "Failed to allocate new picture");
+ else
+ decoder_QueueVideo(dec, pic);
+ // Buffer released or attached to pic - do not release again
return;
+ }
- if (sys->component && sys->component->control->is_enabled)
- mmal_port_disable(sys->component->control);
+ if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED)
+ {
+ decoder_sys_t * const sys = dec->p_sys;
+ MMAL_EVENT_FORMAT_CHANGED_T * const fmt = mmal_event_format_changed_get(buffer);
+ MMAL_ES_FORMAT_T * const format = mmal_format_alloc();
- if (sys->input && sys->input->is_enabled)
- mmal_port_disable(sys->input);
+ if (format == NULL)
+ msg_Err(dec, "Failed to allocate new format");
+ else
+ {
+ mmal_format_full_copy(format, fmt->format);
+ format->encoding = MMAL_ENCODING_OPAQUE;
- if (sys->output && sys->output->is_enabled)
- mmal_port_disable(sys->output);
+ if (sys->output_format != NULL)
+ mmal_format_free(sys->output_format);
- if (sys->component && sys->component->is_enabled)
- mmal_component_disable(sys->component);
+ sys->output_format = format;
+ }
+ }
+ else if (buffer->cmd != 0) {
+ msg_Warn(dec, "Unexpected output cb event: %4.4s", (const char*)&buffer->cmd);
+ }
- if (sys->input_pool)
- mmal_pool_destroy(sys->input_pool);
+ // If we get here then we were flushing (cmd == 0 && len == 0) or
+ // that was an EVENT - in either case we want to release the buffer
+ // back to its pool rather than recycle it.
+ mmal_buffer_header_reset(buffer);
+ buffer->user_data = NULL;
+ mmal_buffer_header_release(buffer);
+}
- if (sys->output_format)
- mmal_format_free(sys->output_format);
- if (sys->output_pool)
- mmal_pool_destroy(sys->output_pool);
- if (sys->component)
- mmal_component_release(sys->component);
+static void fill_output_port(decoder_t *dec)
+{
+ decoder_sys_t *sys = dec->p_sys;
- vlc_sem_destroy(&sys->sem);
- free(sys);
+ if (decoder_UpdateVideoFormat(dec) != 0)
+ {
+ // If we have a new format don't bother stuffing the buffer
+ // We should get a reset RSN
+ return;
+ }
- bcm_host_deinit();
+ hw_mmal_port_pool_ref_fill(sys->ppr);
}
static int change_output_format(decoder_t *dec)
{
MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T interlace_type;
- decoder_sys_t *sys = dec->p_sys;
+ decoder_sys_t * const sys = dec->p_sys;
MMAL_STATUS_T status;
int ret = 0;
@@ -297,7 +327,6 @@ static int change_output_format(decoder_t *dec)
ret = -1;
}
- msg_Dbg(dec, "%s: Do full port reset", __func__);
status = mmal_port_disable(sys->output);
if (status != MMAL_SUCCESS) {
msg_Err(dec, "Failed to disable output port (status=%"PRIx32" %s)",
@@ -313,16 +342,10 @@ static int change_output_format(decoder_t *dec)
return -1;
}
- if (sys->opaque) {
- sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
- } else {
- sys->output->buffer_num = __MAX(sys->output->buffer_num_recommended,
- MIN_NUM_BUFFERS_IN_TRANSIT);
- }
-
+ sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
sys->output->buffer_size = sys->output->buffer_size_recommended;
- status = mmal_port_enable(sys->output, output_port_cb);
+ status = mmal_port_enable(sys->output, decoder_output_cb);
if (status != MMAL_SUCCESS) {
msg_Err(dec, "Failed to enable output port (status=%"PRIx32" %s)",
status, mmal_status_to_string(status));
@@ -330,25 +353,11 @@ static int change_output_format(decoder_t *dec)
}
if (!sys->started) {
- if (!sys->opaque) {
- sys->output_pool = mmal_port_pool_create(sys->output, sys->output->buffer_num, 0);
- msg_Dbg(dec, "Created output pool with %d pictures", sys->output_pool->headers_num);
- }
-
sys->started = true;
/* we need one picture from vout for each buffer header on the output
* port */
- dec->i_extra_picture_buffers = sys->output->buffer_num;
-
- /* remove what VLC core reserves as it is part of the pool size
- * already */
- if (dec->fmt_in.i_codec == VLC_CODEC_H264)
- dec->i_extra_picture_buffers -= 19;
- else
- dec->i_extra_picture_buffers -= 3;
-
- msg_Dbg(dec, "Request %d extra pictures", dec->i_extra_picture_buffers);
+ dec->i_extra_picture_buffers = 10;
}
apply_fmt:
@@ -374,151 +383,84 @@ apply_fmt:
sys->b_progressive = (interlace_type.eMode == MMAL_InterlaceProgressive);
sys->b_top_field_first = sys->b_progressive ? true :
(interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst);
- msg_Dbg(dec, "Detected %s%s video (%d)",
- sys->b_progressive ? "progressive" : "interlaced",
- sys->b_progressive ? "" : (sys->b_top_field_first ? " tff" : " bff"),
- interlace_type.eMode);
}
+ // Tell the rest of the world we have changed format
+ vlc_mutex_lock(&sys->pic_lock);
+ ret = decoder_UpdateVideoFormat(dec);
+ vlc_mutex_unlock(&sys->pic_lock);
+
return ret;
}
-static int send_output_buffer(decoder_t *dec)
+static MMAL_STATUS_T
+set_extradata_and_commit(decoder_t * const dec, decoder_sys_t * const sys)
{
- decoder_sys_t *sys = dec->p_sys;
- MMAL_BUFFER_HEADER_T *buffer;
- picture_sys_t *p_sys;
- picture_t *picture = NULL;
MMAL_STATUS_T status;
- int ret = 0;
-
- if (!sys->output->is_enabled)
- return VLC_EGENERIC;
-
- /* If local output pool is allocated, use it - this is only the case for
- * non-opaque modes */
- if (sys->output_pool) {
- buffer = mmal_queue_get(sys->output_pool->queue);
- if (!buffer) {
- msg_Warn(dec, "Failed to get new buffer");
- return VLC_EGENERIC;
- }
- }
- if (!decoder_UpdateVideoFormat(dec))
- picture = decoder_NewPicture(dec);
- if (!picture) {
- msg_Warn(dec, "Failed to get new picture");
- ret = -1;
- goto err;
- }
-
- p_sys = picture->p_sys;
- if (sys->output_pool) {
- unsigned buffer_size = 0;
- for (int i = 0; i < picture->i_planes; i++)
- buffer_size += picture->p[i].i_lines * picture->p[i].i_pitch;
- if (buffer_size < sys->output->buffer_size) {
- msg_Err(dec, "Retrieved picture with too small data block (%d < %d)",
- buffer_size, sys->output->buffer_size);
- ret = VLC_EGENERIC;
- goto err;
- }
-
- mmal_buffer_header_reset(buffer);
- buffer->alloc_size = sys->output->buffer_size;
-
- if (!sys->opaque)
- buffer->data = picture->p[0].p_pixels;
- } else {
- buffer = p_sys->buffer;
- if (!buffer) {
- msg_Warn(dec, "Picture has no buffer attached");
- picture_Release(picture);
- return VLC_EGENERIC;
- }
- buffer->data = p_sys->buffer->data;
- }
- buffer->user_data = picture;
- buffer->cmd = 0;
-
- status = mmal_port_send_buffer(sys->output, buffer);
+ status = mmal_port_format_commit(sys->input);
if (status != MMAL_SUCCESS) {
- msg_Err(dec, "Failed to send buffer to output port (status=%"PRIx32" %s)",
- status, mmal_status_to_string(status));
- ret = -1;
- goto err;
- }
- atomic_fetch_add(&sys->output_in_transit, 1);
-
- return ret;
-
-err:
- if (picture)
- picture_Release(picture);
- if (sys->output_pool && buffer) {
- buffer->data = NULL;
- mmal_buffer_header_release(buffer);
+ msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
+ sys->input->name, status, mmal_status_to_string(status));
}
- return ret;
+ return status;
}
-static void fill_output_port(decoder_t *dec)
+static MMAL_STATUS_T decoder_send_extradata(decoder_t * const dec, decoder_sys_t *const sys)
{
- decoder_sys_t *sys = dec->p_sys;
-
- unsigned max_buffers_in_transit = 0;
- int buffers_available = 0;
- int buffers_to_send = 0;
- int i;
-
- if (sys->output_pool) {
- max_buffers_in_transit = __MAX(sys->output_pool->headers_num,
- MIN_NUM_BUFFERS_IN_TRANSIT);
- buffers_available = mmal_queue_length(sys->output_pool->queue);
- } else {
- max_buffers_in_transit = NUM_DECODER_BUFFER_HEADERS;
- buffers_available = NUM_DECODER_BUFFER_HEADERS - atomic_load(&sys->output_in_transit);
+ if (dec->fmt_in.i_codec == VLC_CODEC_H264 &&
+ dec->fmt_in.i_extra > 0)
+ {
+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->input_pool->queue);
+ MMAL_STATUS_T status;
+
+ mmal_buffer_header_reset(buf);
+ buf->cmd = 0;
+ buf->user_data = NULL;
+ buf->alloc_size = sys->input->buffer_size;
+ buf->length = dec->fmt_in.i_extra;
+ buf->data = dec->fmt_in.p_extra;
+ buf->flags = MMAL_BUFFER_HEADER_FLAG_CONFIG;
+
+ status = mmal_port_send_buffer(sys->input, buf);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(dec, "Failed to send extradata buffer to input port (status=%"PRIx32" %s)",
+ status, mmal_status_to_string(status));
+ return status;
+ }
}
- buffers_to_send = max_buffers_in_transit - atomic_load(&sys->output_in_transit);
-
- if (buffers_to_send > buffers_available)
- buffers_to_send = buffers_available;
-#ifndef NDEBUG
- msg_Dbg(dec, "Send %d buffers to output port (available: %d, "
- "in_transit: %d, buffer_num: %d)",
- buffers_to_send, buffers_available,
- atomic_load(&sys->output_in_transit),
- sys->output->buffer_num);
-#endif
- for (i = 0; i < buffers_to_send; ++i)
- if (send_output_buffer(dec) < 0)
- break;
+ return MMAL_SUCCESS;
}
static void flush_decoder(decoder_t *dec)
{
- decoder_sys_t *sys = dec->p_sys;
+ decoder_sys_t *const sys = dec->p_sys;
- msg_Dbg(dec, "Flushing decoder ports...");
- mmal_port_flush(sys->output);
- mmal_port_flush(sys->input);
-
- while (atomic_load(&sys->output_in_transit) ||
- atomic_load(&sys->input_in_transit))
- vlc_sem_wait(&sys->sem);
+ if (!sys->b_flushed) {
+ mmal_port_disable(sys->input);
+ mmal_port_disable(sys->output);
+ // We can leave the input disabled, but we want the output enabled
+ // in order to sink any buffers returning from other modules
+ mmal_port_enable(sys->output, decoder_output_cb);
+ sys->b_flushed = true;
+ }
}
static int decode(decoder_t *dec, block_t *block)
{
decoder_sys_t *sys = dec->p_sys;
MMAL_BUFFER_HEADER_T *buffer;
- bool need_flush = false;
uint32_t len;
uint32_t flags = 0;
MMAL_STATUS_T status;
+ if (sys->err_stream != MMAL_SUCCESS) {
+ msg_Err(dec, "MMAL error reported by ctrl");
+ flush_decoder(dec);
+ return VLCDEC_ECRITICAL; /// I think they are all fatal
+ }
+
/*
* Configure output port if necessary
*/
@@ -529,18 +471,47 @@ static int decode(decoder_t *dec, block_t *block)
sys->output_format = NULL;
}
- if (!block)
- goto out;
+ if (block == NULL)
+ return VLCDEC_SUCCESS;
/*
* Check whether full flush is required
*/
- if (block && block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
+ if (block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
flush_decoder(dec);
+ }
+
+ if (block->i_buffer == 0)
+ {
block_Release(block);
return VLCDEC_SUCCESS;
}
+ // Reenable stuff if the last thing we did was flush
+ if (!sys->output->is_enabled &&
+ (status = mmal_port_enable(sys->output, decoder_output_cb)) != MMAL_SUCCESS)
+ {
+ msg_Err(dec, "Output port enable failed");
+ goto fail;
+ }
+
+ if (!sys->input->is_enabled)
+ {
+ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
+ goto fail;
+
+ if ((status = mmal_port_enable(sys->input, input_port_cb)) != MMAL_SUCCESS)
+ {
+ msg_Err(dec, "Input port enable failed");
+ goto fail;
+ }
+
+ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
+ goto fail;
+ }
+
+ // *** We cannot get a picture to put the result in 'till we have
+ // reported the size & the output stages have been set up
if (sys->started)
fill_output_port(dec);
@@ -551,18 +522,21 @@ static int decode(decoder_t *dec, block_t *block)
if (block->i_flags & BLOCK_FLAG_CORRUPTED)
flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED;
- while (block && block->i_buffer > 0) {
- buffer = mmal_queue_timedwait(sys->input_pool->queue, 100);
+ while (block != NULL)
+ {
+ buffer = mmal_queue_wait(sys->input_pool->queue);
if (!buffer) {
msg_Err(dec, "Failed to retrieve buffer header for input data");
- need_flush = true;
- break;
+ goto fail;
}
+
mmal_buffer_header_reset(buffer);
buffer->cmd = 0;
- buffer->pts = block->i_pts != 0 ? block->i_pts : block->i_dts;
+ buffer->pts = block->i_pts != VLC_TICK_INVALID ? block->i_pts :
+ block->i_dts != VLC_TICK_INVALID ? block->i_dts : MMAL_TIME_UNKNOWN;
buffer->dts = block->i_dts;
buffer->alloc_size = sys->input->buffer_size;
+ buffer->user_data = NULL;
len = block->i_buffer;
if (len > buffer->alloc_size)
@@ -582,87 +556,151 @@ static int decode(decoder_t *dec, block_t *block)
if (status != MMAL_SUCCESS) {
msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)",
status, mmal_status_to_string(status));
- break;
+ goto fail;
}
- atomic_fetch_add(&sys->input_in_transit, 1);
+
+ // Reset flushed flag once we have sent a buf
+ sys->b_flushed = false;
}
+ return VLCDEC_SUCCESS;
-out:
- if (need_flush)
- flush_decoder(dec);
+fail:
+ flush_decoder(dec);
+ return VLCDEC_ECRITICAL;
- return VLCDEC_SUCCESS;
}
-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+static void CloseDecoder(vlc_object_t *p_this)
{
- decoder_t *dec = (decoder_t *)port->userdata;
- MMAL_STATUS_T status;
+ decoder_t *dec = (decoder_t*)p_this;
+ decoder_sys_t *sys = dec->p_sys;
- if (buffer->cmd == MMAL_EVENT_ERROR) {
- status = *(uint32_t *)buffer->data;
- msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
- mmal_status_to_string(status));
+ if (!sys)
+ return;
+
+ if (sys->component != NULL) {
+ if (sys->input->is_enabled)
+ mmal_port_disable(sys->input);
+
+ if (sys->output->is_enabled)
+ mmal_port_disable(sys->output);
+
+ if (sys->component->control->is_enabled)
+ mmal_port_disable(sys->component->control);
+
+ if (sys->component->is_enabled)
+ mmal_component_disable(sys->component);
+
+ mmal_component_release(sys->component);
}
- mmal_buffer_header_release(buffer);
-}
+ if (sys->input_pool != NULL)
+ mmal_pool_destroy(sys->input_pool);
-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
-{
- block_t *block = (block_t *)buffer->user_data;
- decoder_t *dec = (decoder_t *)port->userdata;
- decoder_sys_t *sys = dec->p_sys;
- buffer->user_data = NULL;
+ if (sys->output_format != NULL)
+ mmal_format_free(sys->output_format);
- mmal_buffer_header_release(buffer);
- if (block)
- block_Release(block);
- atomic_fetch_sub(&sys->input_in_transit, 1);
- vlc_sem_post(&sys->sem);
+ hw_mmal_port_pool_ref_release(sys->ppr, false);
+
+ cma_vcsm_exit(sys->vcsm_init_type);
+
+ vlc_mutex_destroy(&sys->pic_lock);
+ free(sys);
}
-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+static int OpenDecoder(vlc_object_t *p_this)
{
- decoder_t *dec = (decoder_t *)port->userdata;
- decoder_sys_t *sys = dec->p_sys;
- picture_t *picture;
- MMAL_EVENT_FORMAT_CHANGED_T *fmt;
- MMAL_ES_FORMAT_T *format;
-
- if (buffer->cmd == 0) {
- picture = (picture_t *)buffer->user_data;
- if (buffer->length > 0) {
- picture->date = buffer->pts;
- picture->b_progressive = sys->b_progressive;
- picture->b_top_field_first = sys->b_top_field_first;
- decoder_QueueVideo(dec, picture);
- } else {
- picture_Release(picture);
- if (sys->output_pool) {
- buffer->user_data = NULL;
- buffer->alloc_size = 0;
- buffer->data = NULL;
- mmal_buffer_header_release(buffer);
- }
- }
- atomic_fetch_sub(&sys->output_in_transit, 1);
- vlc_sem_post(&sys->sem);
- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
- fmt = mmal_event_format_changed_get(buffer);
+ decoder_t *dec = (decoder_t*)p_this;
+ int ret = VLC_EGENERIC;
+ decoder_sys_t *sys;
+ MMAL_STATUS_T status;
+ const MMAL_FOURCC_T in_fcc = vlc_to_mmal_es_fourcc(dec->fmt_in.i_codec);
- format = mmal_format_alloc();
- mmal_format_full_copy(format, fmt->format);
+ if (!is_enc_supported(&supported_decode_in_enc, in_fcc))
+ return VLC_EGENERIC;
- if (sys->opaque)
- format->encoding = MMAL_ENCODING_OPAQUE;
+ sys = calloc(1, sizeof(decoder_sys_t));
+ if (unlikely(sys == NULL))
+ return VLC_ENOMEM;
+ dec->p_sys = sys;
+ vlc_mutex_init(&sys->pic_lock);
- if (sys->output_format)
- mmal_format_free(sys->output_format);
- sys->output_format = format;
+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
+ msg_Err(dec, "VCSM init failed");
+ goto fail;
+ }
+ msg_Info(dec, "VCSM init succeeded: %s", cma_vcsm_init_str(sys->vcsm_init_type));
- mmal_buffer_header_release(buffer);
- } else {
- mmal_buffer_header_release(buffer);
+ sys->err_stream = MMAL_SUCCESS;
+
+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
+ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
+ goto fail;
}
+
+ sys->input = sys->component->input[0];
+ sys->output = sys->component->output[0];
+
+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
+ sys->input->format->encoding = in_fcc;
+
+ if (!set_and_test_enc_supported(&supported_decode_in_enc, sys->input, in_fcc)) {
+ goto fail;
+ }
+
+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
+ status = mmal_port_enable(sys->component->control, control_port_cb);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
+ sys->component->control->name, status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
+ goto fail;
+
+ sys->input->buffer_size = sys->input->buffer_size_recommended;
+ sys->input->buffer_num = sys->input->buffer_num_recommended;
+
+ status = mmal_port_enable(sys->input, input_port_cb);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
+ sys->input->name, status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(dec), &sys->ppr,
+ sys->output, NUM_EXTRA_BUFFERS, decoder_output_cb)) != MMAL_SUCCESS)
+ goto fail;
+
+ status = mmal_component_enable(sys->component);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
+ sys->component->name, status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ if ((sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
+ {
+ msg_Err(dec, "Failed to create input pool");
+ goto fail;
+ }
+
+ sys->b_flushed = true;
+ dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
+ dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
+
+ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
+ goto fail;
+
+ dec->pf_decode = decode;
+ dec->pf_flush = flush_decoder;
+
+ return 0;
+
+fail:
+ CloseDecoder(p_this);
+ return ret;
}
diff --git a/modules/hw/mmal/deinterlace.c b/modules/hw/mmal/deinterlace.c
index 66afd2bf7fd..cf61b670421 100644
--- a/modules/hw/mmal/deinterlace.c
+++ b/modules/hw/mmal/deinterlace.c
@@ -5,6 +5,7 @@
*
* Authors: Julian Scheel <julian at jusst.de>
* Dennis Hamester <dennis.hamester at gmail.com>
+ * John Cox <jc at kynesim.co.uk>
*
* 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
@@ -25,25 +26,39 @@
#include "config.h"
#endif
-#include <stdatomic.h>
-
#include <vlc_common.h>
-#include <vlc_picture_pool.h>
#include <vlc_plugin.h>
#include <vlc_filter.h>
#include "mmal_picture.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 MIN_NUM_BUFFERS_IN_TRANSIT 2
+#define MMAL_DEINTERLACE_NO_QPU "mmal-deinterlace-no-qpu"
+#define MMAL_DEINTERLACE_NO_QPU_TEXT N_("Do not use QPUs for advanced HD deinterlacing.")
+#define MMAL_DEINTERLACE_NO_QPU_LONGTEXT N_("Do not make use of the QPUs to allow higher quality deinterlacing of HD content.")
+
+#define MMAL_DEINTERLACE_ADV "mmal-deinterlace-adv"
+#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace")
+#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace")
+
+#define MMAL_DEINTERLACE_FAST "mmal-deinterlace-fast"
+#define MMAL_DEINTERLACE_FAST_TEXT N_("Force fast deinterlace")
+#define MMAL_DEINTERLACE_FAST_LONGTEXT N_("Force fast deinterlace")
+
+#define MMAL_DEINTERLACE_NONE "mmal-deinterlace-none"
+#define MMAL_DEINTERLACE_NONE_TEXT N_("Force no deinterlace")
+#define MMAL_DEINTERLACE_NONE_LONGTEXT N_("Force no interlace. Simply strips off the interlace markers and passes the frame straight through. "\
+ "This is the default for > SD if < 96M gpu-mem")
-#define MMAL_DEINTERLACE_QPU "mmal-deinterlace-adv-qpu"
-#define MMAL_DEINTERLACE_QPU_TEXT N_("Use QPUs for advanced HD deinterlacing.")
-#define MMAL_DEINTERLACE_QPU_LONGTEXT N_("Make use of the QPUs to allow higher quality deinterlacing of HD content.")
+#define MMAL_DEINTERLACE_HALF_RATE "mmal-deinterlace-half-rate"
+#define MMAL_DEINTERLACE_HALF_RATE_TEXT N_("Halve output framerate")
+#define MMAL_DEINTERLACE_HALF_RATE_LONGTEXT N_("Halve output framerate. 1 output frame for each pair of interlaced fields input")
+
+#define MMAL_DEINTERLACE_FULL_RATE "mmal-deinterlace-full-rate"
+#define MMAL_DEINTERLACE_FULL_RATE_TEXT N_("Full output framerate")
+#define MMAL_DEINTERLACE_FULL_RATE_LONGTEXT N_("Full output framerate. 1 output frame for each interlaced field input")
static int OpenMmalDeinterlace(vlc_object_t *);
static void CloseMmalDeinterlace(vlc_object_t *);
@@ -51,13 +66,24 @@ static void CloseMmalDeinterlace(vlc_object_t *);
vlc_module_begin()
set_shortname(N_("MMAL deinterlace"))
set_description(N_("MMAL-based deinterlace filter plugin"))
- set_capability("video filter", 0)
+ set_capability("video filter", 900)
set_category(CAT_VIDEO)
set_subcategory(SUBCAT_VIDEO_VFILTER)
set_callbacks(OpenMmalDeinterlace, CloseMmalDeinterlace)
add_shortcut("deinterlace")
- add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT,
- MMAL_DEINTERLACE_QPU_LONGTEXT, true);
+ add_bool(MMAL_DEINTERLACE_NO_QPU, false, MMAL_DEINTERLACE_NO_QPU_TEXT,
+ MMAL_DEINTERLACE_NO_QPU_LONGTEXT, true);
+ add_bool(MMAL_DEINTERLACE_ADV, false, MMAL_DEINTERLACE_ADV_TEXT,
+ MMAL_DEINTERLACE_ADV_LONGTEXT, true);
+ add_bool(MMAL_DEINTERLACE_FAST, false, MMAL_DEINTERLACE_FAST_TEXT,
+ MMAL_DEINTERLACE_FAST_LONGTEXT, true);
+ add_bool(MMAL_DEINTERLACE_NONE, false, MMAL_DEINTERLACE_NONE_TEXT,
+ MMAL_DEINTERLACE_NONE_LONGTEXT, true);
+ add_bool(MMAL_DEINTERLACE_HALF_RATE, false, MMAL_DEINTERLACE_HALF_RATE_TEXT,
+ MMAL_DEINTERLACE_HALF_RATE_LONGTEXT, true);
+ add_bool(MMAL_DEINTERLACE_FULL_RATE, false, MMAL_DEINTERLACE_FULL_RATE_TEXT,
+ MMAL_DEINTERLACE_FULL_RATE_LONGTEXT, true);
+
vlc_module_end()
typedef struct
@@ -65,439 +91,518 @@ typedef struct
MMAL_COMPONENT_T *component;
MMAL_PORT_T *input;
MMAL_PORT_T *output;
+ MMAL_POOL_T *in_pool;
+
+ MMAL_QUEUE_T * out_q;
- MMAL_QUEUE_T *filtered_pictures;
- vlc_sem_t sem;
+ MMAL_POOL_T * out_pool;
- atomic_bool started;
+ hw_mmal_port_pool_ref_t *out_ppr;
+
+ bool half_rate;
+ bool use_qpu;
+ bool use_fast;
+ bool use_passthrough;
+ unsigned int seq_in; // Seq of next frame to submit (1-15) [Init=1]
+ unsigned int seq_out; // Seq of last frame received (1-15) [Init=15]
+
+ vcsm_init_type_t vcsm_init_type;
- /* statistics */
- int output_in_transit;
- int input_in_transit;
} filter_sys_t;
-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);
-static void flush(filter_t *filter);
#define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx"
-static int OpenMmalDeinterlace(vlc_object_t *obj)
+
+
+// Buffer attached to pic on success, is still valid on failure
+static picture_t * di_alloc_opaque(filter_t * const p_filter, MMAL_BUFFER_HEADER_T * const buf)
{
- filter_t *filter = (filter_t *)obj;
- if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
- return VLC_EGENERIC;
+ filter_sys_t *const filter_sys = p_filter->p_sys;
+ picture_t * const pic = filter_NewPicture(p_filter);
- if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
- return VLC_EGENERIC;
+ if (pic == NULL)
+ goto fail1;
- int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
- vlc_tick_from_samples( filter->fmt_in.video.i_frame_rate_base,
- filter->fmt_in.video.i_frame_rate ) : 0;
- bool use_qpu = var_InheritBool(filter, MMAL_DEINTERLACE_QPU);
+ if (buf->length == 0) {
+ msg_Err(p_filter, "Empty buffer");
+ goto fail2;
+ }
- MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
- { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
- MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
- 4,
- { 3, frame_duration, 0, use_qpu }
- };
+ if ((pic->context = hw_mmal_gen_context(buf, filter_sys->out_ppr)) == NULL)
+ goto fail2;
- int ret = VLC_SUCCESS;
- MMAL_STATUS_T status;
- filter_sys_t *sys;
+ buf_to_pic_copy_props(pic, buf);
- msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!",
- frame_duration, use_qpu ? "used" : "unused");
+ return pic;
- sys = calloc(1, sizeof(filter_sys_t));
- if (!sys)
- return VLC_ENOMEM;
- filter->p_sys = sys;
+fail2:
+ picture_Release(pic);
+fail1:
+// mmal_buffer_header_release(buf);
+ return NULL;
+}
- bcm_host_init();
+static void di_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ VLC_UNUSED(port);
- 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;
- }
+ mmal_buffer_header_release(buffer);
+}
- 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;
+static void di_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
+{
+ if (buf->cmd == 0 && buf->length != 0)
+ {
+ // The filter structure etc. should always exist if we have contents
+ // but might not on later flushes as we shut down
+ filter_t * const p_filter = (filter_t *)port->userdata;
+ filter_sys_t * const sys = p_filter->p_sys;
+
+ mmal_queue_put(sys->out_q, buf);
+ return;
}
- 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;
- }
+ mmal_buffer_header_reset(buf); // User data stays intact so release will kill pic
+ mmal_buffer_header_release(buf);
+}
- sys->input = sys->component->input[0];
- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
- 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);
- filter->fmt_out.video.i_frame_rate *= 2;
- 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;
+static MMAL_STATUS_T fill_output_from_q(filter_t * const p_filter, filter_sys_t * const sys, MMAL_QUEUE_T * const q)
+{
+ MMAL_BUFFER_HEADER_T * out_buf;
+
+ while ((out_buf = mmal_queue_get(q)) != NULL)
+ {
+ MMAL_STATUS_T err;
+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
+ {
+ msg_Err(p_filter, "Send buffer to output failed");
+ mmal_queue_put_back(q, out_buf);
+ return err;
+ }
}
- sys->input->buffer_size = sys->input->buffer_size_recommended;
- sys->input->buffer_num = sys->input->buffer_num_recommended;
+ return MMAL_SUCCESS;
+}
+
+// Output buffers may contain a pic ref on error or flush
+// Free it
+static unsigned int seq_inc(unsigned int x)
+{
+ return x + 1 >= 16 ? 1 : x + 1;
+}
- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
- 1
- };
+static unsigned int seq_delta(unsigned int sseq, unsigned int fseq)
+{
+ return fseq == 0 ? 0 : fseq <= sseq ? sseq - fseq : 15 - (fseq - sseq);
+}
- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
- if (status != MMAL_SUCCESS) {
- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
- sys->input->name, status, mmal_status_to_string(status));
- goto out;
+static picture_t *deinterlace(filter_t * p_filter, picture_t * p_pic)
+{
+ filter_sys_t * const sys = p_filter->p_sys;
+ picture_t *ret_pics = NULL;
+ MMAL_STATUS_T err;
+ MMAL_BUFFER_HEADER_T * out_buf = NULL;
+
+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
+ {
+ // ****** Breaks on opaque (at least)
+
+ if (sys->input->is_enabled)
+ mmal_port_disable(sys->input);
+
+ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS)
+ msg_Err(p_filter, "Failed to update pic format");
+ sys->input->buffer_num = 30;
+ sys->input->buffer_size = sys->input->buffer_size_recommended;
+ mmal_log_dump_format(sys->input->format);
}
- 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;
+ // Reenable stuff if the last thing we did was flush
+ // Output should always be enabled
+ if (!sys->input->is_enabled &&
+ (err = mmal_port_enable(sys->input, di_input_port_cb)) != MMAL_SUCCESS)
+ {
+ msg_Err(p_filter, "Input port reenable failed");
+ goto fail;
}
- 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);
-
- 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;
+ {
+ // Fill output from anything that has turned up in pool Q
+ if (hw_mmal_port_pool_ref_fill(sys->out_ppr) != MMAL_SUCCESS)
+ {
+ msg_Err(p_filter, "Out port fill fail");
+ goto fail;
+ }
}
- sys->output->buffer_num = 3;
+ // Stuff into input
+ // We assume the BH is already set up with values reflecting pic date etc.
+ {
+ MMAL_BUFFER_HEADER_T * const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->in_pool);
- MMAL_PARAMETER_UINT32_T extra_buffers = {
- { MMAL_PARAMETER_EXTRA_BUFFERS, sizeof(MMAL_PARAMETER_UINT32_T) },
- 5
- };
- 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));
- goto out;
- }
+ if (pic_buf == NULL)
+ {
+ msg_Err(p_filter, "Pic has not attached buffer");
+ goto fail;
+ }
- zero_copy = (MMAL_PARAMETER_BOOLEAN_T) {
- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
- 1
- };
+ picture_Release(p_pic);
- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
- if (status != MMAL_SUCCESS) {
- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
- sys->output->name, status, mmal_status_to_string(status));
- goto out;
+ // Add a sequence to the flags so we can track what we have actually
+ // deinterlaced
+ pic_buf->flags = (pic_buf->flags & ~(0xfU * MMAL_BUFFER_HEADER_FLAG_USER0)) | (sys->seq_in * (MMAL_BUFFER_HEADER_FLAG_USER0));
+ sys->seq_in = seq_inc(sys->seq_in);
+
+ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
+ {
+ msg_Err(p_filter, "Send buffer to input failed");
+ mmal_buffer_header_release(pic_buf);
+ goto fail;
+ }
}
- 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;
+ // Return anything that is in the out Q
+ {
+ picture_t ** pp_pic = &ret_pics;
+
+ // Advanced di has a 3 frame latency, so if the seq delta is greater
+ // than that then we are expecting at least two frames of output. Wait
+ // for one of those.
+ // seq_in is seq of the next frame we are going to submit (1-15, no 0)
+ // seq_out is last frame we removed from Q
+ // So after 4 frames sent (1st time we want to wait), 0 rx seq_in=5, seq_out=15, delta=5
+
+ while ((out_buf = (seq_delta(sys->seq_in, sys->seq_out) >= 5 ? mmal_queue_timedwait(sys->out_q, 1000) : mmal_queue_get(sys->out_q))) != NULL)
+ {
+ const unsigned int seq_out = (out_buf->flags / MMAL_BUFFER_HEADER_FLAG_USER0) & 0xf;
+ picture_t * out_pic;
+
+ {
+ out_pic = di_alloc_opaque(p_filter, out_buf);
+
+ if (out_pic == NULL) {
+ msg_Warn(p_filter, "Failed to alloc new filter output pic");
+ mmal_queue_put_back(sys->out_q, out_buf); // Wedge buf back into Q in the hope we can alloc a pic later
+ out_buf = NULL;
+ break;
+ }
+ }
+ out_buf = NULL; // Now attached to pic or recycled
+
+ *pp_pic = out_pic;
+ pp_pic = &out_pic->p_next;
+
+ // Ignore 0 seqs
+ // Don't think these should actually happen
+ if (seq_out != 0)
+ sys->seq_out = seq_out;
+ }
+
+ // Crash on lockup
+ assert(ret_pics != NULL || seq_delta(sys->seq_in, sys->seq_out) < 5);
}
- 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;
+ return ret_pics;
+
+fail:
+ if (out_buf != NULL)
+ mmal_buffer_header_release(out_buf);
+ picture_Release(p_pic);
+ return NULL;
+}
+
+static void di_flush(filter_t *p_filter)
+{
+ filter_sys_t * const sys = p_filter->p_sys;
+
+ if (sys->input != NULL && sys->input->is_enabled)
+ mmal_port_disable(sys->input);
+
+ if (sys->output != NULL && sys->output->is_enabled)
+ {
+ {
+ // Wedge anything we've got into the output port as that will free the underlying buffers
+ fill_output_from_q(p_filter, sys, sys->out_q);
+
+ mmal_port_disable(sys->output);
+
+ // If that dumped anything real into the out_q then have another go
+ if (mmal_queue_length(sys->out_q) != 0)
+ {
+ mmal_port_enable(sys->output, di_output_port_cb);
+ fill_output_from_q(p_filter, sys, sys->out_q);
+ mmal_port_disable(sys->output);
+ // Out q should now be empty & should remain so until the input is reenabled
+ }
+ }
+ mmal_port_enable(sys->output, di_output_port_cb);
+
+ // Leaving the input disabled is fine - but we want to leave the output enabled
+ // so we can retrieve buffers that are still bound to pictures
}
- sys->filtered_pictures = mmal_queue_create();
+ sys->seq_in = 1;
+ sys->seq_out = 15;
+}
- filter->pf_video_filter = deinterlace;
- filter->pf_flush = flush;
- vlc_sem_init(&sys->sem, 0);
+static void pass_flush(filter_t *p_filter)
+{
+ // Nothing to do
+ VLC_UNUSED(p_filter);
+}
-out:
- if (ret != VLC_SUCCESS)
- CloseMmalDeinterlace(obj);
+static picture_t * pass_deinterlace(filter_t * p_filter, picture_t * p_pic)
+{
+ VLC_UNUSED(p_filter);
- return ret;
+ p_pic->b_progressive = true;
+ return p_pic;
}
-static void CloseMmalDeinterlace(vlc_object_t *obj)
+
+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
- filter_t *filter = (filter_t *)obj;
- filter_sys_t *sys = filter->p_sys;
- MMAL_BUFFER_HEADER_T *buffer;
+ filter_t *filter = (filter_t *)port->userdata;
+ MMAL_STATUS_T status;
- if (sys->component && sys->component->control->is_enabled)
- mmal_port_disable(sys->component->control);
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
+ status = *(uint32_t *)buffer->data;
+ msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
+ mmal_status_to_string(status));
+ }
- if (sys->input && sys->input->is_enabled)
- mmal_port_disable(sys->input);
+ mmal_buffer_header_reset(buffer);
+ mmal_buffer_header_release(buffer);
+}
+
+static void CloseMmalDeinterlace(vlc_object_t *p_this)
+{
+ filter_t *filter = (filter_t*)p_this;
+ filter_sys_t * const sys = filter->p_sys;
- if (sys->output && sys->output->is_enabled)
- mmal_port_disable(sys->output);
+ if (sys == NULL)
+ return;
+
+ if (sys->use_passthrough)
+ {
+ free(sys);
+ return;
+ }
+
+ di_flush(filter);
+
+ if (sys->component && sys->component->control->is_enabled)
+ mmal_port_disable(sys->component->control);
if (sys->component && sys->component->is_enabled)
mmal_component_disable(sys->component);
- while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
- picture_t *pic = (picture_t *)buffer->user_data;
- picture_Release(pic);
- }
+ if (sys->in_pool != NULL)
+ mmal_pool_destroy(sys->in_pool);
+
+ hw_mmal_port_pool_ref_release(sys->out_ppr, false);
+ // Once we exit filter & sys are invalid so mark as such
+ if (sys->output != NULL)
+ sys->output->userdata = NULL;
- if (sys->filtered_pictures)
- mmal_queue_destroy(sys->filtered_pictures);
+ if (sys->out_q != NULL)
+ mmal_queue_destroy(sys->out_q);
if (sys->component)
mmal_component_release(sys->component);
- vlc_sem_destroy(&sys->sem);
+ cma_vcsm_exit(sys->vcsm_init_type);
+
free(sys);
+}
+
- bcm_host_deinit();
+static bool is_fmt_valid_in(const vlc_fourcc_t fmt)
+{
+ return fmt == VLC_CODEC_MMAL_OPAQUE;
}
-static int send_output_buffer(filter_t *filter)
+static int OpenMmalDeinterlace(vlc_object_t *p_this)
{
- filter_sys_t *sys = filter->p_sys;
- MMAL_BUFFER_HEADER_T *buffer;
+ filter_t *filter = (filter_t*)p_this;
+ int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
+ CLOCK_FREQ * filter->fmt_in.video.i_frame_rate_base /
+ filter->fmt_in.video.i_frame_rate : 0;
+
+ int ret = VLC_EGENERIC;
MMAL_STATUS_T status;
- picture_t *picture;
- int ret = 0;
+ filter_sys_t *sys;
+
+ if (!is_fmt_valid_in(filter->fmt_in.video.i_chroma) ||
+ filter->fmt_out.video.i_chroma != filter->fmt_in.video.i_chroma)
+ return VLC_EGENERIC;
- if (!sys->output->is_enabled) {
- ret = VLC_EGENERIC;
- goto out;
+ sys = calloc(1, sizeof(filter_sys_t));
+ if (!sys)
+ return VLC_ENOMEM;
+ filter->p_sys = sys;
+
+ sys->seq_in = 1;
+ sys->seq_out = 15;
+
+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
+ msg_Err(filter, "VCSM init failed");
+ goto fail;
}
- picture = filter_NewPicture(filter);
- if (!picture) {
- msg_Warn(filter, "Failed to get new picture");
- ret = -1;
- goto out;
+ if (rpi_is_model_pi4())
+ {
+ sys->half_rate = true;
+ sys->use_qpu = false;
+ sys->use_fast = true;
+ }
+ else
+ {
+ sys->half_rate = false;
+ sys->use_qpu = true;
+ sys->use_fast = false;
}
- picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate;
- picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base;
+ sys->use_passthrough = false;
- picture_sys_t *p_sys = picture->p_sys;
- buffer = p_sys->buffer;
- buffer->user_data = picture;
- buffer->cmd = 0;
+ if (filter->fmt_in.video.i_width * filter->fmt_in.video.i_height > 768 * 576)
+ {
+ // We get stressed if we have to try too hard - so make life easier
+ sys->half_rate = true;
+ }
- mmal_picture_lock(picture);
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU))
+ sys->use_qpu = false;
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV))
+ {
+ sys->use_fast = false;
+ sys->use_passthrough = false;
+ }
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_FAST))
+ {
+ sys->use_fast = true;
+ sys->use_passthrough = false;
+ }
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_NONE))
+ sys->use_passthrough = true;
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_FULL_RATE))
+ sys->half_rate = false;
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_HALF_RATE))
+ sys->half_rate = true;
+
+ if (sys->use_passthrough)
+ {
+ filter->pf_video_filter = pass_deinterlace;
+ filter->pf_flush = pass_flush;
+ // Don't need VCSM - get rid of it now
+ cma_vcsm_exit(sys->vcsm_init_type);
+ sys->vcsm_init_type = VCSM_INIT_NONE;
+ return 0;
+ }
- status = mmal_port_send_buffer(sys->output, buffer);
+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
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);
- picture_Release(picture);
- ret = -1;
- } else {
- atomic_fetch_add(&sys->output_in_transit, 1);
- vlc_sem_post(&sys->sem);
+ msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
+ goto fail;
}
-out:
- return ret;
-}
+ {
+ const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
+ { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
+ sys->use_fast ?
+ MMAL_PARAM_IMAGEFX_DEINTERLACE_FAST :
+ MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
+ 4,
+ { 5 /* Frame type: mixed */, frame_duration, sys->half_rate, sys->use_qpu }
+ };
-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(2, MIN_NUM_BUFFERS_IN_TRANSIT);
- int buffers_available = sys->output->buffer_num -
- atomic_load(&sys->output_in_transit) -
- mmal_queue_length(sys->filtered_pictures);
- int buffers_to_send = max_buffers_in_transit - sys->output_in_transit;
- int 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, sys->output_in_transit,
- sys->output->buffer_num);
-#endif
- for (i = 0; i < buffers_to_send; ++i) {
- if (send_output_buffer(filter) < 0)
- break;
+ 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));
+ goto fail;
+ }
}
-}
-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;
- unsigned i = 0;
+ 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));
+ goto fail;
+ }
- fill_output_port(filter);
+ sys->input = sys->component->input[0];
+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
+ sys->input->format->encoding = vlc_to_mmal_video_fourcc(&filter->fmt_in.video);
+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &filter->fmt_in.video);
- picture_sys_t *p_sys = picture->p_sys;
- buffer = p_sys->buffer;
- buffer->user_data = picture;
- buffer->pts = picture->date;
- buffer->cmd = 0;
+ es_format_Copy(&filter->fmt_out, &filter->fmt_in);
+ if (!sys->half_rate)
+ filter->fmt_out.video.i_frame_rate *= 2;
- if (!p_sys->displayed) {
- 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));
- picture_Release(picture);
- } else {
- p_sys->displayed = true;
- atomic_fetch_add(&sys->input_in_transit, 1);
- vlc_sem_post(&sys->sem);
- }
- } else {
- picture_Release(picture);
+ 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));
+ goto fail;
}
+ sys->input->buffer_size = sys->input->buffer_size_recommended;
+ sys->input->buffer_num = 30;
+// sys->input->buffer_num = sys->input->buffer_num_recommended;
- /*
- * Send output buffers
- */
- while(atomic_load(&sys->started) && i < 2) {
- if ((buffer = mmal_queue_timedwait(sys->filtered_pictures, 2000))) {
- 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;
- } else {
- msg_Dbg(filter, "Failed waiting for filtered picture");
- break;
- }
+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
+ {
+ msg_Err(filter, "Failed to create input pool");
+ goto fail;
}
- if (out_picture)
- out_picture->p_next = NULL;
- return ret;
-}
+ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
+ sys->input->name, status, mmal_status_to_string(status));
+ goto fail;
+ }
-static void flush(filter_t *filter)
-{
- filter_sys_t *sys = filter->p_sys;
- MMAL_BUFFER_HEADER_T *buffer;
-
- msg_Dbg(filter, "flush deinterlace filter");
-
- msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)",
- sys->input_in_transit, sys->output_in_transit);
- mmal_port_flush(sys->output);
- mmal_port_flush(sys->input);
-
- msg_Dbg(filter, "flush: wait for all buffers to be returned");
- while (atomic_load(&sys->input_in_transit) ||
- atomic_load(&sys->output_in_transit))
- vlc_sem_wait(&sys->sem);
-
- while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
- picture_t *pic = (picture_t *)buffer->user_data;
- msg_Dbg(filter, "flush: release already filtered pic %p",
- (void *)pic);
- picture_Release(pic);
+ status = mmal_port_enable(sys->input, di_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));
+ goto fail;
}
- atomic_store(&sys->started, false);
- msg_Dbg(filter, "flush: done");
-}
-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));
+ if ((sys->out_q = mmal_queue_create()) == NULL)
+ {
+ msg_Err(filter, "Failed to create out Q");
+ goto fail;
}
- mmal_buffer_header_release(buffer);
-}
+ sys->output = sys->component->output[0];
+ mmal_format_full_copy(sys->output->format, sys->input->format);
-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;
+ {
+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS)
+ goto fail;
+ }
- if (picture) {
- picture_Release(picture);
- } else {
- msg_Warn(filter, "Got buffer without picture on input port - OOOPS");
- mmal_buffer_header_release(buffer);
+ 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));
+ goto fail;
}
- atomic_fetch_sub(&sys->input_in_transit, 1);
- vlc_sem_post(&sys->sem);
+ filter->pf_video_filter = deinterlace;
+ filter->pf_flush = di_flush;
+ return 0;
+
+fail:
+ CloseMmalDeinterlace(p_this);
+ return ret;
}
-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;
-
- if (buffer->cmd == 0) {
- if (buffer->length > 0) {
- atomic_store(&sys->started, true);
- mmal_queue_put(sys->filtered_pictures, buffer);
- picture = (picture_t *)buffer->user_data;
- } else {
- picture = (picture_t *)buffer->user_data;
- picture_Release(picture);
- }
- atomic_fetch_sub(&sys->output_in_transit, 1);
- vlc_sem_post(&sys->sem);
- } 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);
- }
-}
diff --git a/modules/hw/mmal/mmal_picture.c b/modules/hw/mmal/mmal_picture.c
index e24db2c35f8..0da944b72cf 100644
--- a/modules/hw/mmal/mmal_picture.c
+++ b/modules/hw/mmal/mmal_picture.c
@@ -4,6 +4,7 @@
* Copyright © 2014 jusst technologies GmbH
*
* Authors: Julian Scheel <julian at jusst.de>
+ * John Cox <jc at kynesim.co.uk>
*
* 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
@@ -22,26 +23,1028 @@
#include <vlc_common.h>
#include <vlc_picture.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>
+#include <interface/vmcs_host/vcgencmd.h>
#include "mmal_picture.h"
-const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[] = { VLC_CODEC_RGBA, 0 };
+const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[] = { VLC_CODEC_RGBA, VLC_CODEC_BGRA, VLC_CODEC_ARGB, 0 };
+#define UINT64_SIZE(s) (((s) + sizeof(uint64_t) - 1)/sizeof(uint64_t))
-int mmal_picture_lock(picture_t *picture)
+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs)
{
- picture_sys_t *pic_sys = picture->p_sys;
- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
+ switch (vlc_cs)
+ {
+ case COLOR_SPACE_BT601:
+ return MMAL_COLOR_SPACE_ITUR_BT601;
+ case COLOR_SPACE_BT709:
+ return MMAL_COLOR_SPACE_ITUR_BT709;
+ default:
+ break;
+ }
+ return MMAL_COLOR_SPACE_UNKNOWN;
+}
- int offset = 0;
- picture->p[0].p_pixels = buffer->data;
- for (int i = 1; i < picture->i_planes; i++) {
- offset = offset + picture->p[i - 1].i_pitch * picture->p[i - 1].i_lines;
- picture->p[i].p_pixels = (ptrdiff_t)buffer->data + offset;
+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc)
+{
+ switch (vf_vlc->i_chroma) {
+ case VLC_CODEC_RGB32:
+ {
+ // VLC RGB32 aka RV32 means we have to look at the mask values
+ const uint32_t r = vf_vlc->i_rmask;
+ const uint32_t g = vf_vlc->i_gmask;
+ const uint32_t b = vf_vlc->i_bmask;
+ if (r == 0xff0000 && g == 0xff00 && b == 0xff)
+ return MMAL_ENCODING_BGRA;
+ if (r == 0xff && g == 0xff00 && b == 0xff0000)
+ return MMAL_ENCODING_RGBA;
+ if (r == 0xff000000 && g == 0xff0000 && b == 0xff00)
+ return MMAL_ENCODING_ABGR;
+ if (r == 0xff00 && g == 0xff0000 && b == 0xff000000)
+ return MMAL_ENCODING_ARGB;
+ break;
+ }
+ case VLC_CODEC_RGB16:
+ {
+ // VLC RGB16 aka RV16 means we have to look at the mask values
+ const uint32_t r = vf_vlc->i_rmask;
+ const uint32_t g = vf_vlc->i_gmask;
+ const uint32_t b = vf_vlc->i_bmask;
+ if (r == 0xf800 && g == 0x7e0 && b == 0x1f)
+ return MMAL_ENCODING_RGB16;
+ break;
+ }
+ case VLC_CODEC_I420:
+ return MMAL_ENCODING_I420;
+ case VLC_CODEC_RGBA:
+ return MMAL_ENCODING_RGBA;
+ case VLC_CODEC_BGRA:
+ return MMAL_ENCODING_BGRA;
+ case VLC_CODEC_ARGB:
+ return MMAL_ENCODING_ARGB;
+ // VLC_CODEC_ABGR does not exist in VLC
+ case VLC_CODEC_MMAL_OPAQUE:
+ return MMAL_ENCODING_OPAQUE;
+ default:
+ break;
}
+ return 0;
+}
+
+static void vlc_fmt_to_video_format(MMAL_VIDEO_FORMAT_T *const vf_mmal, const video_frame_format_t * const vf_vlc)
+{
+ const unsigned int wmask = (vf_vlc->i_chroma == VLC_CODEC_I420) ? 31 : 15;
+
+ vf_mmal->width = (vf_vlc->i_width + wmask) & ~wmask;
+ vf_mmal->height = (vf_vlc->i_height + 15) & ~15;
+ vf_mmal->crop.x = vf_vlc->i_x_offset;
+ vf_mmal->crop.y = vf_vlc->i_y_offset;
+ vf_mmal->crop.width = vf_vlc->i_visible_width;
+ vf_mmal->crop.height = vf_vlc->i_visible_height;
+ if (vf_vlc->i_sar_num == 0 || vf_vlc->i_sar_den == 0) {
+ vf_mmal->par.num = 1;
+ vf_mmal->par.den = 1;
+ } else {
+ vf_mmal->par.num = vf_vlc->i_sar_num;
+ vf_mmal->par.den = vf_vlc->i_sar_den;
+ }
+ vf_mmal->frame_rate.num = vf_vlc->i_frame_rate;
+ vf_mmal->frame_rate.den = vf_vlc->i_frame_rate_base;
+ vf_mmal->color_space = vlc_to_mmal_color_space(vf_vlc->space);
+}
+
+
+void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc)
+{
+ vlc_fmt_to_video_format(&es_fmt->es->video, vf_vlc);
+}
+
+bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic)
+{
+ MMAL_VIDEO_FORMAT_T vf_new_ss;
+ MMAL_VIDEO_FORMAT_T *const vf_old = &es_fmt->es->video;
+ MMAL_VIDEO_FORMAT_T *const vf_new = &vf_new_ss;
+
+ vlc_fmt_to_video_format(vf_new, &pic->format);
+
+ if (
+ vf_new->width != vf_old->width ||
+ vf_new->height != vf_old->height ||
+ vf_new->crop.x != vf_old->crop.x ||
+ vf_new->crop.y != vf_old->crop.y ||
+ vf_new->crop.width != vf_old->crop.width ||
+ vf_new->crop.height != vf_old->crop.height ||
+ vf_new->par.num != vf_old->par.num ||
+ vf_new->par.den != vf_old->par.den ||
+ // Frame rate ignored
+ vf_new->color_space != vf_old->color_space)
+ {
+ *vf_old = *vf_new;
+ return true;
+ }
+ return false;
+}
+
- pic_sys->displayed = false;
+static hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port,
+ const unsigned int headers, const uint32_t payload_size)
+{
+ hw_mmal_port_pool_ref_t * ppr = calloc(1, sizeof(hw_mmal_port_pool_ref_t));
+ if (ppr == NULL)
+ return NULL;
+
+ if ((ppr->pool = mmal_port_pool_create(port, headers, payload_size)) == NULL)
+ goto fail;
+
+ ppr->port = port;
+ atomic_store(&ppr->refs, 1);
+ return ppr;
+
+fail:
+ free(ppr);
+ return NULL;
+}
+
+static void do_detached(void *(*fn)(void *), void * v)
+{
+ pthread_t dothread;
+ pthread_create(&dothread, NULL, fn, v);
+ pthread_detach(dothread);
+}
+
+// Destroy a ppr - aranged s.t. it has the correct prototype for a pthread
+static void * kill_ppr(void * v)
+{
+ hw_mmal_port_pool_ref_t * const ppr = v;
+ if (ppr->port->is_enabled)
+ mmal_port_disable(ppr->port); // Avoid annoyed messages from MMAL when we kill the pool
+ mmal_port_pool_destroy(ppr->port, ppr->pool);
+ free(ppr);
+ return NULL;
+}
+
+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb)
+{
+ if (ppr == NULL)
+ return;
+ if (atomic_fetch_sub(&ppr->refs, 1) != 1)
+ return;
+ if (in_cb)
+ do_detached(kill_ppr, ppr);
+ else
+ kill_ppr(ppr);
+}
+
+// Put buffer in port if possible - if not then release to pool
+// Returns true if sent, false if recycled
+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf)
+{
+ mmal_buffer_header_reset(buf);
+ buf->user_data = NULL;
+
+ if (mmal_port_send_buffer(ppr->port, buf) == MMAL_SUCCESS)
+ return true;
+ mmal_buffer_header_release(buf);
+ return false;
+}
+
+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr)
+{
+ MMAL_BUFFER_HEADER_T * buf;
+ MMAL_STATUS_T err = MMAL_SUCCESS;
+
+ while ((buf = mmal_queue_get(ppr->pool->queue)) != NULL) {
+ if ((err = mmal_port_send_buffer(ppr->port, buf)) != MMAL_SUCCESS)
+ {
+ mmal_queue_put_back(ppr->pool->queue, buf);
+ break;
+ }
+ }
+ return err;
+}
+
+
+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj,
+ hw_mmal_port_pool_ref_t ** pppr,
+ MMAL_PORT_T * const port,
+ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback)
+{
+ MMAL_STATUS_T status;
+
+ port->userdata = (struct MMAL_PORT_USERDATA_T *)obj;
+
+ status = port_parameter_set_uint32(port, MMAL_PARAMETER_EXTRA_BUFFERS, extra_buffers);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(obj, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
+ status, mmal_status_to_string(status));
+ return status;
+ }
+
+ status = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, 1);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(obj, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
+ port->name, status, mmal_status_to_string(status));
+ return status;
+ }
+
+ port->format->encoding = MMAL_ENCODING_OPAQUE;
+ port->format->encoding_variant = 0;
+ if ((status = mmal_port_format_commit(port)) != MMAL_SUCCESS)
+ {
+ msg_Err(obj, "Failed to commit format on port %s (status=%"PRIx32" %s)",
+ port->name, status, mmal_status_to_string(status));
+ return status;
+ }
+
+ port->buffer_num = 30;
+ port->buffer_size = port->buffer_size_recommended;
+
+ if ((*pppr = hw_mmal_port_pool_ref_create(port, port->buffer_num, port->buffer_size)) == NULL) {
+ msg_Err(obj, "Failed to create output pool");
+ return status;
+ }
+
+ status = mmal_port_enable(port, callback);
+ if (status != MMAL_SUCCESS) {
+ hw_mmal_port_pool_ref_release(*pppr, false);
+ *pppr = NULL;
+ msg_Err(obj, "Failed to enable output port %s (status=%"PRIx32" %s)",
+ port->name, status, mmal_status_to_string(status));
+ return status;
+ }
+
+ return MMAL_SUCCESS;
+}
+
+
+static void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn)
+{
+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
+ unsigned int i;
+
+ for (i = 0; i != ctx->buf_count; ++i) {
+ if (ctx->bufs[i] != NULL)
+ mmal_buffer_header_release(ctx->bufs[i]);
+ }
+
+ free(ctx);
+}
+
+static picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn)
+{
+ const pic_ctx_mmal_t * const src_ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
+ pic_ctx_mmal_t * const dst_ctx = calloc(1, sizeof(*dst_ctx));
+ unsigned int i;
+
+ if (dst_ctx == NULL)
+ return NULL;
+
+ // Copy
+ dst_ctx->cmn = src_ctx->cmn;
+
+ dst_ctx->buf_count = src_ctx->buf_count;
+ for (i = 0; i != src_ctx->buf_count; ++i) {
+ dst_ctx->bufs[i] = src_ctx->bufs[i];
+ if (dst_ctx->bufs[i] != NULL)
+ mmal_buffer_header_acquire(dst_ctx->bufs[i]);
+ }
+
+ return &dst_ctx->cmn;
+}
+
+static MMAL_BOOL_T
+buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata)
+{
+ hw_mmal_port_pool_ref_t * const ppr = userdata;
+
+ // Kill the callback - otherwise we will go in circles!
+ mmal_buffer_header_pre_release_cb_set(buf, (MMAL_BH_PRE_RELEASE_CB_T)0, NULL);
+ mmal_buffer_header_acquire(buf); // Ref it again
+
+ // As we have re-acquired the buffer we need a full release
+ // (not continue) to zap the ref count back to zero
+ // This is "safe" 'cos we have already reset the cb
+ hw_mmal_port_pool_ref_recycle(ppr, buf);
+ hw_mmal_port_pool_ref_release(ppr, true); // Assume in callback
+
+ return MMAL_TRUE;
+}
+
+// Buffer belongs to context on successful return from this fn
+// is still valid on failure
+picture_context_t *
+hw_mmal_gen_context(MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr)
+{
+ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t));
+
+ if (ctx == NULL)
+ return NULL;
+
+ // If we have an associated ppr then ref & set appropriate callbacks
+ if (ppr != NULL) {
+ atomic_fetch_add(&ppr->refs, 1);
+ mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr);
+ buf->user_data = NULL;
+ }
+
+ ctx->cmn.copy = hw_mmal_pic_ctx_copy;
+ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
+
+ ctx->buf_count = 1;
+ ctx->bufs[0] = buf;
+
+ return &ctx->cmn;
+}
+
+// n is els
+typedef void piccpy_fn(void * dest, const void * src, size_t n);
+
+static void piccpy_10_to_8_c(void * dest, const void * src, size_t n)
+{
+ uint8_t * d = dest;
+ const uint16_t * s = src;
+ while (n-- != 0)
+ *d++ = *s++ >> 2;
+}
+
+// Do a stride converting copy - if the strides are the same and line_len is
+// close then do a single block copy - we don't expect to have to preserve
+// pixels in the output frame
+static void mem_copy_2d(uint8_t * d_ptr, const size_t d_stride,
+ const uint8_t * s_ptr, const size_t s_stride,
+ size_t lines, const size_t line_len)
+{
+ if (s_stride == d_stride && d_stride < line_len + 32)
+ {
+ memcpy(d_ptr, s_ptr, d_stride * lines);
+ }
+ else
+ {
+ while (lines-- != 0) {
+ memcpy(d_ptr, s_ptr, line_len);
+ d_ptr += d_stride;
+ s_ptr += s_stride;
+ }
+ }
+}
+
+// line_len in D units
+static void mem_copy_2d_10_to_8(uint8_t * d_ptr, const size_t d_stride,
+ const uint8_t * s_ptr, const size_t s_stride,
+ size_t lines, const size_t line_len)
+{
+ piccpy_fn * const docpy = piccpy_10_to_8_c;
+ if (s_stride == d_stride * 2 && d_stride < line_len + 32)
+ {
+ docpy(d_ptr, s_ptr, d_stride * lines);
+ }
+ else
+ {
+ while (lines-- != 0) {
+ docpy(d_ptr, s_ptr, line_len);
+ d_ptr += d_stride;
+ s_ptr += s_stride;
+ }
+ }
+}
+
+
+int hw_mmal_copy_pic_to_buf(void * const buf_data,
+ uint32_t * const pLength,
+ const MMAL_ES_FORMAT_T * const fmt,
+ const picture_t * const pic)
+{
+ const MMAL_VIDEO_FORMAT_T *const video = &fmt->es->video;
+ uint8_t * const dest = buf_data;
+ size_t length = 0;
+
+ //**** Worry about x/y_offsets
+
+ assert(fmt->encoding == MMAL_ENCODING_I420);
+
+ switch (pic->format.i_chroma) {
+ case VLC_CODEC_I420:
+ {
+ // TODO replace by plane_CopyPixels() or a fake picture_CopyPixels
+ // or use copy.c Copy420_P_to_P
+ const size_t y_size = video->width * video->height;
+ mem_copy_2d(dest, video->width,
+ pic->p[0].p_pixels, pic->p[0].i_pitch,
+ video->crop.height,
+ video->crop.width);
+
+ mem_copy_2d(dest + y_size, video->width / 2,
+ pic->p[1].p_pixels, pic->p[1].i_pitch,
+ video->crop.height / 2,
+ video->crop.width / 2);
+
+ mem_copy_2d(dest + y_size + y_size / 4, video->width / 2,
+ pic->p[2].p_pixels, pic->p[2].i_pitch,
+ video->crop.height / 2,
+ video->crop.width / 2);
+
+ // And make sure it is actually in memory
+ length = y_size + y_size / 2;
+ break;
+ }
+
+ case VLC_CODEC_I420_10L:
+ {
+ const size_t y_size = video->width * video->height;
+ mem_copy_2d_10_to_8(dest, video->width,
+ pic->p[0].p_pixels, pic->p[0].i_pitch,
+ video->crop.height,
+ video->crop.width);
+
+ mem_copy_2d_10_to_8(dest + y_size, video->width / 2,
+ pic->p[1].p_pixels, pic->p[1].i_pitch,
+ video->crop.height / 2,
+ video->crop.width / 2);
+
+ mem_copy_2d_10_to_8(dest + y_size + y_size / 4, video->width / 2,
+ pic->p[2].p_pixels, pic->p[2].i_pitch,
+ video->crop.height / 2,
+ video->crop.width / 2);
+
+ // And make sure it is actually in memory
+ length = y_size + y_size / 2;
+ break;
+ }
+
+ default:
+ if (pLength != NULL)
+ *pLength = 0;
+ return VLC_EBADVAR;
+ }
+
+ if (pLength != NULL)
+ *pLength = (uint32_t)length;
return VLC_SUCCESS;
}
+
+
+static void pic_to_buf_copy_props(MMAL_BUFFER_HEADER_T * const buf, const picture_t * const pic)
+{
+ if (!pic->b_progressive)
+ {
+ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
+ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
+ }
+ else
+ {
+ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
+ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
+ }
+ if (pic->b_top_field_first)
+ {
+ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
+ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
+ }
+ else
+ {
+ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
+ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
+ }
+ buf->pts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
+ buf->dts = buf->pts;
+}
+
+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool)
+{
+ pic_ctx_mmal_t *const ctx = (pic_ctx_mmal_t *)pic->context;
+ MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue);
+
+ if (rep_buf == NULL)
+ return NULL;
+
+ if (ctx->bufs[0] != NULL)
+ {
+ // Existing buffer - replicate it
+ if (mmal_buffer_header_replicate(rep_buf, ctx->bufs[0]) != MMAL_SUCCESS)
+ goto fail;
+ }
+ else
+ goto fail;
+
+ pic_to_buf_copy_props(rep_buf, pic);
+ return rep_buf;
+
+fail:
+ mmal_buffer_header_release(rep_buf);
+ return NULL;
+}
+
+
+
+// ===========================================================================
+
+typedef struct pool_ent_s
+{
+ struct pool_ent_s * next;
+ struct pool_ent_s * prev;
+
+ atomic_int ref_count;
+ unsigned int seq;
+
+ size_t size;
+
+ int vc_hdl;
+ void * buf;
+
+ unsigned int width;
+ unsigned int height;
+ MMAL_FOURCC_T enc_type;
+
+ picture_t * pic;
+} pool_ent_t;
+
+
+typedef struct ent_list_hdr_s
+{
+ pool_ent_t * ents;
+ pool_ent_t * tail;
+ unsigned int n;
+} ent_list_hdr_t;
+
+#define ENT_LIST_HDR_INIT (ent_list_hdr_t){ \
+ .ents = NULL, \
+ .tail = NULL, \
+ .n = 0 \
+}
+
+struct vzc_pool_ctl_s
+{
+ atomic_int ref_count;
+
+ ent_list_hdr_t ent_pool;
+ ent_list_hdr_t ents_cur;
+ ent_list_hdr_t ents_prev;
+
+ unsigned int max_n;
+ unsigned int seq;
+
+ vlc_mutex_t lock;
+
+ MMAL_POOL_T * buf_pool;
+
+ vcsm_init_type_t vcsm_init_type;
+};
+
+typedef struct vzc_subbuf_ent_s
+{
+ pool_ent_t * ent;
+ MMAL_RECT_T pic_rect;
+ MMAL_RECT_T orig_dest_rect;
+ MMAL_DISPLAYREGION_T dreg;
+} vzc_subbuf_ent_t;
+
+
+static pool_ent_t * ent_extract(ent_list_hdr_t * const elh, pool_ent_t * const ent)
+{
+ if (ent == NULL)
+ return NULL;
+
+ if (ent->next == NULL)
+ elh->tail = ent->prev;
+ else
+ ent->next->prev = ent->prev;
+
+ if (ent->prev == NULL)
+ elh->ents = ent->next;
+ else
+ ent->prev->next = ent->next;
+
+ ent->prev = ent->next = NULL;
+
+ --elh->n;
+
+ return ent; // For convienience
+}
+
+static pool_ent_t * ent_extract_tail(ent_list_hdr_t * const elh)
+{
+ return ent_extract(elh, elh->tail);
+}
+
+static void ent_add_head(ent_list_hdr_t * const elh, pool_ent_t * const ent)
+{
+ if ((ent->next = elh->ents) == NULL)
+ elh->tail = ent;
+ else
+ ent->next->prev = ent;
+
+ ent->prev = NULL;
+ elh->ents = ent;
+ ++elh->n;
+}
+
+static void ent_free(pool_ent_t * const ent)
+{
+ if (ent != NULL) {
+ // If we still have a ref to a pic - kill it now
+ if (ent->pic != NULL)
+ picture_Release(ent->pic);
+
+ free(ent);
+ }
+}
+
+static void ent_free_list(ent_list_hdr_t * const elh)
+{
+ pool_ent_t * ent = elh->ents;
+
+ *elh = ENT_LIST_HDR_INIT;
+
+ while (ent != NULL) {
+ pool_ent_t * const t = ent;
+ ent = t->next;
+ ent_free(t);
+ }
+}
+
+static void ent_list_move(ent_list_hdr_t * const dst, ent_list_hdr_t * const src)
+{
+ *dst = *src;
+ *src = ENT_LIST_HDR_INIT;
+}
+
+// Scans "backwards" as that should give us the fastest match if we are
+// presented with pics in the same order each time
+static pool_ent_t * ent_list_extract_pic_ent(ent_list_hdr_t * const elh, picture_t * const pic)
+{
+ pool_ent_t *ent = elh->tail;
+
+ while (ent != NULL) {
+ if (ent->pic == pic)
+ return ent_extract(elh, ent);
+ ent = ent->prev;
+ }
+ return NULL;
+}
+
+#define POOL_ENT_ALLOC_BLOCK 0x10000
+
+static pool_ent_t * pool_ent_ref(pool_ent_t * const ent)
+{
+// int n = atomic_fetch_add(&ent->ref_count, 1) + 1;
+ atomic_fetch_add(&ent->ref_count, 1);
+ return ent;
+}
+
+static void pool_recycle(vzc_pool_ctl_t * const pc, pool_ent_t * const ent)
+{
+ pool_ent_t * xs = NULL;
+ int n;
+
+ if (ent == NULL)
+ return;
+
+ n = atomic_fetch_sub(&ent->ref_count, 1) - 1;
+
+ if (n != 0)
+ return;
+
+ if (ent->pic != NULL) {
+ picture_Release(ent->pic);
+ ent->pic = NULL;
+ }
+
+ vlc_mutex_lock(&pc->lock);
+
+ // If we have a full pool then extract the LRU and free it
+ // Free done outside mutex
+ if (pc->ent_pool.n >= pc->max_n)
+ xs = ent_extract_tail(&pc->ent_pool);
+
+ ent_add_head(&pc->ent_pool, ent);
+
+ vlc_mutex_unlock(&pc->lock);
+
+ ent_free(xs);
+}
+
+// * This could be made more efficient, but this is easy
+static void pool_recycle_list(vzc_pool_ctl_t * const pc, ent_list_hdr_t * const elh)
+{
+ pool_ent_t * ent;
+ while ((ent = ent_extract_tail(elh)) != NULL) {
+ pool_recycle(pc, ent);
+ }
+}
+
+
+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt)
+{
+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
+
+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
+ es_fmt->encoding = ent->enc_type;
+ es_fmt->encoding_variant = 0;
+
+ v_fmt->width = ent->width;
+ v_fmt->height = ent->height;
+ v_fmt->crop.x = 0;
+ v_fmt->crop.y = 0;
+ v_fmt->crop.width = ent->width;
+ v_fmt->crop.height = ent->height;
+
+ return true;
+}
+
+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf)
+{
+ vzc_subbuf_ent_t * sb = buf->user_data;
+ return &sb->dreg;
+}
+
+static int rescale_x(int x, int mul, int div)
+{
+ return div == 0 ? x * mul : (x * mul + div/2) / div;
+}
+
+static void rescale_rect(MMAL_RECT_T * const d, const MMAL_RECT_T * const s, const MMAL_RECT_T * mul_rect, const MMAL_RECT_T * div_rect)
+{
+ d->x = rescale_x(s->x - div_rect->x, mul_rect->width, div_rect->width) + mul_rect->x;
+ d->y = rescale_x(s->y - div_rect->y, mul_rect->height, div_rect->height) + mul_rect->y;
+ d->width = rescale_x(s->width, mul_rect->width, div_rect->width);
+ d->height = rescale_x(s->height, mul_rect->height, div_rect->height);
+}
+
+void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect)
+{
+ vzc_subbuf_ent_t * sb = buf->user_data;
+ if (scale_rect == NULL) {
+ sb->dreg.dest_rect = sb->orig_dest_rect;
+ }
+ else
+ {
+ rescale_rect(&sb->dreg.dest_rect, &sb->orig_dest_rect,
+ scale_rect, &sb->pic_rect);
+ }
+}
+
+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf)
+{
+ vzc_subbuf_ent_t * sb = buf->user_data;
+ return sb->ent->seq;
+}
+
+
+// The intent with the ents_cur & ents_last stuff is to remember the buffers
+// we used on the last frame and reuse them on the current one if they are the
+// same. Unfortunately detection of "is_first" is only a heuristic (there are
+// no rules governing the order in which things are blended) so we must deal
+// (fairly) gracefully with it never (or always) being set.
+
+// dst_fmt gives the number space in which the destination pixels are specified
+
+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc,
+ picture_t * const pic,
+ const MMAL_RECT_T dst_pic_rect,
+ const int x_offset, const int y_offset,
+ const unsigned int alpha,
+ const bool is_first)
+{
+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(pc->buf_pool->queue);
+ vzc_subbuf_ent_t * sb;
+
+ if (buf == NULL)
+ return NULL;
+
+ if ((sb = calloc(1, sizeof(*sb))) == NULL)
+ goto fail1;
+
+ // If first or we've had a lot of stuff move everything to the last list
+ // (we could deal more gracefully with the "too many" case but it shouldn't
+ // really happen)
+ if (is_first || pc->ents_cur.n >= CTX_BUFS_MAX) {
+ pool_recycle_list(pc, &pc->ents_prev);
+ ent_list_move(&pc->ents_prev, &pc->ents_cur);
+ }
+
+ sb->dreg.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
+ sb->dreg.hdr.size = sizeof(sb->dreg);
+
+ {
+ // ?? Round start offset as well as length
+ const video_format_t *const fmt = &pic->format;
+
+ const unsigned int bpp = (fmt->i_bits_per_pixel + 7) >> 3;
+ const unsigned int xl = (fmt->i_x_offset & ~15);
+ const unsigned int xr = (fmt->i_x_offset + fmt->i_visible_width + 15) & ~15;
+ const size_t dst_stride = (xr - xl) * bpp;
+ const size_t dst_lines = ((fmt->i_visible_height + 15) & ~15);
+ const size_t dst_size = dst_stride * dst_lines;
+
+ pool_ent_t * ent = ent_list_extract_pic_ent(&pc->ents_prev, pic);
+
+ // If we didn't find ent in last then look in cur in case is_first
+ // isn't working
+ if (ent == NULL)
+ ent = ent_list_extract_pic_ent(&pc->ents_cur, pic);
+
+ if (ent == NULL)
+ {
+ goto fail2;
+ }
+ buf->user_data = sb;
+
+ ent_add_head(&pc->ents_cur, ent);
+
+ sb->ent = pool_ent_ref(ent);
+ hw_mmal_vzc_pool_ref(pc);
+
+ // Copy data
+ buf->next = NULL;
+ buf->cmd = 0;
+ buf->data = (uint8_t *)(ent->vc_hdl);
+ buf->alloc_size = buf->length = dst_size;
+ buf->offset = 0;
+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
+ buf->pts = buf->dts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
+ buf->type->video = (MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T){
+ .planes = 1,
+ .pitch = { dst_stride }
+ };
+
+ // Remember offsets
+ sb->dreg.set = MMAL_DISPLAY_SET_SRC_RECT |
+ MMAL_DISPLAY_SET_DEST_RECT |
+ MMAL_DISPLAY_SET_FULLSCREEN |
+ MMAL_DISPLAY_SET_ALPHA;
+
+ sb->dreg.fullscreen = 0;
+ // Will be set later - zero now to avoid any confusion
+ sb->dreg.dest_rect = (MMAL_RECT_T){0, 0, 0, 0};
+
+ sb->dreg.alpha = (uint32_t)(alpha & 0xff) | MMAL_DISPLAY_ALPHA_FLAGS_MIX;
+
+ sb->dreg.src_rect = (MMAL_RECT_T){
+ .x = (fmt->i_x_offset - xl),
+ .y = 0,
+ .width = fmt->i_visible_width,
+ .height = fmt->i_visible_height
+ };
+
+ sb->pic_rect = dst_pic_rect;
+
+ sb->orig_dest_rect = (MMAL_RECT_T){
+ .x = x_offset,
+ .y = y_offset,
+ .width = fmt->i_visible_width,
+ .height = fmt->i_visible_height
+ };
+ }
+
+ return buf;
+
+fail2:
+ free(sb);
+fail1:
+ mmal_buffer_header_release(buf);
+ return NULL;
+}
+
+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc)
+{
+ pool_recycle_list(pc, &pc->ents_prev);
+ pool_recycle_list(pc, &pc->ents_cur);
+}
+
+static void hw_mmal_vzc_pool_delete(vzc_pool_ctl_t * const pc)
+{
+
+ hw_mmal_vzc_pool_flush(pc);
+
+ ent_free_list(&pc->ent_pool);
+
+ if (pc->buf_pool != NULL)
+ mmal_pool_destroy(pc->buf_pool);
+
+ vlc_mutex_destroy(&pc->lock);
+
+ cma_vcsm_exit(pc->vcsm_init_type);
+
+// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash
+ free (pc);
+}
+
+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc)
+{
+ int n;
+
+ if (pc == NULL)
+ return;
+
+ n = atomic_fetch_sub(&pc->ref_count, 1) - 1;
+
+ if (n != 0)
+ return;
+
+ hw_mmal_vzc_pool_delete(pc);
+}
+
+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc)
+{
+ atomic_fetch_add(&pc->ref_count, 1);
+}
+
+static MMAL_BOOL_T vcz_pool_release_cb(MMAL_POOL_T * buf_pool, MMAL_BUFFER_HEADER_T *buf, void *userdata)
+{
+ vzc_pool_ctl_t * const pc = userdata;
+ vzc_subbuf_ent_t * const sb = buf->user_data;
+
+ VLC_UNUSED(buf_pool);
+
+ if (sb != NULL) {
+ buf->user_data = NULL;
+ pool_recycle(pc, sb->ent);
+ hw_mmal_vzc_pool_release(pc);
+ free(sb);
+ }
+
+ return MMAL_TRUE;
+}
+
+vzc_pool_ctl_t * hw_mmal_vzc_pool_new()
+{
+ vzc_pool_ctl_t * const pc = calloc(1, sizeof(*pc));
+
+ if (pc == NULL)
+ return NULL;
+
+ if ((pc->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE)
+ {
+ free(pc);
+ return NULL;
+ }
+
+ pc->max_n = 8;
+ vlc_mutex_init(&pc->lock); // Must init before potential destruction
+
+ if ((pc->buf_pool = mmal_pool_create(64, 0)) == NULL)
+ {
+ hw_mmal_vzc_pool_delete(pc);
+ return NULL;
+ }
+
+ atomic_store(&pc->ref_count, 1);
+
+ mmal_pool_callback_set(pc->buf_pool, vcz_pool_release_cb, pc);
+
+ return pc;
+}
+
+//----------------------------------------------------------------------------
+
+/* Returns the type of the Pi being used
+*/
+bool rpi_is_model_pi4(void) {
+ return bcm_host_is_model_pi4();
+}
+
+// Preferred mode - none->cma on Pi4 otherwise legacy
+static volatile vcsm_init_type_t last_vcsm_type = VCSM_INIT_NONE;
+
+vcsm_init_type_t cma_vcsm_type(void)
+{
+ return last_vcsm_type;
+}
+
+vcsm_init_type_t cma_vcsm_init(void)
+{
+ vcsm_init_type_t rv = VCSM_INIT_LEGACY;
+
+ bcm_host_init();
+ last_vcsm_type = rv;
+ return rv;
+}
+
+void cma_vcsm_exit(const vcsm_init_type_t init_mode)
+{
+ if (init_mode != VCSM_INIT_NONE)
+ {
+ bcm_host_deinit(); // Does nothing but add in case it ever does
+ }
+}
+
+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode)
+{
+ switch (init_mode)
+ {
+ case VCSM_INIT_LEGACY:
+ return "Legacy";
+ case VCSM_INIT_NONE:
+ return "none";
+ default:
+ break;
+ }
+ return "???";
+}
diff --git a/modules/hw/mmal/mmal_picture.h b/modules/hw/mmal/mmal_picture.h
index c2ed09785a6..ab64081b0a1 100644
--- a/modules/hw/mmal/mmal_picture.h
+++ b/modules/hw/mmal/mmal_picture.h
@@ -4,6 +4,7 @@
* Copyright © 2014 jusst technologies GmbH
*
* Authors: Julian Scheel <julian at jusst.de>
+ * John Cox <jc at kynesim.co.uk>
*
* 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
@@ -29,18 +30,128 @@
/* Think twice before changing this. Incorrect values cause havoc. */
#define NUM_ACTUAL_OPAQUE_BUFFERS 30
-typedef struct
+typedef struct mmal_port_pool_ref_s
{
- vlc_object_t *owner;
+ atomic_uint refs;
+ MMAL_POOL_T * pool;
+ MMAL_PORT_T * port;
+} hw_mmal_port_pool_ref_t;
- MMAL_BUFFER_HEADER_T *buffer;
- bool displayed;
-} picture_sys_t;
-int mmal_picture_lock(picture_t *picture);
+#define CTX_BUFS_MAX 4
+typedef struct pic_ctx_mmal_s {
+ picture_context_t cmn; // PARENT: Common els at start
+
+ unsigned int buf_count;
+ MMAL_BUFFER_HEADER_T * bufs[CTX_BUFS_MAX];
+
+} pic_ctx_mmal_t;
+
+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc);
+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs);
+void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc);
+// Returns true if fmt_changed
+// frame_rate ignored for compare, but is set if something else is updated
+bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic);
+
+// Copy pic contents into an existing buffer
+int hw_mmal_copy_pic_to_buf(void * const buf_data, uint32_t * const pLength,
+ const MMAL_ES_FORMAT_T * const fmt, const picture_t * const pic);
+
+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb);
+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf);
+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr);
+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj,
+ hw_mmal_port_pool_ref_t ** pppr,
+ MMAL_PORT_T * const port,
+ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback);
+
+static inline MMAL_BUFFER_HEADER_T * hw_mmal_pic_sub_buf_get(picture_t * const pic, const unsigned int n)
+{
+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
+
+ return n + 1 > ctx->buf_count ? NULL : ctx->bufs[n + 1];
+}
+
+static inline bool hw_mmal_chroma_is_mmal(const vlc_fourcc_t chroma)
+{
+ return
+ chroma == VLC_CODEC_MMAL_OPAQUE;
+}
+
+picture_context_t * hw_mmal_gen_context(
+ MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr);
+
+
+static inline MMAL_STATUS_T port_parameter_set_uint32(MMAL_PORT_T * port, uint32_t id, uint32_t val)
+{
+ const MMAL_PARAMETER_UINT32_T param = {
+ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_UINT32_T)},
+ .value = val
+ };
+ return mmal_port_parameter_set(port, ¶m.hdr);
+}
+
+static inline MMAL_STATUS_T port_parameter_set_bool(MMAL_PORT_T * const port, const uint32_t id, const bool val)
+{
+ const MMAL_PARAMETER_BOOLEAN_T param = {
+ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_BOOLEAN_T)},
+ .enable = val
+ };
+ return mmal_port_parameter_set(port, ¶m.hdr);
+}
+
+static inline void buf_to_pic_copy_props(picture_t * const pic, const MMAL_BUFFER_HEADER_T * const buf)
+{
+ // Contrary to docn the interlace & tff flags turn up in the header flags rather than the
+ // video specific flags (which appear to be currently unused).
+ pic->b_progressive = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED) == 0;
+ pic->b_top_field_first = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST) != 0;
+
+ pic->date = buf->pts != MMAL_TIME_UNKNOWN ? buf->pts :
+ buf->dts != MMAL_TIME_UNKNOWN ? buf->dts :
+ VLC_TICK_INVALID;
+}
+
+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool);
+
+struct vzc_pool_ctl_s;
+typedef struct vzc_pool_ctl_s vzc_pool_ctl_t;
// At the moment we cope with any mono-planar RGBA thing
// We could cope with many other things but they currently don't occur
extern const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[];
+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt);
+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf);
+void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect);
+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf);
+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic,
+ const MMAL_RECT_T dst_pic_rect,
+ const int x_offset, const int y_offset,
+ const unsigned int alpha, const bool is_first);
+
+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc);
+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc);
+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc);
+vzc_pool_ctl_t * hw_mmal_vzc_pool_new(void);
+
+
+bool rpi_is_model_pi4(void);
+
+typedef enum vcsm_init_type_e {
+ VCSM_INIT_NONE = 0,
+ VCSM_INIT_LEGACY,
+} vcsm_init_type_t;
+
+vcsm_init_type_t cma_vcsm_init(void);
+void cma_vcsm_exit(const vcsm_init_type_t init_mode);
+vcsm_init_type_t cma_vcsm_type(void);
+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode);
+
+
+#define MMAL_COMPONENT_DEFAULT_RESIZER "vc.ril.resize"
+#define MMAL_COMPONENT_ISP_RESIZER "vc.ril.isp"
+#define MMAL_COMPONENT_HVS "vc.ril.hvs"
+
#endif
diff --git a/modules/hw/mmal/subpic.c b/modules/hw/mmal/subpic.c
new file mode 100644
index 00000000000..1d8662f0294
--- /dev/null
+++ b/modules/hw/mmal/subpic.c
@@ -0,0 +1,229 @@
+/*****************************************************************************
+ * subpic.c:
+ *****************************************************************************
+ * Copyright © 2018-2020 John Cox
+ *
+ * Authors: John Cox <jc at kynesim.co.uk>
+ *
+ * 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_codec.h>
+
+#include <interface/mmal/mmal.h>
+
+#include "mmal_picture.h"
+#include "subpic.h"
+
+
+static bool cmp_rect(const MMAL_RECT_T * const a, const MMAL_RECT_T * const b)
+{
+ return a->x == b->x && a->y == b->y && a->width == b->width && a->height == b->height;
+}
+
+static void hw_mmal_subpic_flush(subpic_reg_stash_t * const sub)
+{
+ if (sub->port != NULL && sub->port->is_enabled)
+ mmal_port_disable(sub->port);
+ sub->seq = 0;
+}
+
+void hw_mmal_subpic_close(subpic_reg_stash_t * const spe)
+{
+ hw_mmal_subpic_flush(spe);
+
+ if (spe->pool != NULL)
+ mmal_pool_destroy(spe->pool);
+
+ // Zap to avoid any accidental reuse
+ *spe = (subpic_reg_stash_t){NULL};
+}
+
+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const obj, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port,
+ const int display_id, const unsigned int layer)
+{
+ MMAL_STATUS_T err;
+
+ // Start by zapping all to zero
+ *spe = (subpic_reg_stash_t){NULL};
+
+ if ((err = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
+ {
+ msg_Err(obj, "Failed to set sub port zero copy");
+ return err;
+ }
+
+ if ((spe->pool = mmal_pool_create(30, 0)) == NULL)
+ {
+ msg_Err(obj, "Failed to create sub pool");
+ return MMAL_ENOMEM;
+ }
+
+ port->userdata = (void *)obj;
+ spe->port = port;
+ spe->display_id = display_id;
+ spe->layer = layer;
+
+ return MMAL_SUCCESS;
+}
+
+static void conv_subpic_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
+{
+ VLC_UNUSED(port);
+
+ mmal_buffer_header_release(buf); // Will extract & release pic in pool callback
+}
+
+static MMAL_STATUS_T port_send_replicated(MMAL_PORT_T * const port, MMAL_POOL_T * const rep_pool,
+ MMAL_BUFFER_HEADER_T * const src_buf,
+ const uint64_t seq)
+{
+ MMAL_STATUS_T err;
+ MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue);
+
+ if (rep_buf == NULL)
+ return MMAL_ENOSPC;
+
+ if ((err = mmal_buffer_header_replicate(rep_buf, src_buf)) != MMAL_SUCCESS)
+ return err;
+
+ rep_buf->pts = seq;
+
+ if ((err = mmal_port_send_buffer(port, rep_buf)) != MMAL_SUCCESS)
+ {
+ mmal_buffer_header_release(rep_buf);
+ return err;
+ }
+
+ return MMAL_SUCCESS;
+}
+
+int hw_mmal_subpic_update(vlc_object_t * const obj,
+ MMAL_BUFFER_HEADER_T * const sub_buf,
+ subpic_reg_stash_t * const spe,
+ const video_format_t * const fmt,
+ const MMAL_RECT_T * const scale_out,
+ const uint64_t pts)
+{
+ MMAL_STATUS_T err;
+
+ if (sub_buf == NULL)
+ {
+ if (spe->port->is_enabled && spe->seq != 0)
+ {
+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(spe->pool->queue);
+
+ if (buf == NULL) {
+ msg_Err(obj, "Buffer get for subpic failed");
+ return -1;
+ }
+ buf->cmd = 0;
+ buf->data = NULL;
+ buf->alloc_size = 0;
+ buf->offset = 0;
+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
+ buf->pts = pts;
+ buf->dts = MMAL_TIME_UNKNOWN;
+ buf->user_data = NULL;
+
+ if ((err = mmal_port_send_buffer(spe->port, buf)) != MMAL_SUCCESS)
+ {
+ msg_Err(obj, "Send buffer to subput failed");
+ mmal_buffer_header_release(buf);
+ return -1;
+ }
+
+ spe->seq = 0;
+ }
+ }
+ else
+ {
+ const unsigned int seq = hw_mmal_vzc_buf_seq(sub_buf);
+ bool needs_update = (spe->seq != seq);
+
+ hw_mmal_vzc_buf_scale_dest_rect(sub_buf, scale_out);
+
+ if (hw_mmal_vzc_buf_set_format(sub_buf, spe->port->format))
+ {
+ MMAL_DISPLAYREGION_T * const dreg = hw_mmal_vzc_buf_region(sub_buf);
+ MMAL_VIDEO_FORMAT_T *const v_fmt = &spe->port->format->es->video;
+
+ v_fmt->frame_rate.den = fmt->i_frame_rate_base;
+ v_fmt->frame_rate.num = fmt->i_frame_rate;
+ v_fmt->par.den = fmt->i_sar_den;
+ v_fmt->par.num = fmt->i_sar_num;
+ v_fmt->color_space = MMAL_COLOR_SPACE_UNKNOWN;
+
+ if (needs_update || dreg->alpha != spe->alpha || !cmp_rect(&dreg->dest_rect, &spe->dest_rect)) {
+
+ spe->alpha = dreg->alpha;
+ spe->dest_rect = dreg->dest_rect;
+ needs_update = true;
+
+ if (spe->display_id >= 0)
+ {
+ dreg->display_num = spe->display_id;
+ dreg->set |= MMAL_DISPLAY_SET_NUM;
+ }
+ dreg->layer = spe->layer;
+ dreg->set |= MMAL_DISPLAY_SET_LAYER;
+
+ if ((err = mmal_port_parameter_set(spe->port, &dreg->hdr)) != MMAL_SUCCESS)
+ {
+ msg_Err(obj, "Set display region on subput failed");
+ return -1;
+ }
+
+ if ((err = mmal_port_format_commit(spe->port)) != MMAL_SUCCESS)
+ {
+ msg_Dbg(obj, "Subpic commit fail: %d", err);
+ return -1;
+ }
+ }
+ }
+
+ if (!spe->port->is_enabled)
+ {
+ spe->port->buffer_num = 30;
+ spe->port->buffer_size = spe->port->buffer_size_recommended; // Not used but shuts up the error checking
+
+ if ((err = mmal_port_enable(spe->port, conv_subpic_cb)) != MMAL_SUCCESS)
+ {
+ msg_Dbg(obj, "Subpic enable fail: %d", err);
+ return -1;
+ }
+ }
+
+ if (needs_update)
+ {
+ if ((err = port_send_replicated(spe->port, spe->pool, sub_buf, pts)) != MMAL_SUCCESS)
+ {
+ msg_Err(obj, "Send buffer to subput failed");
+ return -1;
+ }
+
+ spe->seq = seq;
+ }
+ }
+ return 1;
+}
+
+
+
diff --git a/modules/hw/mmal/subpic.h b/modules/hw/mmal/subpic.h
new file mode 100644
index 00000000000..b33475b4b4c
--- /dev/null
+++ b/modules/hw/mmal/subpic.h
@@ -0,0 +1,52 @@
+/*****************************************************************************
+ * subpic.h:
+ *****************************************************************************
+ * Copyright © 2018-2020 John Cox
+ *
+ * Authors: John Cox <jc at kynesim.co.uk>
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifndef VLC_HW_MMAL_SUBPIC_H_
+#define VLC_HW_MMAL_SUBPIC_H_
+
+typedef struct subpic_reg_stash_s
+{
+ MMAL_PORT_T * port;
+ MMAL_POOL_T * pool;
+ int display_id; // -1 => do not set
+ unsigned int layer;
+ // Shadow vars so we can tell if stuff has changed
+ MMAL_RECT_T dest_rect;
+ unsigned int alpha;
+ unsigned int seq;
+} subpic_reg_stash_t;
+
+int hw_mmal_subpic_update(vlc_object_t * const p_filter,
+ MMAL_BUFFER_HEADER_T * const sub_buf,
+ subpic_reg_stash_t * const spe,
+ const video_format_t * const fmt,
+ const MMAL_RECT_T * const scale_out,
+ const uint64_t pts);
+
+void hw_mmal_subpic_close(subpic_reg_stash_t * const spe);
+
+// If display id is -1 it will be unset
+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port,
+ const int display_id, const unsigned int layer);
+
+#endif
+
diff --git a/modules/hw/mmal/vout.c b/modules/hw/mmal/vout.c
index fff8031d6ea..78f2dbbeb5d 100644
--- a/modules/hw/mmal/vout.c
+++ b/modules/hw/mmal/vout.c
@@ -5,6 +5,7 @@
*
* Authors: Dennis Hamester <dennis.hamester at gmail.com>
* Julian Scheel <julian at jusst.de>
+ * John Cox <jc at kynesim.co.uk>
*
* 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
@@ -25,12 +26,8 @@
#include "config.h"
#endif
-#include <math.h>
-#include <stdatomic.h>
-
#include <vlc_common.h>
#include <vlc_plugin.h>
-#include <vlc_threads.h>
#include <vlc_vout_display.h>
#include "mmal_picture.h"
@@ -39,8 +36,8 @@
#include <interface/mmal/mmal.h>
#include <interface/mmal/util/mmal_util.h>
#include <interface/mmal/util/mmal_default_components.h>
-#include <interface/vmcs_host/vc_tvservice.h>
-#include <interface/vmcs_host/vc_dispmanx.h>
+
+#include "subpic.h"
#define MAX_BUFFERS_IN_TRANSIT 1
#define VC_TV_MAX_MODE_IDS 127
@@ -49,10 +46,12 @@
#define MMAL_LAYER_TEXT N_("VideoCore layer where the video is displayed.")
#define MMAL_LAYER_LONGTEXT N_("VideoCore layer where the video is displayed. Subpictures are displayed directly above and a black background directly below.")
-#define MMAL_BLANK_BACKGROUND_NAME "mmal-blank-background"
-#define MMAL_BLANK_BACKGROUND_TEXT N_("Blank screen below video.")
-#define MMAL_BLANK_BACKGROUND_LONGTEXT N_("Render blank screen below video. " \
- "Increases VideoCore load.")
+#define MMAL_DISPLAY_NAME "mmal-display"
+#define MMAL_DISPLAY_TEXT N_("Output device for Rpi fullscreen.")
+#define MMAL_DISPLAY_LONGTEXT N_("Output device for Rpi fullscreen. " \
+"Valid values are HDMI-1,HDMI-2. By default if qt-fullscreen-screennumber " \
+"is specified (or set by Fullscreen Output Device in Preferences) " \
+"HDMI-<qt-fullscreen-screennumber+1> will be used, otherwise HDMI-1.")
#define MMAL_ADJUST_REFRESHRATE_NAME "mmal-adjust-refreshrate"
#define MMAL_ADJUST_REFRESHRATE_TEXT N_("Adjust HDMI refresh rate to the video.")
@@ -69,61 +68,50 @@
static int OpenMmalVout(vout_display_t *, const vout_display_cfg_t *,
video_format_t *, vlc_video_context *);
-static void CloseMmalVout(vout_display_t *);
+
+#define SUBS_MAX 4
+
vlc_module_begin()
set_shortname(N_("MMAL vout"))
set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
add_shortcut("mmal_vout")
+ set_category( CAT_VIDEO )
+ set_subcategory( SUBCAT_VIDEO_VOUT )
+
add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
- add_bool(MMAL_BLANK_BACKGROUND_NAME, true, MMAL_BLANK_BACKGROUND_TEXT,
- MMAL_BLANK_BACKGROUND_LONGTEXT, true);
add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
MMAL_NATIVE_INTERLACE_LONGTEXT, false)
- set_callback_display(OpenMmalVout, 90)
+ add_string(MMAL_DISPLAY_NAME, "auto", MMAL_DISPLAY_TEXT,
+ MMAL_DISPLAY_LONGTEXT, false)
+ set_callback_display(OpenMmalVout, 16) // 1 point better than ASCII art
vlc_module_end()
-struct dmx_region_t {
- struct dmx_region_t *next;
- picture_t *picture;
- VC_RECT_T bmp_rect;
- VC_RECT_T src_rect;
- VC_RECT_T dst_rect;
- VC_DISPMANX_ALPHA_T alpha;
- DISPMANX_ELEMENT_HANDLE_T element;
- DISPMANX_RESOURCE_HANDLE_T resource;
- int32_t pos_x;
- int32_t pos_y;
-};
+typedef struct vout_subpic_s {
+ MMAL_COMPONENT_T *component;
+ subpic_reg_stash_t sub;
+} vout_subpic_t;
struct vout_display_sys_t {
- vlc_cond_t buffer_cond;
- vlc_mutex_t buffer_mutex;
vlc_mutex_t manage_mutex;
- plane_t planes[3]; /* Depending on video format up to 3 planes are used */
- picture_t **pictures; /* Actual list of alloced pictures passed into picture_pool */
- picture_pool_t *picture_pool;
- vout_display_cfg_t last_cfg;
-
+ vcsm_init_type_t init_type;
MMAL_COMPONENT_T *component;
MMAL_PORT_T *input;
MMAL_POOL_T *pool; /* mmal buffer headers, used for pushing pictures to component*/
- struct dmx_region_t *dmx_region;
int i_planes; /* Number of actually used planes, 1 for opaque, 3 for i420 */
- uint32_t buffer_size; /* size of actual mmal buffers */
int buffers_in_transit; /* number of buffers currently pushed to mmal component */
unsigned num_buffers; /* number of buffers allocated at mmal port */
- DISPMANX_DISPLAY_HANDLE_T dmx_handle;
- DISPMANX_ELEMENT_HANDLE_T bkg_element;
- DISPMANX_RESOURCE_HANDLE_T bkg_resource;
+ int display_id;
unsigned display_width;
unsigned display_height;
+ MMAL_RECT_T dest_rect; // Output rectangle in display coords
+
unsigned int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
unsigned int i_frame_rate;
@@ -136,261 +124,436 @@ struct vout_display_sys_t {
bool native_interlaced;
bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */
bool b_progressive;
- bool opaque; /* indicated use of opaque picture format (zerocopy) */
-
- subpicture_t *prepare_subpicture;
+ bool force_config;
+
+ vout_subpic_t subs[SUBS_MAX];
+ // Stash for subpics derived from the passed subpicture rather than
+ // included with the main pic
+ MMAL_BUFFER_HEADER_T * subpic_bufs[SUBS_MAX];
+
+ picture_pool_t * pic_pool;
+
+ struct vout_isp_conf_s {
+ MMAL_COMPONENT_T *component;
+ MMAL_PORT_T * input;
+ MMAL_PORT_T * output;
+ MMAL_QUEUE_T * out_q;
+ MMAL_POOL_T * in_pool;
+ MMAL_POOL_T * out_pool;
+ bool pending;
+ } isp;
+
+ MMAL_POOL_T * copy_pool;
+ MMAL_BUFFER_HEADER_T * copy_buf;
+
+ // Subpic blend if we have to do it here
+ vzc_pool_ctl_t * vzc;
};
-/* Utility functions */
-static inline uint32_t align(uint32_t x, uint32_t y);
-static void configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
- const video_format_t *fmt);
-/* VLC vout display callbacks */
-static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count);
-static void vd_prepare(vout_display_t *vd, picture_t *picture,
- subpicture_t *subpicture, vlc_tick_t date);
-static void vd_display(vout_display_t *vd, picture_t *picture);
-static int vd_control(vout_display_t *vd, int query, va_list args);
-static void vd_manage(vout_display_t *vd);
+// ISP setup
-/* MMAL callbacks */
-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 bool want_copy(const vout_display_t * const vd)
+{
+ return (vd->fmt.i_chroma == VLC_CODEC_I420 || vd->fmt.i_chroma == VLC_CODEC_I420_10L);
+}
-/* TV service */
-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height);
-static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
- uint32_t param2);
-static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
-static int set_latency_target(vout_display_t *vd, bool enable);
+static vlc_fourcc_t req_chroma(const vout_display_t * const vd)
+{
+ return !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma) && !want_copy(vd) ?
+ VLC_CODEC_I420 :
+ vd->fmt.i_chroma;
+}
-/* DispManX */
-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture);
-static void close_dmx(vout_display_t *vd);
-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region);
-static void dmx_region_update(struct dmx_region_t *dmx_region,
- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture);
-static void dmx_region_delete(struct dmx_region_t *dmx_region,
- DISPMANX_UPDATE_HANDLE_T update);
-static void show_background(vout_display_t *vd, bool enable);
-static void maintain_phase_sync(vout_display_t *vd);
+static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc)
+{
+ switch (fcc){
+ case VLC_CODEC_MMAL_OPAQUE:
+ return MMAL_ENCODING_OPAQUE;
+ default:
+ break;
+ }
+ return MMAL_ENCODING_I420;
+}
-static int OpenMmalVout(vout_display_t *vd, const vout_display_cfg_t *cfg,
- video_format_t *fmt, vlc_video_context *context)
+static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate)
{
- vout_display_sys_t *sys;
- uint32_t buffer_pitch, buffer_height;
- vout_display_place_t place;
- MMAL_DISPLAYREGION_T display_region;
- MMAL_STATUS_T status;
- int ret = VLC_SUCCESS;
- int i;
+ const unsigned int w = is_intermediate ? vd->fmt.i_visible_width : vd->fmt.i_width ;
+ const unsigned int h = is_intermediate ? vd->fmt.i_visible_height : vd->fmt.i_height;
+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
+
+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
+ es_fmt->encoding = is_intermediate ? MMAL_ENCODING_I420 : vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
+ es_fmt->encoding_variant = 0;
+
+ v_fmt->width = (w + 31) & ~31;
+ v_fmt->height = (h + 15) & ~15;
+ v_fmt->crop.x = 0;
+ v_fmt->crop.y = 0;
+ v_fmt->crop.width = w;
+ v_fmt->crop.height = h;
+ if (vd->fmt.i_sar_num == 0 || vd->fmt.i_sar_den == 0) {
+ v_fmt->par.num = 1;
+ v_fmt->par.den = 1;
+ } else {
+ v_fmt->par.num = vd->fmt.i_sar_num;
+ v_fmt->par.den = vd->fmt.i_sar_den;
+ }
+ v_fmt->frame_rate.num = vd->fmt.i_frame_rate;
+ v_fmt->frame_rate.den = vd->fmt.i_frame_rate_base;
+ v_fmt->color_space = vlc_to_mmal_color_space(vd->fmt.space);
+}
- if (vout_display_cfg_IsWindowed(cfg))
- return VLC_EGENERIC;
+static void display_src_rect(const vout_display_t * const vd, MMAL_RECT_T *const rect)
+{
+ const bool wants_isp = false;
+ rect->x = wants_isp ? 0 : vd->fmt.i_x_offset;
+ rect->y = wants_isp ? 0 : vd->fmt.i_y_offset;
+ rect->width = vd->fmt.i_visible_width;
+ rect->height = vd->fmt.i_visible_height;
+}
- sys = calloc(1, sizeof(struct vout_display_sys_t));
- if (!sys)
- return VLC_ENOMEM;
- vd->sys = sys;
+static void isp_input_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
+{
+ VLC_UNUSED(port);
- sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
- bcm_host_init();
+ mmal_buffer_header_release(buf);
+}
- sys->opaque = fmt->i_chroma == VLC_CODEC_MMAL_OPAQUE;
+static void isp_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ vout_display_t *vd = (vout_display_t *)port->userdata;
+ MMAL_STATUS_T status;
- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
- if (status != MMAL_SUCCESS) {
- msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
- MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
- ret = VLC_EGENERIC;
- goto out;
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
+ status = *(uint32_t *)buffer->data;
+ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
}
- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
- status = mmal_port_enable(sys->component->control, control_port_cb);
- if (status != MMAL_SUCCESS) {
- msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
- sys->component->control->name, status, mmal_status_to_string(status));
- ret = VLC_EGENERIC;
- goto out;
- }
+ mmal_buffer_header_release(buffer);
+}
- sys->input = sys->component->input[0];
- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
+static void isp_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
+{
+ if (buf->cmd == 0 && buf->length != 0)
+ {
+ // The filter structure etc. should always exist if we have contents
+ // but might not on later flushes as we shut down
+ vout_display_t * const vd = (vout_display_t *)port->userdata;
+ struct vout_isp_conf_s *const isp = &vd->sys->isp;
- if (sys->opaque) {
- sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
- sys->i_planes = 1;
- sys->buffer_size = sys->input->buffer_size_recommended;
- } else {
- sys->input->format->encoding = MMAL_ENCODING_I420;
- fmt->i_chroma = VLC_CODEC_I420;
- buffer_pitch = align(fmt->i_width, 32);
- buffer_height = align(fmt->i_height, 16);
- sys->i_planes = 3;
- sys->buffer_size = 3 * buffer_pitch * buffer_height / 2;
+ mmal_queue_put(isp->out_q, buf);
}
+ else
+ {
+ mmal_buffer_header_reset(buf);
+ mmal_buffer_header_release(buf);
+ }
+}
- sys->input->format->es->video.width = fmt->i_width;
- sys->input->format->es->video.height = fmt->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 = fmt->i_width;
- sys->input->format->es->video.crop.height = fmt->i_height;
- sys->input->format->es->video.par.num = vd->source.i_sar_num;
- sys->input->format->es->video.par.den = vd->source.i_sar_den;
- sys->last_cfg = *cfg;
+static void isp_empty_out_q(struct vout_isp_conf_s * const isp)
+{
+ MMAL_BUFFER_HEADER_T * buf;
+ // We can be called as part of error recovery so allow for missing Q
+ if (isp->out_q == NULL)
+ return;
- status = mmal_port_format_commit(sys->input);
- if (status != MMAL_SUCCESS) {
- msg_Err(vd, "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;
+ while ((buf = mmal_queue_get(isp->out_q)) != NULL)
+ mmal_buffer_header_release(buf);
+}
+
+static void isp_flush(struct vout_isp_conf_s * const isp)
+{
+ if (!isp->input->is_enabled)
+ mmal_port_disable(isp->input);
+
+ if (isp->output->is_enabled)
+ mmal_port_disable(isp->output);
+
+ isp_empty_out_q(isp);
+ isp->pending = false;
+}
+
+static MMAL_STATUS_T isp_prepare(vout_display_t * const vd, struct vout_isp_conf_s * const isp)
+{
+ MMAL_STATUS_T err;
+ MMAL_BUFFER_HEADER_T * buf;
+
+ if (!isp->output->is_enabled) {
+ if ((err = mmal_port_enable(isp->output, isp_output_cb)) != MMAL_SUCCESS)
+ {
+ msg_Err(vd, "ISP output port enable failed");
+ return err;
+ }
}
- sys->input->buffer_size = sys->input->buffer_size_recommended;
- vout_display_PlacePicture(&place, &vd->source, cfg);
- display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
- display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
- display_region.fullscreen = MMAL_FALSE;
- display_region.src_rect.x = fmt->i_x_offset;
- display_region.src_rect.y = fmt->i_y_offset;
- display_region.src_rect.width = fmt->i_visible_width;
- display_region.src_rect.height = fmt->i_visible_height;
- display_region.dest_rect.x = place.x;
- display_region.dest_rect.y = place.y;
- display_region.dest_rect.width = place.width;
- display_region.dest_rect.height = place.height;
- display_region.layer = sys->layer;
- display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
- status = mmal_port_parameter_set(sys->input, &display_region.hdr);
- if (status != MMAL_SUCCESS) {
- msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
- status, mmal_status_to_string(status));
- ret = VLC_EGENERIC;
- goto out;
+ while ((buf = mmal_queue_get(isp->out_pool->queue)) != NULL) {
+ if ((err = mmal_port_send_buffer(isp->output, buf)) != MMAL_SUCCESS)
+ {
+ msg_Err(vd, "ISP output port stuff failed");
+ return err;
+ }
}
- for (i = 0; i < sys->i_planes; ++i) {
- sys->planes[i].i_lines = buffer_height;
- sys->planes[i].i_pitch = buffer_pitch;
- sys->planes[i].i_visible_lines = fmt->i_visible_height;
- sys->planes[i].i_visible_pitch = fmt->i_visible_width;
-
- if (i > 0) {
- sys->planes[i].i_lines /= 2;
- sys->planes[i].i_pitch /= 2;
- sys->planes[i].i_visible_lines /= 2;
- sys->planes[i].i_visible_pitch /= 2;
+ if (!isp->input->is_enabled) {
+ if ((err = mmal_port_enable(isp->input, isp_input_cb)) != MMAL_SUCCESS)
+ {
+ msg_Err(vd, "ISP input port enable failed");
+ return err;
}
}
+ return MMAL_SUCCESS;
+}
- vlc_mutex_init(&sys->buffer_mutex);
- vlc_cond_init(&sys->buffer_cond);
- vlc_mutex_init(&sys->manage_mutex);
+static void isp_close(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
+{
+ struct vout_isp_conf_s * const isp = &vd_sys->isp;
+ VLC_UNUSED(vd);
- vd->pool = vd_pool;
- vd->prepare = vd_prepare;
- vd->display = vd_display;
- vd->control = vd_control;
- vd->close = CloseMmalVout;
+ if (isp->component == NULL)
+ return;
- vc_tv_register_callback(tvservice_cb, vd);
+ isp_flush(isp);
- if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) {
- vout_window_ReportSize(cfg->window,
- sys->display_width, sys->display_height);
- } else {
- sys->display_width = cfg->display.width;
- sys->display_height = cfg->display.height;
+ if (isp->component->control->is_enabled)
+ mmal_port_disable(isp->component->control);
+
+ if (isp->out_q != NULL) {
+ // 1st junk anything lying around
+ isp_empty_out_q(isp);
+
+ mmal_queue_destroy(isp->out_q);
+ isp->out_q = NULL;
}
- sys->dmx_handle = vc_dispmanx_display_open(0);
- vd->info.subpicture_chromas = hw_mmal_vzc_subpicture_chromas;
+ if (isp->out_pool != NULL) {
+ mmal_port_pool_destroy(isp->output, isp->out_pool);
+ isp->out_pool = NULL;
+ }
-out:
- if (ret != VLC_SUCCESS)
- CloseMmalVout(vd);
+ isp->input = NULL;
+ isp->output = NULL;
- (void) context;
- return ret;
+ mmal_component_release(isp->component);
+ isp->component = NULL;
+
+ return;
}
-static void CloseMmalVout(vout_display_t *vd)
+// Restuff into output rather than return to pool is we can
+static MMAL_BOOL_T isp_out_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata)
{
- vout_display_sys_t *sys = vd->sys;
- char response[20]; /* answer is hvs_update_fields=%1d */
- unsigned i;
+ struct vout_isp_conf_s * const isp = userdata;
+ VLC_UNUSED(pool);
+ if (isp->output->is_enabled) {
+ mmal_buffer_header_reset(buffer);
+ if (mmal_port_send_buffer(isp->output, buffer) == MMAL_SUCCESS)
+ return MMAL_FALSE;
+ }
+ return MMAL_TRUE;
+}
- vc_tv_unregister_callback_full(tvservice_cb, vd);
+static MMAL_STATUS_T isp_setup(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
+{
+ struct vout_isp_conf_s * const isp = &vd_sys->isp;
+ MMAL_STATUS_T err;
- if (sys->dmx_handle)
- close_dmx(vd);
+ if ((err = mmal_component_create(MMAL_COMPONENT_ISP_RESIZER, &isp->component)) != MMAL_SUCCESS) {
+ msg_Err(vd, "Cannot create ISP component");
+ return err;
+ }
+ isp->input = isp->component->input[0];
+ isp->output = isp->component->output[0];
- if (sys->component && sys->component->control->is_enabled)
- mmal_port_disable(sys->component->control);
+ isp->component->control->userdata = (void *)vd;
+ if ((err = mmal_port_enable(isp->component->control, isp_control_port_cb)) != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to enable ISP control port");
+ goto fail;
+ }
- if (sys->input && sys->input->is_enabled)
- mmal_port_disable(sys->input);
+ isp->input->userdata = (void *)vd;
+ display_set_format(vd, isp->input->format, false);
- if (sys->component && sys->component->is_enabled)
- mmal_component_disable(sys->component);
+ if ((err = port_parameter_set_bool(isp->input, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
+ goto fail;
- if (sys->pool)
- mmal_port_pool_destroy(sys->input, sys->pool);
+ if ((err = mmal_port_format_commit(isp->input)) != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to set ISP input format");
+ goto fail;
+ }
- if (sys->component)
- mmal_component_release(sys->component);
+ isp->input->buffer_size = isp->input->buffer_size_recommended;
+ isp->input->buffer_num = 30;
- if (sys->picture_pool)
- picture_pool_Release(sys->picture_pool);
- else
- for (i = 0; i < sys->num_buffers; ++i)
- if (sys->pictures[i]) {
- picture_sys_t *p_sys = sys->pictures[i]->p_sys;
- mmal_buffer_header_release(p_sys->buffer);
- picture_Release(sys->pictures[i]);
- }
+ if ((isp->in_pool = mmal_pool_create(isp->input->buffer_num, 0)) == NULL)
+ {
+ msg_Err(vd, "Failed to create input pool");
+ goto fail;
+ }
- vlc_mutex_destroy(&sys->buffer_mutex);
- vlc_cond_destroy(&sys->buffer_cond);
- vlc_mutex_destroy(&sys->manage_mutex);
+ if ((isp->out_q = mmal_queue_create()) == NULL)
+ {
+ err = MMAL_ENOMEM;
+ goto fail;
+ }
- if (sys->native_interlaced) {
- if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
- response[18] != '0')
- msg_Warn(vd, "Could not reset hvs field mode");
+ display_set_format(vd, isp->output->format, true);
+
+ if ((err = port_parameter_set_bool(isp->output, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
+ goto fail;
+
+ if ((err = mmal_port_format_commit(isp->output)) != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to set ISP input format");
+ goto fail;
}
- free(sys->pictures);
- free(sys);
+ isp->output->buffer_size = isp->output->buffer_size_recommended;
+ isp->output->buffer_num = 2;
+ isp->output->userdata = (void *)vd;
+
+ if ((isp->out_pool = mmal_port_pool_create(isp->output, isp->output->buffer_num, isp->output->buffer_size)) == NULL)
+ {
+ msg_Err(vd, "Failed to make ISP port pool");
+ goto fail;
+ }
+
+ mmal_pool_callback_set(isp->out_pool, isp_out_pool_cb, isp);
+
+ if ((err = isp_prepare(vd, isp)) != MMAL_SUCCESS)
+ goto fail;
+
+ return MMAL_SUCCESS;
- bcm_host_deinit();
+fail:
+ isp_close(vd, vd_sys);
+ return err;
}
-static inline uint32_t align(uint32_t x, uint32_t y) {
- uint32_t mod = x % y;
- if (mod == 0)
- return x;
+static MMAL_STATUS_T isp_check(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
+{
+ struct vout_isp_conf_s *const isp = &vd_sys->isp;
+ const bool has_isp = (isp->component != NULL);
+ const bool wants_isp = false;
+
+ if (has_isp == wants_isp)
+ {
+ // All OK - do nothing
+ }
+ else if (has_isp)
+ {
+ // ISP active but we don't want it
+ isp_flush(isp);
+
+ // Check we have everything back and then kill it
+ if (mmal_queue_length(isp->out_pool->queue) == isp->output->buffer_num)
+ isp_close(vd, vd_sys);
+ }
else
- return x + y - mod;
+ {
+ // ISP closed but we want it
+ return isp_setup(vd, vd_sys);
+ }
+
+ return MMAL_SUCCESS;
}
-static void configure_display(vout_display_t *vd,
- const vout_display_cfg_t *cfg,
- const video_format_t *fmt)
+/* TV service */
+static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
+ uint32_t param2);
+static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
+static int set_latency_target(vout_display_t *vd, bool enable);
+
+// Mmal
+static void maintain_phase_sync(vout_display_t *vd);
+
+
+
+static void vd_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
{
- vout_display_sys_t *sys = vd->sys;
+ VLC_UNUSED(port);
+
+ mmal_buffer_header_release(buf);
+}
+
+static int query_resolution(vout_display_t *vd, const int display_id, unsigned *width, unsigned *height)
+{
+ TV_DISPLAY_STATE_T display_state = {0};
+ int ret = 0;
+
+ if (vc_tv_get_display_state_id(display_id, &display_state) == 0) {
+ msg_Dbg(vd, "State=%#x", display_state.state);
+ if (display_state.state & 0xFF) {
+ msg_Dbg(vd, "HDMI: %dx%d", display_state.display.hdmi.width, display_state.display.hdmi.height);
+ *width = display_state.display.hdmi.width;
+ *height = display_state.display.hdmi.height;
+ } else if (display_state.state & 0xFF00) {
+ msg_Dbg(vd, "SDTV: %dx%d", display_state.display.sdtv.width, display_state.display.sdtv.height);
+ *width = display_state.display.sdtv.width;
+ *height = display_state.display.sdtv.height;
+ } else {
+ msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
+ ret = -1;
+ }
+ } else {
+ msg_Warn(vd, "Failed to query display resolution");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static MMAL_RECT_T
+place_to_mmal_rect(const vout_display_place_t place)
+{
+ return (MMAL_RECT_T){
+ .x = place.x,
+ .y = place.y,
+ .width = place.width,
+ .height = place.height
+ };
+}
+
+static void
+place_dest(vout_display_t *vd, vout_display_sys_t * const sys,
+ const vout_display_cfg_t * const cfg, const video_format_t * fmt)
+{
+ video_format_t tfmt;
+
+ // Fix SAR if unknown
+ if (fmt->i_sar_den == 0 || fmt->i_sar_num == 0) {
+ tfmt = *fmt;
+ tfmt.i_sar_den = 1;
+ tfmt.i_sar_num = 1;
+ fmt = &tfmt;
+ }
+
+ // Ignore what VLC thinks might be going on with display size
+ vout_display_cfg_t tcfg = *cfg;
vout_display_place_t place;
+ tcfg.display.width = sys->display_width;
+ tcfg.display.height = sys->display_height;
+ tcfg.is_display_filled = true;
+ vout_display_PlacePicture(&place, fmt, &tcfg);
+
+ sys->dest_rect = place_to_mmal_rect(place);
+}
+
+
+
+static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
+ const video_format_t *fmt)
+{
+ vout_display_sys_t * const sys = vd->sys;
MMAL_DISPLAYREGION_T display_region;
MMAL_STATUS_T status;
- assert(cfg != NULL || fmt != NULL);
+ if (!cfg && !fmt)
+ {
+ msg_Err(vd, "Missing cfg & fmt");
+ return -EINVAL;
+ }
+
+ isp_check(vd, sys);
if (fmt) {
sys->input->format->es->video.par.num = fmt->i_sar_num;
@@ -400,257 +563,177 @@ static void configure_display(vout_display_t *vd,
if (status != MMAL_SUCCESS) {
msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
sys->input->name, status, mmal_status_to_string(status));
- return;
+ return -EINVAL;
}
} else {
fmt = &vd->source;
}
if (!cfg)
- cfg = &sys->last_cfg;
+ cfg = vd->cfg;
- vout_display_PlacePicture(&place, fmt, cfg);
+ place_dest(vd, sys, cfg, fmt);
display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
display_region.fullscreen = MMAL_FALSE;
- display_region.src_rect.x = fmt->i_x_offset;
- display_region.src_rect.y = fmt->i_y_offset;
- display_region.src_rect.width = fmt->i_visible_width;
- display_region.src_rect.height = fmt->i_visible_height;
- display_region.dest_rect.x = place.x;
- display_region.dest_rect.y = place.y;
- display_region.dest_rect.width = place.width;
- display_region.dest_rect.height = place.height;
+ display_src_rect(vd, &display_region.src_rect);
+ display_region.dest_rect = sys->dest_rect;
display_region.layer = sys->layer;
+ display_region.alpha = 0xff | (1 << 29);
display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
+ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_ALPHA;
status = mmal_port_parameter_set(sys->input, &display_region.hdr);
if (status != MMAL_SUCCESS) {
msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
status, mmal_status_to_string(status));
- return;
+ return -EINVAL;
}
- show_background(vd, var_InheritBool(vd, MMAL_BLANK_BACKGROUND_NAME));
sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME);
sys->native_interlaced = var_InheritBool(vd, MMAL_NATIVE_INTERLACED);
if (sys->adjust_refresh_rate) {
adjust_refresh_rate(vd, fmt);
set_latency_target(vd, true);
}
+
+ return 0;
}
-static void pic_destroy(picture_t *pic)
+static void kill_pool(vout_display_sys_t * const sys)
{
- free(pic->p_sys);
+ if (sys->pic_pool != NULL) {
+ picture_pool_Release(sys->pic_pool);
+ sys->pic_pool = NULL;
+ }
}
-static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count)
+static void vd_display(vout_display_t *vd, picture_t *p_pic)
{
- vout_display_sys_t *sys = vd->sys;
- picture_resource_t picture_res;
- picture_pool_configuration_t picture_pool_cfg;
- MMAL_STATUS_T status;
- unsigned i;
+ vout_display_sys_t * const sys = vd->sys;
+ MMAL_STATUS_T err;
- if (sys->picture_pool) {
- if (sys->num_buffers < count)
- msg_Warn(vd, "Picture pool with %u pictures requested, but we already have one with %u pictures",
- count, sys->num_buffers);
-
- goto out;
+ if (!sys->input->is_enabled &&
+ (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS)
+ {
+ msg_Err(vd, "Input port enable failed");
+ goto fail;
}
-
- if (sys->opaque) {
- if (count <= NUM_ACTUAL_OPAQUE_BUFFERS)
- count = NUM_ACTUAL_OPAQUE_BUFFERS;
-
- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
- 1
- };
-
- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
- if (status != MMAL_SUCCESS) {
- msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
- sys->input->name, status, mmal_status_to_string(status));
- goto out;
+ // Stuff into input
+ // We assume the BH is already set up with values reflecting pic date etc.
+ if (sys->copy_buf != NULL) {
+ MMAL_BUFFER_HEADER_T *const buf = sys->copy_buf;
+ sys->copy_buf = NULL;
+ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS)
+ {
+ mmal_buffer_header_release(buf);
+ msg_Err(vd, "Send copy buffer to render input failed");
+ goto fail;
}
}
-
- if (count < sys->input->buffer_num_recommended)
- count = sys->input->buffer_num_recommended;
-
-#ifndef NDEBUG
- msg_Dbg(vd, "Creating picture pool with %u pictures", count);
-#endif
-
- sys->input->buffer_num = count;
- status = mmal_port_enable(sys->input, input_port_cb);
- if (status != MMAL_SUCCESS) {
- msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
- sys->input->name, status, mmal_status_to_string(status));
- goto out;
- }
-
- status = mmal_component_enable(sys->component);
- if (status != MMAL_SUCCESS) {
- msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
- sys->component->name, status, mmal_status_to_string(status));
- goto out;
- }
-
- sys->num_buffers = count;
- sys->pool = mmal_port_pool_create(sys->input, sys->num_buffers,
- sys->input->buffer_size);
- if (!sys->pool) {
- msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
- count, sys->input->buffer_size);
- goto out;
- }
-
- memset(&picture_res, 0, sizeof(picture_resource_t));
- picture_res.pf_destroy = pic_destroy;
-
- sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *));
- for (i = 0; i < sys->num_buffers; ++i) {
- picture_sys_t *p_sys = picture_res.p_sys = calloc(1, sizeof(picture_sys_t));
- if (unlikely(p_sys==NULL)) {
- goto out;
+ else if (sys->isp.pending) {
+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q);
+ sys->isp.pending = false;
+ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS)
+ {
+ mmal_buffer_header_release(buf);
+ msg_Err(vd, "Send ISP buffer to render input failed");
+ goto fail;
}
- p_sys->owner = (vlc_object_t *)vd;
- p_sys->buffer = mmal_queue_get(sys->pool->queue);
-
- sys->pictures[i] = picture_NewFromResource(&vd->fmt, &picture_res);
- if (!sys->pictures[i]) {
- msg_Err(vd, "Failed to create picture");
- free(picture_res.p_sys);
- goto out;
- }
-
- sys->pictures[i]->i_planes = sys->i_planes;
- memcpy(sys->pictures[i]->p, sys->planes, sys->i_planes * sizeof(plane_t));
- }
-
- memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t));
- picture_pool_cfg.picture_count = sys->num_buffers;
- picture_pool_cfg.picture = sys->pictures;
- picture_pool_cfg.lock = mmal_picture_lock;
-
- sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
- if (!sys->picture_pool) {
- msg_Err(vd, "Failed to create picture pool");
- goto out;
}
-
-out:
- if (!sys->picture_pool)
+ else
{
- while(i-- != 0)
- picture_Release(sys->pictures[i]);
- free(sys->pictures);
- sys->pictures = NULL;
- }
- return sys->picture_pool;
-}
-
-static void vd_prepare(vout_display_t *vd, picture_t *picture,
- subpicture_t *subpicture, vlc_tick_t date)
-{
- vd_manage(vd);
- VLC_UNUSED(date);
- vout_display_sys_t *sys = vd->sys;
- picture_sys_t *pic_sys = picture->p_sys;
+ MMAL_BUFFER_HEADER_T *const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->pool);
+ if (pic_buf == NULL)
+ {
+ msg_Err(vd, "Replicated buffer get fail");
+ goto fail;
+ }
- if (!sys->adjust_refresh_rate || pic_sys->displayed)
- return;
- /* Apply the required phase_offset to the picture, so that vd_display()
- * will be called at the corrected time from the core */
- picture->date += sys->phase_offset;
- /* trick for now as we don't get the subpicture during display anymore */
- sys->prepare_subpicture = subpicture;
-}
+ // If dimensions have chnaged then fix that
+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
+ {
+ msg_Dbg(vd, "Reset port format");
-static void vd_display(vout_display_t *vd, picture_t *picture)
-{
- vout_display_sys_t *sys = vd->sys;
- picture_sys_t *pic_sys = picture->p_sys;
- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
- MMAL_STATUS_T status;
+ // HVS can deal with on-line dimension changes
+ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS)
+ msg_Warn(vd, "Input format commit failed");
+ }
- if (picture->format.i_frame_rate != sys->i_frame_rate ||
- picture->format.i_frame_rate_base != sys->i_frame_rate_base ||
- picture->b_progressive != sys->b_progressive ||
- picture->b_top_field_first != sys->b_top_field_first) {
- sys->b_top_field_first = picture->b_top_field_first;
- sys->b_progressive = picture->b_progressive;
- sys->i_frame_rate = picture->format.i_frame_rate;
- sys->i_frame_rate_base = picture->format.i_frame_rate_base;
- configure_display(vd, NULL, &picture->format);
+ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
+ {
+ mmal_buffer_header_release(pic_buf);
+ msg_Err(vd, "Send buffer to input failed");
+ goto fail;
+ }
}
- if (!pic_sys->displayed || !sys->opaque) {
- buffer->cmd = 0;
- buffer->length = sys->input->buffer_size;
- buffer->user_data = picture_Hold(picture);
-
- status = mmal_port_send_buffer(sys->input, buffer);
- if (status == MMAL_SUCCESS)
- atomic_fetch_add(&sys->buffers_in_transit, 1);
-
- if (status != MMAL_SUCCESS) {
- msg_Err(vd, "Failed to send buffer to input port. Frame dropped");
- picture_Release(picture);
+ {
+ unsigned int sub_no = 0;
+ MMAL_BUFFER_HEADER_T **psub_bufs2 = sys->subpic_bufs;
+ const bool is_mmal_pic = hw_mmal_chroma_is_mmal(p_pic->format.i_chroma);
+
+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
+ int rv;
+ MMAL_BUFFER_HEADER_T * const sub_buf = !is_mmal_pic ? NULL :
+ hw_mmal_pic_sub_buf_get(p_pic, sub_no);
+
+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(vd),
+ sub_buf != NULL ? sub_buf : *psub_bufs2++,
+ &sys->subs[sub_no].sub,
+ &p_pic->format,
+ &sys->dest_rect,
+ p_pic->date)) == 0)
+ break;
+ else if (rv < 0)
+ goto fail;
}
-
- pic_sys->displayed = true;
}
- display_subpicture(vd, sys->prepare_subpicture);
+fail:
+ for (unsigned int i = 0; i != SUBS_MAX && sys->subpic_bufs[i] != NULL; ++i) {
+ mmal_buffer_header_release(sys->subpic_bufs[i]);
+ sys->subpic_bufs[i] = NULL;
+ }
if (sys->next_phase_check == 0 && sys->adjust_refresh_rate)
maintain_phase_sync(vd);
sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL;
-
- if (sys->opaque) {
- vlc_mutex_lock(&sys->buffer_mutex);
- while (atomic_load(&sys->buffers_in_transit) >= MAX_BUFFERS_IN_TRANSIT)
- vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex);
- vlc_mutex_unlock(&sys->buffer_mutex);
- }
}
static int vd_control(vout_display_t *vd, int query, va_list args)
{
- vout_display_sys_t *sys = vd->sys;
- vout_display_cfg_t cfg;
- const vout_display_cfg_t *tmp_cfg;
+ vout_display_sys_t * const sys = vd->sys;
+ int ret = VLC_EGENERIC;
+ VLC_UNUSED(args);
switch (query) {
case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
- tmp_cfg = va_arg(args, const vout_display_cfg_t *);
- if (tmp_cfg->display.width == sys->display_width &&
- tmp_cfg->display.height == sys->display_height) {
- cfg = sys->last_cfg;
- cfg.display.width = sys->display_width;
- cfg.display.height = sys->display_height;
- configure_display(vd, &cfg, NULL);
- }
+ {
+ const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t *);
+ if (configure_display(vd, cfg, NULL) >= 0)
+ ret = VLC_SUCCESS;
break;
+ }
case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
- sys->last_cfg = *va_arg(args, const vout_display_cfg_t *);
- configure_display(vd, NULL, &vd->source);
+ if (configure_display(vd, NULL, &vd->source) >= 0)
+ ret = VLC_SUCCESS;
break;
case VOUT_DISPLAY_RESET_PICTURES:
- vlc_assert_unreachable();
+ msg_Warn(vd, "Reset Pictures");
+ kill_pool(sys);
+ vd->fmt = vd->source; // Take (nearly) whatever source wants to give us
+ vd->fmt.i_chroma = req_chroma(vd); // Adjust chroma to something we can actually deal with
+ ret = VLC_SUCCESS;
+ break;
+
case VOUT_DISPLAY_CHANGE_ZOOM:
- case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
msg_Warn(vd, "Unsupported control query %d", query);
+ ret = VLC_SUCCESS;
break;
default:
@@ -658,7 +741,7 @@ static int vd_control(vout_display_t *vd, int query, va_list args)
break;
}
- return VLC_SUCCESS;
+ return ret;
}
static void vd_manage(vout_display_t *vd)
@@ -669,13 +752,10 @@ static void vd_manage(vout_display_t *vd)
vlc_mutex_lock(&sys->manage_mutex);
if (sys->need_configure_display) {
- close_dmx(vd);
- sys->dmx_handle = vc_dispmanx_display_open(0);
-
- if (query_resolution(vd, &width, &height) >= 0) {
+ if (query_resolution(vd, sys->display_id, &width, &height) >= 0) {
sys->display_width = width;
sys->display_height = height;
- vout_window_ReportSize(sys->last_cfg.window, width, height);
+// vout_window_ReportSize(vd->cfg->window, width, height);
}
sys->need_configure_display = false;
@@ -684,56 +764,109 @@ static void vd_manage(vout_display_t *vd)
vlc_mutex_unlock(&sys->manage_mutex);
}
-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+
+static int attach_subpics(vout_display_t * const vd, vout_display_sys_t * const sys,
+ subpicture_t * const subpicture)
{
- vout_display_t *vd = (vout_display_t *)port->userdata;
- MMAL_STATUS_T status;
+ unsigned int n = 0;
- if (buffer->cmd == MMAL_EVENT_ERROR) {
- status = *(uint32_t *)buffer->data;
- msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
+ if (sys->vzc == NULL) {
+ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL)
+ {
+ msg_Err(vd, "Failed to allocate VZC");
+ return VLC_ENOMEM;
+ }
}
- mmal_buffer_header_release(buffer);
+ // Attempt to import the subpics
+ for (subpicture_t * spic = subpicture; spic != NULL; spic = spic->p_next)
+ {
+ for (subpicture_region_t *sreg = spic->p_region; sreg != NULL; sreg = sreg->p_next) {
+ picture_t *const src = sreg->p_picture;
+
+ // At this point I think the subtitles are being placed in the
+ // coord space of the cfg rectangle
+ if ((sys->subpic_bufs[n] = hw_mmal_vzc_buf_from_pic(sys->vzc,
+ src,
+ (MMAL_RECT_T){.width = vd->cfg->display.width, .height=vd->cfg->display.height},
+ sreg->i_x, sreg->i_y,
+ sreg->i_alpha,
+ n == 0)) == NULL)
+ {
+ msg_Err(vd, "Failed to allocate vzc buffer for subpic");
+ return VLC_ENOMEM;
+ }
+
+ if (++n == SUBS_MAX)
+ return VLC_SUCCESS;
+ }
+ }
+ return VLC_SUCCESS;
}
-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+
+static void vd_prepare(vout_display_t *vd, picture_t *p_pic,
+ subpicture_t *subpicture, vlc_tick_t date)
{
- vout_display_t *vd = (vout_display_t *)port->userdata;
- vout_display_sys_t *sys = vd->sys;
- picture_t *picture = (picture_t *)buffer->user_data;
+ MMAL_STATUS_T err;
+ vout_display_sys_t * const sys = vd->sys;
+
+ vd_manage(vd);
+
+ if (sys->force_config ||
+ p_pic->format.i_frame_rate != sys->i_frame_rate ||
+ p_pic->format.i_frame_rate_base != sys->i_frame_rate_base ||
+ p_pic->b_progressive != sys->b_progressive ||
+ p_pic->b_top_field_first != sys->b_top_field_first)
+ {
+ sys->force_config = false;
+ sys->b_top_field_first = p_pic->b_top_field_first;
+ sys->b_progressive = p_pic->b_progressive;
+ sys->i_frame_rate = p_pic->format.i_frame_rate;
+ sys->i_frame_rate_base = p_pic->format.i_frame_rate_base;
+ configure_display(vd, NULL, &p_pic->format);
+ }
+
+ // Subpics can either turn up attached to the main pic or in the
+ // subpic list here - if they turn up here then process into temp
+ // buffers
+ if (subpicture != NULL) {
+ attach_subpics(vd, sys, subpicture);
+ }
- if (picture)
- picture_Release(picture);
+ // *****
+ if (want_copy(vd)) {
+ if (sys->copy_buf != NULL) {
+ msg_Err(vd, "Copy buf not NULL");
+ mmal_buffer_header_release(sys->copy_buf);
+ sys->copy_buf = NULL;
+ }
+
+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->copy_pool->queue);
+ // Copy 2d
+ hw_mmal_copy_pic_to_buf(buf->data, &buf->length, sys->input->format, p_pic);
+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
+
+ sys->copy_buf = buf;
+ }
- vlc_mutex_lock(&sys->buffer_mutex);
- atomic_fetch_sub(&sys->buffers_in_transit, 1);
- vlc_cond_signal(&sys->buffer_cond);
- vlc_mutex_unlock(&sys->buffer_mutex);
+ if (isp_check(vd, sys) != MMAL_SUCCESS) {
+ return;
+ }
}
-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
+
+static void vd_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
{
- TV_DISPLAY_STATE_T display_state;
- int ret = 0;
+ vout_display_t *vd = (vout_display_t *)port->userdata;
+ MMAL_STATUS_T status;
- if (vc_tv_get_display_state(&display_state) == 0) {
- if (display_state.state & 0xFF) {
- *width = display_state.display.hdmi.width;
- *height = display_state.display.hdmi.height;
- } else if (display_state.state & 0xFF00) {
- *width = display_state.display.sdtv.width;
- *height = display_state.display.sdtv.height;
- } else {
- msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
- ret = -1;
- }
- } else {
- msg_Warn(vd, "Failed to query display resolution");
- ret = -1;
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
+ status = *(uint32_t *)buffer->data;
+ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
}
- return ret;
+ mmal_buffer_header_release(buffer);
}
static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2)
@@ -788,9 +921,9 @@ static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt)
double best_score, score;
int i;
- vc_tv_get_display_state(&display_state);
+ vc_tv_get_display_state_id(sys->display_id, &display_state);
if(display_state.display.hdmi.mode != HDMI_MODE_OFF) {
- num_modes = vc_tv_hdmi_get_supported_modes_new(display_state.display.hdmi.group,
+ num_modes = vc_tv_hdmi_get_supported_modes_new_id(sys->display_id, display_state.display.hdmi.group,
supported_modes, VC_TV_MAX_MODE_IDS, NULL, NULL);
for (i = 0; i < num_modes; ++i) {
@@ -801,8 +934,8 @@ static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt)
mode->scan_mode == HDMI_INTERLACED)
continue;
} else {
- if (mode->width != fmt->i_visible_width ||
- mode->height != fmt->i_visible_height)
+ if (mode->width != vd->fmt.i_visible_width ||
+ mode->height != vd->fmt.i_visible_height)
continue;
if (mode->scan_mode != sys->b_progressive ? HDMI_NONINTERLACED : HDMI_INTERLACED)
continue;
@@ -818,7 +951,7 @@ static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt)
if((best_id >= 0) && (display_state.display.hdmi.mode != supported_modes[best_id].code)) {
msg_Info(vd, "Setting HDMI refresh rate to %"PRIu32,
supported_modes[best_id].frame_rate);
- vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI,
+ vc_tv_hdmi_power_on_explicit_new_id(sys->display_id, HDMI_MODE_HDMI,
supported_modes[best_id].group,
supported_modes[best_id].code);
}
@@ -836,142 +969,6 @@ static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt)
}
}
-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture)
-{
- vout_display_sys_t *sys = vd->sys;
- struct dmx_region_t **dmx_region = &sys->dmx_region;
- struct dmx_region_t *unused_dmx_region;
- DISPMANX_UPDATE_HANDLE_T update = 0;
- picture_t *picture;
- video_format_t *fmt;
- struct dmx_region_t *dmx_region_next;
-
- if(subpicture) {
- subpicture_region_t *region = subpicture->p_region;
- while(region) {
- picture = region->p_picture;
- fmt = ®ion->fmt;
-
- if(!*dmx_region) {
- if(!update)
- update = vc_dispmanx_update_start(10);
- *dmx_region = dmx_region_new(vd, update, region);
- } else if(((*dmx_region)->bmp_rect.width != (int32_t)fmt->i_visible_width) ||
- ((*dmx_region)->bmp_rect.height != (int32_t)fmt->i_visible_height) ||
- ((*dmx_region)->pos_x != region->i_x) ||
- ((*dmx_region)->pos_y != region->i_y) ||
- ((*dmx_region)->alpha.opacity != (uint32_t)region->i_alpha)) {
- dmx_region_next = (*dmx_region)->next;
- if(!update)
- update = vc_dispmanx_update_start(10);
- dmx_region_delete(*dmx_region, update);
- *dmx_region = dmx_region_new(vd, update, region);
- (*dmx_region)->next = dmx_region_next;
- } else if((*dmx_region)->picture != picture) {
- if(!update)
- update = vc_dispmanx_update_start(10);
- dmx_region_update(*dmx_region, update, picture);
- }
-
- dmx_region = &(*dmx_region)->next;
- region = region->p_next;
- }
- }
-
- /* Remove remaining regions */
- unused_dmx_region = *dmx_region;
- while(unused_dmx_region) {
- dmx_region_next = unused_dmx_region->next;
- if(!update)
- update = vc_dispmanx_update_start(10);
- dmx_region_delete(unused_dmx_region, update);
- unused_dmx_region = dmx_region_next;
- }
- *dmx_region = NULL;
-
- if(update)
- vc_dispmanx_update_submit_sync(update);
-}
-
-static void close_dmx(vout_display_t *vd)
-{
- vout_display_sys_t *sys = vd->sys;
- DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(10);
- struct dmx_region_t *dmx_region = sys->dmx_region;
- struct dmx_region_t *dmx_region_next;
-
- while(dmx_region) {
- dmx_region_next = dmx_region->next;
- dmx_region_delete(dmx_region, update);
- dmx_region = dmx_region_next;
- }
-
- vc_dispmanx_update_submit_sync(update);
- sys->dmx_region = NULL;
-
- show_background(vd, false);
-
- vc_dispmanx_display_close(sys->dmx_handle);
- sys->dmx_handle = DISPMANX_NO_HANDLE;
-}
-
-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region)
-{
- vout_display_sys_t *sys = vd->sys;
- video_format_t *fmt = ®ion->fmt;
- struct dmx_region_t *dmx_region = malloc(sizeof(struct dmx_region_t));
- uint32_t image_handle;
-
- dmx_region->pos_x = region->i_x;
- dmx_region->pos_y = region->i_y;
-
- vc_dispmanx_rect_set(&dmx_region->bmp_rect, 0, 0, fmt->i_visible_width,
- fmt->i_visible_height);
- vc_dispmanx_rect_set(&dmx_region->src_rect, 0, 0, fmt->i_visible_width << 16,
- fmt->i_visible_height << 16);
- vc_dispmanx_rect_set(&dmx_region->dst_rect, region->i_x, region->i_y,
- fmt->i_visible_width, fmt->i_visible_height);
-
- dmx_region->resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32,
- dmx_region->bmp_rect.width | (region->p_picture->p[0].i_pitch << 16),
- dmx_region->bmp_rect.height | (dmx_region->bmp_rect.height << 16),
- &image_handle);
- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
- region->p_picture->p[0].i_pitch,
- region->p_picture->p[0].p_pixels, &dmx_region->bmp_rect);
-
- dmx_region->alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_MIX;
- dmx_region->alpha.opacity = region->i_alpha;
- dmx_region->alpha.mask = DISPMANX_NO_HANDLE;
- dmx_region->element = vc_dispmanx_element_add(update, sys->dmx_handle,
- sys->layer + 1, &dmx_region->dst_rect, dmx_region->resource,
- &dmx_region->src_rect, DISPMANX_PROTECTION_NONE,
- &dmx_region->alpha, NULL, VC_IMAGE_ROT0);
-
- dmx_region->next = NULL;
- dmx_region->picture = region->p_picture;
-
- return dmx_region;
-}
-
-static void dmx_region_update(struct dmx_region_t *dmx_region,
- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture)
-{
- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
- picture->p[0].i_pitch, picture->p[0].p_pixels, &dmx_region->bmp_rect);
- vc_dispmanx_element_change_source(update, dmx_region->element, dmx_region->resource);
- dmx_region->picture = picture;
-}
-
-static void dmx_region_delete(struct dmx_region_t *dmx_region,
- DISPMANX_UPDATE_HANDLE_T update)
-{
- vc_dispmanx_element_remove(update, dmx_region->element);
- vc_dispmanx_resource_delete(dmx_region->resource);
- free(dmx_region);
-}
-
static void maintain_phase_sync(vout_display_t *vd)
{
MMAL_PARAMETER_VIDEO_RENDER_STATS_T render_stats = {
@@ -1020,32 +1017,277 @@ static void maintain_phase_sync(vout_display_t *vd)
}
}
-static void show_background(vout_display_t *vd, bool enable)
+static void CloseMmalVout(vout_display_t * vd)
{
- vout_display_sys_t *sys = vd->sys;
- uint32_t image_ptr, color = 0xFF000000;
- VC_RECT_T dst_rect, src_rect;
- DISPMANX_UPDATE_HANDLE_T update;
-
- if (enable && !sys->bkg_element) {
- sys->bkg_resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 1, 1,
- &image_ptr);
- vc_dispmanx_rect_set(&dst_rect, 0, 0, 1, 1);
- vc_dispmanx_resource_write_data(sys->bkg_resource, VC_IMAGE_RGBA32,
- sizeof(color), &color, &dst_rect);
- vc_dispmanx_rect_set(&src_rect, 0, 0, 1 << 16, 1 << 16);
- vc_dispmanx_rect_set(&dst_rect, 0, 0, 0, 0);
- update = vc_dispmanx_update_start(0);
- sys->bkg_element = vc_dispmanx_element_add(update, sys->dmx_handle,
- sys->layer - 1, &dst_rect, sys->bkg_resource, &src_rect,
- DISPMANX_PROTECTION_NONE, NULL, NULL, VC_IMAGE_ROT0);
- vc_dispmanx_update_submit_sync(update);
- } else if (!enable && sys->bkg_element) {
- update = vc_dispmanx_update_start(0);
- vc_dispmanx_element_remove(update, sys->bkg_element);
- vc_dispmanx_resource_delete(sys->bkg_resource);
- vc_dispmanx_update_submit_sync(update);
- sys->bkg_element = DISPMANX_NO_HANDLE;
- sys->bkg_resource = DISPMANX_NO_HANDLE;
+ vout_display_sys_t * const sys = vd->sys;
+ char response[20]; /* answer is hvs_update_fields=%1d */
+
+ kill_pool(sys);
+
+ vc_tv_unregister_callback_full(tvservice_cb, vd);
+
+ // Shouldn't be anything here - but just in case
+ for (unsigned int i = 0; i != SUBS_MAX; ++i)
+ if (sys->subpic_bufs[i] != NULL)
+ mmal_buffer_header_release(sys->subpic_bufs[i]);
+
+ for (unsigned int i = 0; i != SUBS_MAX; ++i) {
+ vout_subpic_t * const sub = sys->subs + i;
+ if (sub->component != NULL) {
+ hw_mmal_subpic_close(&sub->sub);
+ if (sub->component->control->is_enabled)
+ mmal_port_disable(sub->component->control);
+ if (sub->component->is_enabled)
+ mmal_component_disable(sub->component);
+ mmal_component_release(sub->component);
+ sub->component = NULL;
+ }
}
+
+ if (sys->input && sys->input->is_enabled)
+ mmal_port_disable(sys->input);
+
+ if (sys->component && sys->component->control->is_enabled)
+ mmal_port_disable(sys->component->control);
+
+ if (sys->copy_buf != NULL)
+ mmal_buffer_header_release(sys->copy_buf);
+
+ if (sys->input != NULL && sys->copy_pool != NULL)
+ mmal_port_pool_destroy(sys->input, sys->copy_pool);
+
+ if (sys->component && sys->component->is_enabled)
+ mmal_component_disable(sys->component);
+
+ if (sys->pool)
+ mmal_pool_destroy(sys->pool);
+
+ if (sys->component)
+ mmal_component_release(sys->component);
+
+ isp_close(vd, sys);
+
+ hw_mmal_vzc_pool_release(sys->vzc);
+
+ vlc_mutex_destroy(&sys->manage_mutex);
+
+ if (sys->native_interlaced) {
+ if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
+ response[18] != '0')
+ msg_Warn(vd, "Could not reset hvs field mode");
+ }
+
+ cma_vcsm_exit(sys->init_type);
+
+ free(sys);
+}
+
+
+static const struct {
+ const char * name;
+ int num;
+} display_name_to_num[] = {
+ {"auto", -1},
+ {"hdmi-1", DISPMANX_ID_HDMI0},
+ {"hdmi-2", DISPMANX_ID_HDMI1},
+ {NULL, -2}
+};
+
+static int find_display_num(const char * name)
+{
+ unsigned int i;
+ for (i = 0; display_name_to_num[i].name != NULL && strcasecmp(display_name_to_num[i].name, name) != 0; ++i)
+ /* Loop */;
+ return display_name_to_num[i].num;
+}
+
+static int OpenMmalVout(vout_display_t *vd, const vout_display_cfg_t *cfg,
+ video_format_t *fmtp, vlc_video_context *context)
+{
+ vout_display_sys_t *sys;
+ MMAL_DISPLAYREGION_T display_region;
+ MMAL_STATUS_T status;
+ int ret = VLC_EGENERIC;
+ // At the moment all copy is via I420
+ const bool needs_copy = !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma);
+ const MMAL_FOURCC_T enc_in = needs_copy ? MMAL_ENCODING_I420 :
+ vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
+
+ sys = calloc(1, sizeof(struct vout_display_sys_t));
+ if (!sys)
+ return VLC_ENOMEM;
+ vd->sys = sys;
+
+ vlc_mutex_init(&sys->manage_mutex);
+
+ if ((sys->init_type = cma_vcsm_init()) == VCSM_INIT_NONE)
+ {
+ msg_Err(vd, "VCSM init fail");
+ goto fail;
+ }
+
+ vc_tv_register_callback(tvservice_cb, vd);
+
+ sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
+
+ {
+ const char *display_name = var_InheritString(vd, MMAL_DISPLAY_NAME);
+ int qt_num = -1;
+#ifdef HAVE_QT
+ qt_num = var_InheritInteger(vd, "qt-fullscreen-screennumber" );
+#endif
+ int display_id = find_display_num(display_name);
+// sys->display_id = display_id < 0 ? vc_tv_get_default_display_id() : display_id;
+ sys->display_id = display_id >= 0 ? display_id :
+ qt_num == 1 ? DISPMANX_ID_HDMI1 : DISPMANX_ID_HDMI;
+ if (display_id < -1)
+ msg_Warn(vd, "Unknown display device: '%s'", display_name);
+ else
+ msg_Dbg(vd, "Display device: %s, qt=%d id=%d display=%d", display_name,
+ qt_num, display_id, sys->display_id);
+ }
+
+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
+ MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
+ status = mmal_port_enable(sys->component->control, vd_control_port_cb);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
+ sys->component->control->name, status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ sys->input = sys->component->input[0];
+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
+
+ sys->input->format->encoding = enc_in;
+ sys->input->format->encoding_variant = 0;
+ sys->i_planes = 1;
+
+ display_set_format(vd, sys->input->format, false);
+
+ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
+ sys->input->name, status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ status = mmal_port_format_commit(sys->input);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
+ sys->input->name, status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ sys->input->buffer_size = sys->input->buffer_size_recommended;
+
+ if (!needs_copy) {
+ sys->input->buffer_num = 30;
+ }
+ else {
+ sys->input->buffer_num = 2;
+ if ((sys->copy_pool = mmal_port_pool_create(sys->input, 2, sys->input->buffer_size)) == NULL)
+ {
+ msg_Err(vd, "Cannot create copy pool");
+ goto fail;
+ }
+ }
+
+ if (query_resolution(vd, sys->display_id, &sys->display_width, &sys->display_height) < 0)
+ {
+ sys->display_width = vd->cfg->display.width;
+ sys->display_height = vd->cfg->display.height;
+ }
+
+ place_dest(vd, sys, vd->cfg, &vd->source); // Sets sys->dest_rect
+
+ display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
+ display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
+ display_region.display_num = sys->display_id;
+ display_region.fullscreen = MMAL_FALSE;
+ display_src_rect(vd, &display_region.src_rect);
+ display_region.dest_rect = sys->dest_rect;
+ display_region.layer = sys->layer;
+ display_region.set =
+ MMAL_DISPLAY_SET_NUM |
+ MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
+ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
+ status = mmal_port_parameter_set(sys->input, &display_region.hdr);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
+ status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ status = mmal_port_enable(sys->input, vd_input_port_cb);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
+ sys->input->name, status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ status = mmal_component_enable(sys->component);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
+ sys->component->name, status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ if ((sys->pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
+ {
+ msg_Err(vd, "Failed to create input pool");
+ goto fail;
+ }
+
+ for (unsigned int i = 0; i != SUBS_MAX; ++i) {
+ vout_subpic_t * const sub = sys->subs + i;
+ if ((status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sub->component)) != MMAL_SUCCESS)
+ {
+ msg_Dbg(vd, "Failed to create subpic component %d", i);
+ goto fail;
+ }
+ sub->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
+ if ((status = mmal_port_enable(sub->component->control, vd_control_port_cb)) != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to enable control port %s on sub %d (status=%"PRIx32" %s)",
+ sys->component->control->name, i, status, mmal_status_to_string(status));
+ goto fail;
+ }
+ if ((status = hw_mmal_subpic_open(VLC_OBJECT(vd), &sub->sub, sub->component->input[0],
+ sys->display_id, sys->layer + i + 1)) != MMAL_SUCCESS) {
+ msg_Dbg(vd, "Failed to open subpic %d", i);
+ goto fail;
+ }
+ if ((status = mmal_component_enable(sub->component)) != MMAL_SUCCESS)
+ {
+ msg_Dbg(vd, "Failed to enable subpic component %d", i);
+ goto fail;
+ }
+ }
+
+ // If we can't deal with it directly ask for I420
+ vd->fmt.i_chroma = req_chroma(vd);
+
+ vd->info = (vout_display_info_t){
+ .subpicture_chromas = hw_mmal_vzc_subpicture_chromas
+ };
+
+ vd->prepare = vd_prepare;
+ vd->display = vd_display;
+ vd->control = vd_control;
+ vd->close = CloseMmalVout;
+
+
+ return VLC_SUCCESS;
+
+fail:
+ CloseMmalVout(vd);
+
+ return ret;
}
+
--
2.17.1
More information about the vlc-devel
mailing list