[vlc-commits] mmal: move the converter/resizer in its own file
Steve Lhomme
git at videolan.org
Fri Jan 24 14:15:40 CET 2020
vlc | branch: master | Steve Lhomme <robux4 at ycbcr.xyz> | Wed Jan 22 15:40:38 2020 +0100| [c5ccfafef43a915a4a4d0c04f73c4cadced790ac] | committer: Steve Lhomme
mmal: move the converter/resizer in its own file
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=c5ccfafef43a915a4a4d0c04f73c4cadced790ac
---
modules/hw/mmal/Makefile.am | 2 +-
modules/hw/mmal/codec.c | 919 -----------------------------------------
modules/hw/mmal/converter.c | 913 ++++++++++++++++++++++++++++++++++++++++
modules/hw/mmal/mmal_picture.c | 33 ++
modules/hw/mmal/mmal_picture.h | 10 +
5 files changed, 957 insertions(+), 920 deletions(-)
diff --git a/modules/hw/mmal/Makefile.am b/modules/hw/mmal/Makefile.am
index 5335ff33cf..7ed83e1bdc 100644
--- a/modules/hw/mmal/Makefile.am
+++ b/modules/hw/mmal/Makefile.am
@@ -11,7 +11,7 @@ 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 subpic.c subpic.h mmal_picture.c mmal_picture.h \
+libmmal_codec_plugin_la_SOURCES = codec.c converter.c subpic.c subpic.h mmal_picture.c mmal_picture.h \
mmal_cma.c mmal_cma.h
libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS)
libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
diff --git a/modules/hw/mmal/codec.c b/modules/hw/mmal/codec.c
index 15f53ef8d6..d03533d4cf 100644
--- a/modules/hw/mmal/codec.c
+++ b/modules/hw/mmal/codec.c
@@ -37,36 +37,22 @@
#include <interface/mmal/util/mmal_util.h>
#include <interface/mmal/util/mmal_default_components.h>
-#include "mmal_cma.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 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_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 Hardware Video Scaler. This may be faster but has no blend.")
static int OpenDecoder(vlc_object_t *);
static void CloseDecoder(vlc_object_t *);
-static int OpenConverter(vlc_object_t *);
-static void CloseConverter(vlc_object_t *);
vlc_module_begin()
set_category( CAT_INPUT )
@@ -197,39 +183,6 @@ static MMAL_FOURCC_T vlc_to_mmal_es_fourcc(const unsigned int fcc)
return 0;
}
-static MMAL_FOURCC_T pic_to_slice_mmal_fourcc(const MMAL_FOURCC_T fcc)
-{
- switch (fcc){
- case MMAL_ENCODING_I420:
- return MMAL_ENCODING_I420_SLICE;
- case MMAL_ENCODING_I422:
- return MMAL_ENCODING_I422_SLICE;
- case MMAL_ENCODING_ARGB:
- return MMAL_ENCODING_ARGB_SLICE;
- case MMAL_ENCODING_RGBA:
- return MMAL_ENCODING_RGBA_SLICE;
- case MMAL_ENCODING_ABGR:
- return MMAL_ENCODING_ABGR_SLICE;
- case MMAL_ENCODING_BGRA:
- return MMAL_ENCODING_BGRA_SLICE;
- case MMAL_ENCODING_RGB16:
- return MMAL_ENCODING_RGB16_SLICE;
- case MMAL_ENCODING_RGB24:
- return MMAL_ENCODING_RGB24_SLICE;
- case MMAL_ENCODING_RGB32:
- return MMAL_ENCODING_RGB32_SLICE;
- case MMAL_ENCODING_BGR16:
- return MMAL_ENCODING_BGR16_SLICE;
- case MMAL_ENCODING_BGR24:
- return MMAL_ENCODING_BGR24_SLICE;
- case MMAL_ENCODING_BGR32:
- return MMAL_ENCODING_BGR32_SLICE;
- default:
- break;
- }
- return 0;
-}
-
// Buffer either attached to pic or released
static picture_t * alloc_opaque_pic(decoder_t * const dec, MMAL_BUFFER_HEADER_T * const buf)
{
@@ -755,875 +708,3 @@ fail:
CloseDecoder(p_this);
return ret;
}
-
-// ----------------------------
-
-#define CONV_MAX_LATENCY 1 // In frames
-
-typedef struct pic_fifo_s {
- picture_t * head;
- picture_t * tail;
-} pic_fifo_t;
-
-static picture_t * pic_fifo_get(pic_fifo_t * const pf)
-{
- picture_t * const pic = pf->head;;
- if (pic != NULL) {
- pf->head = pic->p_next;
- pic->p_next = NULL;
- }
- return pic;
-}
-
-static void pic_fifo_release_all(pic_fifo_t * const pf)
-{
- picture_t * pic;
- while ((pic = pic_fifo_get(pf)) != NULL) {
- picture_Release(pic);
- }
-}
-
-static void pic_fifo_init(pic_fifo_t * const pf)
-{
- pf->head = NULL;
- pf->tail = NULL; // Not strictly needed
-}
-
-static void pic_fifo_put(pic_fifo_t * const pf, picture_t * pic)
-{
- pic->p_next = NULL;
- if (pf->head == NULL)
- pf->head = pic;
- else
- pf->tail->p_next = pic;
- pf->tail = pic;
-}
-
-#define SUBS_MAX 3
-
-typedef enum filter_resizer_e {
- FILTER_RESIZER_RESIZER,
- FILTER_RESIZER_ISP,
- FILTER_RESIZER_HVS,
-} filter_resizer_t;
-
-typedef struct conv_frame_stash_s
-{
- mtime_t pts;
- MMAL_BUFFER_HEADER_T * sub_bufs[SUBS_MAX];
-} conv_frame_stash_t;
-
-typedef struct
-{
- filter_resizer_t resizer_type;
- MMAL_COMPONENT_T *component;
- MMAL_PORT_T *input;
- MMAL_PORT_T *output;
- MMAL_POOL_T *out_pool; // Free output buffers
- MMAL_POOL_T *in_pool; // Input pool to get BH for replication
-
- cma_buf_pool_t * cma_in_pool;
- cma_buf_pool_t * cma_out_pool;
-
- subpic_reg_stash_t subs[SUBS_MAX];
-
- pic_fifo_t ret_pics;
-
- unsigned int pic_n;
- vlc_sem_t sem;
- vlc_mutex_t lock;
-
- MMAL_STATUS_T err_stream;
-
- bool needs_copy_in;
- bool is_sliced;
- bool out_fmt_set;
- const char * component_name;
- MMAL_PORT_BH_CB_T in_port_cb_fn;
- MMAL_PORT_BH_CB_T out_port_cb_fn;
-
- uint64_t frame_seq;
- conv_frame_stash_t stash[16];
-
- // Slice specific tracking stuff
- struct {
- pic_fifo_t pics;
- unsigned int line; // Lines filled
- } slice;
-
- vcsm_init_type_t vcsm_init_type;
-} converter_sys_t;
-
-
-static MMAL_STATUS_T pic_to_format(MMAL_ES_FORMAT_T * const es_fmt, const picture_t * const pic)
-{
- unsigned int bpp = (pic->format.i_bits_per_pixel + 7) >> 3;
- MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
-
- es_fmt->type = MMAL_ES_TYPE_VIDEO;
- es_fmt->encoding = vlc_to_mmal_video_fourcc(&pic->format);
- es_fmt->encoding_variant = 0;
-
- // Fill in crop etc.
- hw_mmal_vlc_fmt_to_mmal_fmt(es_fmt, &pic->format);
- // Override width / height with strides if appropriate
- if (bpp != 0) {
- v_fmt->width = pic->p[0].i_pitch / bpp;
- v_fmt->height = pic->p[0].i_lines;
- }
- return MMAL_SUCCESS;
-}
-
-
-static MMAL_STATUS_T conv_enable_in(filter_t * const p_filter, converter_sys_t * const sys)
-{
- MMAL_STATUS_T err = MMAL_SUCCESS;
-
- if (!sys->input->is_enabled &&
- (err = mmal_port_enable(sys->input, sys->in_port_cb_fn)) != MMAL_SUCCESS)
- {
- msg_Err(p_filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
- sys->input->name, err, mmal_status_to_string(err));
- }
- return err;
-}
-
-static MMAL_STATUS_T conv_enable_out(filter_t * const p_filter, converter_sys_t * const sys)
-{
- MMAL_STATUS_T err = MMAL_SUCCESS;
-
- {
- cma_buf_pool_deletez(&sys->cma_out_pool);
- }
-
- if (!sys->output->is_enabled &&
- (err = mmal_port_enable(sys->output, sys->out_port_cb_fn)) != MMAL_SUCCESS)
- {
- msg_Err(p_filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
- sys->output->name, err, mmal_status_to_string(err));
- }
- return err;
-}
-
-static void conv_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
-{
- filter_t * const p_filter = (filter_t *)port->userdata;
-
- if (buffer->cmd == MMAL_EVENT_ERROR) {
- MMAL_STATUS_T status = *(uint32_t *)buffer->data;
-
- converter_sys_t * sys = p_filter->p_sys;
- sys->err_stream = status;
-
- msg_Err(p_filter, "MMAL error %"PRIx32" \"%s\"", status,
- mmal_status_to_string(status));
- }
-
- mmal_buffer_header_release(buffer);
-}
-
-static void conv_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
-{
- VLC_UNUSED(port);
-
- mmal_buffer_header_release(buf);
-}
-
-static void conv_out_q_pic(converter_sys_t * const sys, picture_t * const pic)
-{
- pic->p_next = NULL;
-
- vlc_mutex_lock(&sys->lock);
- pic_fifo_put(&sys->ret_pics, pic);
- vlc_mutex_unlock(&sys->lock);
-
- vlc_sem_post(&sys->sem);
-}
-
-static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
-{
- filter_t * const p_filter = (filter_t *)port->userdata;
- converter_sys_t * const sys = p_filter->p_sys;
-
- if (buf->cmd == 0) {
- picture_t * const pic = (picture_t *)buf->user_data;
-
- if (pic == NULL) {
- msg_Err(p_filter, "Buffer has no attached picture");
- }
- else if (buf->data != NULL && buf->length != 0)
- {
- buf_to_pic_copy_props(pic, buf);
-
- buf->user_data = NULL; // Responsability for this pic no longer with buffer
- conv_out_q_pic(sys, pic);
- }
- }
-
- mmal_buffer_header_release(buf);
-}
-
-
-static void slice_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
-{
- filter_t * const p_filter = (filter_t *)port->userdata;
- converter_sys_t * const sys = p_filter->p_sys;
-
- if (buf->cmd != 0)
- {
- mmal_buffer_header_release(buf);
- return;
- }
-
- if (buf->data != NULL && buf->length != 0)
- {
- // Got slice
- picture_t *pic = sys->slice.pics.head;
- const unsigned int scale_lines = sys->output->format->es->video.height; // Expected lines of callback
-
- if (pic == NULL) {
- msg_Err(p_filter, "No output picture");
- goto fail;
- }
-
- // Copy lines
- // * single plane only - fix for I420
- {
- const unsigned int scale_n = __MIN(scale_lines - sys->slice.line, MMAL_SLICE_HEIGHT);
- const unsigned int pic_lines = pic->p[0].i_lines;
- const unsigned int copy_n = sys->slice.line + scale_n <= pic_lines ? scale_n :
- sys->slice.line >= pic_lines ? 0 :
- pic_lines - sys->slice.line;
-
- const unsigned int src_stride = buf->type->video.pitch[0];
- const unsigned int dst_stride = pic->p[0].i_pitch;
- uint8_t *dst = pic->p[0].p_pixels + sys->slice.line * dst_stride;
- const uint8_t *src = buf->data + buf->type->video.offset[0];
-
- if (src_stride == dst_stride) {
- if (copy_n != 0)
- memcpy(dst, src, src_stride * copy_n);
- }
- else {
- unsigned int i;
- for (i = 0; i != copy_n; ++i) {
- memcpy(dst, src, __MIN(dst_stride, src_stride));
- dst += dst_stride;
- src += src_stride;
- }
- }
- sys->slice.line += scale_n;
- }
-
- if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) != 0 || sys->slice.line >= scale_lines) {
-
- if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) == 0 || sys->slice.line != scale_lines) {
- // Stuff doesn't add up...
- msg_Err(p_filter, "Line count (%d/%d) & EOF disagree (flags=%#x)", sys->slice.line, scale_lines, buf->flags);
- goto fail;
- }
- else {
- sys->slice.line = 0;
-
- vlc_mutex_lock(&sys->lock);
- pic_fifo_get(&sys->slice.pics); // Remove head from Q
- vlc_mutex_unlock(&sys->lock);
-
- buf_to_pic_copy_props(pic, buf);
- conv_out_q_pic(sys, pic);
- }
- }
- }
-
- // Put back
- buf->user_data = NULL; // Zap here to make sure we can't reuse later
- mmal_buffer_header_reset(buf);
-
- if (mmal_port_send_buffer(sys->output, buf) != MMAL_SUCCESS) {
- mmal_buffer_header_release(buf);
- }
- return;
-
-fail:
- sys->err_stream = MMAL_EIO;
- vlc_sem_post(&sys->sem); // If we were waiting then break us out - the flush should fix sem values
-}
-
-
-static void conv_flush(filter_t * p_filter)
-{
- converter_sys_t * const sys = p_filter->p_sys;
- unsigned int i;
-
- if (sys->resizer_type == FILTER_RESIZER_HVS)
- {
- for (i = 0; i != SUBS_MAX; ++i) {
- hw_mmal_subpic_flush(sys->subs + i);
- }
- }
-
- if (sys->input != NULL && sys->input->is_enabled)
- mmal_port_disable(sys->input);
-
- if (sys->output != NULL && sys->output->is_enabled)
- mmal_port_disable(sys->output);
-
-// cma_buf_pool_deletez(&sys->cma_out_pool);
-
- // Free up anything we may have already lying around
- // Don't need lock as the above disables should have prevented anything
- // happening in the background
-
- for (i = 0; i != 16; ++i) {
- conv_frame_stash_t *const stash = sys->stash + i;
- unsigned int sub_no;
-
- stash->pts = MMAL_TIME_UNKNOWN;
- for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
- if (stash->sub_bufs[sub_no] != NULL) {
- mmal_buffer_header_release(stash->sub_bufs[sub_no]);
- stash->sub_bufs[sub_no] = NULL;
- }
- }
- }
-
- pic_fifo_release_all(&sys->slice.pics);
- pic_fifo_release_all(&sys->ret_pics);
-
- // Reset sem values - easiest & most reliable way is to just kill & re-init
- vlc_sem_destroy(&sys->sem);
- vlc_sem_init(&sys->sem, 0);
- sys->pic_n = 0;
-
- // Reset error status
- sys->err_stream = MMAL_SUCCESS;
-}
-
-static void conv_stash_fixup(filter_t * const p_filter, converter_sys_t * const sys, picture_t * const p_pic)
-{
- conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf);
- unsigned int sub_no;
- VLC_UNUSED(p_filter);
-
- p_pic->date = stash->pts;
- for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
- if (stash->sub_bufs[sub_no] != NULL) {
- // **** Do stashed blend
- // **** Aaargh, bother... need to rescale subs too
-
- mmal_buffer_header_release(stash->sub_bufs[sub_no]);
- stash->sub_bufs[sub_no] = NULL;
- }
- }
-}
-
-// Output buffers may contain a pic ref on error or flush
-// Free it
-static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
-{
- VLC_UNUSED(userdata);
-
- picture_t * const pic = header->user_data;
- header->user_data = NULL;
-
- if (pic != NULL)
- picture_Release(pic);
-
- return MMAL_FALSE;
-}
-
-static MMAL_STATUS_T conv_set_output(filter_t * const p_filter, converter_sys_t * const sys, picture_t * const pic)
-{
- MMAL_STATUS_T status;
-
- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
- sys->output->format->type = MMAL_ES_TYPE_VIDEO;
- sys->output->format->encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
- sys->output->format->encoding_variant = 0;
- hw_mmal_vlc_fmt_to_mmal_fmt(sys->output->format, &p_filter->fmt_out.video);
-
- if (pic != NULL)
- {
- // Override default format width/height if we have a pic we need to match
- if ((status = pic_to_format(sys->output->format, pic)) != MMAL_SUCCESS)
- {
- msg_Err(p_filter, "Bad format desc: %4.4s, pic=%p, bits=%d", (const char*)&pic->format.i_chroma, pic, pic->format.i_bits_per_pixel);
- return status;
- }
- }
-
- if (sys->is_sliced) {
- // Override height for slice
- sys->output->format->es->video.height = MMAL_SLICE_HEIGHT;
- }
-
- mmal_log_dump_format(sys->output->format);
-
- status = mmal_port_format_commit(sys->output);
- if (status != MMAL_SUCCESS) {
- msg_Err(p_filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
- sys->output->name, status, mmal_status_to_string(status));
- return status;
- }
-
- sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended);
- sys->output->buffer_size = sys->output->buffer_size_recommended;
-
- if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS)
- return status;
-
- return MMAL_SUCCESS;
-}
-
-static picture_t *conv_filter(filter_t *p_filter, picture_t *p_pic)
-{
- converter_sys_t * const sys = p_filter->p_sys;
- picture_t * ret_pics;
- MMAL_STATUS_T err;
- const uint64_t frame_seq = ++sys->frame_seq;
- conv_frame_stash_t * const stash = sys->stash + (frame_seq & 0xf);
- MMAL_BUFFER_HEADER_T * out_buf = NULL;
-
- if (sys->err_stream != MMAL_SUCCESS) {
- goto stream_fail;
- }
-
- // Check pic fmt corresponds to what we have set up
- // ??? ISP may require flush (disable) but actually seems quite happy
- // without
- if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
- {
- msg_Dbg(p_filter, "Reset input port format");
- mmal_port_format_commit(sys->input);
- }
-
- if (p_pic->context == NULL) {
- // Can't have stashed subpics if not one of our pics
- if (!sys->needs_copy_in)
- msg_Dbg(p_filter, "No context");
- }
- else if (sys->resizer_type == FILTER_RESIZER_HVS)
- {
- unsigned int sub_no = 0;
-
- for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
- int rv;
- if ((rv = hw_mmal_subpic_update(VLC_OBJECT(p_filter),
- hw_mmal_pic_sub_buf_get(p_pic, sub_no),
- sys->subs + sub_no,
- &p_pic->format,
- &sys->output->format->es->video.crop,
- frame_seq)) == 0)
- break;
- else if (rv < 0)
- goto fail;
- }
- }
- else
- {
- unsigned int sub_no = 0;
- for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
- if ((stash->sub_bufs[sub_no] = hw_mmal_pic_sub_buf_get(p_pic, sub_no)) != NULL) {
- mmal_buffer_header_acquire(stash->sub_bufs[sub_no]);
- }
- }
- }
-
- if (!sys->out_fmt_set) {
- sys->out_fmt_set = true;
-
- if (sys->is_sliced) {
- // If zc then we will do stride conversion when we copy to arm side
- // so no need to worry about actual pic dimensions here
- if ((err = conv_set_output(p_filter, sys, NULL)) != MMAL_SUCCESS)
- goto fail;
-
- sys->out_pool = mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size);
- }
- else {
- picture_t *pic = filter_NewPicture(p_filter);
- err = conv_set_output(p_filter, sys, pic);
- picture_Release(pic);
- if (err != MMAL_SUCCESS)
- goto fail;
-
- sys->out_pool = mmal_pool_create(sys->output->buffer_num, 0);
- }
-
- if (sys->out_pool == NULL) {
- msg_Err(p_filter, "Failed to create output pool");
- goto fail;
- }
- }
-
- // Reenable stuff if the last thing we did was flush
- if ((err = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS ||
- (err = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
- goto fail;
-
- // We attach pic to buf before stuffing the output port
- // We could attach the pic on output for cma, but it is a lot easier to keep
- // the code common.
- {
- picture_t * const out_pic = filter_NewPicture(p_filter);
-
- if (out_pic == NULL)
- {
- msg_Err(p_filter, "Failed to alloc required filter output pic");
- goto fail;
- }
-
- out_pic->format.i_sar_den = p_filter->fmt_out.video.i_sar_den;
- out_pic->format.i_sar_num = p_filter->fmt_out.video.i_sar_num;
-
- if (sys->is_sliced) {
- vlc_mutex_lock(&sys->lock);
- pic_fifo_put(&sys->slice.pics, out_pic);
- vlc_mutex_unlock(&sys->lock);
-
- // Poke any returned pic buffers into output
- // In general this should only happen immediately after enable
- while ((out_buf = mmal_queue_get(sys->out_pool->queue)) != NULL)
- mmal_port_send_buffer(sys->output, out_buf);
- }
- else
- {
- // 1 in - 1 out
- if ((out_buf = mmal_queue_wait(sys->out_pool->queue)) == NULL)
- {
- msg_Err(p_filter, "Failed to get output buffer");
- picture_Release(out_pic);
- goto fail;
- }
- mmal_buffer_header_reset(out_buf);
-
- // Attach out_pic to the buffer & ensure it is freed when the buffer is released
- // On a good send callback the pic will be extracted to avoid this
- out_buf->user_data = out_pic;
- mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, NULL);
-
- {
- out_buf->data = out_pic->p[0].p_pixels;
- out_buf->alloc_size = out_pic->p[0].i_pitch * out_pic->p[0].i_lines;
- //**** stride ????
- }
-
- if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
- {
- msg_Err(p_filter, "Send buffer to output failed");
- goto fail;
- }
- out_buf = NULL;
- }
- }
-
-
- // Stuff into input
- // We assume the BH is already set up with values reflecting pic date etc.
- stash->pts = p_pic->date;
- {
- MMAL_BUFFER_HEADER_T *const pic_buf = sys->needs_copy_in ?
- hw_mmal_pic_buf_copied(p_pic, sys->in_pool, sys->input, sys->cma_in_pool) :
- hw_mmal_pic_buf_replicated(p_pic, sys->in_pool);
-
- // Whether or not we extracted the pic_buf we are done with the picture
- picture_Release(p_pic);
- p_pic = NULL;
-
- if (pic_buf == NULL) {
- msg_Err(p_filter, "Pic has no attached buffer");
- goto fail;
- }
-
- pic_buf->pts = frame_seq;
-
- 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;
- }
- }
-
- // We have a 1 pic latency for everything except the 1st pic which we
- // wait for.
- // This means we get a single static pic out
- if (sys->pic_n++ == 1) {
- return NULL;
- }
- vlc_sem_wait(&sys->sem);
-
- // Return a single pending buffer
- vlc_mutex_lock(&sys->lock);
- ret_pics = pic_fifo_get(&sys->ret_pics);
- vlc_mutex_unlock(&sys->lock);
-
- if (sys->err_stream != MMAL_SUCCESS)
- goto stream_fail;
-
- conv_stash_fixup(p_filter, sys, ret_pics);
-
- return ret_pics;
-
-stream_fail:
- msg_Err(p_filter, "MMAL error reported by callback");
-fail:
- if (out_buf != NULL)
- mmal_buffer_header_release(out_buf);
- if (p_pic != NULL)
- picture_Release(p_pic);
- conv_flush(p_filter);
- return NULL;
-}
-
-static void CloseConverter(vlc_object_t * obj)
-{
- filter_t * const p_filter = (filter_t *)obj;
- converter_sys_t * const sys = p_filter->p_sys;
-
- if (sys == NULL)
- return;
-
- // Disables input & output ports
- conv_flush(p_filter);
-
- cma_buf_pool_deletez(&sys->cma_in_pool);
- cma_buf_pool_deletez(&sys->cma_out_pool);
-
- 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);
-
- if (sys->resizer_type == FILTER_RESIZER_HVS)
- {
- for (size_t i = 0; i != SUBS_MAX; ++i) {
- hw_mmal_subpic_close(sys->subs + i);
- }
- }
-
- if (sys->out_pool)
- {
- if (sys->is_sliced)
- mmal_port_pool_destroy(sys->output, sys->out_pool);
- else
- mmal_pool_destroy(sys->out_pool);
- }
-
- if (sys->in_pool != NULL)
- mmal_pool_destroy(sys->in_pool);
-
- if (sys->component)
- mmal_component_release(sys->component);
-
- cma_vcsm_exit(sys->vcsm_init_type);
-
- vlc_sem_destroy(&sys->sem);
- vlc_mutex_destroy(&sys->lock);
-
- p_filter->p_sys = NULL;
- free(sys);
-}
-
-
-static MMAL_FOURCC_T filter_enc_in(const video_format_t * const fmt)
-{
- if (hw_mmal_chroma_is_mmal(fmt->i_chroma) || fmt->i_chroma == VLC_CODEC_I420)
- return vlc_to_mmal_video_fourcc(fmt);
-
- if (fmt->i_chroma == VLC_CODEC_I420_10L)
- return MMAL_ENCODING_I420;
-
- return 0;
-}
-
-
-static int OpenConverter(vlc_object_t * obj)
-{
- filter_t * const p_filter = (filter_t *)obj;
- int ret = VLC_EGENERIC;
- converter_sys_t *sys;
- MMAL_STATUS_T status;
- if (p_filter->fmt_out.video.i_chroma == VLC_CODEC_I420)
- return VLC_EGENERIC;
- const MMAL_FOURCC_T enc_out = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
- const MMAL_FOURCC_T enc_in = filter_enc_in(&p_filter->fmt_in.video);
- bool use_resizer;
- bool use_isp;
-
- // At least in principle we should deal with any mmal format as input
- if (enc_in == 0 || enc_out == 0)
- return VLC_EGENERIC;
-
- use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME);
- use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME);
-
-retry:
- // ** Make more generic by checking supported encs
- //
- // Must use ISP - HVS can't do this, nor can resizer
- if (enc_in == MMAL_ENCODING_YUVUV64_10) {
- // If resizer selected then just give up
- if (use_resizer)
- return VLC_EGENERIC;
- // otherwise downgrade HVS to ISP
- use_isp = true;
- }
- // HVS can't do I420
- if (enc_out == MMAL_ENCODING_I420) {
- use_isp = true;
- }
- // Only HVS can deal with SAND30
- if (enc_in == MMAL_ENCODING_YUV10_COL) {
- if (use_isp || use_resizer)
- return VLC_EGENERIC;
- }
-
-
- if (use_resizer) {
- // use resizer overrides use_isp
- use_isp = false;
- }
-
- // Check we have a sliced version of the fourcc if we want the resizer
- if (use_resizer && pic_to_slice_mmal_fourcc(enc_out) == 0) {
- return VLC_EGENERIC;
- }
-
- sys = calloc(1, sizeof(converter_sys_t));
- if (!sys) {
- ret = VLC_ENOMEM;
- goto fail;
- }
- p_filter->p_sys = sys;
-
- // Init stuff the we destroy unconditionaly in Close first
- vlc_mutex_init(&sys->lock);
- vlc_sem_init(&sys->sem, 0);
- sys->err_stream = MMAL_SUCCESS;
- pic_fifo_init(&sys->ret_pics);
- pic_fifo_init(&sys->slice.pics);
-
- sys->needs_copy_in = !hw_mmal_chroma_is_mmal(p_filter->fmt_in.video.i_chroma);
- sys->in_port_cb_fn = conv_input_port_cb;
-
- if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
- msg_Err(p_filter, "VCSM init failed");
- goto fail;
- }
-
- if (use_resizer) {
- sys->resizer_type = FILTER_RESIZER_RESIZER;
- sys->is_sliced = true;
- sys->component_name = MMAL_COMPONENT_DEFAULT_RESIZER;
- sys->out_port_cb_fn = slice_output_port_cb;
- }
- else if (use_isp) {
- sys->resizer_type = FILTER_RESIZER_ISP;
- sys->is_sliced = false; // Copy directly into filter picture
- sys->component_name = MMAL_COMPONENT_ISP_RESIZER;
- sys->out_port_cb_fn = conv_output_port_cb;
- } else {
- sys->resizer_type = FILTER_RESIZER_HVS;
- sys->is_sliced = false; // Copy directly into filter picture
- sys->component_name = MMAL_COMPONENT_HVS;
- sys->out_port_cb_fn = conv_output_port_cb;
- }
-
- status = mmal_component_create(sys->component_name, &sys->component);
- if (status != MMAL_SUCCESS) {
- if (!use_isp && !use_resizer) {
- msg_Warn(p_filter, "Failed to rcreate HVS resizer - retrying with ISP");
- CloseConverter(obj);
- use_isp = true;
- goto retry;
- }
- msg_Err(p_filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
- MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
- goto fail;
- }
- sys->output = sys->component->output[0];
- sys->input = sys->component->input[0];
-
- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
- status = mmal_port_enable(sys->component->control, conv_control_port_cb);
- if (status != MMAL_SUCCESS) {
- msg_Err(p_filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
- sys->component->control->name, status, mmal_status_to_string(status));
- goto fail;
- }
-
- if (sys->needs_copy_in &&
- (sys->cma_in_pool = cma_buf_pool_new(2, 2, true, "conv-copy-in")) == NULL)
- {
- msg_Err(p_filter, "Failed to allocate input CMA pool");
- goto fail;
- }
-
- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
- sys->input->format->type = MMAL_ES_TYPE_VIDEO;
- sys->input->format->encoding = enc_in;
- sys->input->format->encoding_variant = MMAL_ENCODING_I420;
- hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &p_filter->fmt_in.video);
- port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, 1);
-
- mmal_log_dump_format(sys->input->format);
-
- status = mmal_port_format_commit(sys->input);
- if (status != MMAL_SUCCESS) {
- msg_Err(p_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 = NUM_DECODER_BUFFER_HEADERS;
-
- if ((status = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
- goto fail;
-
- port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->is_sliced);
-
- status = mmal_component_enable(sys->component);
- if (status != MMAL_SUCCESS) {
- msg_Err(p_filter, "Failed to enable component %s (status=%"PRIx32" %s)",
- sys->component->name, status, mmal_status_to_string(status));
- goto fail;
- }
-
- if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
- {
- msg_Err(p_filter, "Failed to create input pool");
- goto fail;
- }
-
- if (sys->resizer_type == FILTER_RESIZER_HVS)
- {
- unsigned int i;
- for (i = 0; i != SUBS_MAX; ++i) {
- if (hw_mmal_subpic_open(VLC_OBJECT(p_filter), sys->subs + i, sys->component->input[i + 1], -1, i + 1) != MMAL_SUCCESS)
- {
- msg_Err(p_filter, "Failed to open subpic %d", i);
- goto fail;
- }
- }
- }
-
- p_filter->pf_video_filter = conv_filter;
- p_filter->pf_flush = conv_flush;
- // video_drain NIF in filter structure
-
- return VLC_SUCCESS;
-
-fail:
- CloseConverter(obj);
-
- if (!use_resizer && status == MMAL_ENOMEM) {
- use_resizer = true;
- msg_Warn(p_filter, "Lack of memory to use HVS/ISP: trying resizer");
- goto retry;
- }
-
- return ret;
-}
-
diff --git a/modules/hw/mmal/converter.c b/modules/hw/mmal/converter.c
new file mode 100644
index 0000000000..5ba1d7fa9c
--- /dev/null
+++ b/modules/hw/mmal/converter.c
@@ -0,0 +1,913 @@
+/*****************************************************************************
+ * converter.c: MMAL-based resizer
+ *****************************************************************************
+ * Copyright © 2014 jusst technologies GmbH
+ *
+ * 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
+ * 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 <stdatomic.h>
+
+#include <vlc_common.h>
+#include <vlc_codec.h>
+#include <vlc_filter.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"
+
+#define MMAL_SLICE_HEIGHT 16
+
+#define CONV_MAX_LATENCY 1 // In frames
+
+typedef struct pic_fifo_s {
+ picture_t * head;
+ picture_t * tail;
+} pic_fifo_t;
+
+static picture_t * pic_fifo_get(pic_fifo_t * const pf)
+{
+ picture_t * const pic = pf->head;;
+ if (pic != NULL) {
+ pf->head = pic->p_next;
+ pic->p_next = NULL;
+ }
+ return pic;
+}
+
+static void pic_fifo_release_all(pic_fifo_t * const pf)
+{
+ picture_t * pic;
+ while ((pic = pic_fifo_get(pf)) != NULL) {
+ picture_Release(pic);
+ }
+}
+
+static void pic_fifo_init(pic_fifo_t * const pf)
+{
+ pf->head = NULL;
+ pf->tail = NULL; // Not strictly needed
+}
+
+static void pic_fifo_put(pic_fifo_t * const pf, picture_t * pic)
+{
+ pic->p_next = NULL;
+ if (pf->head == NULL)
+ pf->head = pic;
+ else
+ pf->tail->p_next = pic;
+ pf->tail = pic;
+}
+
+#define SUBS_MAX 3
+
+typedef enum filter_resizer_e {
+ FILTER_RESIZER_RESIZER,
+ FILTER_RESIZER_ISP,
+ FILTER_RESIZER_HVS,
+} filter_resizer_t;
+
+typedef struct conv_frame_stash_s
+{
+ mtime_t pts;
+ MMAL_BUFFER_HEADER_T * sub_bufs[SUBS_MAX];
+} conv_frame_stash_t;
+
+typedef struct
+{
+ filter_resizer_t resizer_type;
+ MMAL_COMPONENT_T *component;
+ MMAL_PORT_T *input;
+ MMAL_PORT_T *output;
+ MMAL_POOL_T *out_pool; // Free output buffers
+ MMAL_POOL_T *in_pool; // Input pool to get BH for replication
+
+ cma_buf_pool_t * cma_in_pool;
+ cma_buf_pool_t * cma_out_pool;
+
+ subpic_reg_stash_t subs[SUBS_MAX];
+
+ pic_fifo_t ret_pics;
+
+ unsigned int pic_n;
+ vlc_sem_t sem;
+ vlc_mutex_t lock;
+
+ MMAL_STATUS_T err_stream;
+
+ bool needs_copy_in;
+ bool is_sliced;
+ bool out_fmt_set;
+ const char * component_name;
+ MMAL_PORT_BH_CB_T in_port_cb_fn;
+ MMAL_PORT_BH_CB_T out_port_cb_fn;
+
+ uint64_t frame_seq;
+ conv_frame_stash_t stash[16];
+
+ // Slice specific tracking stuff
+ struct {
+ pic_fifo_t pics;
+ unsigned int line; // Lines filled
+ } slice;
+
+ vcsm_init_type_t vcsm_init_type;
+} converter_sys_t;
+
+
+static MMAL_STATUS_T pic_to_format(MMAL_ES_FORMAT_T * const es_fmt, const picture_t * const pic)
+{
+ unsigned int bpp = (pic->format.i_bits_per_pixel + 7) >> 3;
+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
+
+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
+ es_fmt->encoding = vlc_to_mmal_video_fourcc(&pic->format);
+ es_fmt->encoding_variant = 0;
+
+ // Fill in crop etc.
+ hw_mmal_vlc_fmt_to_mmal_fmt(es_fmt, &pic->format);
+ // Override width / height with strides if appropriate
+ if (bpp != 0) {
+ v_fmt->width = pic->p[0].i_pitch / bpp;
+ v_fmt->height = pic->p[0].i_lines;
+ }
+ return MMAL_SUCCESS;
+}
+
+
+static MMAL_STATUS_T conv_enable_in(filter_t * const p_filter, converter_sys_t * const sys)
+{
+ MMAL_STATUS_T err = MMAL_SUCCESS;
+
+ if (!sys->input->is_enabled &&
+ (err = mmal_port_enable(sys->input, sys->in_port_cb_fn)) != MMAL_SUCCESS)
+ {
+ msg_Err(p_filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
+ sys->input->name, err, mmal_status_to_string(err));
+ }
+ return err;
+}
+
+static MMAL_STATUS_T conv_enable_out(filter_t * const p_filter, converter_sys_t * const sys)
+{
+ MMAL_STATUS_T err = MMAL_SUCCESS;
+
+ {
+ cma_buf_pool_deletez(&sys->cma_out_pool);
+ }
+
+ if (!sys->output->is_enabled &&
+ (err = mmal_port_enable(sys->output, sys->out_port_cb_fn)) != MMAL_SUCCESS)
+ {
+ msg_Err(p_filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
+ sys->output->name, err, mmal_status_to_string(err));
+ }
+ return err;
+}
+
+static void conv_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ filter_t * const p_filter = (filter_t *)port->userdata;
+
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
+ MMAL_STATUS_T status = *(uint32_t *)buffer->data;
+
+ converter_sys_t * sys = p_filter->p_sys;
+ sys->err_stream = status;
+
+ msg_Err(p_filter, "MMAL error %"PRIx32" \"%s\"", status,
+ mmal_status_to_string(status));
+ }
+
+ mmal_buffer_header_release(buffer);
+}
+
+static void conv_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
+{
+ VLC_UNUSED(port);
+
+ mmal_buffer_header_release(buf);
+}
+
+static void conv_out_q_pic(converter_sys_t * const sys, picture_t * const pic)
+{
+ pic->p_next = NULL;
+
+ vlc_mutex_lock(&sys->lock);
+ pic_fifo_put(&sys->ret_pics, pic);
+ vlc_mutex_unlock(&sys->lock);
+
+ vlc_sem_post(&sys->sem);
+}
+
+static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
+{
+ filter_t * const p_filter = (filter_t *)port->userdata;
+ converter_sys_t * const sys = p_filter->p_sys;
+
+ if (buf->cmd == 0) {
+ picture_t * const pic = (picture_t *)buf->user_data;
+
+ if (pic == NULL) {
+ msg_Err(p_filter, "Buffer has no attached picture");
+ }
+ else if (buf->data != NULL && buf->length != 0)
+ {
+ buf_to_pic_copy_props(pic, buf);
+
+ buf->user_data = NULL; // Responsability for this pic no longer with buffer
+ conv_out_q_pic(sys, pic);
+ }
+ }
+
+ mmal_buffer_header_release(buf);
+}
+
+
+static void slice_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
+{
+ filter_t * const p_filter = (filter_t *)port->userdata;
+ converter_sys_t * const sys = p_filter->p_sys;
+
+ if (buf->cmd != 0)
+ {
+ mmal_buffer_header_release(buf);
+ return;
+ }
+
+ if (buf->data != NULL && buf->length != 0)
+ {
+ // Got slice
+ picture_t *pic = sys->slice.pics.head;
+ const unsigned int scale_lines = sys->output->format->es->video.height; // Expected lines of callback
+
+ if (pic == NULL) {
+ msg_Err(p_filter, "No output picture");
+ goto fail;
+ }
+
+ // Copy lines
+ // * single plane only - fix for I420
+ {
+ const unsigned int scale_n = __MIN(scale_lines - sys->slice.line, MMAL_SLICE_HEIGHT);
+ const unsigned int pic_lines = pic->p[0].i_lines;
+ const unsigned int copy_n = sys->slice.line + scale_n <= pic_lines ? scale_n :
+ sys->slice.line >= pic_lines ? 0 :
+ pic_lines - sys->slice.line;
+
+ const unsigned int src_stride = buf->type->video.pitch[0];
+ const unsigned int dst_stride = pic->p[0].i_pitch;
+ uint8_t *dst = pic->p[0].p_pixels + sys->slice.line * dst_stride;
+ const uint8_t *src = buf->data + buf->type->video.offset[0];
+
+ if (src_stride == dst_stride) {
+ if (copy_n != 0)
+ memcpy(dst, src, src_stride * copy_n);
+ }
+ else {
+ unsigned int i;
+ for (i = 0; i != copy_n; ++i) {
+ memcpy(dst, src, __MIN(dst_stride, src_stride));
+ dst += dst_stride;
+ src += src_stride;
+ }
+ }
+ sys->slice.line += scale_n;
+ }
+
+ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) != 0 || sys->slice.line >= scale_lines) {
+
+ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) == 0 || sys->slice.line != scale_lines) {
+ // Stuff doesn't add up...
+ msg_Err(p_filter, "Line count (%d/%d) & EOF disagree (flags=%#x)", sys->slice.line, scale_lines, buf->flags);
+ goto fail;
+ }
+ else {
+ sys->slice.line = 0;
+
+ vlc_mutex_lock(&sys->lock);
+ pic_fifo_get(&sys->slice.pics); // Remove head from Q
+ vlc_mutex_unlock(&sys->lock);
+
+ buf_to_pic_copy_props(pic, buf);
+ conv_out_q_pic(sys, pic);
+ }
+ }
+ }
+
+ // Put back
+ buf->user_data = NULL; // Zap here to make sure we can't reuse later
+ mmal_buffer_header_reset(buf);
+
+ if (mmal_port_send_buffer(sys->output, buf) != MMAL_SUCCESS) {
+ mmal_buffer_header_release(buf);
+ }
+ return;
+
+fail:
+ sys->err_stream = MMAL_EIO;
+ vlc_sem_post(&sys->sem); // If we were waiting then break us out - the flush should fix sem values
+}
+
+
+static void conv_flush(filter_t * p_filter)
+{
+ converter_sys_t * const sys = p_filter->p_sys;
+ unsigned int i;
+
+ if (sys->resizer_type == FILTER_RESIZER_HVS)
+ {
+ for (i = 0; i != SUBS_MAX; ++i) {
+ hw_mmal_subpic_flush(sys->subs + i);
+ }
+ }
+
+ if (sys->input != NULL && sys->input->is_enabled)
+ mmal_port_disable(sys->input);
+
+ if (sys->output != NULL && sys->output->is_enabled)
+ mmal_port_disable(sys->output);
+
+// cma_buf_pool_deletez(&sys->cma_out_pool);
+
+ // Free up anything we may have already lying around
+ // Don't need lock as the above disables should have prevented anything
+ // happening in the background
+
+ for (i = 0; i != ARRAY_SIZE(sys->stash); ++i) {
+ conv_frame_stash_t *const stash = sys->stash + i;
+ unsigned int sub_no;
+
+ stash->pts = MMAL_TIME_UNKNOWN;
+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
+ if (stash->sub_bufs[sub_no] != NULL) {
+ mmal_buffer_header_release(stash->sub_bufs[sub_no]);
+ stash->sub_bufs[sub_no] = NULL;
+ }
+ }
+ }
+
+ pic_fifo_release_all(&sys->slice.pics);
+ pic_fifo_release_all(&sys->ret_pics);
+
+ // Reset sem values - easiest & most reliable way is to just kill & re-init
+ vlc_sem_destroy(&sys->sem);
+ vlc_sem_init(&sys->sem, 0);
+ sys->pic_n = 0;
+
+ // Reset error status
+ sys->err_stream = MMAL_SUCCESS;
+}
+
+static void conv_stash_fixup(filter_t * const p_filter, converter_sys_t * const sys, picture_t * const p_pic)
+{
+ conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf);
+ unsigned int sub_no;
+ VLC_UNUSED(p_filter);
+
+ p_pic->date = stash->pts;
+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
+ if (stash->sub_bufs[sub_no] != NULL) {
+ // **** Do stashed blend
+ // **** Aaargh, bother... need to rescale subs too
+
+ mmal_buffer_header_release(stash->sub_bufs[sub_no]);
+ stash->sub_bufs[sub_no] = NULL;
+ }
+ }
+}
+
+// Output buffers may contain a pic ref on error or flush
+// Free it
+static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
+{
+ VLC_UNUSED(userdata);
+
+ picture_t * const pic = header->user_data;
+ header->user_data = NULL;
+
+ if (pic != NULL)
+ picture_Release(pic);
+
+ return MMAL_FALSE;
+}
+
+static MMAL_STATUS_T conv_set_output(filter_t * const p_filter, converter_sys_t * const sys, picture_t * const pic)
+{
+ MMAL_STATUS_T status;
+
+ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
+ sys->output->format->type = MMAL_ES_TYPE_VIDEO;
+ sys->output->format->encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
+ sys->output->format->encoding_variant = 0;
+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->output->format, &p_filter->fmt_out.video);
+
+ if (pic != NULL)
+ {
+ // Override default format width/height if we have a pic we need to match
+ if ((status = pic_to_format(sys->output->format, pic)) != MMAL_SUCCESS)
+ {
+ msg_Err(p_filter, "Bad format desc: %4.4s, pic=%p, bits=%d", (const char*)&pic->format.i_chroma, pic, pic->format.i_bits_per_pixel);
+ return status;
+ }
+ }
+
+ if (sys->is_sliced) {
+ // Override height for slice
+ sys->output->format->es->video.height = MMAL_SLICE_HEIGHT;
+ }
+
+ mmal_log_dump_format(sys->output->format);
+
+ status = mmal_port_format_commit(sys->output);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(p_filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
+ sys->output->name, status, mmal_status_to_string(status));
+ return status;
+ }
+
+ sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended);
+ sys->output->buffer_size = sys->output->buffer_size_recommended;
+
+ if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS)
+ return status;
+
+ return MMAL_SUCCESS;
+}
+
+static picture_t *conv_filter(filter_t *p_filter, picture_t *p_pic)
+{
+ converter_sys_t * const sys = p_filter->p_sys;
+ picture_t * ret_pics;
+ MMAL_STATUS_T err;
+ const uint64_t frame_seq = ++sys->frame_seq;
+ conv_frame_stash_t * const stash = sys->stash + (frame_seq & 0xf);
+ MMAL_BUFFER_HEADER_T * out_buf = NULL;
+
+ if (sys->err_stream != MMAL_SUCCESS) {
+ goto stream_fail;
+ }
+
+ // Check pic fmt corresponds to what we have set up
+ // ??? ISP may require flush (disable) but actually seems quite happy
+ // without
+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
+ {
+ msg_Dbg(p_filter, "Reset input port format");
+ mmal_port_format_commit(sys->input);
+ }
+
+ if (p_pic->context == NULL) {
+ // Can't have stashed subpics if not one of our pics
+ if (!sys->needs_copy_in)
+ msg_Dbg(p_filter, "No context");
+ }
+ else if (sys->resizer_type == FILTER_RESIZER_HVS)
+ {
+ unsigned int sub_no = 0;
+
+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
+ int rv;
+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(p_filter),
+ hw_mmal_pic_sub_buf_get(p_pic, sub_no),
+ sys->subs + sub_no,
+ &p_pic->format,
+ &sys->output->format->es->video.crop,
+ frame_seq)) == 0)
+ break;
+ else if (rv < 0)
+ goto fail;
+ }
+ }
+ else
+ {
+ unsigned int sub_no = 0;
+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
+ if ((stash->sub_bufs[sub_no] = hw_mmal_pic_sub_buf_get(p_pic, sub_no)) != NULL) {
+ mmal_buffer_header_acquire(stash->sub_bufs[sub_no]);
+ }
+ }
+ }
+
+ if (!sys->out_fmt_set) {
+ sys->out_fmt_set = true;
+
+ if (sys->is_sliced) {
+ // If zc then we will do stride conversion when we copy to arm side
+ // so no need to worry about actual pic dimensions here
+ if ((err = conv_set_output(p_filter, sys, NULL)) != MMAL_SUCCESS)
+ goto fail;
+
+ sys->out_pool = mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size);
+ }
+ else {
+ picture_t *pic = filter_NewPicture(p_filter);
+ err = conv_set_output(p_filter, sys, pic);
+ picture_Release(pic);
+ if (err != MMAL_SUCCESS)
+ goto fail;
+
+ sys->out_pool = mmal_pool_create(sys->output->buffer_num, 0);
+ }
+
+ if (sys->out_pool == NULL) {
+ msg_Err(p_filter, "Failed to create output pool");
+ goto fail;
+ }
+ }
+
+ // Reenable stuff if the last thing we did was flush
+ if ((err = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS ||
+ (err = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
+ goto fail;
+
+ // We attach pic to buf before stuffing the output port
+ // We could attach the pic on output for cma, but it is a lot easier to keep
+ // the code common.
+ {
+ picture_t * const out_pic = filter_NewPicture(p_filter);
+
+ if (out_pic == NULL)
+ {
+ msg_Err(p_filter, "Failed to alloc required filter output pic");
+ goto fail;
+ }
+
+ out_pic->format.i_sar_den = p_filter->fmt_out.video.i_sar_den;
+ out_pic->format.i_sar_num = p_filter->fmt_out.video.i_sar_num;
+
+ if (sys->is_sliced) {
+ vlc_mutex_lock(&sys->lock);
+ pic_fifo_put(&sys->slice.pics, out_pic);
+ vlc_mutex_unlock(&sys->lock);
+
+ // Poke any returned pic buffers into output
+ // In general this should only happen immediately after enable
+ while ((out_buf = mmal_queue_get(sys->out_pool->queue)) != NULL)
+ mmal_port_send_buffer(sys->output, out_buf);
+ }
+ else
+ {
+ // 1 in - 1 out
+ if ((out_buf = mmal_queue_wait(sys->out_pool->queue)) == NULL)
+ {
+ msg_Err(p_filter, "Failed to get output buffer");
+ picture_Release(out_pic);
+ goto fail;
+ }
+ mmal_buffer_header_reset(out_buf);
+
+ // Attach out_pic to the buffer & ensure it is freed when the buffer is released
+ // On a good send callback the pic will be extracted to avoid this
+ out_buf->user_data = out_pic;
+ mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, NULL);
+
+ {
+ out_buf->data = out_pic->p[0].p_pixels;
+ out_buf->alloc_size = out_pic->p[0].i_pitch * out_pic->p[0].i_lines;
+ //**** stride ????
+ }
+
+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
+ {
+ msg_Err(p_filter, "Send buffer to output failed");
+ goto fail;
+ }
+ out_buf = NULL;
+ }
+ }
+
+
+ // Stuff into input
+ // We assume the BH is already set up with values reflecting pic date etc.
+ stash->pts = p_pic->date;
+ {
+ MMAL_BUFFER_HEADER_T *const pic_buf = sys->needs_copy_in ?
+ hw_mmal_pic_buf_copied(p_pic, sys->in_pool, sys->input, sys->cma_in_pool) :
+ hw_mmal_pic_buf_replicated(p_pic, sys->in_pool);
+
+ // Whether or not we extracted the pic_buf we are done with the picture
+ picture_Release(p_pic);
+ p_pic = NULL;
+
+ if (pic_buf == NULL) {
+ msg_Err(p_filter, "Pic has no attached buffer");
+ goto fail;
+ }
+
+ pic_buf->pts = frame_seq;
+
+ 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;
+ }
+ }
+
+ // We have a 1 pic latency for everything except the 1st pic which we
+ // wait for.
+ // This means we get a single static pic out
+ if (sys->pic_n++ == 1) {
+ return NULL;
+ }
+ vlc_sem_wait(&sys->sem);
+
+ // Return a single pending buffer
+ vlc_mutex_lock(&sys->lock);
+ ret_pics = pic_fifo_get(&sys->ret_pics);
+ vlc_mutex_unlock(&sys->lock);
+
+ if (sys->err_stream != MMAL_SUCCESS)
+ goto stream_fail;
+
+ conv_stash_fixup(p_filter, sys, ret_pics);
+
+ return ret_pics;
+
+stream_fail:
+ msg_Err(p_filter, "MMAL error reported by callback");
+fail:
+ if (out_buf != NULL)
+ mmal_buffer_header_release(out_buf);
+ if (p_pic != NULL)
+ picture_Release(p_pic);
+ conv_flush(p_filter);
+ return NULL;
+}
+
+void CloseConverter(vlc_object_t * obj)
+{
+ filter_t * const p_filter = (filter_t *)obj;
+ converter_sys_t * const sys = p_filter->p_sys;
+
+ if (sys == NULL)
+ return;
+
+ // Disables input & output ports
+ conv_flush(p_filter);
+
+ cma_buf_pool_deletez(&sys->cma_in_pool);
+ cma_buf_pool_deletez(&sys->cma_out_pool);
+
+ 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);
+
+ if (sys->resizer_type == FILTER_RESIZER_HVS)
+ {
+ for (size_t i = 0; i != SUBS_MAX; ++i) {
+ hw_mmal_subpic_close(sys->subs + i);
+ }
+ }
+
+ if (sys->out_pool)
+ {
+ if (sys->is_sliced)
+ mmal_port_pool_destroy(sys->output, sys->out_pool);
+ else
+ mmal_pool_destroy(sys->out_pool);
+ }
+
+ if (sys->in_pool != NULL)
+ mmal_pool_destroy(sys->in_pool);
+
+ if (sys->component)
+ mmal_component_release(sys->component);
+
+ cma_vcsm_exit(sys->vcsm_init_type);
+
+ vlc_sem_destroy(&sys->sem);
+ vlc_mutex_destroy(&sys->lock);
+
+ p_filter->p_sys = NULL;
+ free(sys);
+}
+
+
+static MMAL_FOURCC_T filter_enc_in(const video_format_t * const fmt)
+{
+ if (hw_mmal_chroma_is_mmal(fmt->i_chroma) || fmt->i_chroma == VLC_CODEC_I420)
+ return vlc_to_mmal_video_fourcc(fmt);
+
+ if (fmt->i_chroma == VLC_CODEC_I420_10L)
+ return MMAL_ENCODING_I420;
+
+ return 0;
+}
+
+
+int OpenConverter(vlc_object_t * obj)
+{
+ filter_t * const p_filter = (filter_t *)obj;
+ int ret = VLC_EGENERIC;
+ converter_sys_t *sys;
+ MMAL_STATUS_T status;
+ if (p_filter->fmt_out.video.i_chroma == VLC_CODEC_I420)
+ return VLC_EGENERIC;
+ const MMAL_FOURCC_T enc_out = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
+ const MMAL_FOURCC_T enc_in = filter_enc_in(&p_filter->fmt_in.video);
+ bool use_resizer;
+ bool use_isp;
+
+ // At least in principle we should deal with any mmal format as input
+ if (enc_in == 0 || enc_out == 0)
+ return VLC_EGENERIC;
+
+ use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME);
+ use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME);
+
+retry:
+ // ** Make more generic by checking supported encs
+ //
+ // Must use ISP - HVS can't do this, nor can resizer
+ if (enc_in == MMAL_ENCODING_YUVUV64_10) {
+ // If resizer selected then just give up
+ if (use_resizer)
+ return VLC_EGENERIC;
+ // otherwise downgrade HVS to ISP
+ use_isp = true;
+ }
+ // HVS can't do I420
+ if (enc_out == MMAL_ENCODING_I420) {
+ use_isp = true;
+ }
+ // Only HVS can deal with SAND30
+ if (enc_in == MMAL_ENCODING_YUV10_COL) {
+ if (use_isp || use_resizer)
+ return VLC_EGENERIC;
+ }
+
+
+ if (use_resizer) {
+ // use resizer overrides use_isp
+ use_isp = false;
+ }
+
+ // Check we have a sliced version of the fourcc if we want the resizer
+ if (use_resizer && pic_to_slice_mmal_fourcc(enc_out) == 0) {
+ return VLC_EGENERIC;
+ }
+
+ sys = calloc(1, sizeof(converter_sys_t));
+ if (!sys) {
+ ret = VLC_ENOMEM;
+ goto fail;
+ }
+ p_filter->p_sys = sys;
+
+ // Init stuff the we destroy unconditionaly in Close first
+ vlc_mutex_init(&sys->lock);
+ vlc_sem_init(&sys->sem, 0);
+ sys->err_stream = MMAL_SUCCESS;
+ pic_fifo_init(&sys->ret_pics);
+ pic_fifo_init(&sys->slice.pics);
+
+ sys->needs_copy_in = !hw_mmal_chroma_is_mmal(p_filter->fmt_in.video.i_chroma);
+ sys->in_port_cb_fn = conv_input_port_cb;
+
+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
+ msg_Err(p_filter, "VCSM init failed");
+ goto fail;
+ }
+
+ if (use_resizer) {
+ sys->resizer_type = FILTER_RESIZER_RESIZER;
+ sys->is_sliced = true;
+ sys->component_name = MMAL_COMPONENT_DEFAULT_RESIZER;
+ sys->out_port_cb_fn = slice_output_port_cb;
+ }
+ else if (use_isp) {
+ sys->resizer_type = FILTER_RESIZER_ISP;
+ sys->is_sliced = false; // Copy directly into filter picture
+ sys->component_name = MMAL_COMPONENT_ISP_RESIZER;
+ sys->out_port_cb_fn = conv_output_port_cb;
+ } else {
+ sys->resizer_type = FILTER_RESIZER_HVS;
+ sys->is_sliced = false; // Copy directly into filter picture
+ sys->component_name = MMAL_COMPONENT_HVS;
+ sys->out_port_cb_fn = conv_output_port_cb;
+ }
+
+ status = mmal_component_create(sys->component_name, &sys->component);
+ if (status != MMAL_SUCCESS) {
+ if (!use_isp && !use_resizer) {
+ msg_Warn(p_filter, "Failed to rcreate HVS resizer - retrying with ISP");
+ CloseConverter(obj);
+ use_isp = true;
+ goto retry;
+ }
+ msg_Err(p_filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
+ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
+ goto fail;
+ }
+ sys->output = sys->component->output[0];
+ sys->input = sys->component->input[0];
+
+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
+ status = mmal_port_enable(sys->component->control, conv_control_port_cb);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(p_filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
+ sys->component->control->name, status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ if (sys->needs_copy_in &&
+ (sys->cma_in_pool = cma_buf_pool_new(2, 2, true, "conv-copy-in")) == NULL)
+ {
+ msg_Err(p_filter, "Failed to allocate input CMA pool");
+ goto fail;
+ }
+
+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
+ sys->input->format->type = MMAL_ES_TYPE_VIDEO;
+ sys->input->format->encoding = enc_in;
+ sys->input->format->encoding_variant = MMAL_ENCODING_I420;
+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &p_filter->fmt_in.video);
+ port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, 1);
+
+ mmal_log_dump_format(sys->input->format);
+
+ status = mmal_port_format_commit(sys->input);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(p_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 = NUM_DECODER_BUFFER_HEADERS;
+
+ if ((status = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
+ goto fail;
+
+ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->is_sliced);
+
+ status = mmal_component_enable(sys->component);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(p_filter, "Failed to enable component %s (status=%"PRIx32" %s)",
+ sys->component->name, status, mmal_status_to_string(status));
+ goto fail;
+ }
+
+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
+ {
+ msg_Err(p_filter, "Failed to create input pool");
+ goto fail;
+ }
+
+ if (sys->resizer_type == FILTER_RESIZER_HVS)
+ {
+ unsigned int i;
+ for (i = 0; i != SUBS_MAX; ++i) {
+ if (hw_mmal_subpic_open(VLC_OBJECT(p_filter), sys->subs + i, sys->component->input[i + 1], -1, i + 1) != MMAL_SUCCESS)
+ {
+ msg_Err(p_filter, "Failed to open subpic %d", i);
+ goto fail;
+ }
+ }
+ }
+
+ p_filter->pf_video_filter = conv_filter;
+ p_filter->pf_flush = conv_flush;
+ // video_drain NIF in filter structure
+
+ return VLC_SUCCESS;
+
+fail:
+ CloseConverter(obj);
+
+ if (!use_resizer && status == MMAL_ENOMEM) {
+ use_resizer = true;
+ msg_Warn(p_filter, "Lack of memory to use HVS/ISP: trying resizer");
+ goto retry;
+ }
+
+ return ret;
+}
+
diff --git a/modules/hw/mmal/mmal_picture.c b/modules/hw/mmal/mmal_picture.c
index 65b068876f..d477d5751e 100644
--- a/modules/hw/mmal/mmal_picture.c
+++ b/modules/hw/mmal/mmal_picture.c
@@ -1279,3 +1279,36 @@ const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode)
}
return "???";
}
+
+MMAL_FOURCC_T pic_to_slice_mmal_fourcc(MMAL_FOURCC_T fcc)
+{
+ switch (fcc){
+ case MMAL_ENCODING_I420:
+ return MMAL_ENCODING_I420_SLICE;
+ case MMAL_ENCODING_I422:
+ return MMAL_ENCODING_I422_SLICE;
+ case MMAL_ENCODING_ARGB:
+ return MMAL_ENCODING_ARGB_SLICE;
+ case MMAL_ENCODING_RGBA:
+ return MMAL_ENCODING_RGBA_SLICE;
+ case MMAL_ENCODING_ABGR:
+ return MMAL_ENCODING_ABGR_SLICE;
+ case MMAL_ENCODING_BGRA:
+ return MMAL_ENCODING_BGRA_SLICE;
+ case MMAL_ENCODING_RGB16:
+ return MMAL_ENCODING_RGB16_SLICE;
+ case MMAL_ENCODING_RGB24:
+ return MMAL_ENCODING_RGB24_SLICE;
+ case MMAL_ENCODING_RGB32:
+ return MMAL_ENCODING_RGB32_SLICE;
+ case MMAL_ENCODING_BGR16:
+ return MMAL_ENCODING_BGR16_SLICE;
+ case MMAL_ENCODING_BGR24:
+ return MMAL_ENCODING_BGR24_SLICE;
+ case MMAL_ENCODING_BGR32:
+ return MMAL_ENCODING_BGR32_SLICE;
+ default:
+ break;
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/modules/hw/mmal/mmal_picture.h b/modules/hw/mmal/mmal_picture.h
index 2f9fc9b6a9..75ad981886 100644
--- a/modules/hw/mmal/mmal_picture.h
+++ b/modules/hw/mmal/mmal_picture.h
@@ -145,6 +145,10 @@ void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc);
vzc_pool_ctl_t * hw_mmal_vzc_pool_new(void);
+#define NUM_DECODER_BUFFER_HEADERS 30
+
+MMAL_FOURCC_T pic_to_slice_mmal_fourcc(MMAL_FOURCC_T);
+
bool rpi_is_model_pi4(void);
typedef enum vcsm_init_type_e {
@@ -158,6 +162,12 @@ 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_RESIZE_NAME "mmal-resize"
+#define MMAL_ISP_NAME "mmal-isp"
+
+int OpenConverter(vlc_object_t *);
+void CloseConverter(vlc_object_t *);
+
#define MMAL_COMPONENT_DEFAULT_RESIZER "vc.ril.resize"
#define MMAL_COMPONENT_ISP_RESIZER "vc.ril.isp"
More information about the vlc-commits
mailing list