[vlc-commits] mmal: add a converter/resizer filter
John Cox
git at videolan.org
Mon Jan 20 08:25:36 CET 2020
vlc | branch: master | John Cox <jc at kynesim.co.uk> | Fri Jan 17 08:03:09 2020 +0100| [e281b67bf51e47b18dac9218d65dacd79b1e27d7] | committer: Steve Lhomme
mmal: add a converter/resizer filter
>From MMA opaque or I420 8bits/10bits to I420/RGB or MMA opaque
Signed-off-by: Steve Lhomme <robux4 at ycbcr.xyz>
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=e281b67bf51e47b18dac9218d65dacd79b1e27d7
---
modules/hw/mmal/codec.c | 854 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 850 insertions(+), 4 deletions(-)
diff --git a/modules/hw/mmal/codec.c b/modules/hw/mmal/codec.c
index c4de245943..bbb21e468e 100644
--- a/modules/hw/mmal/codec.c
+++ b/modules/hw/mmal/codec.c
@@ -31,11 +31,13 @@
#include <vlc_common.h>
#include <vlc_plugin.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_cma.h"
#include "mmal_picture.h"
#include "subpic.h"
@@ -61,12 +63,10 @@
#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 *);
+static int OpenConverter(vlc_object_t *);
+static void CloseConverter(vlc_object_t *);
vlc_module_begin()
set_category( CAT_INPUT )
@@ -77,6 +77,16 @@ vlc_module_begin()
add_shortcut("mmal_decoder")
add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
set_callbacks(OpenDecoder, CloseDecoder)
+
+ add_submodule()
+ set_category( CAT_VIDEO )
+ set_subcategory( SUBCAT_VIDEO_VFILTER )
+ set_shortname(N_("MMAL resizer"))
+ set_description(N_("MMAL resizing conversion filter"))
+ add_shortcut("mmal_converter")
+ set_capability( "video converter", 900 )
+ add_bool(MMAL_RESIZE_NAME, /* default */ false, MMAL_RESIZE_TEXT, MMAL_RESIZE_LONGTEXT, /* advanced option */ false)
+ set_callbacks(OpenConverter, CloseConverter)
vlc_module_end()
typedef struct
@@ -186,6 +196,39 @@ 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)
{
@@ -704,3 +747,806 @@ 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_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->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
+ {
+ 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->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;
+
+ // 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);
+
+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;
+ }
+ // Only HVS can deal with SAND30
+ if (enc_in == MMAL_ENCODING_YUV10_COL) {
+ if (use_resizer)
+ return VLC_EGENERIC;
+ }
+
+ // 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 {
+ 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;
+ }
+
+ status = mmal_component_create(sys->component_name, &sys->component);
+ if (status != MMAL_SUCCESS) {
+ 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;
+ }
+
+ 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;
+}
+
More information about the vlc-commits
mailing list