diff --git a/mmal_10.patch b/mmal_16.patch similarity index 75% rename from mmal_10.patch rename to mmal_16.patch index 6ed981e..b56b533 100644 --- a/mmal_10.patch +++ b/mmal_16.patch @@ -29,12 +29,13 @@ dnl evas plugin --- a/include/vlc_fourcc.h +++ b/include/vlc_fourcc.h -@@ -365,6 +365,10 @@ +@@ -365,6 +365,11 @@ /* Broadcom MMAL opaque buffer type */ #define VLC_CODEC_MMAL_OPAQUE VLC_FOURCC('M','M','A','L') +#define VLC_CODEC_MMAL_ZC_SAND8 VLC_FOURCC('Z','S','D','8') +#define VLC_CODEC_MMAL_ZC_SAND10 VLC_FOURCC('Z','S','D','0') ++#define VLC_CODEC_MMAL_ZC_SAND30 VLC_FOURCC('Z','S','D','3') +#define VLC_CODEC_MMAL_ZC_I420 VLC_FOURCC('Z','4','2','0') +#define VLC_CODEC_MMAL_ZC_RGB32 VLC_FOURCC('Z','R','G','B') @@ -52,14 +53,14 @@ +AM_LDFLAGS += -pthread -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 mmal_picture.c subpic.h mmal_picture.h ++libmmal_vout_plugin_la_SOURCES = vout.c subpic.c mmal_picture.c subpic.h mmal_cma.c mmal_picture.h mmal_piccpy_neon.S libmmal_vout_plugin_la_CFLAGS = $(AM_CFLAGS) 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 subpic.c mmal_picture.c blend_rgba_neon.S subpic.h mmal_picture.h\ ++libmmal_codec_plugin_la_SOURCES = codec.c subpic.c mmal_picture.c blend_rgba_neon.S subpic.h mmal_picture.h mmal_piccpy_neon.S\ + mmal_cma.c mmal_cma.h libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS) libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS) @@ -67,26 +68,26 @@ mmal_LTLIBRARIES += libmmal_codec_plugin.la -libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c -+libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c mmal_picture.h ++libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c mmal_cma.c mmal_picture.h mmal_piccpy_neon.S libmmal_deinterlace_plugin_la_CFLAGS = $(AM_CFLAGS) libmmal_deinterlace_plugin_la_LDFLAGS = $(AM_LDFLAGS) libmmal_deinterlace_plugin_la_LIBADD = $(LIBS_mmal) mmal_LTLIBRARIES += libmmal_deinterlace_plugin.la + -+libmmal_xsplitter_plugin_la_SOURCES = xsplitter.c mmal_picture.c mmal_picture.h ++libmmal_xsplitter_plugin_la_SOURCES = xsplitter.c mmal_picture.c mmal_cma.c mmal_picture.h mmal_piccpy_neon.S +libmmal_xsplitter_plugin_la_CFLAGS = $(AM_CFLAGS) +libmmal_xsplitter_plugin_la_LDFLAGS = $(AM_LDFLAGS) +libmmal_xsplitter_plugin_la_LIBADD = $(LIBS_mmal) +mmal_LTLIBRARIES += libmmal_xsplitter_plugin.la + -+libmmal_converter_plugin_la_SOURCES = converter_mmal.c mmal_cma.c mmal_picture.c mmal_cma.h mmal_picture.h ++libmmal_converter_plugin_la_SOURCES = converter_mmal.c mmal_cma.c mmal_picture.c mmal_cma.h mmal_picture.h mmal_piccpy_neon.S +libmmal_converter_plugin_la_CFLAGS = $(AM_CFLAGS) +libmmal_converter_plugin_la_LDFLAGS = $(AM_LDFLAGS) +libmmal_converter_plugin_la_LIBADD = $(LIBS_mmal) +mmal_LTLIBRARIES += libmmal_converter_plugin.la + +if HAVE_MMAL_AVCODEC -+libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_picture.c mmal_picture.h ++libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_cma.c mmal_picture.c mmal_picture.h mmal_cma.h mmal_piccpy_neon.S +libmmal_avcodec_plugin_la_CFLAGS = $(AM_CFLAGS) +libmmal_avcodec_plugin_la_LDFLAGS = $(AM_LDFLAGS) +libmmal_avcodec_plugin_la_LIBADD = $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(LIBS_mmal) @@ -499,7 +500,7 @@ + --- a/modules/hw/mmal/codec.c +++ b/modules/hw/mmal/codec.c -@@ -26,10 +26,12 @@ +@@ -26,267 +26,396 @@ #include "config.h" #endif @@ -512,8 +513,8 @@ +#include #include - #include -@@ -37,256 +39,383 @@ +-#include + #include #include #include @@ -526,22 +527,23 @@ +#include "blend_rgba_neon.h" + +#define TRACE_ALL 0 ++ ++#define OPT_TO_FROM_ZC 0 + /* * This seems to be a bit high, but reducing it causes instabilities */ --#define NUM_EXTRA_BUFFERS 5 -+#define NUM_EXTRA_BUFFERS 3 -+//#define NUM_EXTRA_BUFFERS 6 + #define NUM_EXTRA_BUFFERS 5 +//#define NUM_EXTRA_BUFFERS 10 #define NUM_DECODER_BUFFER_HEADERS 30 - #define MIN_NUM_BUFFERS_IN_TRANSIT 2 - +-#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.") @@ -884,7 +886,7 @@ - ret = VLC_EGENERIC; - goto out; - } -+ if ((pic->context = hw_mmal_gen_context(MMAL_ENCODING_OPAQUE, buf, dec_sys->ppr)) == NULL) ++ if ((pic->context = hw_mmal_gen_context(buf, dec_sys->ppr)) == NULL) + goto fail2; - sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0); @@ -1199,7 +1201,7 @@ - ret = VLC_EGENERIC; - goto err; - } - +- - if (!sys->opaque) - buffer->data = picture->p[0].p_pixels; - } else { @@ -1213,7 +1215,7 @@ - } - 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) { @@ -1243,11 +1245,6 @@ +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 (dec->fmt_in.i_codec == VLC_CODEC_H264 && + dec->fmt_in.i_extra > 0) + { @@ -1262,6 +1259,11 @@ + buf->data = dec->fmt_in.p_extra; + buf->flags = MMAL_BUFFER_HEADER_FLAG_CONFIG; +- 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); @@ -1277,10 +1279,10 @@ + } } - 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)", @@ -1428,7 +1430,7 @@ len = block->i_buffer; if (len > buffer->alloc_size) -@@ -590,89 +685,1549 @@ +@@ -590,89 +685,1733 @@ } buffer->flags = flags; @@ -1501,8 +1503,6 @@ + + vlc_mutex_destroy(&sys->pic_lock); + free(sys); -+ -+ bcm_host_deinit(); +} + +static int OpenDecoder(decoder_t *dec) @@ -1512,7 +1512,7 @@ MMAL_STATUS_T status; + const MMAL_FOURCC_T in_fcc = vlc_to_mmal_es_fourcc(dec->fmt_in.i_codec); + -+#if TRACE_ALL ++#if TRACE_ALL || 1 + { + char buf1[5], buf2[5], buf2a[5]; + char buf3[5], buf4[5]; @@ -1538,8 +1538,6 @@ + dec->p_sys = sys; + vlc_mutex_init(&sys->pic_lock); + -+ bcm_host_init(); -+ + if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { + msg_Err(dec, "VCSM init failed"); + goto fail; @@ -1702,25 +1700,23 @@ + MMAL_POOL_T *out_pool; // Free output buffers + MMAL_POOL_T *in_pool; // Input pool to get BH for replication + -+ cma_pool_fixed_t * cma_out_pool; ++ 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; + -+ bool b_top_field_first; -+ bool b_progressive; -+ + MMAL_STATUS_T err_stream; -+ int in_count; + ++ bool needs_copy_in; + bool is_cma; + bool is_sliced; + bool out_fmt_set; -+ bool latency_set; + const char * component_name; + MMAL_PORT_BH_CB_T in_port_cb_fn; + MMAL_PORT_BH_CB_T out_port_cb_fn; @@ -1734,6 +1730,7 @@ + unsigned int line; // Lines filled + } slice; + ++ vcsm_init_type_t vcsm_init_type; +} filter_sys_t; + + @@ -1747,7 +1744,7 @@ + es_fmt->encoding_variant = 0; + + // Fill in crop etc. -+ vlc_to_mmal_video_fmt(es_fmt, &pic->format); ++ 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; @@ -1777,7 +1774,7 @@ + if (sys->is_cma) + { + if (sys->cma_out_pool == NULL && -+ (sys->cma_out_pool = cma_buf_pool_new()) == NULL) ++ (sys->cma_out_pool = cma_buf_pool_new(CONVERTER_BUFFERS, CONVERTER_BUFFERS, true, "mmal_resizer")) == NULL) + { + msg_Err(p_filter, "Failed to alloc cma buf pool"); + return MMAL_ENOMEM; @@ -1859,10 +1856,7 @@ } -static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) -+static const uint8_t shift_00[] = {0,0,0,0}; -+static const uint8_t shift_01[] = {0,1,1,1}; -+ -+static int cma_pic_set_data(filter_t * const p_filter, picture_t * const pic, const MMAL_BUFFER_HEADER_T * const buf) ++static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) { - decoder_t *dec = (decoder_t *)port->userdata; - decoder_sys_t *sys = dec->p_sys; @@ -1890,58 +1884,11 @@ - vlc_sem_post(&sys->sem); - } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { - fmt = mmal_event_format_changed_get(buffer); -+ filter_sys_t *const sys = p_filter->p_sys; -+ const MMAL_VIDEO_FORMAT_T * const mm_fmt = &sys->output->format->es->video; -+ const MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T *const buf_vid = &buf->type->video; -+ -+ uint8_t * const data = cma_buf_pic_addr(pic); -+ if (data == NULL) { -+ return VLC_ENOMEM; -+ } -+ -+ const uint8_t * ws = shift_00; -+ const uint8_t * hs = shift_00; -+ int pb = 1; -+ -+ switch (p_filter->fmt_out.video.i_chroma) -+ { -+ case VLC_CODEC_MMAL_ZC_RGB32: -+ pb = 4; -+ break; ++ filter_t * const p_filter = (filter_t *)port->userdata; ++ filter_sys_t * const sys = p_filter->p_sys; - format = mmal_format_alloc(); - mmal_format_full_copy(format, fmt->format); -+ case VLC_CODEC_MMAL_ZC_I420: -+ case VLC_CODEC_MMAL_ZC_SAND8: -+ hs = shift_01; -+ break; - -- if (sys->opaque) -- format->encoding = MMAL_ENCODING_OPAQUE; -+ default: -+ msg_Err(p_filter, "%s: Unexpected format", __func__); -+ return VLC_EGENERIC; -+ } -+ -+ pic->i_planes = buf_vid->planes; -+ for (unsigned int i = 0; i != buf_vid->planes; ++i) { -+ pic->p[i] = (plane_t){ -+ .p_pixels = data + buf_vid->offset[i], -+ .i_lines = mm_fmt->height >> hs[i], -+ .i_pitch = buf_vid->pitch[i], -+ .i_pixel_pitch = pb, -+ .i_visible_lines = mm_fmt->crop.height >> hs[i], -+ .i_visible_pitch = mm_fmt->crop.width >> ws[i] -+ }; -+ } -+ return VLC_SUCCESS; -+} -+ -+static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) -+{ -+ filter_t * const p_filter = (filter_t *)port->userdata; -+ filter_sys_t * const sys = p_filter->p_sys; -+ +#if TRACE_ALL + msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld/%lld", __func__, + buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size, @@ -1949,7 +1896,9 @@ +#endif + if (buf->cmd == 0) { + picture_t * const pic = (picture_t *)buf->user_data; -+ + +- if (sys->opaque) +- format->encoding = MMAL_ENCODING_OPAQUE; + if (pic == NULL) { + msg_Err(p_filter, "%s: Buffer has no attached picture", __func__); + } @@ -1958,14 +1907,14 @@ +#if TRACE_ALL + msg_Dbg(p_filter, "%s: Buffer has no data", __func__); +#endif -+ picture_Release(pic); + } + else + { + buf_to_pic_copy_props(pic, buf); + ++ // Set pic data pointers from buf aux info now it has it + if (sys->is_cma) { -+ if (cma_pic_set_data(p_filter, pic, buf) != VLC_SUCCESS) ++ if (cma_pic_set_data(pic, sys->output->format, buf) != VLC_SUCCESS) + msg_Err(p_filter, "Failed to set data"); + } + @@ -1975,11 +1924,12 @@ + draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 32, 0, 32, 32, 0xff00ff00); + draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 64, 0, 32, 32, 0xff0000ff); +#endif ++ ++ buf->user_data = NULL; // Responsability for this pic no longer with buffer + conv_out_q_pic(sys, pic); + } + } + -+ buf->user_data = NULL; // Zap here to make sure we can't reuse later + mmal_buffer_header_release(buf); +} + @@ -2065,8 +2015,7 @@ + } + } + } - -- sys->output_format = format; ++ + // Put back + buf->user_data = NULL; // Zap here to make sure we can't reuse later + mmal_buffer_header_reset(buf); @@ -2104,7 +2053,7 @@ + if (sys->output != NULL && sys->output->is_enabled) + mmal_port_disable(sys->output); + -+ cma_buf_pool_deletez(&sys->cma_out_pool); ++// 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 @@ -2127,12 +2076,9 @@ + pic_fifo_release_all(&sys->ret_pics); + + // Reset sem values - easiest & most reliable way is to just kill & re-init -+ // This will also dig us out of situations where we have got out of sync somehow + vlc_sem_destroy(&sys->sem); -+ vlc_sem_init(&sys->sem, CONV_MAX_LATENCY); -+ -+ // No buffers in either port now -+ sys->in_count = 0; ++ vlc_sem_init(&sys->sem, 0); ++ sys->pic_n = 0; + + // Reset error status + sys->err_stream = MMAL_SUCCESS; @@ -2142,11 +2088,6 @@ +#endif +} + -+static void conv_flush_passthrough(filter_t * p_filter) -+{ -+ VLC_UNUSED(p_filter); -+} -+ +static void conv_stash_fixup(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const p_pic) +{ + conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf); @@ -2165,6 +2106,21 @@ + } +} + ++// 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, filter_sys_t * const sys, picture_t * const pic) +{ + MMAL_STATUS_T status; @@ -2173,11 +2129,11 @@ + 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; -+ vlc_to_mmal_video_fmt(sys->output->format, &p_filter->fmt_out.video); ++ hw_mmal_vlc_fmt_to_mmal_fmt(sys->output->format, &p_filter->fmt_out.video); + -+ // Override default format width/height if we have a pic we need to match + 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) + { + char cbuf[5]; @@ -2189,6 +2145,11 @@ + msg_Dbg(p_filter, "%s: %dx%d [(0,0) %dx%d]", __func__, fmt->width, fmt->height, fmt->crop.width, fmt->crop.height); + } + ++ 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); @@ -2200,8 +2161,7 @@ + + sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended); + sys->output->buffer_size = sys->output->buffer_size_recommended; - -- mmal_buffer_header_release(buffer); ++ + if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS) + return status; + @@ -2215,14 +2175,12 @@ + 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 TRACE_ALL -+ msg_Dbg(p_filter, "<<< %s", __func__); -+#endif -+#if 0 + { + char dbuf0[5], dbuf1[5]; -+ msg_Dbg(p_filter, "%s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] sar:%d/%d", __func__, ++ msg_Dbg(p_filter, "<<< %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] sar:%d/%d", __func__, + str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, + p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, + p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, @@ -2238,8 +2196,19 @@ + 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) { -+ msg_Dbg(p_filter, "%s: No context", __func__); ++ // Can't have stashed subpics if not one of our pics ++ if (!sys->needs_copy_in) ++ msg_Dbg(p_filter, "%s: No context", __func__); + } + else if (sys->resizer_type == FILTER_RESIZER_HVS) + { @@ -2276,6 +2245,8 @@ + // 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); @@ -2283,11 +2254,9 @@ + picture_Release(pic); + if (err != MMAL_SUCCESS) + goto fail; -+ } + -+ sys->out_pool = sys->is_sliced ? -+ mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size) : -+ mmal_pool_create(sys->output->buffer_num, 0); ++ 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"); @@ -2300,9 +2269,10 @@ + (err = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS) + goto fail; + -+ // If ZC then we need to allocate the out pic before we stuff the input -+ if (sys->is_sliced) { -+ MMAL_BUFFER_HEADER_T * out_buf; ++ // 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) @@ -2311,88 +2281,71 @@ + goto fail; + } + -+ 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); -+ -+ ++sys->in_count; -+ } -+ -+ // Stuff into input -+ // We assume the BH is already set up with values reflecting pic date etc. -+ { -+ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic); -+#if TRACE_ALL -+ msg_Dbg(p_filter, "In buf send: pic=%p, buf=%p, user=%p, pts=%lld/%lld", -+ p_pic, pic_buf, pic_buf->user_data, (long long)frame_seq, (long long)p_pic->date); -+#endif -+ if (pic_buf == NULL) { -+ msg_Err(p_filter, "Pic has no attached buffer"); -+ 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; + -+ stash->pts = p_pic->date; -+ if ((err = port_send_replicated(sys->input, sys->in_pool, pic_buf, frame_seq)) != MMAL_SUCCESS) -+ { -+ msg_Err(p_filter, "Send buffer to input failed"); -+ goto fail; ++ 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); + } -+ -+ picture_Release(p_pic); -+ p_pic = NULL; -+ --sys->in_count; -+ } -+ -+ if (!sys->is_sliced) { -+ MMAL_BUFFER_HEADER_T * out_buf; -+ -+ while ((out_buf = sys->in_count < 0 ? -+ mmal_queue_wait(sys->out_pool->queue) : mmal_queue_get(sys->out_pool->queue)) != NULL) ++ else + { -+ picture_t * const out_pic = filter_NewPicture(p_filter); -+ -+ if (out_pic == NULL) { -+ msg_Warn(p_filter, "Failed to alloc new filter output pic"); -+ mmal_buffer_header_release(out_buf); -+ break; ++ // 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); + +#if 0 -+ char dbuf0[5]; -+ msg_Dbg(p_filter, "out_pic %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", -+ str_fourcc(dbuf0, out_pic->format.i_chroma), -+ out_pic->format.i_width, out_pic->format.i_height, -+ out_pic->format.i_x_offset, out_pic->format.i_y_offset, -+ out_pic->format.i_visible_width, out_pic->format.i_visible_height, -+ out_pic->format.i_sar_num, out_pic->format.i_sar_den); ++ { ++ char dbuf0[5]; ++ msg_Dbg(p_filter, "out_pic %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", ++ str_fourcc(dbuf0, out_pic->format.i_chroma), ++ out_pic->format.i_width, out_pic->format.i_height, ++ out_pic->format.i_x_offset, out_pic->format.i_y_offset, ++ out_pic->format.i_visible_width, out_pic->format.i_visible_height, ++ out_pic->format.i_sar_num, out_pic->format.i_sar_den); ++ } +#endif + -+ mmal_buffer_header_reset(out_buf); -+ out_buf->user_data = out_pic; -+ + if (sys->is_cma) { + int rv; -+ if ((rv = cma_buf_pic_attach(sys->cma_out_pool, out_pic, sys->output->buffer_size)) != VLC_SUCCESS) -+ { ++ ++ cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size); ++ if (cb == NULL) { + char dbuf0[5]; -+ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d", ++ msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d", + str_fourcc(dbuf0, out_pic->format.i_chroma), -+ rv); ++ sys->output->buffer_size); + goto fail; + } -+ const unsigned int vc_h = cma_buf_pic_vc_handle(out_pic); -+ if (vc_h == 0) ++ const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable ++ out_buf->data = (uint8_t *)vc_h; ++ out_buf->alloc_size = sys->output->buffer_size; ++ ++ if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS) + { -+ msg_Err(p_filter, "Pic has no vc handle"); ++ char dbuf0[5]; ++ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d", ++ str_fourcc(dbuf0, out_pic->format.i_chroma), ++ rv); ++ cma_buf_unref(cb); + goto fail; + } -+ out_buf->data = (uint8_t *)vc_h; -+ out_buf->alloc_size = sys->output->buffer_size; + } + else { + out_buf->data = out_pic->p[0].p_pixels; @@ -2409,63 +2362,66 @@ + if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS) + { + msg_Err(p_filter, "Send buffer to output failed"); -+ mmal_buffer_header_release(out_buf); -+ break; ++ goto fail; + } -+ -+ ++sys->in_count; ++ out_buf = NULL; + } + } + -+ if (sys->in_count < 0) ++ ++ // Stuff into input ++ // We assume the BH is already set up with values reflecting pic date etc. ++ stash->pts = p_pic->date; + { -+ msg_Err(p_filter, "Buffer count somehow negative"); -+ goto fail; -+ } ++ 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); + -+ // Avoid being more than 1 pic behind -+ vlc_sem_wait(&sys->sem); ++ // Whether or not we extracted the pic_buf we are done with the picture ++ picture_Release(p_pic); ++ p_pic = NULL; + -+ // Set sem for delayed scale if not already set -+ if (!sys->latency_set) { -+ unsigned int i; -+ sys->latency_set = true; -+ for (i = 0; i != CONV_MAX_LATENCY; ++i) { -+ vlc_sem_post(&sys->sem); ++ if (pic_buf == NULL) { ++ msg_Err(p_filter, "Pic has no attached buffer"); ++ goto fail; ++ } ++ ++ pic_buf->pts = frame_seq; ++ ++#if TRACE_ALL ++ msg_Dbg(p_filter, "In buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d/%d, pts=%lld", ++ p_pic, pic_buf->data, pic_buf->user_data, pic_buf->flags, ++ pic_buf->length, pic_buf->alloc_size, sys->input->buffer_size, (long long)pic_buf->pts); ++#endif ++ ++ 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; + } + } + -+ // Return all pending buffers ++ // 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) { ++#if TRACE_ALL ++ msg_Dbg(p_filter, ">>> %s: Pic1=NULL", __func__); ++#endif ++ return NULL; ++ } ++ vlc_sem_wait(&sys->sem); ++ ++ // Return a single pending buffer + vlc_mutex_lock(&sys->lock); -+ ret_pics = pic_fifo_get_all(&sys->ret_pics); ++ ret_pics = pic_fifo_get(&sys->ret_pics); + vlc_mutex_unlock(&sys->lock); + + if (sys->err_stream != MMAL_SUCCESS) + goto stream_fail; + -+ // Sink as many sem posts as we have pics -+ // (shouldn't normally wait, but there is a small race) -+ if (ret_pics != NULL) -+ { -+ picture_t *next_pic = ret_pics->p_next; -+ -+ conv_stash_fixup(p_filter, sys, ret_pics); -+#if 0 -+ char dbuf0[5]; -+ -+ msg_Dbg(p_filter, "pic_out %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", -+ str_fourcc(dbuf0, ret_pics->format.i_chroma), -+ ret_pics->format.i_width, ret_pics->format.i_height, -+ ret_pics->format.i_x_offset, ret_pics->format.i_y_offset, -+ ret_pics->format.i_visible_width, ret_pics->format.i_visible_height, -+ ret_pics->format.i_sar_num, ret_pics->format.i_sar_den); -+#endif -+ while (next_pic != NULL) { -+ vlc_sem_wait(&sys->sem); -+ conv_stash_fixup(p_filter, sys, next_pic); -+ next_pic = next_pic->p_next; -+ } -+ } ++ conv_stash_fixup(p_filter, sys, ret_pics); + +#if TRACE_ALL + msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics); @@ -2476,21 +2432,18 @@ +stream_fail: + msg_Err(p_filter, "MMAL error reported by callback"); +fail: ++#if TRACE_ALL ++ msg_Err(p_filter, ">>> %s: FAIL", __func__); ++ picture_Release(ret_pics); ++#endif ++ 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 picture_t *conv_filter_passthrough(filter_t *p_filter, picture_t *p_pic) -+{ -+ VLC_UNUSED(p_filter); -+#if TRACE_ALL -+ msg_Dbg(p_filter, "<<< %s", __func__); -+#endif -+ return p_pic; -+} -+ +static void CloseConverter(vlc_object_t * obj) +{ + filter_t * const p_filter = (filter_t *)obj; @@ -2507,6 +2460,9 @@ + // 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); + @@ -2534,60 +2490,53 @@ + 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 int open_converter_passthrough(filter_t * const p_filter) ++static inline MMAL_FOURCC_T filter_enc_in(const video_format_t * const fmt) +{ -+ { -+ char dbuf0[5], dbuf1[5]; -+ msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d", __func__, -+ "passthrough", -+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), -+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, -+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, -+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, -+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den, -+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), -+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, -+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, -+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height, -+ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask, -+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den); -+ } ++ if (hw_mmal_chroma_is_mmal(fmt->i_chroma)) ++ return vlc_to_mmal_video_fourcc(fmt); + ++ if (fmt->i_chroma == VLC_CODEC_I420 || ++ fmt->i_chroma == VLC_CODEC_I420_10L) ++ return MMAL_ENCODING_I420; + -+ p_filter->pf_video_filter = conv_filter_passthrough; -+ p_filter->pf_flush = conv_flush_passthrough; -+ return VLC_SUCCESS; ++ return 0; ++} + +- sys->output_format = format; ++static inline MMAL_FOURCC_T filter_enc_out(const video_format_t * const fmt) ++{ ++ const MMAL_FOURCC_T mmes = vlc_to_mmal_video_fourcc(fmt); ++ // Can only copy out single plane stuff currently - this could be fixed! ++ return hw_mmal_chroma_is_mmal(fmt->i_chroma) || mmes != MMAL_ENCODING_I420 ? mmes : 0; +} + ++ +static int OpenConverter(vlc_object_t * obj) +{ + filter_t * const p_filter = (filter_t *)obj; + int ret = VLC_EGENERIC; + filter_sys_t *sys; + MMAL_STATUS_T status; -+ MMAL_FOURCC_T enc_out; -+ const MMAL_FOURCC_T enc_in = vlc_to_mmal_video_fourcc(&p_filter->fmt_in.video); ++ MMAL_FOURCC_T enc_out = filter_enc_out(&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; + int gpu_mem; + -+ if ((enc_in != MMAL_ENCODING_OPAQUE && -+ enc_in != MMAL_ENCODING_YUVUV128 && -+ enc_in != MMAL_ENCODING_YUVUV64_10) || -+ (enc_out = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video)) == 0) ++ // At least in principle we should deal with any mmal format as input ++ if (enc_in == 0 || enc_out == 0) + return VLC_EGENERIC; + -+ if (enc_in == enc_out) { -+ return open_converter_passthrough(p_filter); -+ } -+ + use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME); + use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME); + @@ -2606,6 +2555,11 @@ + 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) { @@ -2645,7 +2599,8 @@ + goto fail; + } + p_filter->p_sys = sys; -+ + +- mmal_buffer_header_release(buffer); + // Init stuff the we destroy unconditionaly in Close first + vlc_mutex_init(&sys->lock); + vlc_sem_init(&sys->sem, 0); @@ -2653,8 +2608,14 @@ + 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; @@ -2696,13 +2657,20 @@ + 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; -+ vlc_to_mmal_video_fmt(sys->input->format, &p_filter->fmt_in.video); ++ 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); @@ -2738,7 +2706,7 @@ + { + 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], i + 1) != MMAL_SUCCESS) ++ 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; @@ -2763,18 +2731,205 @@ + use_resizer = true; + msg_Warn(p_filter, "Lack of memory to use HVS/ISP: trying resizer"); + goto retry; -+ } + } + +#if TRACE_ALL + msg_Dbg(p_filter, ">>> %s: FAIL: %d", __func__, ret); +#endif + return ret; + } ++ ++#if OPT_TO_FROM_ZC ++//---------------------------------------------------------------------------- ++// ++// Simple copy in to ZC ++ ++typedef struct to_zc_sys_s { ++ vcsm_init_type_t vcsm_init_type; ++ cma_buf_pool_t * cma_out_pool; ++} to_zc_sys_t; ++ ++ ++static size_t buf_alloc_size(const vlc_fourcc_t i_chroma, const unsigned int width, const unsigned int height) ++{ ++ const unsigned int pels = width * height; ++ ++ switch (i_chroma) ++ { ++ case VLC_CODEC_MMAL_ZC_RGB32: ++ return pels * 4; ++ case VLC_CODEC_MMAL_ZC_I420: ++ return pels * 3 / 2; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++ ++static picture_t * ++to_zc_filter(filter_t *p_filter, picture_t *in_pic) ++{ ++ to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys; ++#if TRACE_ALL ++ msg_Dbg(p_filter, "<<< %s", __func__); ++#endif ++ ++ assert(p_filter->fmt_out.video.i_chroma == VLC_CODEC_MMAL_ZC_I420); ++ ++ picture_t * const out_pic = filter_NewPicture(p_filter); ++ if (out_pic == NULL) ++ goto fail0; ++ ++ MMAL_ES_SPECIFIC_FORMAT_T mm_vfmt = {.video={0}}; ++ MMAL_ES_FORMAT_T mm_esfmt = { ++ .encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video), ++ .es = &mm_vfmt}; ++ ++ hw_mmal_vlc_fmt_to_mmal_fmt(&mm_esfmt, &p_filter->fmt_out.video); ++ ++ const size_t buf_alloc = buf_alloc_size(p_filter->fmt_out.video.i_chroma, ++ mm_vfmt.video.width, mm_vfmt.video.height); ++ if (buf_alloc == 0) ++ goto fail1; ++ cma_buf_t *const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, buf_alloc); ++ if (cb == NULL) ++ goto fail1; ++ ++ if (cma_buf_pic_attach(cb, out_pic) != VLC_SUCCESS) ++ goto fail2; ++ cma_pic_set_data(out_pic, &mm_esfmt, NULL); ++ ++ hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), NULL, &mm_esfmt, in_pic); ++ ++ // Copy pic properties ++ out_pic->date = in_pic->date; ++ out_pic->b_force = in_pic->b_force; ++ out_pic->b_progressive = in_pic->b_progressive; ++ out_pic->b_top_field_first = in_pic->b_top_field_first; ++ out_pic->i_nb_fields = in_pic->i_nb_fields; ++ ++ picture_Release(in_pic); ++ ++ return out_pic; ++ ++fail2: ++ cma_buf_unref(cb); ++fail1: ++ picture_Release(out_pic); ++fail0: ++ picture_Release(in_pic); ++ return NULL; ++} ++ ++static void to_zc_flush(filter_t * p_filter) ++{ ++ VLC_UNUSED(p_filter); ++} ++ ++static void CloseConverterToZc(vlc_object_t * obj) ++{ ++ filter_t * const p_filter = (filter_t *)obj; ++ to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys; ++ ++ if (sys == NULL) ++ return; ++ ++ p_filter->p_sys = NULL; ++ ++ cma_buf_pool_deletez(&sys->cma_out_pool); ++ cma_vcsm_exit(sys->vcsm_init_type); ++ ++ free(sys); ++} ++ ++static bool to_zc_validate_fmt(const video_format_t * const f_in, const video_format_t * const f_out) ++{ ++ if (!((f_in->i_chroma == VLC_CODEC_I420 || f_in->i_chroma == VLC_CODEC_I420_10L) && ++ f_out->i_chroma == VLC_CODEC_MMAL_ZC_I420)) ++ { ++ return false; ++ } ++ if (f_in->i_height != f_out->i_height || ++ f_in->i_width != f_out->i_width) ++ { ++ return false; ++ } ++ ++ return true; ++} ++ ++static int OpenConverterToZc(vlc_object_t * obj) ++{ ++ int ret = VLC_EGENERIC; ++ filter_t * const p_filter = (filter_t *)obj; ++ ++ if (!to_zc_validate_fmt(&p_filter->fmt_in.video, &p_filter->fmt_out.video)) ++ goto fail; ++ ++ { ++ char dbuf0[5], dbuf1[5]; ++ msg_Dbg(p_filter, "%s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d", __func__, ++ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), ++ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, ++ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, ++ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, ++ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den, ++ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), ++ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, ++ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, ++ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height, ++ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask, ++ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den); ++ } ++ ++ to_zc_sys_t * const sys = calloc(1, sizeof(*sys)); ++ if (!sys) { ++ ret = VLC_ENOMEM; ++ goto fail; ++ } ++ p_filter->p_sys = (filter_sys_t *)sys; ++ ++ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { ++ msg_Err(p_filter, "VCSM init failed"); ++ goto fail; ++ } ++ ++ if ((sys->cma_out_pool = cma_buf_pool_new(5, 5, true, "conv-to-zc")) == NULL) ++ { ++ msg_Err(p_filter, "Failed to allocate input CMA pool"); ++ goto fail; ++ } ++ ++ p_filter->pf_video_filter = to_zc_filter; ++ p_filter->pf_flush = to_zc_flush; ++ return VLC_SUCCESS; ++ ++fail: ++ CloseConverterToZc(obj); ++ return ret; ++} ++ ++//---------------------------------------------------------------------------- ++// ++// Simple "copy" from ZC ++ ++static void CloseConverterFromZc(vlc_object_t * obj) ++{ ++ VLC_UNUSED(obj); +} + ++static int OpenConverterFromZc(vlc_object_t * obj) ++{ ++ return VLC_EGENERIC; ++} ++#endif ++//---------------------------------------------------------------------------- + +typedef struct blend_sys_s { + vzc_pool_ctl_t * vzc; + const picture_t * last_dst; // Not a ref, just a hint that we have a new pic ++ vcsm_init_type_t vcsm_init_type; +} blend_sys_t; + +static void FilterBlendMmal(filter_t *p_filter, @@ -2821,14 +2976,26 @@ + hw_mmal_vzc_pool_flush(sys->vzc); +} + ++static void CloseBlendMmal(vlc_object_t *object) ++{ ++ filter_t * const p_filter = (filter_t *)object; ++ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys; ++ ++ if (sys != NULL) { ++ p_filter->p_sys = NULL; ++ ++ hw_mmal_vzc_pool_release(sys->vzc); ++ cma_vcsm_exit(sys->vcsm_init_type); ++ free(sys); ++ } ++} ++ +static int OpenBlendMmal(vlc_object_t *object) +{ + filter_t * const p_filter = (filter_t *)object; + const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma; + -+ if ((vfcc_dst != VLC_CODEC_MMAL_OPAQUE && -+ vfcc_dst != VLC_CODEC_MMAL_ZC_SAND8 && -+ vfcc_dst != VLC_CODEC_MMAL_ZC_SAND10) || ++ if (!hw_mmal_chroma_is_mmal(vfcc_dst) || + !hw_mmal_vzc_subpic_fmt_valid(&p_filter->fmt_in.video)) + { + return VLC_EGENERIC; @@ -2850,28 +3017,27 @@ + blend_sys_t * const sys = calloc(1, sizeof (*sys)); + if (sys == NULL) + return VLC_ENOMEM; -+ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL) -+ { -+ free(sys); -+ return VLC_ENOMEM; -+ } ++ + p_filter->p_sys = (filter_sys_t *)sys; ++ ++ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { ++ msg_Err(p_filter, "VCSM init failed"); ++ goto fail; ++ } ++ ++ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL) ++ goto fail; + } + + p_filter->pf_video_blend = FilterBlendMmal; + p_filter->pf_flush = FlushBlendMmal; + + return VLC_SUCCESS; -+} + -+static void CloseBlendMmal(vlc_object_t *object) -+{ -+ filter_t * const p_filter = (filter_t *)object; -+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys; -+ -+ hw_mmal_vzc_pool_release(sys->vzc); -+ free(sys); - } ++fail: ++ CloseBlendMmal(VLC_OBJECT(p_filter)); ++ return VLC_ENOMEM; ++} + +// --------------------------------------------------------------------------- + @@ -3004,14 +3170,34 @@ + add_submodule() + set_category( CAT_VIDEO ) + set_subcategory( SUBCAT_VIDEO_VFILTER ) -+ set_shortname(N_("MMAL converterer")) -+ set_description(N_("MMAL conversion filter")) ++ 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) + add_bool(MMAL_ISP_NAME, /* default */ false, MMAL_ISP_TEXT, MMAL_ISP_LONGTEXT, /* advanced option */ false) + set_callbacks(OpenConverter, CloseConverter) + ++#if OPT_TO_FROM_ZC ++ add_submodule() ++ set_category( CAT_VIDEO ) ++ set_subcategory( SUBCAT_VIDEO_VFILTER ) ++ set_shortname(N_("MMAL to ZC")) ++ set_description(N_("MMAL conversion to ZC filter")) ++ add_shortcut("mmal_to_zc") ++ set_capability( "video converter", 901 ) ++ set_callbacks(OpenConverterToZc, CloseConverterToZc) ++ ++ add_submodule() ++ set_category( CAT_VIDEO ) ++ set_subcategory( SUBCAT_VIDEO_VFILTER ) ++ set_shortname(N_("MMAL from ZC")) ++ set_description(N_("MMAL conversion from ZC filter")) ++ add_shortcut("mmal_from_zc") ++ set_capability( "video converter", 902 ) ++ set_callbacks(OpenConverterFromZc, CloseConverterFromZc) ++#endif ++ + add_submodule() + set_category( CAT_VIDEO ) + set_subcategory( SUBCAT_VIDEO_VFILTER ) @@ -3033,7 +3219,7 @@ + --- /dev/null +++ b/modules/hw/mmal/converter_mmal.c -@@ -0,0 +1,448 @@ +@@ -0,0 +1,479 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif @@ -3062,28 +3248,13 @@ + +#include + -+#define OPT_SAND 0 -+#define OPT_I420 1 -+#define OPT_RGB32 0 -+ -+ -+#if OPT_SAND -+#define FMT_IN VLC_CODEC_MMAL_ZC_SAND8 -+#elif OPT_I420 -+#define FMT_IN VLC_CODEC_MMAL_ZC_I420 -+#elif OPT_RGB32 -+#define FMT_IN VLC_CODEC_MMAL_ZC_RGB32 -+#elif -+#error Missing input format -+#endif -+ +#define TRACE_ALL 0 + +typedef struct mmal_gl_converter_s +{ + EGLint drm_fourcc; + vcsm_init_type_t vcsm_init_type; -+ struct cma_pic_context_s * last_ctx_ref; ++ cma_buf_t * last_cb; + + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; +} mmal_gl_converter_t; @@ -3100,7 +3271,7 @@ + return MMAL_FOURCC('Y','V','1','2'); + case MMAL_ENCODING_I422: + return MMAL_FOURCC('Y','U','1','6'); -+ case MMAL_ENCODING_YUVUV128: ++// case MMAL_ENCODING_YUVUV128: // Doesn't actually work yet + case MMAL_ENCODING_NV12: + return MMAL_FOURCC('N','V','1','2'); + case MMAL_ENCODING_NV21: @@ -3133,7 +3304,6 @@ +static void tex_context_delete(tex_context_t * const tex) +{ + tex->DeleteTextures(1, &tex->texture); -+ + free(tex); +} + @@ -3147,11 +3317,10 @@ + return pic_ctx; +} + -+static tex_context_t * get_tex_context(const opengl_tex_converter_t * const tc, picture_t * const pic) ++static tex_context_t * get_tex_context(const opengl_tex_converter_t * const tc, picture_t * const pic, cma_buf_t * const cb) +{ + mmal_gl_converter_t * const sys = tc->priv; -+ -+ tex_context_t * tex = (tex_context_t *)cma_buf_pic_context2(pic); ++ tex_context_t * tex = (tex_context_t *)cma_buf_context2(cb); + if (tex != NULL) + return tex; + @@ -3170,12 +3339,12 @@ + { + EGLint attribs[30]; + EGLint * a = attribs; -+ const int fd = cma_buf_pic_fd(pic); -+ uint8_t * base_addr = cma_buf_pic_addr(pic); ++ const int fd = cma_buf_fd(cb); ++ uint8_t * base_addr = cma_buf_addr(cb); + + if (pic->i_planes >= 4 || pic->i_planes <= 0) + { -+ msg_Err(tc, "%s: Bad planes", __func__); ++ msg_Err(tc, "%s: Bad planes: %d", __func__, pic->i_planes); + goto fail; + } + @@ -3268,7 +3437,7 @@ + tc->gl->egl.destroyImageKHR(tc->gl, image); + } + -+ if (cma_buf_pic_add_context2(pic, &tex->cmn) != VLC_SUCCESS) ++ if (cma_buf_add_context2(cb, &tex->cmn) != VLC_SUCCESS) + { + msg_Err(tc, "%s: add_context2 failed", __func__); + goto fail; @@ -3288,7 +3457,12 @@ +{ + mmal_gl_converter_t * const sys = tc->priv; +#if TRACE_ALL -+ msg_Err(tc, "%s: %d*%dx%d : %d*%dx%d", __func__, tc->tex_count, tex_width[0], tex_height[0], pic->i_planes, pic->p[0].i_pitch, pic->p[0].i_lines); ++ { ++ char cbuf[5]; ++ msg_Dbg(tc, "%s: %s %d*%dx%d : %d*%dx%d", __func__, ++ str_fourcc(cbuf, pic->format.i_chroma), ++ tc->tex_count, tex_width[0], tex_height[0], pic->i_planes, pic->p[0].i_pitch, pic->p[0].i_lines); ++ } +#endif + VLC_UNUSED(tex_width); + VLC_UNUSED(tex_height); @@ -3301,14 +3475,21 @@ + return VLC_EGENERIC; + } + -+ tex_context_t * const tex = get_tex_context(tc, pic); ++ cma_buf_t * const cb = cma_buf_pic_get(pic); ++ if (cb == NULL) ++ { ++ msg_Err(tc, "Pic missing cma buf"); ++ return VLC_EGENERIC; ++ } ++ ++ tex_context_t * const tex = get_tex_context(tc, pic, cb); + if (tex == NULL) + return VLC_EGENERIC; + +// tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture); + -+ cma_buf_pic_context_unref(sys->last_ctx_ref); // ?? Needed ?? -+ sys->last_ctx_ref = cma_buf_pic_context_ref(pic); ++ cma_buf_unref(sys->last_cb); ++ sys->last_cb = cma_buf_ref(cb); + + textures[0] = tex->texture; + return VLC_SUCCESS; @@ -3375,23 +3556,55 @@ + if (sys == NULL) + return; + -+ cma_buf_pic_context_unref(sys->last_ctx_ref); ++ cma_buf_unref(sys->last_cb); + cma_vcsm_exit(sys->vcsm_init_type); + free(sys); +} + ++ ++// Pick a chroma that we can convert to ++// Prefer I420 as smallest ++static vlc_fourcc_t chroma_in_out(const vlc_fourcc_t chroma_in) ++{ ++ switch (chroma_in) ++ { ++ case VLC_CODEC_MMAL_OPAQUE: ++ case VLC_CODEC_MMAL_ZC_I420: ++ case VLC_CODEC_MMAL_ZC_SAND8: ++ case VLC_CODEC_MMAL_ZC_SAND10: // ISP only ++ return VLC_CODEC_MMAL_ZC_I420; ++ case VLC_CODEC_MMAL_ZC_SAND30: // HVS only ++ case VLC_CODEC_MMAL_ZC_RGB32: ++ return VLC_CODEC_MMAL_ZC_RGB32; // HVS can't generate YUV of any sort ++ default: ++ break; ++ } ++ return 0; ++} ++ ++ +static int +OpenGLConverter(vlc_object_t *obj) +{ + opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj; + int rv = VLC_EGENERIC; + const EGLint eglfmt = vlc_to_gl_fourcc(&tc->fmt); ++ const vlc_fourcc_t chroma_out = chroma_in_out(tc->fmt.i_chroma); + -+ // Accept Opaque (as it can definitely be converted) or what we actually want -+ if (!(tc->fmt.i_chroma == FMT_IN || -+ tc->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE)) -+ { ++ // Do we know what to do with this? ++ if (chroma_out == 0) + return rv; ++ ++ { ++ char dbuf0[5], dbuf1[5], dbuf2[5]; ++ msg_Dbg(tc, "<<< %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__, ++ str_fourcc(dbuf0, tc->fmt.i_chroma), ++ str_fourcc(dbuf1, eglfmt), ++ tc->fmt.i_width, tc->fmt.i_height, ++ tc->fmt.i_x_offset, tc->fmt.i_y_offset, ++ tc->fmt.i_visible_width, tc->fmt.i_visible_height, ++ tc->fmt.i_sar_num, tc->fmt.i_sar_den, ++ str_fourcc(dbuf2, chroma_out)); + } + + if (tc->gl->ext != VLC_GL_EXT_EGL || @@ -3402,17 +3615,6 @@ + return rv; + } + -+ { -+ char dbuf0[5], dbuf1[5]; -+ msg_Dbg(tc, ">>> %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__, -+ str_fourcc(dbuf0, tc->fmt.i_chroma), -+ str_fourcc(dbuf1, eglfmt), -+ tc->fmt.i_width, tc->fmt.i_height, -+ tc->fmt.i_x_offset, tc->fmt.i_y_offset, -+ tc->fmt.i_visible_width, tc->fmt.i_visible_height, -+ tc->fmt.i_sar_num, tc->fmt.i_sar_den); -+ } -+ + if ((tc->priv = calloc(1, sizeof(mmal_gl_converter_t))) == NULL) + { + msg_Err(tc, "priv alloc failure"); @@ -3444,7 +3646,7 @@ + + if (eglfmt == 0) + { -+ tc->fmt.i_chroma = FMT_IN; ++ tc->fmt.i_chroma = chroma_out; + tc->fmt.i_bits_per_pixel = 8; + if (tc->fmt.i_chroma == VLC_CODEC_MMAL_ZC_RGB32) + { @@ -3465,6 +3667,21 @@ + + tc->handle_texs_gen = true; // We manage the texs + tc->pf_update = tc_mmal_update; ++ ++#if TRACE_ALL ++ { ++ char dbuf0[5], dbuf1[5], dbuf2[5]; ++ msg_Dbg(tc, ">>> %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__, ++ str_fourcc(dbuf0, tc->fmt.i_chroma), ++ str_fourcc(dbuf1, sys->drm_fourcc), ++ tc->fmt.i_width, tc->fmt.i_height, ++ tc->fmt.i_x_offset, tc->fmt.i_y_offset, ++ tc->fmt.i_visible_width, tc->fmt.i_visible_height, ++ tc->fmt.i_sar_num, tc->fmt.i_sar_den, ++ str_fourcc(dbuf2, chroma_out)); ++ } ++#endif ++ + return VLC_SUCCESS; + +fail: @@ -3499,23 +3716,28 @@ #include "mmal_picture.h" -@@ -41,466 +42,569 @@ - - #define MIN_NUM_BUFFERS_IN_TRANSIT 2 +@@ -39,468 +40,814 @@ + #include + #include --#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 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.") --static int Open(filter_t *filter); --static void Close(filter_t *filter); +-#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_ADV "mmal-deinterlace-adv" +#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace") +#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace") +-static int Open(filter_t *filter); +-static void Close(filter_t *filter); ++#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") + -vlc_module_begin() - set_shortname(N_("MMAL deinterlace")) - set_description(N_("MMAL-based deinterlace filter plugin")) @@ -3527,10 +3749,6 @@ - add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT, - MMAL_DEINTERLACE_QPU_LONGTEXT, true); -vlc_module_end() -+#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. "\ @@ -3552,19 +3770,28 @@ MMAL_PORT_T *input; MMAL_PORT_T *output; + MMAL_POOL_T *in_pool; -+ hw_mmal_port_pool_ref_t *out_ppr; - -- MMAL_QUEUE_T *filtered_pictures; -- vlc_sem_t sem; ++ + MMAL_QUEUE_T * out_q; - -- atomic_bool started; -+ bool half_rate; -+ bool use_qpu; ++ ++ // Bind this lot somehow into ppr???? ++ bool is_cma; ++ cma_buf_pool_t * cma_out_pool; ++ MMAL_POOL_T * out_pool; ++ ++ hw_mmal_port_pool_ref_t *out_ppr; ++ ++ bool half_rate; ++ bool use_qpu; + bool use_fast; + bool use_passthrough; -+ unsigned int seq_in; -+ unsigned int seq_out; ++ 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] + +- MMAL_QUEUE_T *filtered_pictures; +- vlc_sem_t sem; ++ vcsm_init_type_t vcsm_init_type; + +- atomic_bool started; +} filter_sys_t; - /* statistics */ @@ -3581,12 +3808,19 @@ #define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx" -static int Open(filter_t *filter) --{ ++#define TRACE_ALL 0 ++ ++ ++ ++// 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) + { - int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ? - (int64_t)1000000 * 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); -+#define TRACE_ALL 0 ++ filter_sys_t *const filter_sys = p_filter->p_sys; ++ picture_t * const pic = filter_NewPicture(p_filter); - MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = { - { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) }, @@ -3594,40 +3828,45 @@ - 4, - { 3, frame_duration, 0, use_qpu } - }; ++ if (pic == NULL) ++ goto fail1; - int ret = VLC_SUCCESS; - MMAL_STATUS_T status; - filter_sys_t *sys; ++ if (buf->length == 0) { ++ msg_Err(p_filter, "%s: Empty buffer", __func__); ++ goto fail2; ++ } - msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!", - frame_duration, use_qpu ? "used" : "unused"); -+// 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_sys_t *const filter_sys = p_filter->p_sys; -+ picture_t * const pic = filter_NewPicture(p_filter); ++ if ((pic->context = hw_mmal_gen_context(buf, filter_sys->out_ppr)) == NULL) ++ goto fail2; - if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE) - return VLC_EGENERIC; -+ if (pic == NULL) -+ goto fail1; ++ buf_to_pic_copy_props(pic, buf); - if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE) - return VLC_EGENERIC; -+ if (buf->length == 0) { -+ msg_Err(p_filter, "%s: Empty buffer", __func__); -+ goto fail2; -+ } ++#if TRACE_ALL ++ msg_Dbg(p_filter, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date); ++#endif - sys = calloc(1, sizeof(filter_sys_t)); - if (!sys) - return VLC_ENOMEM; - filter->p_sys = sys; -+ if ((pic->context = hw_mmal_gen_context(MMAL_ENCODING_OPAQUE, buf, filter_sys->out_ppr)) == NULL) -+ goto fail2; ++ return pic; - bcm_host_init(); -+ buf_to_pic_copy_props(pic, buf); ++fail2: ++ picture_Release(pic); ++fail1: ++// mmal_buffer_header_release(buf); ++ return NULL; ++} - status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component); - if (status != MMAL_SUCCESS) { @@ -3636,8 +3875,16 @@ - ret = VLC_EGENERIC; - goto out; - } ++static void di_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) ++{ +#if TRACE_ALL -+ msg_Dbg(p_filter, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date); ++ pic_ctx_mmal_t * ctx = buffer->user_data; ++// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys; ++ ++ msg_Dbg((filter_t *)port->userdata, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buffer->cmd, ctx, buffer, ++ buffer->flags, (long long)buffer->pts); ++#else ++ VLC_UNUSED(port); +#endif - status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr); @@ -3647,7 +3894,7 @@ - ret = VLC_EGENERIC; - goto out; - } -+ return pic; ++ mmal_buffer_header_release(buffer); - sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter; - status = mmal_port_enable(sys->component->control, control_port_cb); @@ -3656,13 +3903,29 @@ - sys->component->control->name, status, mmal_status_to_string(status)); - ret = VLC_EGENERIC; - goto out; -- } -+fail2: -+ picture_Release(pic); -+fail1: -+// mmal_buffer_header_release(buf); -+ return NULL; ++#if TRACE_ALL ++ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__); ++#endif +} ++ ++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; ++ ++#if TRACE_ALL ++ msg_Dbg(p_filter, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts); ++#endif ++ mmal_queue_put(sys->out_q, buf); ++#if TRACE_ALL ++ msg_Dbg(p_filter, ">>> %s: out Q len=%d", __func__, mmal_queue_length(sys->out_q)); ++#endif ++ return; + } - sys->input = sys->component->input[0]; - sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter; @@ -3676,21 +3939,12 @@ - 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; -+static void di_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) -+{ -+#if TRACE_ALL -+ pic_ctx_mmal_t * ctx = buffer->user_data; -+// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys; -+ -+ msg_Dbg((filter_t *)port->userdata, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buffer->cmd, ctx, buffer, -+ buffer->flags, (long long)buffer->pts); -+#else -+ VLC_UNUSED(port); -+#endif ++ mmal_buffer_header_reset(buf); // User data stays intact so release will kill pic ++ mmal_buffer_header_release(buf); ++} - es_format_Copy(&filter->fmt_out, &filter->fmt_in); - filter->fmt_out.video.i_frame_rate *= 2; -+ mmal_buffer_header_release(buffer); - status = mmal_port_format_commit(sys->input); - if (status != MMAL_SUCCESS) { @@ -3698,36 +3952,9 @@ - sys->input->name, status, mmal_status_to_string(status)); - ret = VLC_EGENERIC; - goto out; -+#if TRACE_ALL -+ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__); -+#endif -+} -+ -+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; -+ -+#if TRACE_ALL -+ msg_Dbg(p_filter, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts); -+#endif -+ mmal_queue_put(sys->out_q, buf); -+#if TRACE_ALL -+ msg_Dbg(p_filter, ">>> %s: out Q len=%d", __func__, mmal_queue_length(sys->out_q)); -+#endif - } +- } - sys->input->buffer_size = sys->input->buffer_size_recommended; - sys->input->buffer_num = sys->input->buffer_num_recommended; -+ else -+ { -+ mmal_buffer_header_reset(buf); -+ mmal_buffer_header_release(buf); -+ } -+} - if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) { - MMAL_PARAMETER_BOOLEAN_T zero_copy = { @@ -3763,18 +3990,18 @@ - ret = VLC_EGENERIC; - goto out; - } -+static inline unsigned int seq_inc(unsigned int x) ++// 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) +{ -+ return x + 1 >= 16 ? 1 : x + 1; -+} ++ VLC_UNUSED(userdata); - 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); -+static inline unsigned int seq_delta(unsigned int sseq, unsigned int fseq) -+{ -+ return fseq == 0 ? 0 : fseq <= sseq ? sseq - fseq : 15 - (fseq - sseq); -+} ++ cma_buf_t * const cb = header->user_data; ++ header->user_data = NULL; ++ cma_buf_unref(cb); // Copes fine with NULL - status = mmal_port_format_commit(sys->output); - if (status != MMAL_SUCCESS) { @@ -3782,16 +4009,106 @@ - sys->input->name, status, mmal_status_to_string(status)); - ret = VLC_EGENERIC; - goto out; -- } ++ return MMAL_FALSE; ++} ++ ++static inline unsigned int seq_inc(unsigned int x) ++{ ++ return x + 1 >= 16 ? 1 : x + 1; ++} ++ ++static inline unsigned int seq_delta(unsigned int sseq, unsigned int fseq) ++{ ++ return fseq == 0 ? 0 : fseq <= sseq ? sseq - fseq : 15 - (fseq - sseq); ++} ++ +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 TRACE_ALL ++ msg_Dbg(p_filter, "<<< %s", __func__); ++#endif ++ ++ 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 0 ++ if (sys->output->is_enabled) ++ mmal_port_disable(sys->output); ++ ++ mmal_format_full_copy(sys->output->format, sys->input->format); ++ mmal_port_format_commit(sys->output); ++ sys->output->buffer_num = 30; ++ sys->output->buffer_size = sys->input->buffer_size_recommended; ++ mmal_port_enable(sys->output, di_output_port_cb); ++#endif ++ 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); ++ } ++ ++ // 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; ++ } ++ ++ if (!sys->is_cma) ++ { ++ // 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; ++ } + } ++ else ++ { ++ // We are expecting one in - one out so simply wedge a new bufer ++ // into the output port. Flow control will happen on cma alloc. ++ ++ if ((out_buf = mmal_queue_get(sys->out_pool->queue)) == NULL) ++ { ++ // Should never happen ++ msg_Err(p_filter, "Failed to get output buffer"); ++ goto fail; ++ } ++ mmal_buffer_header_reset(out_buf); - sys->output->buffer_num = 3; ++ // Attach cma_buf 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 ++ mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, p_filter); ++ ++ cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size); ++ if ((out_buf->user_data = cb) == NULL) // Check & attach cb to buf ++ { ++ char dbuf0[5]; ++ msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d", ++ str_fourcc(dbuf0, p_pic->format.i_chroma), ++ sys->output->buffer_size); ++ goto fail; ++ } ++ const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable ++ out_buf->data = (uint8_t *)vc_h; ++ out_buf->alloc_size = sys->output->buffer_size; ++ +#if TRACE_ALL -+ msg_Dbg(p_filter, "<<< %s", __func__); ++ msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld", ++ p_pic, out_buf->data, out_buf->user_data, out_buf->flags, ++ out_buf->length, out_buf->alloc_size, (long long)out_buf->pts); +#endif - if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) { @@ -3804,42 +4121,28 @@ - msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)", - status, mmal_status_to_string(status)); - 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 enable failed"); -+ goto fail; -+ } -+ -+ // 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; -+ } -+ -+ // Stuff into input -+ // We assume the BH is already set up with values reflecting pic date etc. -+ { -+ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic); -+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->in_pool->queue); -+ -+ if ((err = mmal_buffer_header_replicate(buf, pic_buf)) != MMAL_SUCCESS) ++ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS) + { -+ msg_Err(p_filter, "Failed to replicate input buffer: %d", err); ++ msg_Err(p_filter, "Send buffer to output failed"); + goto fail; } ++ out_buf = NULL; ++ } - MMAL_PARAMETER_BOOLEAN_T zero_copy = { - { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, - 1 - }; -+#if TRACE_ALL -+ msg_Dbg(p_filter, "In buf send: pic=%p, buf=%p/%p, ctx=%p, flags=%#x, len=%d/%d, pts=%lld", -+ p_pic, pic_buf, buf, pic_buf->user_data, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts); -+#endif ++ // 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); ++ ++ if (pic_buf == NULL) ++ { ++ msg_Err(p_filter, "Pic has not attached buffer"); ++ goto fail; ++ } - status = mmal_port_parameter_set(sys->output, &zero_copy.hdr); - if (status != MMAL_SUCCESS) { @@ -3850,12 +4153,13 @@ + + // Add a sequence to the flags so we can track what we have actually + // deinterlaced -+ buf->flags = (buf->flags & ~(0xfU * MMAL_BUFFER_HEADER_FLAG_USER0)) | (sys->seq_in * (MMAL_BUFFER_HEADER_FLAG_USER0)); ++ 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, buf)) != MMAL_SUCCESS) ++ 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; } } @@ -3866,135 +4170,177 @@ - sys->output->name, status, mmal_status_to_string(status)); - ret = VLC_EGENERIC; - goto out; -- } + // Return anything that is in the out Q + { -+ MMAL_BUFFER_HEADER_T * out_buf; + 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. -+ while ((out_buf = (seq_delta(sys->seq_in, sys->seq_out) > 3 ? mmal_queue_wait(sys->out_q) : mmal_queue_get(sys->out_q))) != NULL) ++ // 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) + { -+ picture_t * const out_pic = di_alloc_opaque(p_filter, out_buf); + const unsigned int seq_out = (out_buf->flags / MMAL_BUFFER_HEADER_FLAG_USER0) & 0xf; ++ int rv; + -+ if (out_pic == NULL) { -+ msg_Warn(p_filter, "Failed to alloc new filter output pic"); -+ mmal_queue_put_back(sys->out_q, out_buf); -+ break; -+ } - -- 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; -- } -+#if TRACE_ALL -+ msg_Dbg(p_filter, "-- %s: Q pic=%p: seq_in=%d, seq_out=%d, delta=%d", __func__, out_pic, sys->seq_in, seq_out, seq_delta(sys->seq_in, seq_out)); -+#endif - -- sys->filtered_pictures = mmal_queue_create(); -+ *pp_pic = out_pic; -+ pp_pic = &out_pic->p_next; - -- filter->pf_video_filter = deinterlace; -- filter->pf_flush = flush; -+ // Ignore 0 seqs ++ picture_t * out_pic; ++ ++ if (sys->is_cma) ++ { ++ // Alloc pic ++ if ((out_pic = filter_NewPicture(p_filter)) == NULL) ++ { ++ // Can't alloc pic - just stop extraction ++ mmal_queue_put_back(sys->out_q, out_buf); ++ out_buf = NULL; ++ msg_Warn(p_filter, "Failed to alloc new filter output pic"); ++ break; ++ } ++ ++ // Extract cma_buf from buf & attach to pic ++ cma_buf_t * const cb = (cma_buf_t *)out_buf->user_data; ++ if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS) ++ { ++ char dbuf0[5]; ++ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d", ++ str_fourcc(dbuf0, out_pic->format.i_chroma), ++ rv); ++ // cb still attached to buffer and will be freed with it ++ goto fail; ++ } ++ out_buf->user_data = NULL; ++ ++ buf_to_pic_copy_props(out_pic, out_buf); ++ ++ // Set pic data pointers from buf aux info now it has it ++ if ((rv = cma_pic_set_data(out_pic, sys->output->format, out_buf)) != VLC_SUCCESS) ++ { ++ char dbuf0[5]; ++ msg_Err(p_filter, "Failed to set data: fmt=%s, rv=%d", ++ str_fourcc(dbuf0, sys->output->format->encoding), ++ rv); ++ } ++ ++ out_buf->user_data = NULL; // Responsability for this pic no longer with buffer ++ mmal_buffer_header_release(out_buf); ++ } ++ else ++ { ++ 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 ++ ++#if TRACE_ALL ++ msg_Dbg(p_filter, "-- %s: Q pic=%p: seq_in=%d, seq_out=%d, delta=%d", __func__, out_pic, sys->seq_in, seq_out, seq_delta(sys->seq_in, seq_out)); ++#endif ++ ++ *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); + } -- vlc_sem_init(&sys->sem, 0); +- 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; +#if TRACE_ALL + msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics); +#endif - --out: -- if (ret != VLC_SUCCESS) -- Close(filter); ++ + return ret_pics; - -- return ret; ++ +fail: ++ if (out_buf != NULL) ++ mmal_buffer_header_release(out_buf); + picture_Release(p_pic); + return NULL; - } - --static void Close(filter_t *filter) ++} ++ +static void di_flush(filter_t *p_filter) - { -- filter_sys_t *sys = filter->p_sys; -- MMAL_BUFFER_HEADER_T *buffer; ++{ + filter_sys_t * const sys = p_filter->p_sys; - -- if (!sys) -- return; -- -- if (sys->component && sys->component->control->is_enabled) -- mmal_port_disable(sys->component->control); ++ +#if TRACE_ALL + msg_Dbg(p_filter, "<<< %s", __func__); +#endif - -- if (sys->input && sys->input->is_enabled) ++ + if (sys->input != NULL && sys->input->is_enabled) - mmal_port_disable(sys->input); - -- if (sys->output && sys->output->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 (sys->component && sys->component->is_enabled) -- mmal_component_disable(sys->component); -+ // If that dumped anything real into the out_q then have another go -+ if (mmal_queue_length(sys->out_q) != 0) ++ if (sys->is_cma) ++ { ++ MMAL_BUFFER_HEADER_T * buf; ++ mmal_port_disable(sys->output); ++ while ((buf = mmal_queue_get(sys->out_q)) != NULL) ++ mmal_buffer_header_release(buf); ++ } ++ else + { -+ mmal_port_enable(sys->output, di_output_port_cb); ++ // 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); -+ // Out q should now be empty & should remain so until the input is reenabled ++ ++ // 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); - -- while ((buffer = mmal_queue_get(sys->filtered_pictures))) { -- picture_t *pic = (picture_t *)buffer->user_data; -- picture_Release(pic); ++ + // 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 } -- if (sys->filtered_pictures) -- mmal_queue_destroy(sys->filtered_pictures); +- sys->filtered_pictures = mmal_queue_create(); + sys->seq_in = 1; -+ sys->seq_out = 1; ++ sys->seq_out = 15; -- if (sys->component) -- mmal_component_release(sys->component); +- filter->pf_video_filter = deinterlace; +- filter->pf_flush = flush; +#if TRACE_ALL + msg_Dbg(p_filter, ">>> %s", __func__); +#endif +} -- vlc_sem_destroy(&sys->sem); -- free(sys); +- vlc_sem_init(&sys->sem, 0); -- bcm_host_deinit(); +-out: +- if (ret != VLC_SUCCESS) +- Close(filter); +static void pass_flush(filter_t *p_filter) +{ + // Nothing to do + VLC_UNUSED(p_filter); +} -+ + +- return ret; +static picture_t * pass_deinterlace(filter_t * p_filter, picture_t * p_pic) +{ + VLC_UNUSED(p_filter); @@ -4003,34 +4349,22 @@ + return p_pic; } --static int send_output_buffer(filter_t *filter) +-static void Close(filter_t *filter) + +static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) { - filter_sys_t *sys = filter->p_sys; - MMAL_BUFFER_HEADER_T *buffer; + filter_t *filter = (filter_t *)port->userdata; - MMAL_STATUS_T status; -- picture_t *picture; -- int ret = 0; ++ MMAL_STATUS_T status; -- if (!sys->output->is_enabled) { -- ret = VLC_EGENERIC; -- goto out; +- if (!sys) + if (buffer->cmd == MMAL_EVENT_ERROR) { + status = *(uint32_t *)buffer->data; + msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status, + mmal_status_to_string(status)); - } - -- picture = filter_NewPicture(filter); -- if (!picture) { -- msg_Warn(filter, "Failed to get new picture"); -- ret = -1; -- goto out; -- } -- 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; ++ } ++ + mmal_buffer_header_reset(buffer); + mmal_buffer_header_release(buffer); +} @@ -4038,112 +4372,129 @@ +static void CloseMmalDeinterlace(filter_t *filter) +{ + filter_sys_t * const sys = filter->p_sys; - -- buffer = picture->p_sys->buffer; -- buffer->user_data = picture; -- buffer->cmd = 0; ++ +#if TRACE_ALL + msg_Dbg(filter, "<<< %s", __func__); +#endif - -- mmal_picture_lock(picture); ++ + if (sys == NULL) -+ return; + return; -- status = mmal_port_send_buffer(sys->output, buffer); -- if (status != MMAL_SUCCESS) { -- msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)", -- status, mmal_status_to_string(status)); -- mmal_buffer_header_release(buffer); -- picture_Release(picture); -- ret = -1; -- } else { -- atomic_fetch_add(&sys->output_in_transit, 1); -- vlc_sem_post(&sys->sem); +- if (sys->component && sys->component->control->is_enabled) +- mmal_port_disable(sys->component->control); + if (sys->use_passthrough) + { + free(sys); + return; - } ++ } --out: -- return ret; +- if (sys->input && sys->input->is_enabled) +- mmal_port_disable(sys->input); + di_flush(filter); -+ + +- if (sys->output && sys->output->is_enabled) +- mmal_port_disable(sys->output); + 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->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 -+ sys->output->userdata = NULL; ++ if (sys->output != NULL) ++ sys->output->userdata = NULL; ++ ++ if (sys->is_cma) ++ { ++ if (sys->output && sys->output->is_enabled) ++ mmal_port_disable(sys->output); ++ ++ cma_buf_pool_deletez(&sys->cma_out_pool); + ++ if (sys->out_pool != NULL) ++ mmal_pool_destroy(sys->out_pool); + } + +- 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); + -+ if (sys->component) -+ mmal_component_release(sys->component); -+ -+ free(sys); + free(sys); ++} + -+ bcm_host_deinit(); + +- bcm_host_deinit(); ++static bool is_fmt_valid_in(const vlc_fourcc_t fmt) ++{ ++ return fmt == VLC_CODEC_MMAL_OPAQUE || ++ fmt == VLC_CODEC_MMAL_ZC_I420 || ++ fmt == VLC_CODEC_MMAL_ZC_SAND8; } --static void fill_output_port(filter_t *filter) -+ +-static int send_output_buffer(filter_t *filter) +static int OpenMmalDeinterlace(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; +- MMAL_BUFFER_HEADER_T *buffer; + 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; + MMAL_STATUS_T status; +- picture_t *picture; +- int ret = 0; + filter_sys_t *sys; + + msg_Dbg(filter, "<<< %s", __func__); - -- if (buffers_to_send > buffers_available) -- buffers_to_send = buffers_available; -+ if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE || -+ filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE) ++ ++ 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; --#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); -+#if TRACE_ALL -+ msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!", -+ frame_duration, use_qpu ? "used" : "unused"); - #endif -- for (i = 0; i < buffers_to_send; ++i) { -- if (send_output_buffer(filter) < 0) -- break; -+ +- 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 = 1; -+ sys->half_rate = false; -+ sys->use_qpu = true; -+ sys->use_fast = false; ++ sys->seq_out = 15; ++ sys->is_cma = is_cma_buf_pic_chroma(filter->fmt_out.video.i_chroma); ++ ++ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { ++ msg_Err(filter, "VCSM init failed"); ++ goto fail; ++ } ++ ++ 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; ++ } + sys->use_passthrough = false; + + if (filter->fmt_in.video.i_width * filter->fmt_in.video.i_height > 768 * 576) @@ -4151,20 +4502,21 @@ + // We get stressed if we have to try too hard - so make life easier + sys->half_rate = true; + // Also check we actually have enough memory to do this -+ if (hw_mmal_get_gpu_mem() < (96 << 20)) ++ // Memory always comes from GPU if Opaque ++ // Assume we have plenty of memory if it comes from CMA ++ if ((!sys->is_cma || sys->vcsm_init_type == VCSM_INIT_LEGACY) && ++ hw_mmal_get_gpu_mem() < (96 << 20)) ++ { + sys->use_passthrough = true; -+ ++ msg_Warn(filter, "Deinterlace bypassed due to lack of GPU memory"); ++ } } --} --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; +- picture = filter_NewPicture(filter); +- if (!picture) { +- msg_Warn(filter, "Failed to get new picture"); +- ret = -1; +- goto out; + if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU)) + sys->use_qpu = false; + if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV)) @@ -4188,12 +4540,33 @@ + { + 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; + } + -+ bcm_host_init(); ++ { ++ char dbuf0[5], dbuf1[5]; ++ msg_Dbg(filter, "%s: %s,%dx%d [(%d,%d) %d/%d] -> %s,%dx%d [(%d,%d) %dx%d]: %s %s %s", __func__, ++ str_fourcc(dbuf0, filter->fmt_in.video.i_chroma), ++ filter->fmt_in.video.i_width, filter->fmt_in.video.i_height, ++ filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset, ++ filter->fmt_in.video.i_visible_width, filter->fmt_in.video.i_visible_height, ++ str_fourcc(dbuf1, filter->fmt_out.video.i_chroma), ++ filter->fmt_out.video.i_width, filter->fmt_out.video.i_height, ++ filter->fmt_out.video.i_x_offset, filter->fmt_out.video.i_y_offset, ++ filter->fmt_out.video.i_visible_width, filter->fmt_out.video.i_visible_height, ++ sys->use_qpu ? "QPU" : "VPU", ++ sys->use_fast ? "FAST" : "ADV", ++ sys->use_passthrough ? "PASS" : sys->half_rate ? "HALF" : "FULL"); + } +- 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; -- fill_output_port(filter); +- buffer = picture->p_sys->buffer; +- buffer->user_data = picture; +- buffer->cmd = 0; + 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)", @@ -4201,10 +4574,7 @@ + goto fail; + } -- buffer = picture->p_sys->buffer; -- buffer->user_data = picture; -- buffer->pts = picture->date; -- buffer->cmd = 0; +- mmal_picture_lock(picture); + { + const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = { + { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) }, @@ -4215,10 +4585,111 @@ + { 5 /* Frame type: mixed */, frame_duration, sys->half_rate, sys->use_qpu } + }; +- status = mmal_port_send_buffer(sys->output, 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)); ++ goto fail; ++ } ++ } ++ ++ 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 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 enable control port %s (status=%"PRIx32" %s)", ++ sys->component->control->name, status, mmal_status_to_string(status)); ++ goto fail; + } + +-out: +- return ret; +-} ++ 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); + +-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; ++ es_format_Copy(&filter->fmt_out, &filter->fmt_in); ++ if (!sys->half_rate) ++ filter->fmt_out.video.i_frame_rate *= 2; + +- if (buffers_to_send > buffers_available) +- buffers_to_send = buffers_available; ++ 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; + +-#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; ++ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL) ++ { ++ msg_Err(filter, "Failed to create input pool"); ++ 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; ++ 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; ++ } + +- fill_output_port(filter); ++ 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; ++ } + +- buffer = picture->p_sys->buffer; +- buffer->user_data = picture; +- buffer->pts = picture->date; +- buffer->cmd = 0; + - if (!picture->p_sys->displayed) { - status = mmal_port_send_buffer(sys->input, buffer); -+ status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr); - if (status != MMAL_SUCCESS) { +- 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); @@ -4226,14 +4697,11 @@ - picture->p_sys->displayed = true; - atomic_fetch_add(&sys->input_in_transit, 1); - vlc_sem_post(&sys->sem); -+ msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)", -+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status)); -+ goto fail; - } +- } - } else { - picture_Release(picture); - } - +- } +- - /* - * Send output buffers - */ @@ -4252,11 +4720,9 @@ - msg_Dbg(filter, "Failed waiting for filtered picture"); - break; - } -+ 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)); ++ if ((sys->out_q = mmal_queue_create()) == NULL) ++ { ++ msg_Err(filter, "Failed to create out Q"); + goto fail; } - if (out_picture) @@ -4264,30 +4730,30 @@ - return ret; -} -+ sys->input = sys->component->input[0]; -+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter; -+ if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) -+ sys->input->format->encoding = MMAL_ENCODING_OPAQUE; -+ vlc_to_mmal_video_fmt(sys->input->format, &filter->fmt_in.video); - +- -static void flush(filter_t *filter) -{ - filter_sys_t *sys = filter->p_sys; - MMAL_BUFFER_HEADER_T *buffer; -+ es_format_Copy(&filter->fmt_out, &filter->fmt_in); -+ if (!sys->half_rate) -+ filter->fmt_out.video.i_frame_rate *= 2; ++ sys->output = sys->component->output[0]; ++ mmal_format_full_copy(sys->output->format, sys->input->format); - msg_Dbg(filter, "flush deinterlace filter"); -+ 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; ++ if (!sys->is_cma) ++ { ++ if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS) ++ 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; ++ else ++ { ++ // CMA stuff ++ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter; ++ ++ if ((sys->cma_out_pool = cma_buf_pool_new(8, 8, true, "deinterlace")) == NULL) ++ { ++ msg_Err(filter, "Failed to alloc cma buf pool"); ++ goto fail; ++ } - msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)", - sys->input_in_transit, sys->output_in_transit); @@ -4304,49 +4770,51 @@ - msg_Dbg(filter, "flush: release already filtered pic %p", - (void *)pic); - picture_Release(pic); -+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL) -+ { -+ msg_Err(filter, "Failed to create input pool"); -+ goto fail; - } +- } - atomic_store(&sys->started, false); - msg_Dbg(filter, "flush: done"); -} ++ // Rate control done by CMA in flight logic, so have "inexhaustable" pool here ++ if ((sys->out_pool = mmal_pool_create(30, 0)) == NULL) ++ { ++ msg_Err(filter, "Failed to alloc out pool"); ++ goto fail; ++ } -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; -+ 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; -+ } ++ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, true); - if (buffer->cmd == MMAL_EVENT_ERROR) { - status = *(uint32_t *)buffer->data; - msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status, - mmal_status_to_string(status)); -+ 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; - } +- } ++ if ((status = mmal_port_format_commit(sys->output)) != MMAL_SUCCESS) ++ { ++ msg_Err(filter, "Output port format commit failed"); ++ goto fail; ++ } - mmal_buffer_header_release(buffer); -} ++ sys->output->buffer_num = 30; ++ sys->output->buffer_size = sys->output->buffer_size_recommended; -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 ((sys->out_q = mmal_queue_create()) == NULL) -+ { -+ msg_Err(filter, "Failed to create out Q"); -+ goto fail; ++ // CB just drops all bufs into out_q ++ if ((status = mmal_port_enable(sys->output, di_output_port_cb)) != MMAL_SUCCESS) ++ { ++ msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)", ++ sys->output->name, status, mmal_status_to_string(status)); ++ goto fail; ++ } + } - if (picture) { @@ -4354,12 +4822,6 @@ - } else { - msg_Warn(filter, "Got buffer without picture on input port - OOOPS"); - mmal_buffer_header_release(buffer); -+ sys->output = sys->component->output[0]; -+ mmal_format_full_copy(sys->output->format, sys->input->format); -+ -+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS) -+ goto fail; -+ + status = mmal_component_enable(sys->component); + if (status != MMAL_SUCCESS) { + msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)", @@ -4405,6 +4867,7 @@ + MMAL_DEINTERLACE_FULL_RATE_LONGTEXT, true); + +vlc_module_end() ++ - if (buffer->cmd == 0) { - if (buffer->length > 0) { @@ -4415,7 +4878,7 @@ - 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) { @@ -4427,7 +4890,7 @@ -} --- /dev/null +++ b/modules/hw/mmal/mmal_avcodec.c -@@ -0,0 +1,2275 @@ +@@ -0,0 +1,2172 @@ +/***************************************************************************** + * video.c: video decoder using the libavcodec library + ***************************************************************************** @@ -4455,11 +4918,8 @@ +/***************************************************************************** + * Preamble + *****************************************************************************/ -+#ifdef HAVE_CONFIG_H -+# include "config.h" -+#endif ++#include "config.h" + -+#include +#include +#include +#include @@ -4467,125 +4927,310 @@ +#include +#include + -+#include "../../codec/avcodec/avcommon.h" -+ -+#include -+ +#include +#include +#include -+#include +#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) ) +#include +#endif + ++//#include "avcodec.h" ++//#include "va.h" ++ ++#include ++#include ++#include ++#include "../../codec/cc.h" ++#include "../../codec/avcodec/avcommon.h" // ??? Beware over inclusion ++#include "mmal_cma.h" +#include "mmal_picture.h" + +#define TRACE_ALL 0 + -+#define AVPROVIDER(lib) ((lib##_VERSION_MICRO < 100) ? "libav" : "ffmpeg") -+ -+#ifdef HAVE_LIBAVCODEC_AVCODEC_H -+#include -+ -+/* LIBAVCODEC_VERSION_CHECK checks for the right version of libav and FFmpeg -+ * a is the major version -+ * b and c the minor and micro versions of libav -+ * d and e the minor and micro versions of FFmpeg */ -+#define LIBAVCODEC_VERSION_CHECK( a, b, c, d, e ) \ -+ ( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \ -+ (LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) ) ++#define BUFFERS_IN_FLIGHT 5 // Default max value for in flight buffers ++#define BUFFERS_IN_FLIGHT_UHD 3 // Fewer if very big + -+#ifndef AV_CODEC_FLAG_OUTPUT_CORRUPT -+# define AV_CODEC_FLAG_OUTPUT_CORRUPT CODEC_FLAG_OUTPUT_CORRUPT -+#endif -+#ifndef AV_CODEC_FLAG_GRAY -+# define AV_CODEC_FLAG_GRAY CODEC_FLAG_GRAY -+#endif -+#ifndef AV_CODEC_FLAG_DR1 -+# define AV_CODEC_FLAG_DR1 CODEC_FLAG_DR1 -+#endif -+#ifndef AV_CODEC_FLAG_DELAY -+# define AV_CODEC_FLAG_DELAY CODEC_FLAG_DELAY -+#endif -+#ifndef AV_CODEC_FLAG2_FAST -+# define AV_CODEC_FLAG2_FAST CODEC_FLAG2_FAST -+#endif -+#ifndef FF_INPUT_BUFFER_PADDING_SIZE -+# define FF_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE -+#endif -+#ifndef AV_CODEC_FLAG_INTERLACED_DCT -+# define AV_CODEC_FLAG_INTERLACED_DCT CODEC_FLAG_INTERLACED_DCT -+#endif -+#ifndef AV_CODEC_FLAG_INTERLACED_ME -+# define AV_CODEC_FLAG_INTERLACED_ME CODEC_FLAG_INTERLACED_ME -+#endif -+#ifndef AV_CODEC_FLAG_GLOBAL_HEADER -+# define AV_CODEC_FLAG_GLOBAL_HEADER CODEC_FLAG_GLOBAL_HEADER -+#endif -+#ifndef AV_CODEC_FLAG_LOW_DELAY -+# define AV_CODEC_FLAG_LOW_DELAY CODEC_FLAG_LOW_DELAY -+#endif -+#ifndef AV_CODEC_CAP_SMALL_LAST_FRAME -+# define AV_CODEC_CAP_SMALL_LAST_FRAME CODEC_CAP_SMALL_LAST_FRAME -+#endif -+#ifndef AV_INPUT_BUFFER_MIN_SIZE -+# define AV_INPUT_BUFFER_MIN_SIZE FF_MIN_BUFFER_SIZE -+#endif -+#ifndef FF_MAX_B_FRAMES -+# define FF_MAX_B_FRAMES 16 // FIXME: remove this -+#endif ++#define MMAL_AVCODEC_BUFFERS "mmal-avcodec-buffers" ++#define MMAL_AVCODEC_BUFFERS_TEXT N_("In flight buffer count before blocking.") ++#define MMAL_AVCODEC_BUFFERS_LONGTEXT N_("In flight buffer count before blocking. " \ ++"Beware that incautious changing of this can lead to lockup. " \ ++"Zero will disable the module.") + -+#endif /* HAVE_LIBAVCODEC_AVCODEC_H */ + -+#ifdef HAVE_LIBAVUTIL_AVUTIL_H -+# include ++// Fwd declarations required due to wanting to avoid reworking the original ++// code too much ++static void MmalAvcodecCloseDecoder( vlc_object_t *obj ); + -+/* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg -+ * a is the major version -+ * b and c the minor and micro versions of libav -+ * d and e the minor and micro versions of FFmpeg */ -+#define LIBAVUTIL_VERSION_CHECK( a, b, c, d, e ) \ -+ ( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \ -+ (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) ) + -+#if !LIBAVUTIL_VERSION_CHECK( 52, 11, 0, 32, 100 ) -+# define AV_PIX_FMT_FLAG_HWACCEL PIX_FMT_HWACCEL -+#endif ++/***************************************************************************** ++ * decoder_sys_t : decoder descriptor ++ *****************************************************************************/ ++struct decoder_sys_t ++{ ++ AVCodecContext *p_context; ++ const AVCodec *p_codec; + -+#endif /* HAVE_LIBAVUTIL_AVUTIL_H */ ++ /* Video decoder specific part */ ++ date_t pts; + -+#if LIBAVUTIL_VERSION_MAJOR >= 55 -+# define FF_API_AUDIOCONVERT 1 -+#endif ++ /* Closed captions for decoders */ ++ cc_data_t cc; + -+/* libavutil/pixfmt.h */ -+#ifndef PixelFormat -+# define PixelFormat AVPixelFormat -+#endif ++ /* for frame skipping algo */ ++ bool b_hurry_up; ++ bool b_show_corrupted; ++ bool b_from_preroll; ++ enum AVDiscard i_skip_frame; + -+#ifdef HAVE_LIBAVFORMAT_AVFORMAT_H -+# include ++ /* how many decoded frames are late */ ++ int i_late_frames; ++ mtime_t i_late_frames_start; ++ mtime_t i_last_late_delay; + -+#define LIBAVFORMAT_VERSION_CHECK( a, b, c, d, e ) \ -+ ( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \ -+ (LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) ) ++ /* for direct rendering */ ++ bool b_direct_rendering; ++ atomic_bool b_dr_failure; + -+#endif ++ /* Hack to force display of still pictures */ ++ bool b_first_frame; + -+/***************************************************************************** -+ * Codec fourcc -> libavcodec Codec_id mapping -+ * Sorted by AVCodecID enumeration order -+ *****************************************************************************/ -+struct vlc_avcodec_fourcc ++ ++ /* */ ++ bool palette_sent; ++ ++ /* VA API */ ++// vlc_va_t *p_va; ++ enum PixelFormat pix_fmt; ++ int profile; ++ int level; ++ ++ vlc_sem_t sem_mt; ++ ++ // Rpi vars ++ cma_buf_pool_t * cma_pool; ++ bool pool_alloc_1; ++ vcsm_init_type_t vcsm_init_type; ++ int cma_in_flight_max; ++ // Debug ++ decoder_t * p_dec; ++}; ++ ++ ++static vlc_fourcc_t ++ZcFindVlcChroma(const int i_ffmpeg_chroma) +{ -+ vlc_fourcc_t i_fourcc; -+ unsigned i_codec; ++ switch (i_ffmpeg_chroma) ++ { ++ // This is all we claim to deal with ++ // In theory RGB should be doable within our current framework ++ case AV_PIX_FMT_YUV420P: ++ return VLC_CODEC_MMAL_ZC_I420; ++ case AV_PIX_FMT_SAND128: ++ case AV_PIX_FMT_RPI4_8: ++ return VLC_CODEC_MMAL_ZC_SAND8; ++ case AV_PIX_FMT_SAND64_10: ++ return VLC_CODEC_MMAL_ZC_SAND10; ++ case AV_PIX_FMT_RPI4_10: ++ return VLC_CODEC_MMAL_ZC_SAND30; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++// Pix Fmt conv for MMal ++// video_fromat from ffmpeg pic_fmt ++static int ++ZcGetVlcChroma( video_format_t *fmt, int i_ffmpeg_chroma ) ++{ ++ fmt->i_rmask = 0; ++ fmt->i_gmask = 0; ++ fmt->i_bmask = 0; ++ fmt->i_chroma = ZcFindVlcChroma(i_ffmpeg_chroma); ++ ++ return fmt->i_chroma == 0 ? -1 : 0; ++} ++ ++ ++// Format chooser is way simpler than vlc ++static enum PixelFormat ++ZcGetFormat(AVCodecContext *p_context, const enum PixelFormat *pi_fmt) ++{ ++ enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt); ++ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++) ++ { ++ if (ZcFindVlcChroma(pi_fmt[i]) != 0) ++ return pi_fmt[i]; ++ } ++ return swfmt; ++} ++ ++ ++static void cma_avbuf_pool_free(void * v) ++{ ++ cma_buf_unref(v); ++} ++ ++static unsigned int zc_buf_vcsm_handle(void * v) ++{ ++ return cma_buf_vcsm_handle(v); ++} ++ ++static unsigned int zc_buf_vc_handle(void * v) ++{ ++ return cma_buf_vc_handle(v); ++} ++ ++static void * zc_buf_map_arm(void * v) ++{ ++ return cma_buf_addr(v); ++} ++ ++static unsigned int zc_buf_map_vc(void * v) ++{ ++ return cma_buf_vc_addr(v); ++} ++ ++ ++ ++static const av_rpi_zc_buf_fn_tab_t zc_buf_fn_tab = { ++ .free = cma_avbuf_pool_free, ++ ++ .vcsm_handle = zc_buf_vcsm_handle, ++ .vc_handle = zc_buf_vc_handle, ++ .map_arm = zc_buf_map_arm, ++ .map_vc = zc_buf_map_vc +}; + ++ ++static AVBufferRef * ++zc_alloc_buf(void * v, size_t size, const AVRpiZcFrameGeometry * geo) ++{ ++ decoder_t * const dec = v; ++ decoder_sys_t * const sys = dec->p_sys; ++ ++ VLC_UNUSED(geo); ++ ++ assert(sys != NULL); ++ ++ const unsigned int dec_pool_req = av_rpi_zc_get_decoder_pool_size(sys->p_context->opaque); ++ if (dec_pool_req != 0) ++ { ++ cma_buf_pool_resize(sys->cma_pool, dec_pool_req + sys->cma_in_flight_max, sys->cma_in_flight_max); ++ ++ if (!sys->pool_alloc_1) ++ { ++ sys->pool_alloc_1 = true; ++ msg_Dbg(dec, "Pool size: (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size); ++ if (cma_buf_pool_fill(sys->cma_pool, size) != 0) ++ msg_Warn(dec, "Failed to preallocate decoder pool (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size); ++ } ++ } ++ ++ void * const cmabuf = cma_buf_pool_alloc_buf(sys->cma_pool, size); ++ ++ if (cmabuf == NULL) ++ { ++ msg_Err(dec, "CMA buf pool alloc buf failed"); ++ return NULL; ++ } ++ ++ AVBufferRef *const avbuf = av_rpi_zc_buf(cma_buf_size(cmabuf), 0, cmabuf, &zc_buf_fn_tab); ++ ++ if (avbuf == NULL) ++ { ++ msg_Err(dec, "av_rpi_zc_buf failed"); ++ cma_buf_unref(cmabuf); ++ return NULL; ++ } ++ ++ return avbuf; ++} ++ ++static void ++zc_free_pool(void * v) ++{ ++ decoder_t * const dec = v; ++ cma_buf_pool_delete(dec->p_sys->cma_pool); ++} ++ ++ ++static const uint8_t shift_01[] = {0,1,1,1}; ++static const uint8_t pb_1[] = {1,1,1,1}; ++static const uint8_t pb_12[] = {1,2,2,2}; ++static const uint8_t pb_24[] = {2,4,4,4}; ++static const uint8_t pb_4[] = {4,4,4,4}; ++ ++static int set_pic_from_frame(picture_t * const pic, const AVFrame * const frame) ++{ ++ const uint8_t * hs = shift_01; ++ const uint8_t * ws = shift_01; ++ const uint8_t * pb = pb_1; ++ ++ switch (pic->format.i_chroma) ++ { ++ case VLC_CODEC_MMAL_ZC_RGB32: ++ pic->i_planes = 1; ++ pb = pb_4; ++ break; ++ case VLC_CODEC_MMAL_ZC_I420: ++ pic->i_planes = 3; ++ break; ++ case VLC_CODEC_MMAL_ZC_SAND8: ++ pic->i_planes = 2; ++ pb = pb_12; ++ break; ++ case VLC_CODEC_MMAL_ZC_SAND10: ++ case VLC_CODEC_MMAL_ZC_SAND30: // Lies: SAND30 is "special" ++ pic->i_planes = 2; ++ pb = pb_24; ++ break; ++ default: ++ return VLC_EGENERIC; ++ } ++ ++ const cma_buf_t * const cb = cma_buf_pic_get(pic); ++ uint8_t * const data = cma_buf_addr(cb); ++ if (data == NULL) { ++ return VLC_ENOMEM; ++ } ++ ++ uint8_t * frame_end = frame->data[0] + cma_buf_size(cb); ++ for (int i = 0; i != pic->i_planes; ++i) { ++ // Calculate lines from gap between planes ++ // This will give us an accurate "height" for later use by MMAL ++ const int lines = ((i + 1 == pic->i_planes ? frame_end : frame->data[i + 1]) - ++ frame->data[i]) / frame->linesize[i]; ++ pic->p[i] = (plane_t){ ++ .p_pixels = data + (frame->data[i] - frame->data[0]), ++ .i_lines = lines, ++ .i_pitch = frame->linesize[i], ++ .i_pixel_pitch = pb[i], ++ .i_visible_lines = av_frame_cropped_height(frame) >> hs[i], ++ .i_visible_pitch = av_frame_cropped_width(frame) >> ws[i] ++ }; ++ } ++ return 0; ++} ++ ++ ++//============================================================================ ++// ++// Nicked from avcodec/fourcc.c ++// ++// * Really we should probably use that directly ++ +/* + * Video Codecs + */ ++ ++struct vlc_avcodec_fourcc ++{ ++ vlc_fourcc_t i_fourcc; ++ unsigned i_codec; ++}; ++ ++ +static const struct vlc_avcodec_fourcc video_codecs[] = +{ + { VLC_CODEC_MP1V, AV_CODEC_ID_MPEG1VIDEO }, @@ -4723,6 +5368,9 @@ + /* AV_CODEC_ID_V210X */ + { VLC_CODEC_TMV, AV_CODEC_ID_TMV }, + { VLC_CODEC_V210, AV_CODEC_ID_V210 }, ++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 54, 50, 100 ) && LIBAVCODEC_VERSION_MICRO >= 100 ++ { VLC_CODEC_VUYA, AV_CODEC_ID_AYUV }, ++#endif + /* AV_CODEC_ID_DPX */ + { VLC_CODEC_MAD, AV_CODEC_ID_MAD }, + { VLC_CODEC_FRWU, AV_CODEC_ID_FRWU }, @@ -4804,7 +5452,6 @@ + /* ffmpeg only: AV_CODEC_ID_AVRP */ + /* ffmpeg only: AV_CODEC_ID_012V */ + /* ffmpeg only: AV_CODEC_ID_AVUI */ -+ /* ffmpeg only: AV_CODEC_ID_AYUV */ + /* ffmpeg only: AV_CODEC_ID_TARGA_Y216 */ + /* ffmpeg only: AV_CODEC_ID_V308 */ + /* ffmpeg only: AV_CODEC_ID_V408 */ @@ -4837,231 +5484,16 @@ +#endif +}; + -+/* -+ * Audio Codecs -+ */ -+static const struct vlc_avcodec_fourcc audio_codecs[] = -+{ -+ /* PCM */ -+ { VLC_CODEC_S16L, AV_CODEC_ID_PCM_S16LE }, -+ { VLC_CODEC_S16B, AV_CODEC_ID_PCM_S16BE }, -+ { VLC_CODEC_U16L, AV_CODEC_ID_PCM_U16LE }, -+ { VLC_CODEC_U16B, AV_CODEC_ID_PCM_U16BE }, -+ { VLC_CODEC_S8, AV_CODEC_ID_PCM_S8 }, -+ { VLC_CODEC_U8, AV_CODEC_ID_PCM_U8 }, -+ { VLC_CODEC_MULAW, AV_CODEC_ID_PCM_MULAW }, -+ { VLC_CODEC_ALAW, AV_CODEC_ID_PCM_ALAW }, -+ { VLC_CODEC_S32L, AV_CODEC_ID_PCM_S32LE }, -+ { VLC_CODEC_S32B, AV_CODEC_ID_PCM_S32BE }, -+ { VLC_CODEC_U32L, AV_CODEC_ID_PCM_U32LE }, -+ { VLC_CODEC_U32B, AV_CODEC_ID_PCM_U32BE }, -+ { VLC_CODEC_S24L, AV_CODEC_ID_PCM_S24LE }, -+ { VLC_CODEC_S24B, AV_CODEC_ID_PCM_S24BE }, -+ { VLC_CODEC_U24L, AV_CODEC_ID_PCM_U24LE }, -+ { VLC_CODEC_U24B, AV_CODEC_ID_PCM_U24BE }, -+ { VLC_CODEC_S24DAUD, AV_CODEC_ID_PCM_S24DAUD }, -+ /* AV_CODEC_ID_PCM_ZORK */ -+ { VLC_CODEC_S16L_PLANAR, AV_CODEC_ID_PCM_S16LE_PLANAR }, -+ /* AV_CODEC_ID_PCM_DVD */ -+ { VLC_CODEC_F32B, AV_CODEC_ID_PCM_F32BE }, -+ { VLC_CODEC_F32L, AV_CODEC_ID_PCM_F32LE }, -+ { VLC_CODEC_F64B, AV_CODEC_ID_PCM_F64BE }, -+ { VLC_CODEC_F64L, AV_CODEC_ID_PCM_F64LE }, -+ { VLC_CODEC_BD_LPCM, AV_CODEC_ID_PCM_BLURAY }, -+ /* AV_CODEC_ID_PCM_LXF */ -+ /* AV_CODEC_ID_S302M */ -+ /* AV_CODEC_ID_PCM_S8_PLANAR */ -+ /* AV_CODEC_ID_PCM_S24LE_PLANAR */ -+ /* AV_CODEC_ID_PCM_S32LE_PLANAR */ -+ /* ffmpeg only: AV_CODEC_ID_PCM_S16BE_PLANAR */ -+ -+ /* ADPCM */ -+ { VLC_CODEC_ADPCM_IMA_QT, AV_CODEC_ID_ADPCM_IMA_QT }, -+ { VLC_CODEC_ADPCM_IMA_WAV, AV_CODEC_ID_ADPCM_IMA_WAV }, -+ /* AV_CODEC_ID_ADPCM_IMA_DK3 */ -+ /* AV_CODEC_ID_ADPCM_IMA_DK4 */ -+ { VLC_CODEC_ADPCM_IMA_WS, AV_CODEC_ID_ADPCM_IMA_WS }, -+ /* AV_CODEC_ID_ADPCM_IMA_SMJPEG */ -+ { VLC_CODEC_ADPCM_MS, AV_CODEC_ID_ADPCM_MS }, -+ { VLC_CODEC_ADPCM_4XM, AV_CODEC_ID_ADPCM_4XM }, -+ { VLC_CODEC_ADPCM_XA, AV_CODEC_ID_ADPCM_XA }, -+ { VLC_CODEC_ADPCM_ADX, AV_CODEC_ID_ADPCM_ADX }, -+ { VLC_CODEC_ADPCM_EA, AV_CODEC_ID_ADPCM_EA }, -+ { VLC_CODEC_ADPCM_G726, AV_CODEC_ID_ADPCM_G726 }, -+ { VLC_CODEC_ADPCM_CREATIVE, AV_CODEC_ID_ADPCM_CT }, -+ { VLC_CODEC_ADPCM_SWF, AV_CODEC_ID_ADPCM_SWF }, -+ { VLC_CODEC_ADPCM_YAMAHA, AV_CODEC_ID_ADPCM_YAMAHA }, -+ { VLC_CODEC_ADPCM_SBPRO_4, AV_CODEC_ID_ADPCM_SBPRO_4 }, -+ { VLC_CODEC_ADPCM_SBPRO_3, AV_CODEC_ID_ADPCM_SBPRO_3 }, -+ { VLC_CODEC_ADPCM_SBPRO_2, AV_CODEC_ID_ADPCM_SBPRO_2 }, -+ { VLC_CODEC_ADPCM_THP, AV_CODEC_ID_ADPCM_THP }, -+ { VLC_CODEC_ADPCM_IMA_AMV, AV_CODEC_ID_ADPCM_IMA_AMV }, -+ { VLC_CODEC_ADPCM_EA_R1, AV_CODEC_ID_ADPCM_EA_R1 }, -+ /* AV_CODEC_ID_ADPCM_EA_R3 */ -+ /* AV_CODEC_ID_ADPCM_EA_R2 */ -+ { VLC_CODEC_ADPCM_IMA_EA_SEAD, AV_CODEC_ID_ADPCM_IMA_EA_SEAD }, -+ /* AV_CODEC_ID_ADPCM_IMA_EA_EACS */ -+ /* AV_CODEC_ID_ADPCM_EA_XAS */ -+ /* AV_CODEC_ID_ADPCM_EA_MAXIS_XA */ -+ /* AV_CODEC_ID_ADPCM_IMA_ISS */ -+ { VLC_CODEC_ADPCM_G722, AV_CODEC_ID_ADPCM_G722 }, -+ { VLC_CODEC_ADPCM_IMA_APC, AV_CODEC_ID_ADPCM_IMA_APC }, -+ /* ffmpeg only: AV_CODEC_ID_VIMA */ -+ /* ffmpeg only: AV_CODEC_ID_ADPCM_AFC */ -+ /* ffmpeg only: AV_CODEC_ID_ADPCM_IMA_OKI */ -+ /* ffmpeg only: AV_CODEC_ID_ADPCM_DTK */ -+ /* ffmpeg only: AV_CODEC_ID_ADPCM_IMA_RAD */ -+ /* ffmpeg only: AV_CODEC_ID_ADPCM_G726LE */ -+ -+ /* AMR */ -+ { VLC_CODEC_AMR_NB, AV_CODEC_ID_AMR_NB }, -+ { VLC_CODEC_AMR_WB, AV_CODEC_ID_AMR_WB }, -+ -+ /* RealAudio */ -+ { VLC_CODEC_RA_144, AV_CODEC_ID_RA_144 }, -+ { VLC_CODEC_RA_288, AV_CODEC_ID_RA_288 }, -+ -+ /* DPCM */ -+ { VLC_CODEC_ROQ_DPCM, AV_CODEC_ID_ROQ_DPCM }, -+ { VLC_CODEC_INTERPLAY_DPCM, AV_CODEC_ID_INTERPLAY_DPCM }, -+ /* AV_CODEC_ID_XAN_DPCM */ -+ /* AV_CODEC_ID_SOL_DPCM */ -+ -+ /* audio codecs */ -+ { VLC_CODEC_MPGA, AV_CODEC_ID_MP2 }, -+ { VLC_CODEC_MP2, AV_CODEC_ID_MP2 }, -+ { VLC_CODEC_MP3, AV_CODEC_ID_MP3 }, -+ { VLC_CODEC_MP4A, AV_CODEC_ID_AAC }, -+ { VLC_CODEC_A52, AV_CODEC_ID_AC3 }, -+ { VLC_CODEC_DTS, AV_CODEC_ID_DTS }, -+ { VLC_CODEC_VORBIS, AV_CODEC_ID_VORBIS }, -+ { VLC_CODEC_DVAUDIO, AV_CODEC_ID_DVAUDIO }, -+ { VLC_CODEC_WMA1, AV_CODEC_ID_WMAV1 }, -+ { VLC_CODEC_WMA2, AV_CODEC_ID_WMAV2 }, -+ { VLC_CODEC_MACE3, AV_CODEC_ID_MACE3 }, -+ { VLC_CODEC_MACE6, AV_CODEC_ID_MACE6 }, -+ { VLC_CODEC_VMDAUDIO, AV_CODEC_ID_VMDAUDIO }, -+ { VLC_CODEC_FLAC, AV_CODEC_ID_FLAC }, -+ /* AV_CODEC_ID_MP3ADU */ -+ /* AV_CODEC_ID_MP3ON4 */ -+ { VLC_CODEC_SHORTEN, AV_CODEC_ID_SHORTEN }, -+ { VLC_CODEC_ALAC, AV_CODEC_ID_ALAC }, -+ /* AV_CODEC_ID_WESTWOOD_SND1 */ -+ { VLC_CODEC_GSM, AV_CODEC_ID_GSM }, -+ { VLC_CODEC_QDM2, AV_CODEC_ID_QDM2 }, -+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 100 ) -+ { VLC_CODEC_QDMC, AV_CODEC_ID_QDMC }, -+#endif -+ { VLC_CODEC_COOK, AV_CODEC_ID_COOK }, -+ { VLC_CODEC_TRUESPEECH, AV_CODEC_ID_TRUESPEECH }, -+ { VLC_CODEC_TTA, AV_CODEC_ID_TTA }, -+ { VLC_CODEC_SMACKAUDIO, AV_CODEC_ID_SMACKAUDIO }, -+ { VLC_CODEC_QCELP, AV_CODEC_ID_QCELP }, -+ { VLC_CODEC_WAVPACK, AV_CODEC_ID_WAVPACK }, -+ { VLC_CODEC_DSICINAUDIO, AV_CODEC_ID_DSICINAUDIO }, -+ { VLC_CODEC_IMC, AV_CODEC_ID_IMC }, -+ { VLC_CODEC_MUSEPACK7, AV_CODEC_ID_MUSEPACK7 }, -+ { VLC_CODEC_MLP, AV_CODEC_ID_MLP }, -+ { VLC_CODEC_GSM_MS, AV_CODEC_ID_GSM_MS }, -+ { VLC_CODEC_ATRAC3, AV_CODEC_ID_ATRAC3 }, -+ { VLC_CODEC_APE, AV_CODEC_ID_APE }, -+ { VLC_CODEC_NELLYMOSER, AV_CODEC_ID_NELLYMOSER }, -+ { VLC_CODEC_MUSEPACK8, AV_CODEC_ID_MUSEPACK8 }, -+ { VLC_CODEC_SPEEX, AV_CODEC_ID_SPEEX }, -+ { VLC_CODEC_WMAS, AV_CODEC_ID_WMAVOICE }, -+ { VLC_CODEC_WMAP, AV_CODEC_ID_WMAPRO }, -+ { VLC_CODEC_WMAL, AV_CODEC_ID_WMALOSSLESS }, -+ { VLC_CODEC_ATRAC3P, AV_CODEC_ID_ATRAC3P }, -+ { VLC_CODEC_EAC3, AV_CODEC_ID_EAC3 }, -+ { VLC_CODEC_SIPR, AV_CODEC_ID_SIPR }, -+ /* AV_CODEC_ID_MP1 */ -+ { VLC_CODEC_TWINVQ, AV_CODEC_ID_TWINVQ }, -+ { VLC_CODEC_TRUEHD, AV_CODEC_ID_TRUEHD }, -+ { VLC_CODEC_ALS, AV_CODEC_ID_MP4ALS }, -+ { VLC_CODEC_ATRAC1, AV_CODEC_ID_ATRAC1 }, -+ { VLC_CODEC_BINKAUDIO_RDFT, AV_CODEC_ID_BINKAUDIO_RDFT }, -+ { VLC_CODEC_BINKAUDIO_DCT, AV_CODEC_ID_BINKAUDIO_DCT }, -+ { VLC_CODEC_MP4A, AV_CODEC_ID_AAC_LATM }, -+ /* AV_CODEC_ID_QDMC */ -+ /* AV_CODEC_ID_CELT */ -+ { VLC_CODEC_G723_1, AV_CODEC_ID_G723_1 }, -+ /* AV_CODEC_ID_G729 */ -+ /* AV_CODEC_ID_8SVX_EXP */ -+ /* AV_CODEC_ID_8SVX_FIB */ -+ { VLC_CODEC_BMVAUDIO, AV_CODEC_ID_BMV_AUDIO }, -+ { VLC_CODEC_RALF, AV_CODEC_ID_RALF }, -+ { VLC_CODEC_INDEO_AUDIO, AV_CODEC_ID_IAC }, -+ /* AV_CODEC_ID_ILBC */ -+ { VLC_CODEC_OPUS, AV_CODEC_ID_OPUS }, -+ /* AV_CODEC_ID_COMFORT_NOISE */ -+ { VLC_CODEC_TAK, AV_CODEC_ID_TAK }, -+ { VLC_CODEC_METASOUND, AV_CODEC_ID_METASOUND }, -+ /* AV_CODEC_ID_PAF_AUDIO */ -+ { VLC_CODEC_ON2AVC, AV_CODEC_ID_ON2AVC }, -+ -+ /* ffmpeg only: AV_CODEC_ID_FFWAVESYNTH */ -+ /* ffmpeg only: AV_CODEC_ID_SONIC */ -+ /* ffmpeg only: AV_CODEC_ID_SONIC_LS */ -+ /* ffmpeg only: AV_CODEC_ID_PAF_AUDIO */ -+ /* ffmpeg only: AV_CODEC_ID_EVRC */ -+ /* ffmpeg only: AV_CODEC_ID_SMV */ -+}; -+ -+/* Subtitle streams */ -+static const struct vlc_avcodec_fourcc spu_codecs[] = -+{ -+ { VLC_CODEC_SPU, AV_CODEC_ID_DVD_SUBTITLE }, -+ { VLC_CODEC_DVBS, AV_CODEC_ID_DVB_SUBTITLE }, -+ { VLC_CODEC_SUBT, AV_CODEC_ID_TEXT }, -+ { VLC_CODEC_XSUB, AV_CODEC_ID_XSUB }, -+ { VLC_CODEC_SSA, AV_CODEC_ID_SSA }, -+ /* AV_CODEC_ID_MOV_TEXT */ -+ { VLC_CODEC_BD_PG, AV_CODEC_ID_HDMV_PGS_SUBTITLE }, -+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 100 ) -+ { VLC_CODEC_BD_TEXT, AV_CODEC_ID_HDMV_TEXT_SUBTITLE }, -+#endif -+ { VLC_CODEC_TELETEXT, AV_CODEC_ID_DVB_TELETEXT }, -+ /* AV_CODEC_ID_SRT */ -+ /* ffmpeg only: AV_CODEC_ID_MICRODVD */ -+ /* ffmpeg only: AV_CODEC_ID_EIA_608 */ -+ /* ffmpeg only: AV_CODEC_ID_JACOSUB */ -+ /* ffmpeg only: AV_CODEC_ID_SAMI */ -+ /* ffmpeg only: AV_CODEC_ID_REALTEXT */ -+ /* ffmpeg only: AV_CODEC_ID_SUBVIEWER1 */ -+ /* ffmpeg only: AV_CODEC_ID_SUBVIEWER */ -+ /* ffmpeg only: AV_CODEC_ID_SUBRIP */ -+ /* ffmpeg only: AV_CODEC_ID_WEBVTT */ -+ /* ffmpeg only: AV_CODEC_ID_MPL2 */ -+ /* ffmpeg only: AV_CODEC_ID_VPLAYER */ -+ /* ffmpeg only: AV_CODEC_ID_PJS */ -+ /* ffmpeg only: AV_CODEC_ID_ASS */ -+}; -+ -+static bool GetFfmpegCodec( enum es_format_category_e cat, vlc_fourcc_t i_fourcc, ++// *** Really we should probably use GetFfmpegCodec with a pre-kludge for the bits we care about ++static bool ++ZcGetFfmpegCodec( enum es_format_category_e cat, vlc_fourcc_t i_fourcc, + unsigned *pi_ffmpeg_codec, const char **ppsz_name ) +{ + const struct vlc_avcodec_fourcc *base; + size_t count; + -+ switch( cat ) -+ { -+ case VIDEO_ES: -+ base = video_codecs; -+ count = ARRAY_SIZE(video_codecs); -+ break; -+ case AUDIO_ES: -+ base = audio_codecs; -+ count = ARRAY_SIZE(audio_codecs); -+ break; -+ case SPU_ES: -+ base = spu_codecs; -+ count = ARRAY_SIZE(spu_codecs); -+ break; -+ default: -+ base = NULL; -+ count = 0; -+ } -+ ++ base = video_codecs; ++ count = ARRAY_SIZE(video_codecs); + i_fourcc = vlc_fourcc_GetCodec( cat, i_fourcc ); + + for( size_t i = 0; i < count; i++ ) @@ -5078,163 +5510,13 @@ + return false; +} + -+/***************************************************************************** -+ * Chroma fourcc -> libavutil pixfmt mapping -+ *****************************************************************************/ -+#if defined(WORDS_BIGENDIAN) -+# define VLC_RGB_ES( fcc, leid, beid ) \ -+ { fcc, beid, 0, 0, 0 }, -+#else -+# define VLC_RGB_ES( fcc, leid, beid ) \ -+ { fcc, leid, 0, 0, 0 }, -+#endif -+ -+#define VLC_RGB( fcc, leid, beid, rmask, gmask, bmask ) \ -+ { fcc, leid, rmask, gmask, bmask }, \ -+ { fcc, beid, bmask, gmask, rmask }, \ -+ VLC_RGB_ES( fcc, leid, beid ) -+ -+ -+static const struct -+{ -+ vlc_fourcc_t i_chroma; -+ int i_chroma_id; -+ uint32_t i_rmask; -+ uint32_t i_gmask; -+ uint32_t i_bmask; -+ -+} chroma_table[] = -+{ -+ // Sand -+// {VLC_CODEC_MMAL_OPAQUE, AV_PIX_FMT_SAND128, 0, 0, 0 }, -+ {VLC_CODEC_MMAL_ZC_SAND8, AV_PIX_FMT_SAND128, 0, 0, 0 }, -+ {VLC_CODEC_MMAL_ZC_SAND10, AV_PIX_FMT_SAND64_10, 0, 0, 0 }, -+ -+ /* Planar YUV formats */ -+ {VLC_CODEC_I444, AV_PIX_FMT_YUV444P, 0, 0, 0 }, -+ {VLC_CODEC_J444, AV_PIX_FMT_YUVJ444P, 0, 0, 0 }, -+ -+ {VLC_CODEC_I440, AV_PIX_FMT_YUV440P, 0, 0, 0 }, -+ {VLC_CODEC_J440, AV_PIX_FMT_YUVJ440P, 0, 0, 0 }, + -+ {VLC_CODEC_I422, AV_PIX_FMT_YUV422P, 0, 0, 0 }, -+ {VLC_CODEC_J422, AV_PIX_FMT_YUVJ422P, 0, 0, 0 }, + -+ {VLC_CODEC_I420, AV_PIX_FMT_YUV420P, 0, 0, 0 }, -+ {VLC_CODEC_YV12, AV_PIX_FMT_YUV420P, 0, 0, 0 }, -+ {VLC_FOURCC('I','Y','U','V'), AV_PIX_FMT_YUV420P, 0, 0, 0 }, -+ {VLC_CODEC_J420, AV_PIX_FMT_YUVJ420P, 0, 0, 0 }, -+ {VLC_CODEC_I411, AV_PIX_FMT_YUV411P, 0, 0, 0 }, -+ {VLC_CODEC_I410, AV_PIX_FMT_YUV410P, 0, 0, 0 }, -+ {VLC_FOURCC('Y','V','U','9'), AV_PIX_FMT_YUV410P, 0, 0, 0 }, ++//============================================================================ ++// Derived from codec/avcodec/avcodec.c + -+ {VLC_CODEC_NV12, AV_PIX_FMT_NV12, 0, 0, 0 }, -+ {VLC_CODEC_NV21, AV_PIX_FMT_NV21, 0, 0, 0 }, -+ -+ {VLC_CODEC_I420_9L, AV_PIX_FMT_YUV420P9LE, 0, 0, 0 }, -+ {VLC_CODEC_I420_9B, AV_PIX_FMT_YUV420P9BE, 0, 0, 0 }, -+ {VLC_CODEC_I420_10L, AV_PIX_FMT_YUV420P10LE, 0, 0, 0 }, -+ {VLC_CODEC_I420_10B, AV_PIX_FMT_YUV420P10BE, 0, 0, 0 }, -+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) ) -+ {VLC_CODEC_I420_12L, AV_PIX_FMT_YUV420P12LE, 0, 0, 0 }, -+ {VLC_CODEC_I420_12B, AV_PIX_FMT_YUV420P12BE, 0, 0, 0 }, -+#endif -+ {VLC_CODEC_I420_16L, AV_PIX_FMT_YUV420P16LE, 0, 0, 0 }, -+ {VLC_CODEC_I420_16B, AV_PIX_FMT_YUV420P16BE, 0, 0, 0 }, -+#ifdef AV_PIX_FMT_P010 -+ {VLC_CODEC_P010, AV_PIX_FMT_P010, 0, 0, 0 }, -+#endif -+ -+ {VLC_CODEC_I422_9L, AV_PIX_FMT_YUV422P9LE, 0, 0, 0 }, -+ {VLC_CODEC_I422_9B, AV_PIX_FMT_YUV422P9BE, 0, 0, 0 }, -+ {VLC_CODEC_I422_10L, AV_PIX_FMT_YUV422P10LE, 0, 0, 0 }, -+ {VLC_CODEC_I422_10B, AV_PIX_FMT_YUV422P10BE, 0, 0, 0 }, -+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) ) -+ {VLC_CODEC_I422_12L, AV_PIX_FMT_YUV422P12LE, 0, 0, 0 }, -+ {VLC_CODEC_I422_12B, AV_PIX_FMT_YUV422P12BE, 0, 0, 0 }, -+#endif -+ -+ {VLC_CODEC_YUV420A, AV_PIX_FMT_YUVA420P, 0, 0, 0 }, -+ {VLC_CODEC_YUV422A, AV_PIX_FMT_YUVA422P, 0, 0, 0 }, -+ {VLC_CODEC_YUVA, AV_PIX_FMT_YUVA444P, 0, 0, 0 }, -+ -+ {VLC_CODEC_YUVA_444_10L, AV_PIX_FMT_YUVA444P10LE, 0, 0, 0 }, -+ {VLC_CODEC_YUVA_444_10B, AV_PIX_FMT_YUVA444P10BE, 0, 0, 0 }, -+ -+ {VLC_CODEC_I444_9L, AV_PIX_FMT_YUV444P9LE, 0, 0, 0 }, -+ {VLC_CODEC_I444_9B, AV_PIX_FMT_YUV444P9BE, 0, 0, 0 }, -+ {VLC_CODEC_I444_10L, AV_PIX_FMT_YUV444P10LE, 0, 0, 0 }, -+ {VLC_CODEC_I444_10B, AV_PIX_FMT_YUV444P10BE, 0, 0, 0 }, -+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) ) -+ {VLC_CODEC_I444_12L, AV_PIX_FMT_YUV444P12LE, 0, 0, 0 }, -+ {VLC_CODEC_I444_12B, AV_PIX_FMT_YUV444P12BE, 0, 0, 0 }, -+#endif -+ {VLC_CODEC_I444_16L, AV_PIX_FMT_YUV444P16LE, 0, 0, 0 }, -+ {VLC_CODEC_I444_16B, AV_PIX_FMT_YUV444P16BE, 0, 0, 0 }, -+ -+ /* Packed YUV formats */ -+ {VLC_CODEC_YUYV, AV_PIX_FMT_YUYV422, 0, 0, 0 }, -+ {VLC_FOURCC('Y','U','Y','V'), AV_PIX_FMT_YUYV422, 0, 0, 0 }, -+ {VLC_CODEC_UYVY, AV_PIX_FMT_UYVY422, 0, 0, 0 }, -+ {VLC_CODEC_YVYU, AV_PIX_FMT_YVYU422, 0, 0, 0 }, -+ {VLC_FOURCC('Y','4','1','1'), AV_PIX_FMT_UYYVYY411, 0, 0, 0 }, -+ -+ /* Packed RGB formats */ -+ VLC_RGB( VLC_FOURCC('R','G','B','4'), AV_PIX_FMT_RGB4, AV_PIX_FMT_BGR4, 0x10, 0x06, 0x01 ) -+ VLC_RGB( VLC_CODEC_RGB8, AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, 0xC0, 0x38, 0x07 ) -+ -+ VLC_RGB( VLC_CODEC_RGB15, AV_PIX_FMT_RGB555, AV_PIX_FMT_BGR555, 0x7c00, 0x03e0, 0x001f ) -+ VLC_RGB( VLC_CODEC_RGB16, AV_PIX_FMT_RGB565, AV_PIX_FMT_BGR565, 0xf800, 0x07e0, 0x001f ) -+ VLC_RGB( VLC_CODEC_RGB24, AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, 0xff0000, 0x00ff00, 0x0000ff ) -+ -+ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_RGB32, AV_PIX_FMT_BGR32, 0x00ff0000, 0x0000ff00, 0x000000ff ) -+ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_RGB32_1, AV_PIX_FMT_BGR32_1, 0xff000000, 0x00ff0000, 0x0000ff00 ) -+ -+#ifdef AV_PIX_FMT_0BGR32 -+ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_0BGR32, AV_PIX_FMT_0RGB32, 0x000000ff, 0x0000ff00, 0x00ff0000 ) -+#endif -+ -+ {VLC_CODEC_RGBA, AV_PIX_FMT_RGBA, 0, 0, 0 }, -+ {VLC_CODEC_ARGB, AV_PIX_FMT_ARGB, 0, 0, 0 }, -+ {VLC_CODEC_BGRA, AV_PIX_FMT_BGRA, 0, 0, 0 }, -+ {VLC_CODEC_GREY, AV_PIX_FMT_GRAY8, 0, 0, 0}, -+ -+ /* Paletized RGB */ -+ {VLC_CODEC_RGBP, AV_PIX_FMT_PAL8, 0, 0, 0}, -+ -+ {VLC_CODEC_GBR_PLANAR, AV_PIX_FMT_GBRP, 0, 0, 0 }, -+ {VLC_CODEC_GBR_PLANAR_9L, AV_PIX_FMT_GBRP9LE, 0, 0, 0 }, -+ {VLC_CODEC_GBR_PLANAR_9B, AV_PIX_FMT_GBRP9BE, 0, 0, 0 }, -+ {VLC_CODEC_GBR_PLANAR_10L, AV_PIX_FMT_GBRP10LE, 0, 0, 0 }, -+ {VLC_CODEC_GBR_PLANAR_10B, AV_PIX_FMT_GBRP10BE, 0, 0, 0 }, -+ -+ /* XYZ */ -+#if LIBAVUTIL_VERSION_CHECK(52, 10, 0, 25, 100) -+ {VLC_CODEC_XYZ12, AV_PIX_FMT_XYZ12, 0xfff0, 0xfff0, 0xfff0}, -+#endif -+ { 0, 0, 0, 0, 0 } -+}; -+ -+static int GetVlcChroma( video_format_t *fmt, int i_ffmpeg_chroma ) -+{ -+ /* TODO FIXME for rgb format we HAVE to set rgb mask/shift */ -+ for( int i = 0; chroma_table[i].i_chroma != 0; i++ ) -+ { -+ if( chroma_table[i].i_chroma_id == i_ffmpeg_chroma ) -+ { -+ fmt->i_rmask = chroma_table[i].i_rmask; -+ fmt->i_gmask = chroma_table[i].i_gmask; -+ fmt->i_bmask = chroma_table[i].i_bmask; -+ fmt->i_chroma = chroma_table[i].i_chroma; -+ return VLC_SUCCESS; -+ } -+ } -+ return VLC_EGENERIC; -+} -+ -+//#include "../codec/cc.h" -+ -+static AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec, ++static AVCodecContext * ++ZcFfmpeg_AllocContext( decoder_t *p_dec, + const AVCodec **restrict codecp ) +{ + unsigned i_codec_id; @@ -5242,7 +5524,7 @@ + const AVCodec *p_codec = NULL; + + /* *** determine codec type *** */ -+ if( !GetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec, ++ if( !ZcGetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec, + &i_codec_id, &psz_namecodec ) ) + return NULL; + @@ -5267,7 +5549,18 @@ + free( psz_decoder ); + } + if( !p_codec ) -+ p_codec = avcodec_find_decoder( i_codec_id ); ++// p_codec = avcodec_find_decoder( i_codec_id ); ++ { ++ if( p_dec->fmt_in.i_codec != VLC_CODEC_HEVC ) ++ p_codec = avcodec_find_decoder(i_codec_id); ++ else ++ { ++ psz_namecodec = rpi_is_model_pi4() ? "hevc" : "hevc_rpi"; ++ msg_Info(p_dec, "Looking for HEVC decoder '%s'", psz_namecodec); ++ p_codec = avcodec_find_decoder_by_name(psz_namecodec); ++ } ++ } ++ + if( !p_codec ) + { + msg_Dbg( p_dec, "codec not found (%s)", psz_namecodec ); @@ -5286,7 +5579,12 @@ + return avctx; +} + -+static int ffmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *ctx, ++/***************************************************************************** ++ * ffmpeg_OpenCodec: ++ *****************************************************************************/ ++ ++static int ++ZcFfmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *ctx, + const AVCodec *codec ) +{ + char *psz_opts = var_InheritString( p_dec, "avcodec-options" ); @@ -5298,7 +5596,7 @@ + free(psz_opts); + } + -+ if (av_rpi_zc_init(ctx) != 0) ++ if (av_rpi_zc_init2(ctx, p_dec, zc_alloc_buf, zc_free_pool) != 0) + { + msg_Err(p_dec, "Failed to init AV ZC"); + return VLC_EGENERIC; @@ -5324,58 +5622,35 @@ + return VLC_SUCCESS; +} + ++//============================================================================ ++// Derived from 3.0.7.1 codec/avcodec/video.c + -+/***************************************************************************** -+ * decoder_sys_t : decoder descriptor -+ *****************************************************************************/ -+struct decoder_sys_t ++static inline void wait_mt(decoder_sys_t *sys) +{ -+ AVCodecContext *p_context; -+ const AVCodec *p_codec; -+ -+ /* Video decoder specific part */ -+ date_t pts; -+ -+ /* Closed captions for decoders */ -+// cc_data_t cc; -+ -+ /* for frame skipping algo */ -+ bool b_hurry_up; -+ bool b_show_corrupted; -+ bool b_from_preroll; -+ enum AVDiscard i_skip_frame; -+ -+ /* how many decoded frames are late */ -+ int i_late_frames; -+ mtime_t i_late_frames_start; -+ mtime_t i_last_late_delay; -+ -+ /* for direct rendering */ -+ bool b_direct_rendering; -+ atomic_bool b_dr_failure; -+ -+ /* Hack to force display of still pictures */ -+ bool b_first_frame; -+ -+ -+ /* */ -+ bool palette_sent; -+ -+ /* VA API */ -+// vlc_va_t *p_va; -+ enum AVPixelFormat pix_fmt; -+ int profile; -+ int level; ++#if 1 ++ // As we only ever update the output in our main thread this lock is ++ // redundant ++ VLC_UNUSED(sys); ++#else ++ vlc_sem_wait(&sys->sem_mt); ++#endif ++} + -+ MMAL_POOL_T * out_pool; -+}; ++static inline void post_mt(decoder_sys_t *sys) ++{ ++#if 1 ++ // As we only ever update the output in our main thread this lock is ++ // redundant ++ VLC_UNUSED(sys); ++#else ++ vlc_sem_post(&sys->sem_mt); ++#endif ++} + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void ffmpeg_InitCodec ( decoder_t * ); -+static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *, -+ const enum PixelFormat * ); +static int DecodeVideo( decoder_t *, block_t * ); +static void Flush( decoder_t * ); + @@ -5401,6 +5676,11 @@ + + video_format_Init(fmt, 0); + ++#if 1 ++ VLC_UNUSED(sw_pix_fmt); ++ if ((fmt->i_chroma = ZcFindVlcChroma(pix_fmt)) == 0) ++ return -1; ++#else + if (pix_fmt == sw_pix_fmt) + { /* software decoding */ + int aligns[AV_NUM_DATA_POINTERS]; @@ -5417,8 +5697,9 @@ + + avcodec_align_dimensions2(ctx, &width, &height, aligns); + } -+// else /* hardware decoding */ -+// fmt->i_chroma = vlc_va_GetChroma(pix_fmt, sw_pix_fmt); ++ else /* hardware decoding */ ++ fmt->i_chroma = vlc_va_GetChroma(pix_fmt, sw_pix_fmt); ++#endif + + if( width == 0 || height == 0 || width > 8192 || height > 8192 || + width < ctx->width || height < ctx->height ) @@ -5470,8 +5751,21 @@ + * __MAX(ctx->ticks_per_frame, 1); + } + -+ if( ctx->color_range == AVCOL_RANGE_JPEG ) ++ /* FIXME we should only set the known values and let the core decide ++ * later of fallbacks, but we can't do that with a boolean */ ++ switch ( ctx->color_range ) ++ { ++ case AVCOL_RANGE_JPEG: + fmt->b_color_range_full = true; ++ break; ++ case AVCOL_RANGE_UNSPECIFIED: ++ fmt->b_color_range_full = !vlc_fourcc_IsYUV( fmt->i_chroma ); ++ break; ++ case AVCOL_RANGE_MPEG: ++ default: ++ fmt->b_color_range_full = false; ++ break; ++ } + + switch( ctx->colorspace ) + { @@ -5569,10 +5863,15 @@ +{ + video_format_t fmt_out; + int val; -+ ++#if TRACE_ALL ++ msg_Dbg(dec, "<<< %s", __func__); ++#endif + val = lavc_GetVideoFormat(dec, &fmt_out, ctx, fmt, swfmt); + if (val) ++ { ++ msg_Dbg(dec, "Failed to get format"); + return val; ++ } + + /* always have date in fields/ticks units */ + if(dec->p_sys->pts.i_divider_num) @@ -5584,7 +5883,7 @@ + __MAX(ctx->ticks_per_frame, 1), + fmt_out.i_frame_rate_base); + -+ fmt_out.p_palette = dec->fmt_out.video.p_palette; ++ fmt_out.p_palette = dec-> fmt_out.video.p_palette; + dec->fmt_out.video.p_palette = NULL; + + es_format_Change(&dec->fmt_out, VIDEO_ES, fmt_out.i_chroma); @@ -5597,516 +5896,646 @@ + dec->fmt_out.video.mastering = dec->fmt_in.video.mastering; + dec->fmt_out.video.lighting = dec->fmt_in.video.lighting; + -+ return decoder_UpdateVideoFormat(dec); ++ val = decoder_UpdateVideoFormat(dec); ++#if TRACE_ALL ++ msg_Dbg(dec, ">>> %s: rv=%d", __func__, val); ++#endif ++ return val; +} + -+/***************************************************************************** -+ * Flush: -+ *****************************************************************************/ -+static void Flush( decoder_t *p_dec ) ++static int OpenVideoCodec( decoder_t *p_dec ) +{ + decoder_sys_t *p_sys = p_dec->p_sys; -+ AVCodecContext *p_context = p_sys->p_context; -+ -+ date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */ -+ p_sys->i_late_frames = 0; -+// cc_Flush( &p_sys->cc ); ++ AVCodecContext *ctx = p_sys->p_context; ++ const AVCodec *codec = p_sys->p_codec; ++ int ret; + -+ /* Abort pictures in order to unblock all avcodec workers threads waiting -+ * for a picture. This will avoid a deadlock between avcodec_flush_buffers -+ * and workers threads */ -+ decoder_AbortPictures( p_dec, true ); ++ if( ctx->extradata_size <= 0 ) ++ { ++ if( codec->id == AV_CODEC_ID_VC1 || ++ codec->id == AV_CODEC_ID_THEORA ) ++ { ++ msg_Warn( p_dec, "waiting for extra data for codec %s", ++ codec->name ); ++ return 1; ++ } ++ } + -+ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */ -+ if( avcodec_is_open( p_context ) ) -+ avcodec_flush_buffers( p_context ); ++ ctx->width = p_dec->fmt_in.video.i_visible_width; ++ ctx->height = p_dec->fmt_in.video.i_visible_height; + -+ /* Reset cancel state to false */ -+ decoder_AbortPictures( p_dec, false ); -+} ++ ctx->coded_width = p_dec->fmt_in.video.i_width; ++ ctx->coded_height = p_dec->fmt_in.video.i_height; + -+static bool check_block_validity( decoder_sys_t *p_sys, block_t *block ) -+{ -+ if( !block) -+ return true; ++ ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel; ++ p_sys->pix_fmt = AV_PIX_FMT_NONE; ++ p_sys->profile = -1; ++ p_sys->level = -1; ++ cc_Init( &p_sys->cc ); + -+ if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) ++ set_video_color_settings( &p_dec->fmt_in.video, ctx ); ++ if( p_dec->fmt_in.video.i_frame_rate_base && ++ p_dec->fmt_in.video.i_frame_rate && ++ (double) p_dec->fmt_in.video.i_frame_rate / ++ p_dec->fmt_in.video.i_frame_rate_base < 6 ) + { -+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */ -+// cc_Flush( &p_sys->cc ); ++ ctx->flags |= AV_CODEC_FLAG_LOW_DELAY; ++ } + -+ p_sys->i_late_frames = 0; -+ if( block->i_flags & BLOCK_FLAG_CORRUPTED ) -+ { -+ block_Release( block ); -+ return false; -+ } ++ post_mt( p_sys ); ++ ret = ZcFfmpeg_OpenCodec( p_dec, ctx, codec ); ++ wait_mt( p_sys ); ++ if( ret < 0 ) ++ return ret; ++ ++ switch( ctx->active_thread_type ) ++ { ++ case FF_THREAD_FRAME: ++ msg_Dbg( p_dec, "using frame thread mode with %d threads", ++ ctx->thread_count ); ++ break; ++ case FF_THREAD_SLICE: ++ msg_Dbg( p_dec, "using slice thread mode with %d threads", ++ ctx->thread_count ); ++ break; ++ case 0: ++ if( ctx->thread_count > 1 ) ++ msg_Warn( p_dec, "failed to enable threaded decoding" ); ++ break; ++ default: ++ msg_Warn( p_dec, "using unknown thread mode with %d threads", ++ ctx->thread_count ); ++ break; + } -+ return true; ++ return 0; +} + -+static bool check_block_being_late( decoder_sys_t *p_sys, block_t *block, mtime_t current_time) ++/***************************************************************************** ++ * InitVideo: initialize the video decoder ++ ***************************************************************************** ++ * the ffmpeg codec will be opened, some memory allocated. The vout is not yet ++ * opened (done after the first decoded frame). ++ *****************************************************************************/ ++static int MmalAvcodecOpenDecoder( vlc_object_t *obj ) +{ -+ if( !block ) -+ return false; -+ if( block->i_flags & BLOCK_FLAG_PREROLL ) ++ decoder_t *p_dec = (decoder_t *)obj; ++ const AVCodec *p_codec; ++ ++ int extra_buffers = var_InheritInteger(p_dec, MMAL_AVCODEC_BUFFERS); ++ ++ if (extra_buffers < 0) + { -+ /* Do not care about late frames when prerolling -+ * TODO avoid decoding of non reference frame -+ * (ie all B except for H264 where it depends only on nal_ref_idc) */ -+ p_sys->i_late_frames = 0; -+ p_sys->b_from_preroll = true; -+ p_sys->i_last_late_delay = INT64_MAX; ++ extra_buffers = p_dec->fmt_in.video.i_height * p_dec->fmt_in.video.i_width >= 1920 * 1088 ? ++ BUFFERS_IN_FLIGHT_UHD : BUFFERS_IN_FLIGHT; + } + -+ if( p_sys->i_late_frames <= 0 ) -+ return false; ++ if (extra_buffers <= 0) ++ { ++ msg_Dbg(p_dec, "%s: extra_buffers=%d - cannot use module", __func__, extra_buffers); ++ return VLC_EGENERIC; ++ } + -+ if( current_time - p_sys->i_late_frames_start > (5*CLOCK_FREQ)) ++ const vcsm_init_type_t vcsm_type = cma_vcsm_init(); ++ const int vcsm_size = ++ vcsm_type == VCSM_INIT_LEGACY ? hw_mmal_get_gpu_mem() : 512 << 20; ++ ++#if 1 + { -+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */ -+ block_Release( block ); -+ p_sys->i_late_frames--; -+ return true; ++ char buf1[5], buf2[5], buf2a[5]; ++ char buf3[5], buf4[5]; ++ uint32_t in_fcc = 0; ++ msg_Dbg(p_dec, "%s: <<< (%s/%s)[%s] %dx%d -> (%s/%s) %dx%d [%s/%d] xb:%d", __func__, ++ str_fourcc(buf1, p_dec->fmt_in.i_codec), ++ str_fourcc(buf2, p_dec->fmt_in.video.i_chroma), ++ str_fourcc(buf2a, in_fcc), ++ p_dec->fmt_in.video.i_width, p_dec->fmt_in.video.i_height, ++ str_fourcc(buf3, p_dec->fmt_out.i_codec), ++ str_fourcc(buf4, p_dec->fmt_out.video.i_chroma), ++ p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height, ++ cma_vcsm_init_str(vcsm_type), vcsm_size, extra_buffers); + } -+ return false; -+} ++#endif + -+static bool check_frame_should_be_dropped( decoder_sys_t *p_sys, AVCodecContext *p_context, bool *b_need_output_picture ) -+{ -+ if( p_sys->i_late_frames <= 4) -+ return false; ++ if( vcsm_type == VCSM_INIT_NONE ) ++ return VLC_EGENERIC; ++#if 1 ++ if( (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC && ++ (vcsm_type == VCSM_INIT_CMA || vcsm_size < (96 << 20))) || ++ (p_dec->fmt_in.i_codec == VLC_CODEC_HEVC && ++ vcsm_size < (128 << 20))) ++ { ++ cma_vcsm_exit(vcsm_type); ++ return VLC_EGENERIC; ++ } ++#endif + -+ *b_need_output_picture = false; -+ if( p_sys->i_late_frames < 12 ) ++ AVCodecContext *p_context = ZcFfmpeg_AllocContext( p_dec, &p_codec ); ++ if( p_context == NULL ) + { -+ p_context->skip_frame = -+ (p_sys->i_skip_frame <= AVDISCARD_NONREF) ? -+ AVDISCARD_NONREF : p_sys->i_skip_frame; ++ cma_vcsm_exit(vcsm_type); ++ return VLC_EGENERIC; + } -+ else ++ ++ int i_val; ++ ++ /* Allocate the memory needed to store the decoder's structure */ ++ decoder_sys_t *p_sys = calloc( 1, sizeof(*p_sys) ); ++ if( unlikely(p_sys == NULL) ) + { -+ /* picture too late, won't decode -+ * but break picture until a new I, and for mpeg4 ...*/ -+ p_sys->i_late_frames--; /* needed else it will never be decrease */ -+ return true; ++ avcodec_free_context( &p_context ); ++ cma_vcsm_exit(vcsm_type); ++ return VLC_ENOMEM; + } -+ return false; -+} + -+static void interpolate_next_pts( decoder_t *p_dec, AVFrame *frame ) -+{ -+ decoder_sys_t *p_sys = p_dec->p_sys; -+ AVCodecContext *p_context = p_sys->p_context; ++ p_dec->p_sys = p_sys; ++ p_sys->p_context = p_context; ++ p_sys->p_codec = p_codec; ++ p_sys->p_dec = p_dec; ++// p_sys->p_va = NULL; ++ p_sys->cma_in_flight_max = extra_buffers; ++ p_sys->vcsm_init_type = vcsm_type; ++ vlc_sem_init( &p_sys->sem_mt, 0 ); + -+ if( date_Get( &p_sys->pts ) == VLC_TS_INVALID || -+ p_sys->pts.i_divider_num == 0 ) -+ return; ++ /* ***** Fill p_context with init values ***** */ ++ p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ? ++ p_dec->fmt_in.i_original_fourcc : p_dec->fmt_in.i_codec ); + -+ int i_tick = p_context->ticks_per_frame; -+ if( i_tick <= 0 ) -+ i_tick = 1; ++ /* ***** Get configuration of ffmpeg plugin ***** */ ++ p_context->workaround_bugs = ++ var_InheritInteger( p_dec, "avcodec-workaround-bugs" ); ++ p_context->err_recognition = ++ var_InheritInteger( p_dec, "avcodec-error-resilience" ); + -+ /* interpolate the next PTS */ -+ date_Increment( &p_sys->pts, i_tick + frame->repeat_pict ); -+} ++ if( var_CreateGetBool( p_dec, "grayscale" ) ) ++ p_context->flags |= AV_CODEC_FLAG_GRAY; + -+static void update_late_frame_count( decoder_t *p_dec, block_t *p_block, mtime_t current_time, mtime_t i_pts ) -+{ -+ decoder_sys_t *p_sys = p_dec->p_sys; -+ /* Update frame late count (except when doing preroll) */ -+ mtime_t i_display_date = VLC_TS_INVALID; -+ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) -+ i_display_date = decoder_GetDisplayDate( p_dec, i_pts ); ++ /* ***** Output always the frames ***** */ ++ p_context->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT; + -+ if( i_display_date > VLC_TS_INVALID && i_display_date <= current_time ) -+ { -+ /* Out of preroll, consider only late frames on rising delay */ -+ if( p_sys->b_from_preroll ) -+ { -+ if( p_sys->i_last_late_delay > current_time - i_display_date ) -+ { -+ p_sys->i_last_late_delay = current_time - i_display_date; -+ return; -+ } -+ p_sys->b_from_preroll = false; -+ } ++ i_val = var_CreateGetInteger( p_dec, "avcodec-skiploopfilter" ); ++ if( i_val >= 4 ) p_context->skip_loop_filter = AVDISCARD_ALL; ++ else if( i_val == 3 ) p_context->skip_loop_filter = AVDISCARD_NONKEY; ++ else if( i_val == 2 ) p_context->skip_loop_filter = AVDISCARD_BIDIR; ++ else if( i_val == 1 ) p_context->skip_loop_filter = AVDISCARD_NONREF; ++ else p_context->skip_loop_filter = AVDISCARD_DEFAULT; + -+ p_sys->i_late_frames++; -+ if( p_sys->i_late_frames == 1 ) -+ p_sys->i_late_frames_start = current_time; ++ if( var_CreateGetBool( p_dec, "avcodec-fast" ) ) ++ p_context->flags2 |= AV_CODEC_FLAG2_FAST; + -+ } -+ else -+ { -+ p_sys->i_late_frames = 0; -+ } -+} ++ /* ***** libavcodec frame skipping ***** */ ++ p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" ); ++ p_sys->b_show_corrupted = var_CreateGetBool( p_dec, "avcodec-corrupted" ); + ++ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-frame" ); ++ if( i_val >= 4 ) p_sys->i_skip_frame = AVDISCARD_ALL; ++ else if( i_val == 3 ) p_sys->i_skip_frame = AVDISCARD_NONKEY; ++ else if( i_val == 2 ) p_sys->i_skip_frame = AVDISCARD_BIDIR; ++ else if( i_val == 1 ) p_sys->i_skip_frame = AVDISCARD_NONREF; ++ else if( i_val == -1 ) p_sys->i_skip_frame = AVDISCARD_NONE; ++ else p_sys->i_skip_frame = AVDISCARD_DEFAULT; ++ p_context->skip_frame = p_sys->i_skip_frame; + -+static int DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p_pic ) -+{ -+// decoder_sys_t *p_sys = p_dec->p_sys; -+ bool format_changed = false; ++ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-idct" ); ++ if( i_val >= 4 ) p_context->skip_idct = AVDISCARD_ALL; ++ else if( i_val == 3 ) p_context->skip_idct = AVDISCARD_NONKEY; ++ else if( i_val == 2 ) p_context->skip_idct = AVDISCARD_BIDIR; ++ else if( i_val == 1 ) p_context->skip_idct = AVDISCARD_NONREF; ++ else if( i_val == -1 ) p_context->skip_idct = AVDISCARD_NONE; ++ else p_context->skip_idct = AVDISCARD_DEFAULT; + -+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) ) -+#define FROM_AVRAT(default_factor, avrat) \ -+(uint64_t)(default_factor) * (avrat).num / (avrat).den -+ const AVFrameSideData *metadata = -+ av_frame_get_side_data( frame, -+ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA ); -+ if ( metadata ) ++ /* ***** libavcodec direct rendering ***** */ ++ p_sys->b_direct_rendering = false; ++ atomic_init(&p_sys->b_dr_failure, false); ++ if( var_CreateGetBool( p_dec, "avcodec-dr" ) && ++ (p_codec->capabilities & AV_CODEC_CAP_DR1) && ++ /* No idea why ... but this fixes flickering on some TSCC streams */ ++ p_sys->p_codec->id != AV_CODEC_ID_TSCC && ++ p_sys->p_codec->id != AV_CODEC_ID_CSCD && ++ p_sys->p_codec->id != AV_CODEC_ID_CINEPAK ) + { -+ const AVMasteringDisplayMetadata *hdr_meta = -+ (const AVMasteringDisplayMetadata *) metadata->data; -+ if ( hdr_meta->has_luminance ) -+ { -+#define ST2086_LUMA_FACTOR 10000 -+ p_pic->format.mastering.max_luminance = -+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->max_luminance); -+ p_pic->format.mastering.min_luminance = -+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->min_luminance); -+ } -+ if ( hdr_meta->has_primaries ) -+ { -+#define ST2086_RED 2 -+#define ST2086_GREEN 0 -+#define ST2086_BLUE 1 -+#define LAV_RED 0 -+#define LAV_GREEN 1 -+#define LAV_BLUE 2 -+#define ST2086_PRIM_FACTOR 50000 -+ p_pic->format.mastering.primaries[ST2086_RED*2 + 0] = -+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][0]); -+ p_pic->format.mastering.primaries[ST2086_RED*2 + 1] = -+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][1]); -+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 0] = -+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][0]); -+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 1] = -+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][1]); -+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 0] = -+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][0]); -+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 1] = -+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][1]); -+ p_pic->format.mastering.white_point[0] = -+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[0]); -+ p_pic->format.mastering.white_point[1] = -+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[1]); -+ } -+ -+ if ( memcmp( &p_dec->fmt_out.video.mastering, -+ &p_pic->format.mastering, -+ sizeof(p_pic->format.mastering) ) ) -+ { -+ p_dec->fmt_out.video.mastering = p_pic->format.mastering; -+ format_changed = true; -+ } -+#undef FROM_AVRAT ++ /* Some codecs set pix_fmt only after the 1st frame has been decoded, ++ * so we need to do another check in ffmpeg_GetFrameBuf() */ ++ p_sys->b_direct_rendering = true; + } ++ ++ p_context->get_format = ZcGetFormat; ++#if 0 ++ p_context->get_format = ffmpeg_GetFormat; ++ /* Always use our get_buffer wrapper so we can calculate the ++ * PTS correctly */ ++ p_context->get_buffer2 = lavc_GetFrame; ++ p_context->opaque = p_dec; +#endif -+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 60, 100 ) ) -+ const AVFrameSideData *metadata_lt = -+ av_frame_get_side_data( frame, -+ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL ); -+ if ( metadata_lt ) ++ ++ int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" ); ++ if( i_thread_count <= 0 ) ++#if 1 + { -+ const AVContentLightMetadata *light_meta = -+ (const AVContentLightMetadata *) metadata_lt->data; -+ p_pic->format.lighting.MaxCLL = light_meta->MaxCLL; -+ p_pic->format.lighting.MaxFALL = light_meta->MaxFALL; -+ if ( memcmp( &p_dec->fmt_out.video.lighting, -+ &p_pic->format.lighting, -+ sizeof(p_pic->format.lighting) ) ) -+ { -+ p_dec->fmt_out.video.lighting = p_pic->format.lighting; -+ format_changed = true; -+ } ++ // Pick 5 threads for everything on Pi except for HEVC where the h/w ++ // really limits the useful size to 3 ++ i_thread_count = p_codec->id == AV_CODEC_ID_HEVC ? 3 : 5; ++ } ++#else ++ { ++ i_thread_count = vlc_GetCPUCount(); ++ if( i_thread_count > 1 ) ++ i_thread_count++; ++ ++ //FIXME: take in count the decoding time ++#if VLC_WINSTORE_APP ++ i_thread_count = __MIN( i_thread_count, 6 ); ++#else ++ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 10 : 6 ); ++#endif + } ++ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 32 : 16 ); +#endif ++ msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count ); ++ p_context->thread_count = i_thread_count; ++ p_context->thread_safe_callbacks = true; + -+ if (format_changed && decoder_UpdateVideoFormat( p_dec )) -+ return -1; -+#if 0 -+ const AVFrameSideData *p_avcc = av_frame_get_side_data( frame, AV_FRAME_DATA_A53_CC ); -+ if( p_avcc ) ++ switch( p_codec->id ) + { -+ cc_Extract( &p_sys->cc, CC_PAYLOAD_RAW, true, p_avcc->data, p_avcc->size ); -+ if( p_sys->cc.b_reorder || p_sys->cc.i_data ) -+ { -+ block_t *p_cc = block_Alloc( p_sys->cc.i_data ); -+ if( p_cc ) -+ { -+ memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data ); -+ if( p_sys->cc.b_reorder ) -+ p_cc->i_dts = p_cc->i_pts = p_pic->date; -+ else -+ p_cc->i_pts = p_cc->i_dts; -+ decoder_cc_desc_t desc; -+ desc.i_608_channels = p_sys->cc.i_608channels; -+ desc.i_708_channels = p_sys->cc.i_708channels; -+ desc.i_reorder_depth = 4; -+ decoder_QueueCc( p_dec, p_cc, &desc ); -+ } -+ cc_Flush( &p_sys->cc ); -+ } ++ case AV_CODEC_ID_MPEG4: ++ case AV_CODEC_ID_H263: ++ p_context->thread_type = 0; ++ break; ++ case AV_CODEC_ID_MPEG1VIDEO: ++ case AV_CODEC_ID_MPEG2VIDEO: ++ p_context->thread_type &= ~FF_THREAD_SLICE; ++ /* fall through */ ++# if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0)) ++ case AV_CODEC_ID_H264: ++ case AV_CODEC_ID_VC1: ++ case AV_CODEC_ID_WMV3: ++ p_context->thread_type &= ~FF_THREAD_FRAME; ++# endif ++ default: ++ break; ++ } ++ ++ if( p_context->thread_type & FF_THREAD_FRAME ) ++ p_dec->i_extra_picture_buffers = 2 * p_context->thread_count; ++ ++ /* ***** misc init ***** */ ++ date_Init(&p_sys->pts, 1, 30001); ++ date_Set(&p_sys->pts, VLC_TS_INVALID); ++ p_sys->b_first_frame = true; ++ p_sys->i_late_frames = 0; ++ p_sys->b_from_preroll = false; ++ ++ /* Set output properties */ ++ if( ZcGetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS ) ++ { ++ /* we are doomed. but not really, because most codecs set their pix_fmt later on */ ++// p_dec->fmt_out.i_codec = VLC_CODEC_I420; ++ p_dec->fmt_out.i_codec = VLC_CODEC_MMAL_ZC_I420; ++ } ++ p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma; ++ ++ p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation; ++ ++ if( p_dec->fmt_in.video.p_palette ) { ++ p_sys->palette_sent = false; ++ p_dec->fmt_out.video.p_palette = malloc( sizeof(video_palette_t) ); ++ if( p_dec->fmt_out.video.p_palette ) ++ *p_dec->fmt_out.video.p_palette = *p_dec->fmt_in.video.p_palette; ++ } else ++ p_sys->palette_sent = true; ++ ++ if ((p_sys->cma_pool = cma_buf_pool_new(p_sys->cma_in_flight_max, p_sys->cma_in_flight_max, false, "mmal_avcodec")) == NULL) ++ { ++ msg_Err(p_dec, "CMA pool alloc failure"); ++ goto fail; ++ } ++ ++ /* ***** init this codec with special data ***** */ ++ ffmpeg_InitCodec( p_dec ); ++ ++ /* ***** Open the codec ***** */ ++ if( OpenVideoCodec( p_dec ) < 0 ) ++ { ++ vlc_sem_destroy( &p_sys->sem_mt ); ++ free( p_sys ); ++ avcodec_free_context( &p_context ); ++ return VLC_EGENERIC; ++ } ++ ++ p_dec->pf_decode = DecodeVideo; ++ p_dec->pf_flush = Flush; ++ ++ /* XXX: Writing input format makes little sense. */ ++ if( p_context->profile != FF_PROFILE_UNKNOWN ) ++ p_dec->fmt_in.i_profile = p_context->profile; ++ if( p_context->level != FF_LEVEL_UNKNOWN ) ++ p_dec->fmt_in.i_level = p_context->level; ++ ++#if 1 ++ // Most of the time we have nothing useful by way of a format here ++ // wait till we've decoded something ++#else ++ // Update output format ++ if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt, ++ p_context->pix_fmt) != 0) ++ { ++ msg_Err(p_dec, "Unable to update format: pix_fmt=%d", p_context->pix_fmt); ++// goto fail; + } +#endif -+ return 0; -+} + ++#if TRACE_ALL ++ msg_Dbg(p_dec, "<<< %s: OK", __func__); ++#endif ++ return VLC_SUCCESS; + ++fail: ++ MmalAvcodecCloseDecoder(VLC_OBJECT(p_dec)); + -+static int OpenVideoCodec( decoder_t *p_dec ) ++#if TRACE_ALL ++ msg_Dbg(p_dec, "<<< %s: FAIL", __func__); ++#endif ++ ++ return VLC_EGENERIC; ++} ++ ++/***************************************************************************** ++ * Flush: ++ *****************************************************************************/ ++static void Flush( decoder_t *p_dec ) +{ + decoder_sys_t *p_sys = p_dec->p_sys; -+ AVCodecContext *ctx = p_sys->p_context; -+ const AVCodec *codec = p_sys->p_codec; -+ int ret; ++ AVCodecContext *p_context = p_sys->p_context; + +#if TRACE_ALL + msg_Dbg(p_dec, "<<< %s", __func__); +#endif + -+ if( ctx->extradata_size <= 0 ) -+ { -+ if( codec->id == AV_CODEC_ID_VC1 || -+ codec->id == AV_CODEC_ID_THEORA ) -+ { -+ msg_Warn( p_dec, "waiting for extra data for codec %s", -+ codec->name ); -+ return 1; -+ } -+ } ++ date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */ ++ p_sys->i_late_frames = 0; ++ cc_Flush( &p_sys->cc ); + -+ ctx->width = p_dec->fmt_in.video.i_visible_width; -+ ctx->height = p_dec->fmt_in.video.i_visible_height; ++ /* Abort pictures in order to unblock all avcodec workers threads waiting ++ * for a picture. This will avoid a deadlock between avcodec_flush_buffers ++ * and workers threads */ ++// It would probably be good to use AbortPicture but that often deadlocks on close ++// and given that we wait for pics in the main thread it should be unneeded (whereas ++// cma is alloced in the depths of ffmpeg on its own threads) ++// decoder_AbortPictures( p_dec, true ); ++ cma_buf_pool_cancel(p_sys->cma_pool); + -+ ctx->coded_width = p_dec->fmt_in.video.i_width; -+ ctx->coded_height = p_dec->fmt_in.video.i_height; ++ post_mt( p_sys ); ++ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */ ++ if( avcodec_is_open( p_context ) ) ++ avcodec_flush_buffers( p_context ); ++ wait_mt( p_sys ); + -+ ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel; -+ p_sys->pix_fmt = AV_PIX_FMT_NONE; -+ p_sys->profile = -1; -+ p_sys->level = -1; -+// cc_Init( &p_sys->cc ); ++ /* Reset cancel state to false */ ++ cma_buf_pool_uncancel(p_sys->cma_pool); ++// decoder_AbortPictures( p_dec, false ); + -+ set_video_color_settings( &p_dec->fmt_in.video, ctx ); ++#if TRACE_ALL ++ msg_Dbg(p_dec, ">>> %s", __func__); ++#endif + -+ ret = ffmpeg_OpenCodec( p_dec, ctx, codec ); -+ if( ret < 0 ) -+ return ret; ++} + -+ switch( ctx->active_thread_type ) ++static bool check_block_validity( decoder_sys_t *p_sys, block_t *block ) ++{ ++ if( !block) ++ return true; ++ ++ if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) + { -+ case FF_THREAD_FRAME: -+ msg_Dbg( p_dec, "using frame thread mode with %d threads", -+ ctx->thread_count ); -+ break; -+ case FF_THREAD_SLICE: -+ msg_Dbg( p_dec, "using slice thread mode with %d threads", -+ ctx->thread_count ); -+ break; -+ case 0: -+ if( ctx->thread_count > 1 ) -+ msg_Warn( p_dec, "failed to enable threaded decoding" ); -+ break; -+ default: -+ msg_Warn( p_dec, "using unknown thread mode with %d threads", -+ ctx->thread_count ); -+ break; ++ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */ ++ cc_Flush( &p_sys->cc ); ++ ++ p_sys->i_late_frames = 0; ++ if( block->i_flags & BLOCK_FLAG_CORRUPTED ) ++ { ++ block_Release( block ); ++ return false; ++ } + } -+ return 0; ++ return true; +} + -+static MMAL_BOOL_T -+zc_buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata) ++static bool check_block_being_late( decoder_sys_t *p_sys, block_t *block, mtime_t current_time) +{ -+ const AVRpiZcRefPtr fr_ref = userdata; -+ VLC_UNUSED(buf); ++ if( !block ) ++ return false; ++ if( block->i_flags & BLOCK_FLAG_PREROLL ) ++ { ++ /* Do not care about late frames when prerolling ++ * TODO avoid decoding of non reference frame ++ * (ie all B except for H264 where it depends only on nal_ref_idc) */ ++ p_sys->i_late_frames = 0; ++ p_sys->b_from_preroll = true; ++ p_sys->i_last_late_delay = INT64_MAX; ++ } + -+ av_rpi_zc_unref(fr_ref); ++ if( p_sys->i_late_frames <= 0 ) ++ return false; + -+ return MMAL_FALSE; ++ if( current_time - p_sys->i_late_frames_start > (5*CLOCK_FREQ)) ++ { ++ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */ ++ block_Release( block ); ++ p_sys->i_late_frames--; ++ return true; ++ } ++ return false; +} + -+static MMAL_FOURCC_T -+avfmt_to_mmal(const int avfmt) ++static bool check_frame_should_be_dropped( decoder_sys_t *p_sys, AVCodecContext *p_context, bool *b_need_output_picture ) +{ -+ switch( avfmt ) ++ if( p_sys->i_late_frames <= 4) ++ return false; ++ ++ *b_need_output_picture = false; ++ if( p_sys->i_late_frames < 12 ) + { -+ case AV_PIX_FMT_SAND128: -+ return MMAL_ENCODING_YUVUV128; -+ case AV_PIX_FMT_SAND64_10: -+ return MMAL_ENCODING_YUVUV64_10; -+ default: -+ break; ++ p_context->skip_frame = ++ (p_sys->i_skip_frame <= AVDISCARD_NONREF) ? ++ AVDISCARD_NONREF : p_sys->i_skip_frame; + } -+ return MMAL_ENCODING_UNKNOWN; ++ else ++ { ++ /* picture too late, won't decode ++ * but break picture until a new I, and for mpeg4 ...*/ ++ p_sys->i_late_frames--; /* needed else it will never be decrease */ ++ return true; ++ } ++ return false; +} + -+/***************************************************************************** -+ * DecodeBlock: Called to decode one or more frames -+ *****************************************************************************/ ++static mtime_t interpolate_next_pts( decoder_t *p_dec, AVFrame *frame ) ++{ ++ decoder_sys_t *p_sys = p_dec->p_sys; ++ AVCodecContext *p_context = p_sys->p_context; + ++ if( date_Get( &p_sys->pts ) == VLC_TS_INVALID || ++ p_sys->pts.i_divider_num == 0 ) ++ return VLC_TS_INVALID; + -+// Returns -+// -ve error -+// 0 Need more input (EAGAIN) -+// 1 Frame decoded (dropped or Qed) -+// 2 Decode err -+// 3 EOF ++ int i_tick = p_context->ticks_per_frame; ++ if( i_tick <= 0 ) ++ i_tick = 1; + -+static int rx_frame(decoder_t * const p_dec, decoder_sys_t * const p_sys, AVCodecContext * const p_context) ++ /* interpolate the next PTS */ ++ return date_Increment( &p_sys->pts, i_tick + frame->repeat_pict ); ++} ++ ++static void update_late_frame_count( decoder_t *p_dec, block_t *p_block, ++ mtime_t current_time, mtime_t i_pts, ++ mtime_t i_next_pts ) +{ -+ AVFrame * frame = av_frame_alloc(); -+ picture_t * p_pic = NULL; -+ int ret; ++ decoder_sys_t *p_sys = p_dec->p_sys; ++ /* Update frame late count (except when doing preroll) */ ++ mtime_t i_display_date = VLC_TS_INVALID; ++ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) ++ i_display_date = decoder_GetDisplayDate( p_dec, i_pts ); + -+ if (frame == NULL) -+ return VLC_ENOMEM; ++ mtime_t i_threshold = i_next_pts != VLC_TS_INVALID ? (i_next_pts - i_pts) / 2 : 20000; ++ ++ if( i_display_date > VLC_TS_INVALID && i_display_date + i_threshold <= current_time ) ++ { ++ /* Out of preroll, consider only late frames on rising delay */ ++ if( p_sys->b_from_preroll ) ++ { ++ if( p_sys->i_last_late_delay > current_time - i_display_date ) ++ { ++ p_sys->i_last_late_delay = current_time - i_display_date; ++ return; ++ } ++ p_sys->b_from_preroll = false; ++ } ++ ++ p_sys->i_late_frames++; ++ if( p_sys->i_late_frames == 1 ) ++ p_sys->i_late_frames_start = current_time; + -+ ret = avcodec_receive_frame(p_context, frame); ++ } ++ else ++ { ++ p_sys->i_late_frames = 0; ++ } ++} ++ ++ ++static int DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p_pic ) ++{ ++ decoder_sys_t *p_sys = p_dec->p_sys; ++ bool format_changed = false; ++ ++#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) ) ++#define FROM_AVRAT(default_factor, avrat) \ ++(uint64_t)(default_factor) * (avrat).num / (avrat).den ++ const AVFrameSideData *metadata = ++ av_frame_get_side_data( frame, ++ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA ); ++ if ( metadata ) ++ { ++ const AVMasteringDisplayMetadata *hdr_meta = ++ (const AVMasteringDisplayMetadata *) metadata->data; ++ if ( hdr_meta->has_luminance ) ++ { ++#define ST2086_LUMA_FACTOR 10000 ++ p_pic->format.mastering.max_luminance = ++ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->max_luminance); ++ p_pic->format.mastering.min_luminance = ++ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->min_luminance); ++ } ++ if ( hdr_meta->has_primaries ) ++ { ++#define ST2086_RED 2 ++#define ST2086_GREEN 0 ++#define ST2086_BLUE 1 ++#define LAV_RED 0 ++#define LAV_GREEN 1 ++#define LAV_BLUE 2 ++#define ST2086_PRIM_FACTOR 50000 ++ p_pic->format.mastering.primaries[ST2086_RED*2 + 0] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][0]); ++ p_pic->format.mastering.primaries[ST2086_RED*2 + 1] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][1]); ++ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 0] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][0]); ++ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 1] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][1]); ++ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 0] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][0]); ++ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 1] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][1]); ++ p_pic->format.mastering.white_point[0] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[0]); ++ p_pic->format.mastering.white_point[1] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[1]); ++ } + -+ if (ret != 0) -+ { -+ av_frame_free(&frame); -+ switch (ret) ++ if ( memcmp( &p_dec->fmt_out.video.mastering, ++ &p_pic->format.mastering, ++ sizeof(p_pic->format.mastering) ) ) + { -+ case AVERROR(EAGAIN): -+ return 0; -+ -+ case AVERROR(ENOMEM): -+ case AVERROR(EINVAL): -+ msg_Err(p_dec, "avcodec_receive_frame critical error"); -+ return VLC_EGENERIC; -+ -+ case AVERROR_EOF: -+ msg_Dbg(p_dec, "Rx EOF"); -+ avcodec_flush_buffers(p_context); -+ return 2; -+ -+ default: -+ msg_Warn(p_dec, "Decode error: %d", ret); -+ return 1; ++ p_dec->fmt_out.video.mastering = p_pic->format.mastering; ++ format_changed = true; + } ++#undef FROM_AVRAT + } -+ -+ /* Compute the PTS */ -+#ifdef FF_API_PKT_PTS -+ mtime_t i_pts = frame->pts; -+#else -+ mtime_t i_pts = frame->pkt_pts; +#endif -+ if (i_pts == AV_NOPTS_VALUE ) -+ i_pts = frame->pkt_dts; -+ -+ if( i_pts == AV_NOPTS_VALUE ) -+ i_pts = date_Get( &p_sys->pts ); -+ -+ /* Interpolate the next PTS */ -+ if( i_pts > VLC_TS_INVALID ) -+ date_Set( &p_sys->pts, i_pts ); -+ -+ interpolate_next_pts( p_dec, frame ); -+ -+// update_late_frame_count( p_dec, p_block, current_time, i_pts); //********************* -+ -+ if( ( /* !p_sys->p_va && */ !frame->linesize[0] ) || -+ ( p_dec->b_frame_drop_allowed && (frame->flags & AV_FRAME_FLAG_CORRUPT) && -+ !p_sys->b_show_corrupted ) ) -+ { -+ msg_Dbg(p_dec, "Frame drop"); -+ av_frame_free(&frame); -+ return 1; -+ } -+ -+ lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt, -+ p_context->pix_fmt); -+ ++#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 60, 100 ) ) ++ const AVFrameSideData *metadata_lt = ++ av_frame_get_side_data( frame, ++ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL ); ++ if ( metadata_lt ) + { -+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(p_sys->out_pool->queue); -+// MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(p_sys->out_pool->queue); -+ if (buf == NULL) { -+ msg_Err(p_dec, "MMAL buffer alloc failure"); -+ av_frame_free(&frame); -+ return 1; -+ } -+ -+ mmal_buffer_header_reset(buf); // length, offset, flags, pts, dts -+ buf->cmd = 0; -+ buf->user_data = NULL; -+ -+ { -+ const AVRpiZcRefPtr fr_buf = av_rpi_zc_ref(p_context, frame, frame->format, 0); -+ -+ if (fr_buf == NULL) { -+ mmal_buffer_header_release(buf); -+ av_frame_free(&frame); -+ return VLC_ENOMEM; -+ } -+ -+ const intptr_t vc_handle = (intptr_t)av_rpi_zc_vc_handle(fr_buf); -+ -+ buf->data = (uint8_t *)vc_handle; // Cast our handle to a pointer for mmal - 2 steps to avoid gcc warnings -+ buf->offset = av_rpi_zc_offset(fr_buf); -+ buf->length = av_rpi_zc_length(fr_buf); -+ buf->alloc_size = av_rpi_zc_numbytes(fr_buf); -+ buf->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END; -+ -+ mmal_buffer_header_pre_release_cb_set(buf, zc_buf_pre_release_cb, fr_buf); -+ } -+ -+ p_pic = decoder_NewPicture(p_dec); // *** Really want an empy pic -+ if (p_pic == NULL) ++ const AVContentLightMetadata *light_meta = ++ (const AVContentLightMetadata *) metadata_lt->data; ++ p_pic->format.lighting.MaxCLL = light_meta->MaxCLL; ++ p_pic->format.lighting.MaxFALL = light_meta->MaxFALL; ++ if ( memcmp( &p_dec->fmt_out.video.lighting, ++ &p_pic->format.lighting, ++ sizeof(p_pic->format.lighting) ) ) + { -+ msg_Err(p_dec, "Picture alloc failure"); -+ mmal_buffer_header_release(buf); -+ av_frame_free(&frame); -+ return VLC_ENOMEM; ++ p_dec->fmt_out.video.lighting = p_pic->format.lighting; ++ format_changed = true; + } -+ -+ p_pic->context = hw_mmal_gen_context(avfmt_to_mmal(frame->format), buf, NULL); + } ++#endif + ++ if (format_changed && decoder_UpdateVideoFormat( p_dec )) ++ return -1; + -+ if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den ) ++ const AVFrameSideData *p_avcc = av_frame_get_side_data( frame, AV_FRAME_DATA_A53_CC ); ++ if( p_avcc ) + { -+ /* Fetch again the aspect ratio in case it changed */ -+ p_dec->fmt_out.video.i_sar_num -+ = p_context->sample_aspect_ratio.num; -+ p_dec->fmt_out.video.i_sar_den -+ = p_context->sample_aspect_ratio.den; -+ -+ if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den ) ++ cc_Extract( &p_sys->cc, CC_PAYLOAD_RAW, true, p_avcc->data, p_avcc->size ); ++ if( p_sys->cc.b_reorder || p_sys->cc.i_data ) + { -+ p_dec->fmt_out.video.i_sar_num = 1; -+ p_dec->fmt_out.video.i_sar_den = 1; ++ block_t *p_cc = block_Alloc( p_sys->cc.i_data ); ++ if( p_cc ) ++ { ++ memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data ); ++ if( p_sys->cc.b_reorder ) ++ p_cc->i_dts = p_cc->i_pts = p_pic->date; ++ else ++ p_cc->i_pts = p_cc->i_dts; ++ decoder_cc_desc_t desc; ++ desc.i_608_channels = p_sys->cc.i_608channels; ++ desc.i_708_channels = p_sys->cc.i_708channels; ++ desc.i_reorder_depth = 4; ++ decoder_QueueCc( p_dec, p_cc, &desc ); ++ } ++ cc_Flush( &p_sys->cc ); + } + } -+ -+ p_pic->date = i_pts; -+ /* Hack to force display of still pictures */ -+ p_pic->b_force = p_sys->b_first_frame; -+ p_pic->i_nb_fields = 2 + frame->repeat_pict; -+ p_pic->b_progressive = !frame->interlaced_frame; -+ p_pic->b_top_field_first = frame->top_field_first; -+ -+ if (DecodeSidedata(p_dec, frame, p_pic)) -+ i_pts = VLC_TS_INVALID; -+ -+ av_frame_free(&frame); -+ -+ p_sys->b_first_frame = false; -+ decoder_QueueVideo(p_dec, p_pic); -+ -+ return 1; ++ return 0; +} + ++/***************************************************************************** ++ * DecodeBlock: Called to decode one or more frames ++ *****************************************************************************/ + -+ -+ -+ -+static int DecodeVideo( decoder_t *p_dec, block_t * p_block) ++static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error ) +{ + decoder_sys_t *p_sys = p_dec->p_sys; + AVCodecContext *p_context = p_sys->p_context; @@ -6115,8 +6544,18 @@ + + /* Boolean for END_OF_SEQUENCE */ + bool eos_spotted = false; ++ ++#if TRACE_ALL ++ msg_Dbg(p_dec, "<<< %s: (buf_size=%d)", __func__, pp_block == NULL || *pp_block == NULL ? 0 : (*pp_block)->i_buffer); ++#endif ++ ++ block_t *p_block; + mtime_t current_time; -+ int rv = VLCDEC_SUCCESS; ++ picture_t *p_pic = NULL; ++ AVFrame *frame = NULL; ++ ++ // By default we are OK ++ *error = false; + + if( !p_context->extradata_size && p_dec->fmt_in.i_extra ) + { @@ -6125,30 +6564,30 @@ + OpenVideoCodec( p_dec ); + } + ++ p_block = pp_block ? *pp_block : NULL; + if(!p_block && !(p_sys->p_codec->capabilities & AV_CODEC_CAP_DELAY) ) -+ return VLCDEC_SUCCESS; ++ return NULL; + + if( !avcodec_is_open( p_context ) ) + { + if( p_block ) + block_Release( p_block ); -+ return VLCDEC_ECRITICAL; ++ return NULL; + } + + if( !check_block_validity( p_sys, p_block ) ) -+ return VLCDEC_SUCCESS; ++ return NULL; + + current_time = mdate(); + if( p_dec->b_frame_drop_allowed && check_block_being_late( p_sys, p_block, current_time) ) + { + msg_Err( p_dec, "more than 5 seconds of late video -> " + "dropping frame (computer too slow ?)" ); -+ return VLCDEC_SUCCESS; ++ return NULL; + } + + + /* A good idea could be to decode all I pictures and see for the other */ -+ b_need_output_picture = true; + + /* Defaults that if we aren't in prerolling, we want output picture + same for if we are flushing (p_block==NULL) */ @@ -6170,7 +6609,7 @@ + if( p_block ) + block_Release( p_block ); + msg_Warn( p_dec, "More than 11 late frames, dropping frame" ); -+ return VLCDEC_SUCCESS; ++ return NULL; + } + } + if( !b_need_output_picture ) @@ -6191,74 +6630,318 @@ + p_block = block_Realloc( p_block, 0, + p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE ); + if( !p_block ) -+ return VLCDEC_ECRITICAL; -+ ++ return NULL; + p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE; ++ *pp_block = p_block; + memset( p_block->p_buffer + p_block->i_buffer, 0, + FF_INPUT_BUFFER_PADDING_SIZE ); + } + -+ AVPacket pkt = {.data = NULL, .size = 0}; -+ -+ av_init_packet( &pkt ); -+ if( p_block && p_block->i_buffer > 0 ) ++ while( !p_block || p_block->i_buffer > 0 || eos_spotted ) + { -+ pkt.data = p_block->p_buffer; -+ pkt.size = p_block->i_buffer; -+ pkt.pts = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : AV_NOPTS_VALUE; -+ pkt.dts = p_block->i_dts > VLC_TS_INVALID ? p_block->i_dts : AV_NOPTS_VALUE; -+ } ++ int i_used; ++ AVPacket pkt; + -+ if( !p_sys->palette_sent ) -+ { -+ uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); -+ if (pal) { -+ memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE); -+ p_sys->palette_sent = true; ++ post_mt( p_sys ); ++ ++ av_init_packet( &pkt ); ++ if( p_block && p_block->i_buffer > 0 ) ++ { ++ pkt.data = p_block->p_buffer; ++ pkt.size = p_block->i_buffer; ++ pkt.pts = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : AV_NOPTS_VALUE; ++ pkt.dts = p_block->i_dts > VLC_TS_INVALID ? p_block->i_dts : AV_NOPTS_VALUE; ++ } ++ else ++ { ++ /* Return delayed frames if codec has CODEC_CAP_DELAY */ ++ pkt.data = NULL; ++ pkt.size = 0; + } -+ } + -+#if LIBAVCODEC_VERSION_CHECK( 57, 0, 0xFFFFFFFFU, 64, 101 ) -+ if( !b_need_output_picture ) -+ pkt.flags |= AV_PKT_FLAG_DISCARD; ++ if( !p_sys->palette_sent ) ++ { ++ uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); ++ if (pal) { ++ memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE); ++ p_sys->palette_sent = true; ++ } ++ } ++ ++ /* Make sure we don't reuse the same timestamps twice */ ++ if( p_block ) ++ { ++ p_block->i_pts = ++ p_block->i_dts = VLC_TS_INVALID; ++ } ++ ++ int ret = avcodec_send_packet(p_context, &pkt); ++ if( ret != 0 && ret != AVERROR(EAGAIN) ) ++ { ++ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL)) ++ { ++ msg_Err(p_dec, "avcodec_send_packet critical error"); ++ *error = true; ++ } ++ av_packet_unref( &pkt ); ++ break; ++ } ++ i_used = ret != AVERROR(EAGAIN) ? pkt.size : 0; ++ av_packet_unref( &pkt ); ++ ++ frame = av_frame_alloc(); ++ if (unlikely(frame == NULL)) ++ { ++ *error = true; ++ break; ++ } ++ ++ ret = avcodec_receive_frame(p_context, frame); ++ if( ret != 0 && ret != AVERROR(EAGAIN) ) ++ { ++ msg_Dbg(p_dec, "No receive"); ++ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL)) ++ { ++ msg_Err(p_dec, "avcodec_receive_frame critical error"); ++ *error = true; ++ } ++ av_frame_free(&frame); ++ /* After draining, we need to reset decoder with a flush */ ++ if( ret == AVERROR_EOF ) ++ avcodec_flush_buffers( p_sys->p_context ); ++ break; ++ } ++ bool not_received_frame = ret; ++ ++ wait_mt( p_sys ); ++ ++ if( eos_spotted ) ++ p_sys->b_first_frame = true; ++ ++ if( p_block ) ++ { ++ if( p_block->i_buffer <= 0 ) ++ eos_spotted = false; ++ ++ /* Consumed bytes */ ++ p_block->p_buffer += i_used; ++ p_block->i_buffer -= i_used; ++ } ++ ++ /* Nothing to display */ ++ if( not_received_frame ) ++ { ++// msg_Dbg(p_dec, "No rx: used=%d", i_used); ++ av_frame_free(&frame); ++ if( i_used == 0 ) break; ++ continue; ++ } ++ ++ /* Compute the PTS */ ++#ifdef FF_API_PKT_PTS ++ mtime_t i_pts = frame->pts; ++#else ++ mtime_t i_pts = frame->pkt_pts; +#endif ++ if (i_pts == AV_NOPTS_VALUE ) ++ i_pts = frame->pkt_dts; + -+ int ret = avcodec_send_packet(p_context, &pkt); ++ if( i_pts == AV_NOPTS_VALUE ) ++ i_pts = date_Get( &p_sys->pts ); + -+ if (ret == AVERROR(EAGAIN)) -+ { -+ // Cannot send more data until output drained - so do drain -+ while (rx_frame(p_dec, p_sys, p_context) == 1) -+ /* Loop */; ++ /* Interpolate the next PTS */ ++ if( i_pts > VLC_TS_INVALID ) ++ date_Set( &p_sys->pts, i_pts ); + -+ // And try again - should not fail the same way -+ ret = avcodec_send_packet(p_context, &pkt); -+ } ++ const mtime_t i_next_pts = interpolate_next_pts(p_dec, frame); + -+ // Now done with pkt & block -+ av_packet_unref(&pkt); -+ if (p_block != NULL) -+ { -+ block_Release( p_block ); -+ p_block = NULL; -+ } ++ update_late_frame_count( p_dec, p_block, current_time, i_pts, i_next_pts); + -+ if (ret != 0) -+ { -+ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL)) ++ if( !b_need_output_picture || ++// ( !p_sys->p_va && !frame->linesize[0] ) || ++ ( !frame->linesize[0] ) || ++ ( p_dec->b_frame_drop_allowed && (frame->flags & AV_FRAME_FLAG_CORRUPT) && ++ !p_sys->b_show_corrupted ) ) ++ { ++ av_frame_free(&frame); ++// msg_Dbg(p_dec, "Bad frame"); ++ continue; ++ } ++ ++ if( p_context->pix_fmt == AV_PIX_FMT_PAL8 ++ && !p_dec->fmt_out.video.p_palette ) ++ { ++ /* See AV_PIX_FMT_PAL8 comment in avc_GetVideoFormat(): update the ++ * fmt_out palette and change the fmt_out chroma to request a new ++ * vout */ ++ assert( p_dec->fmt_out.video.i_chroma != VLC_CODEC_RGBP ); ++ ++ video_palette_t *p_palette; ++ p_palette = p_dec->fmt_out.video.p_palette ++ = malloc( sizeof(video_palette_t) ); ++ if( !p_palette ) ++ { ++ *error = true; ++ av_frame_free(&frame); ++ break; ++ } ++ static_assert( sizeof(p_palette->palette) == AVPALETTE_SIZE, ++ "Palette size mismatch between vlc and libavutil" ); ++ assert( frame->data[1] != NULL ); ++ memcpy( p_palette->palette, frame->data[1], AVPALETTE_SIZE ); ++ p_palette->i_entries = AVPALETTE_COUNT; ++ p_dec->fmt_out.video.i_chroma = VLC_CODEC_RGBP; ++ if( decoder_UpdateVideoFormat( p_dec ) ) ++ { ++ av_frame_free(&frame); ++ continue; ++ } ++ } ++ ++#if 1 ++ { ++ cma_buf_t * const cb = av_rpi_zc_buf_v(frame->buf[0]); ++ ++ if (cb == NULL) ++ { ++ msg_Err(p_dec, "Frame has no attached CMA buffer"); ++ goto fail; ++ } ++ ++ if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt, ++ p_context->pix_fmt) != 0) ++ { ++ msg_Err(p_dec, "Failed to update format"); ++ goto fail; ++ } ++ ++ if ((p_pic = decoder_NewPicture(p_dec)) == NULL) ++ { ++ msg_Err(p_dec, "Failed to allocate pic"); ++ goto fail; ++ } ++ ++ if (cma_buf_pic_attach(cma_buf_ref(cb), p_pic) != 0) ++ { ++ cma_buf_unref(cb); // Undo the in_flight ++ char dbuf0[5]; ++ msg_Err(p_dec, "Failed to attach bufs to pic: fmt=%s", str_fourcc(dbuf0, p_pic->format.i_chroma)); ++ goto fail; ++ } ++ ++ // ****** Set planes etc. ++ set_pic_from_frame(p_pic, frame); ++ } ++#else ++ picture_t *p_pic = frame->opaque; ++ if( p_pic == NULL ) ++ { /* When direct rendering is not used, get_format() and get_buffer() ++ * might not be called. The output video format must be set here ++ * then picture buffer can be allocated. */ ++ if (p_sys->p_va == NULL ++ && lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt, ++ p_context->pix_fmt) == 0) ++ p_pic = decoder_NewPicture(p_dec); ++ ++ if( !p_pic ) ++ { ++ av_frame_free(&frame); ++ break; ++ } ++ ++ /* Fill picture_t from AVFrame */ ++ if( lavc_CopyPicture( p_dec, p_pic, frame ) != VLC_SUCCESS ) ++ { ++ av_frame_free(&frame); ++ picture_Release( p_pic ); ++ break; ++ } ++ } ++ else ++ { ++ /* Some codecs can return the same frame multiple times. By the ++ * time that the same frame is returned a second time, it will be ++ * too late to clone the underlying picture. So clone proactively. ++ * A single picture CANNOT be queued multiple times. ++ */ ++ p_pic = picture_Clone( p_pic ); ++ if( unlikely(p_pic == NULL) ) ++ { ++ av_frame_free(&frame); ++ break; ++ } ++ } ++#endif ++ ++ if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den ) ++ { ++ /* Fetch again the aspect ratio in case it changed */ ++ p_dec->fmt_out.video.i_sar_num ++ = p_context->sample_aspect_ratio.num; ++ p_dec->fmt_out.video.i_sar_den ++ = p_context->sample_aspect_ratio.den; ++ ++ if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den ) ++ { ++ p_dec->fmt_out.video.i_sar_num = 1; ++ p_dec->fmt_out.video.i_sar_den = 1; ++ } ++ } ++ ++ p_pic->date = i_pts; ++ /* Hack to force display of still pictures */ ++ p_pic->b_force = p_sys->b_first_frame; ++ p_pic->i_nb_fields = 2 + frame->repeat_pict; ++ p_pic->b_progressive = !frame->interlaced_frame; ++ p_pic->b_top_field_first = frame->top_field_first; ++ ++ if (DecodeSidedata(p_dec, frame, p_pic)) ++ i_pts = VLC_TS_INVALID; ++ ++ av_frame_free(&frame); ++ ++ /* Send decoded frame to vout */ ++ if (i_pts > VLC_TS_INVALID) + { -+ msg_Err(p_dec, "avcodec_send_packet critical error"); -+ rv = VLCDEC_ECRITICAL; ++ p_sys->b_first_frame = false; ++#if TRACE_ALL ++ msg_Dbg(p_dec, ">>> %s: Got pic", __func__); ++#endif ++ return p_pic; + } ++ else ++ picture_Release( p_pic ); + } + -+ while (rx_frame(p_dec, p_sys, p_context) == 1) -+ /* Loop */; ++ if( p_block ) ++ block_Release( p_block ); ++ ++#if TRACE_ALL ++ msg_Dbg(p_dec, ">>> %s: NULL", __func__); ++#endif ++ return NULL; + -+ if (eos_spotted) -+ p_sys->b_first_frame = true; ++fail: ++#if TRACE_ALL ++ msg_Dbg(p_dec, ">>> %s: FAIL", __func__); ++#endif ++ av_frame_free(&frame); ++ if (p_pic != NULL) ++ picture_Release(p_pic); ++ if (p_block != NULL) ++ block_Release(p_block); ++ *error = true; ++ return NULL; ++} + -+ return rv; ++static int DecodeVideo( decoder_t *p_dec, block_t *p_block ) ++{ ++ block_t **pp_block = p_block ? &p_block : NULL; ++ picture_t *p_pic; ++ bool error = false; ++ while( ( p_pic = DecodeBlock( p_dec, pp_block, &error ) ) != NULL ) ++ decoder_QueueVideo( p_dec, p_pic ); ++ return error ? VLCDEC_ECRITICAL : VLCDEC_SUCCESS; +} + +/***************************************************************************** @@ -6267,31 +6950,38 @@ + * This function is called when the thread ends after a successful + * initialization. + *****************************************************************************/ -+static void MmalAvcodecCloseDecoder(vlc_object_t *obj) ++static void MmalAvcodecCloseDecoder( vlc_object_t *obj ) +{ + decoder_t *p_dec = (decoder_t *)obj; + decoder_sys_t *p_sys = p_dec->p_sys; + AVCodecContext *ctx = p_sys->p_context; +// void *hwaccel_context; + ++ msg_Dbg(obj, "<<< %s", __func__); ++ ++ post_mt( p_sys ); ++ ++ cma_buf_pool_cancel(p_sys->cma_pool); // Abort any pending frame allocs ++ + /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */ + if( avcodec_is_open( ctx ) ) + avcodec_flush_buffers( ctx ); + -+// cc_Flush( &p_sys->cc ); ++ av_rpi_zc_uninit2(ctx); ++ ++ wait_mt( p_sys ); + -+ avcodec_close(ctx); -+ av_rpi_zc_uninit(ctx); ++ cc_Flush( &p_sys->cc ); + +// hwaccel_context = ctx->hwaccel_context; + avcodec_free_context( &ctx ); + -+ if( p_sys->out_pool != NULL ) -+ mmal_pool_destroy(p_sys->out_pool); -+ +// if( p_sys->p_va ) +// vlc_va_Delete( p_sys->p_va, &hwaccel_context ); + ++ cma_vcsm_exit(p_sys->vcsm_init_type); ++ ++ vlc_sem_destroy( &p_sys->sem_mt ); + free( p_sys ); +} + @@ -6316,396 +7006,66 @@ + if( !p ) + return; + -+ memcpy( &p[0], "SVQ3", 4 ); -+ memset( &p[4], 0, 8 ); -+ memcpy( &p[12], p_dec->fmt_in.p_extra, i_size ); -+ -+ /* Now remove all atoms before the SMI one */ -+ if( p_sys->p_context->extradata_size > 0x5a && -+ strncmp( (char*)&p[0x56], "SMI ", 4 ) ) -+ { -+ uint8_t *psz = &p[0x52]; -+ -+ while( psz < &p[p_sys->p_context->extradata_size - 8] ) -+ { -+ uint_fast32_t atom_size = GetDWBE( psz ); -+ if( atom_size <= 1 ) -+ { -+ /* FIXME handle 1 as long size */ -+ break; -+ } -+ if( !strncmp( (char*)&psz[4], "SMI ", 4 ) ) -+ { -+ memmove( &p[0x52], psz, -+ &p[p_sys->p_context->extradata_size] - psz ); -+ break; -+ } -+ -+ psz += atom_size; -+ } -+ } -+ } -+ else -+ { -+ p_sys->p_context->extradata_size = i_size; -+ p_sys->p_context->extradata = -+ av_malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE ); -+ if( p_sys->p_context->extradata ) -+ { -+ memcpy( p_sys->p_context->extradata, -+ p_dec->fmt_in.p_extra, i_size ); -+ memset( p_sys->p_context->extradata + i_size, -+ 0, FF_INPUT_BUFFER_PADDING_SIZE ); -+ } -+ } -+} -+ -+ -+static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context, -+ const enum PixelFormat *pi_fmt ) -+{ -+ decoder_t *p_dec = p_context->opaque; -+ decoder_sys_t *p_sys = p_dec->p_sys; -+ video_format_t fmt; -+ -+ /* Enumerate available formats */ -+ enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt); -+// bool can_hwaccel = false; -+ -+ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++) -+ { -+ const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(pi_fmt[i]); -+ if (dsc == NULL) -+ continue; -+ bool hwaccel = (dsc->flags & AV_PIX_FMT_FLAG_HWACCEL) != 0; -+ -+ msg_Dbg( p_dec, "available %sware decoder output format %d (%s)", -+ hwaccel ? "hard" : "soft", pi_fmt[i], dsc->name ); -+// if (hwaccel) -+// can_hwaccel = true; -+ } -+ -+ /* If the format did not actually change (e.g. seeking), try to reuse the -+ * existing output format, and if present, hardware acceleration back-end. -+ * This avoids resetting the pipeline downstream. This also avoids -+ * needlessly probing for hardware acceleration support. */ -+ if (p_sys->pix_fmt != AV_PIX_FMT_NONE -+ && lavc_GetVideoFormat(p_dec, &fmt, p_context, p_sys->pix_fmt, swfmt) == 0 -+ && fmt.i_width == p_dec->fmt_out.video.i_width -+ && fmt.i_height == p_dec->fmt_out.video.i_height -+ && p_context->profile == p_sys->profile -+ && p_context->level <= p_sys->level) -+ { -+ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++) -+ if (pi_fmt[i] == p_sys->pix_fmt) -+ { -+ msg_Dbg(p_dec, "reusing decoder output format %d", pi_fmt[i]); -+ return p_sys->pix_fmt; -+ } -+ } -+ -+// if (p_sys->p_va != NULL) -+// { -+// msg_Err(p_dec, "existing hardware acceleration cannot be reused"); -+// vlc_va_Delete(p_sys->p_va, &p_context->hwaccel_context); -+// p_sys->p_va = NULL; -+// } -+ -+ p_sys->profile = p_context->profile; -+ p_sys->level = p_context->level; -+ -+#if 1 -+ return swfmt; -+#else -+ if (!can_hwaccel) -+ return swfmt; -+ -+#if (LIBAVCODEC_VERSION_MICRO >= 100) \ -+ && (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 83, 101)) -+ if (p_context->active_thread_type) -+ { -+ msg_Warn(p_dec, "thread type %d: disabling hardware acceleration", -+ p_context->active_thread_type); -+ return swfmt; -+ } -+#endif -+ -+ wait_mt(p_sys); -+ -+ static const enum PixelFormat hwfmts[] = -+ { -+#ifdef _WIN32 -+#if LIBAVUTIL_VERSION_CHECK(54, 13, 1, 24, 100) -+ AV_PIX_FMT_D3D11VA_VLD, -+#endif -+ AV_PIX_FMT_DXVA2_VLD, -+#endif -+ AV_PIX_FMT_VAAPI_VLD, -+#if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 4, 0)) -+ AV_PIX_FMT_VDPAU, -+#endif -+ AV_PIX_FMT_NONE, -+ }; -+ -+ for( size_t i = 0; hwfmts[i] != AV_PIX_FMT_NONE; i++ ) -+ { -+ enum PixelFormat hwfmt = AV_PIX_FMT_NONE; -+ for( size_t j = 0; hwfmt == AV_PIX_FMT_NONE && pi_fmt[j] != AV_PIX_FMT_NONE; j++ ) -+ if( hwfmts[i] == pi_fmt[j] ) -+ hwfmt = hwfmts[i]; -+ -+ if( hwfmt == AV_PIX_FMT_NONE ) -+ continue; -+ -+ p_dec->fmt_out.video.i_chroma = vlc_va_GetChroma(hwfmt, swfmt); -+ if (p_dec->fmt_out.video.i_chroma == 0) -+ continue; /* Unknown brand of hardware acceleration */ -+ if (p_context->width == 0 || p_context->height == 0) -+ { /* should never happen */ -+ msg_Err(p_dec, "unspecified video dimensions"); -+ continue; -+ } -+ const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(hwfmt); -+ msg_Dbg(p_dec, "trying format %s", dsc ? dsc->name : "unknown"); -+ if (lavc_UpdateVideoFormat(p_dec, p_context, hwfmt, swfmt)) -+ continue; /* Unsupported brand of hardware acceleration */ -+ post_mt(p_sys); -+ -+ picture_t *test_pic = decoder_NewPicture(p_dec); -+ assert(!test_pic || test_pic->format.i_chroma == p_dec->fmt_out.video.i_chroma); -+ vlc_va_t *va = vlc_va_New(VLC_OBJECT(p_dec), p_context, hwfmt, -+ &p_dec->fmt_in, -+ test_pic ? test_pic->p_sys : NULL); -+ if (test_pic) -+ picture_Release(test_pic); -+ if (va == NULL) -+ { -+ wait_mt(p_sys); -+ continue; /* Unsupported codec profile or such */ -+ } -+ -+ if (va->description != NULL) -+ msg_Info(p_dec, "Using %s for hardware decoding", va->description); -+ -+ p_sys->p_va = va; -+ p_sys->pix_fmt = hwfmt; -+ p_context->draw_horiz_band = NULL; -+ return hwfmt; -+ } -+ -+ post_mt(p_sys); -+ /* Fallback to default behaviour */ -+ p_sys->pix_fmt = swfmt; -+ return swfmt; -+#endif -+} -+ -+/***************************************************************************** -+ * InitVideo: initialize the video decoder -+ ***************************************************************************** -+ * the ffmpeg codec will be opened, some memory allocated. The vout is not yet -+ * opened (done after the first decoded frame). -+ *****************************************************************************/ -+ -+/***************************************************************************** -+ * ffmpeg_OpenCodec: -+ *****************************************************************************/ -+ -+static int MmalAvcodecOpenDecoder( vlc_object_t *obj ) -+{ -+ decoder_t *p_dec = (decoder_t *)obj; -+ const AVCodec *p_codec; -+ -+ if (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC) -+ return VLC_EGENERIC; -+ -+ AVCodecContext *p_context = ffmpeg_AllocContext( p_dec, &p_codec ); -+ if( p_context == NULL ) -+ return VLC_EGENERIC; -+ -+ int i_val; -+ -+ /* Allocate the memory needed to store the decoder's structure */ -+ decoder_sys_t *p_sys = calloc( 1, sizeof(*p_sys) ); -+ if( unlikely(p_sys == NULL) ) -+ { -+ avcodec_free_context( &p_context ); -+ return VLC_ENOMEM; -+ } -+ -+ p_dec->p_sys = p_sys; -+ p_sys->p_context = p_context; -+ p_sys->p_codec = p_codec; -+// p_sys->p_va = NULL; -+ -+ /* ***** Fill p_context with init values ***** */ -+ p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ? -+ p_dec->fmt_in.i_original_fourcc : p_dec->fmt_in.i_codec ); -+ -+ /* ***** Get configuration of ffmpeg plugin ***** */ -+ p_context->workaround_bugs = -+ var_InheritInteger( p_dec, "avcodec-workaround-bugs" ); -+ p_context->err_recognition = -+ var_InheritInteger( p_dec, "avcodec-error-resilience" ); -+ -+ if( var_CreateGetBool( p_dec, "grayscale" ) ) -+ p_context->flags |= AV_CODEC_FLAG_GRAY; -+ -+ /* ***** Output always the frames ***** */ -+ p_context->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT; -+ -+ i_val = var_CreateGetInteger( p_dec, "avcodec-skiploopfilter" ); -+ if( i_val >= 4 ) p_context->skip_loop_filter = AVDISCARD_ALL; -+ else if( i_val == 3 ) p_context->skip_loop_filter = AVDISCARD_NONKEY; -+ else if( i_val == 2 ) p_context->skip_loop_filter = AVDISCARD_BIDIR; -+ else if( i_val == 1 ) p_context->skip_loop_filter = AVDISCARD_NONREF; -+ else p_context->skip_loop_filter = AVDISCARD_DEFAULT; -+ -+ if( var_CreateGetBool( p_dec, "avcodec-fast" ) ) -+ p_context->flags2 |= AV_CODEC_FLAG2_FAST; -+ -+ /* ***** libavcodec frame skipping ***** */ -+ p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" ); -+ p_sys->b_show_corrupted = var_CreateGetBool( p_dec, "avcodec-corrupted" ); -+ -+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-frame" ); -+ if( i_val >= 4 ) p_sys->i_skip_frame = AVDISCARD_ALL; -+ else if( i_val == 3 ) p_sys->i_skip_frame = AVDISCARD_NONKEY; -+ else if( i_val == 2 ) p_sys->i_skip_frame = AVDISCARD_BIDIR; -+ else if( i_val == 1 ) p_sys->i_skip_frame = AVDISCARD_NONREF; -+ else if( i_val == -1 ) p_sys->i_skip_frame = AVDISCARD_NONE; -+ else p_sys->i_skip_frame = AVDISCARD_DEFAULT; -+ p_context->skip_frame = p_sys->i_skip_frame; -+ -+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-idct" ); -+ if( i_val >= 4 ) p_context->skip_idct = AVDISCARD_ALL; -+ else if( i_val == 3 ) p_context->skip_idct = AVDISCARD_NONKEY; -+ else if( i_val == 2 ) p_context->skip_idct = AVDISCARD_BIDIR; -+ else if( i_val == 1 ) p_context->skip_idct = AVDISCARD_NONREF; -+ else if( i_val == -1 ) p_context->skip_idct = AVDISCARD_NONE; -+ else p_context->skip_idct = AVDISCARD_DEFAULT; -+ -+ /* ***** libavcodec direct rendering ***** */ -+ p_sys->b_direct_rendering = false; -+ atomic_init(&p_sys->b_dr_failure, false); -+ if( var_CreateGetBool( p_dec, "avcodec-dr" ) && -+ (p_codec->capabilities & AV_CODEC_CAP_DR1) && -+ /* No idea why ... but this fixes flickering on some TSCC streams */ -+ p_sys->p_codec->id != AV_CODEC_ID_TSCC && -+ p_sys->p_codec->id != AV_CODEC_ID_CSCD && -+ p_sys->p_codec->id != AV_CODEC_ID_CINEPAK ) -+ { -+ /* Some codecs set pix_fmt only after the 1st frame has been decoded, -+ * so we need to do another check in ffmpeg_GetFrameBuf() */ -+ p_sys->b_direct_rendering = true; -+ } -+ -+ p_context->get_format = ffmpeg_GetFormat; -+ /* Always use our get_buffer wrapper so we can calculate the -+ * PTS correctly */ -+// p_context->get_buffer2 = lavc_GetFrame; -+// p_context->opaque = p_dec; -+ -+ int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" ); -+ if( i_thread_count <= 0 ) -+ i_thread_count = 6; -+#if 0 -+ if( i_thread_count <= 0 ) -+ { -+ i_thread_count = vlc_GetCPUCount(); -+ if( i_thread_count > 1 ) -+ i_thread_count++; -+ -+ //FIXME: take in count the decoding time -+#if VLC_WINSTORE_APP -+ i_thread_count = __MIN( i_thread_count, 6 ); -+#else -+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 10 : 6 ); -+#endif -+ } -+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 32 : 16 ); -+#endif -+ msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count ); -+ p_context->thread_count = i_thread_count; -+ p_context->thread_safe_callbacks = true; -+ -+ p_context->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME; -+ -+ if( p_context->thread_type & FF_THREAD_FRAME ) -+ p_dec->i_extra_picture_buffers = 2 * p_context->thread_count; -+ -+ /* ***** misc init ***** */ -+ date_Init(&p_sys->pts, 1, 30001); -+ date_Set(&p_sys->pts, VLC_TS_INVALID); -+ p_sys->b_first_frame = true; -+ p_sys->i_late_frames = 0; -+ p_sys->b_from_preroll = false; -+ -+ /* Set output properties */ -+ if( GetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS ) -+ { -+ /* we are doomed. but not really, because most codecs set their pix_fmt later on */ -+ p_dec->fmt_out.i_codec = VLC_CODEC_I420; -+ } -+ p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma; -+ -+ p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation; -+ -+ if( p_dec->fmt_in.video.p_palette ) { -+ p_sys->palette_sent = false; -+ p_dec->fmt_out.video.p_palette = malloc( sizeof(video_palette_t) ); -+ if( p_dec->fmt_out.video.p_palette ) -+ *p_dec->fmt_out.video.p_palette = *p_dec->fmt_in.video.p_palette; -+ } else -+ p_sys->palette_sent = true; ++ memcpy( &p[0], "SVQ3", 4 ); ++ memset( &p[4], 0, 8 ); ++ memcpy( &p[12], p_dec->fmt_in.p_extra, i_size ); + -+ /* ***** init this codec with special data ***** */ -+ ffmpeg_InitCodec( p_dec ); ++ /* Now remove all atoms before the SMI one */ ++ if( p_sys->p_context->extradata_size > 0x5a && ++ strncmp( (char*)&p[0x56], "SMI ", 4 ) ) ++ { ++ uint8_t *psz = &p[0x52]; + -+ /* ***** Open the codec ***** */ -+ if( OpenVideoCodec( p_dec ) < 0 ) -+ { -+ free( p_sys ); -+ avcodec_free_context( &p_context ); -+ return VLC_EGENERIC; -+ } ++ while( psz < &p[p_sys->p_context->extradata_size - 8] ) ++ { ++ uint_fast32_t atom_size = GetDWBE( psz ); ++ if( atom_size <= 1 ) ++ { ++ /* FIXME handle 1 as long size */ ++ break; ++ } ++ if( !strncmp( (char*)&psz[4], "SMI ", 4 ) ) ++ { ++ memmove( &p[0x52], psz, ++ &p[p_sys->p_context->extradata_size] - psz ); ++ break; ++ } + -+ if ((p_sys->out_pool = mmal_pool_create(5, 0)) == NULL) ++ psz += atom_size; ++ } ++ } ++ } ++ else + { -+ msg_Err(p_dec, "Failed to create mmal buffer pool"); -+ goto fail; ++ p_sys->p_context->extradata_size = i_size; ++ p_sys->p_context->extradata = ++ av_malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE ); ++ if( p_sys->p_context->extradata ) ++ { ++ memcpy( p_sys->p_context->extradata, ++ p_dec->fmt_in.p_extra, i_size ); ++ memset( p_sys->p_context->extradata + i_size, ++ 0, FF_INPUT_BUFFER_PADDING_SIZE ); ++ } + } -+ -+ p_dec->pf_decode = DecodeVideo; -+ p_dec->pf_flush = Flush; -+ -+ /* XXX: Writing input format makes little sense. */ -+ if( p_context->profile != FF_PROFILE_UNKNOWN ) -+ p_dec->fmt_in.i_profile = p_context->profile; -+ if( p_context->level != FF_LEVEL_UNKNOWN ) -+ p_dec->fmt_in.i_level = p_context->level; -+ return VLC_SUCCESS; -+ -+fail: -+ MmalAvcodecCloseDecoder(VLC_OBJECT(p_dec)); -+ return VLC_EGENERIC; +} + + -+ +vlc_module_begin() + set_category( CAT_INPUT ) + set_subcategory( SUBCAT_INPUT_VCODEC ) + set_shortname(N_("MMAL avcodec")) + set_description(N_("MMAL buffered avcodec ")) -+ set_capability("video decoder", 800) ++ set_capability("video decoder", 80) + add_shortcut("mmal_avcodec") ++ add_integer(MMAL_AVCODEC_BUFFERS, -1, MMAL_AVCODEC_BUFFERS_TEXT, ++ MMAL_AVCODEC_BUFFERS_LONGTEXT, true) + set_callbacks(MmalAvcodecOpenDecoder, MmalAvcodecCloseDecoder) +vlc_module_end() + --- /dev/null +++ b/modules/hw/mmal/mmal_cma.c -@@ -0,0 +1,377 @@ +@@ -0,0 +1,668 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif @@ -6722,13 +7082,24 @@ +#include + +#include "mmal_cma.h" ++#include "mmal_picture.h" + +#include + ++#define TRACE_ALL 0 ++ ++//----------------------------------------------------------------------------- ++// ++// Generic pool functions ++// Knows nothing about pool entries + +typedef void * cma_pool_alloc_fn(void * v, size_t size); +typedef void cma_pool_free_fn(void * v, void * el, size_t size); + ++#if TRACE_ALL ++static atomic_int pool_seq; ++#endif ++ +// Pool structure +// Ref count is held by pool owner and pool els that have been got +// Els in the pool do not count towards its ref count @@ -6740,95 +7111,145 @@ + unsigned int n_in; + unsigned int n_out; + unsigned int pool_size; ++ int flight_size; + size_t el_size; + void ** pool; + ++ bool cancel; ++ int in_flight; ++ vlc_cond_t flight_cond; ++ + void * alloc_v; + cma_pool_alloc_fn * el_alloc_fn; + cma_pool_free_fn * el_free_fn; ++ cma_pool_on_delete_fn * on_delete_fn; ++ ++ const char * name; ++#if TRACE_ALL ++ int seq; ++#endif +}; + -+typedef struct cma_buf_s { -+ size_t size; -+ unsigned int vcsm_h; // VCSM handle from initial alloc -+ unsigned int vc_h; // VC handle for ZC mmal buffers -+ int fd; // dmabuf handle for GL -+ void * mmap; // ARM mapped address -+ picture_context_t *ctx2; -+} cma_buf_t; ++static inline unsigned int inc_mod(const unsigned int n, const unsigned int m) ++{ ++ return n + 1 >= m ? 0 : n + 1; ++} + -+static int free_pool(const cma_pool_fixed_t * const p, void ** pool, unsigned int n, size_t el_size) ++static void free_pool(const cma_pool_fixed_t * const p, void ** const pool, ++ const unsigned int pool_size, const size_t el_size) +{ -+ int i = 0; -+ assert(pool != NULL); ++ if (pool == NULL) ++ return; + -+ while (pool[n] != NULL) -+ { -+ p->el_free_fn(p->alloc_v, pool[n], el_size); -+ pool[n] = NULL; -+ n = n + 1 < p->pool_size ? n + 1 : 0; -+ ++i; -+ } ++ for (unsigned int n = 0; n != pool_size; ++n) ++ if (pool[n] != NULL) ++ p->el_free_fn(p->alloc_v, pool[n], el_size); + free(pool); -+ return i; +} + +// Just kill this - no checks +static void cma_pool_fixed_delete(cma_pool_fixed_t * const p) +{ -+ if (p->pool != NULL) -+ free_pool(p, p->pool, p->n_in, p->el_size); ++ cma_pool_on_delete_fn *const on_delete_fn = p->on_delete_fn; ++ void *const v = p->alloc_v; ++ ++ free_pool(p, p->pool, p->pool_size, p->el_size); ++ ++ if (p->name != NULL) ++ free((void *)p->name); // Discard const + ++ vlc_cond_destroy(&p->flight_cond); + vlc_mutex_destroy(&p->lock); + free(p); ++ ++ // Inform our container that we are dead (if it cares) ++ if (on_delete_fn) ++ on_delete_fn(v); +} + -+void cma_pool_fixed_unref(cma_pool_fixed_t * const p) ++static void cma_pool_fixed_unref(cma_pool_fixed_t * const p) +{ + if (atomic_fetch_sub(&p->ref_count, 1) <= 1) + cma_pool_fixed_delete(p); +} + -+void cma_pool_fixed_ref(cma_pool_fixed_t * const p) ++static void cma_pool_fixed_ref(cma_pool_fixed_t * const p) +{ + atomic_fetch_add(&p->ref_count, 1); +} + -+void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size) ++static void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p) ++{ ++ vlc_mutex_lock(&p->lock); ++ ++p->in_flight; ++ vlc_mutex_unlock(&p->lock); ++} ++ ++static void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p) ++{ ++ vlc_mutex_lock(&p->lock); ++ if (--p->in_flight == 0) ++ vlc_cond_signal(&p->flight_cond); ++ vlc_mutex_unlock(&p->lock); ++} ++ ++static void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool inc_flight, const bool no_pool) +{ + void * v = NULL; -+ void ** deadpool = NULL; -+ size_t dead_size = 0; -+ unsigned int dead_n = 0; + + vlc_mutex_lock(&p->lock); + -+ if (req_el_size != p->el_size) ++ for (;;) + { -+ deadpool = p->pool; -+ dead_n = p->n_in; -+ dead_size = p->el_size; ++ if (req_el_size != p->el_size) ++ { ++ void ** const deadpool = p->pool; ++ const size_t dead_size = p->el_size; ++ const unsigned int dead_n = p->pool_size; + -+ p->pool = NULL; -+ p->n_in = 0; -+ p->n_out = 0; -+ p->el_size = req_el_size; -+ } -+ else if (p->pool != NULL) -+ { -+ v = p->pool[p->n_in]; -+ if (v != NULL) ++ p->pool = NULL; ++ p->n_in = 0; ++ p->n_out = 0; ++ p->el_size = req_el_size; ++ ++ if (deadpool != NULL) ++ { ++ vlc_mutex_unlock(&p->lock); ++ // Do the free old op outside the mutex in case the free is slow ++ free_pool(p, deadpool, dead_n, dead_size); ++ vlc_mutex_lock(&p->lock); ++ continue; ++ } ++ } ++ ++ // Late abort if flush or cancel so we can still kill the pool ++ if (req_el_size == 0 || p->cancel) ++ { ++ vlc_mutex_unlock(&p->lock); ++ return NULL; ++ } ++ ++ if (p->pool != NULL && !no_pool) + { -+ p->pool[p->n_in] = NULL; -+ p->n_in = p->n_in + 1 < p->pool_size ? p->n_in + 1 : 0; ++ v = p->pool[p->n_in]; ++ if (v != NULL) ++ { ++ p->pool[p->n_in] = NULL; ++ p->n_in = inc_mod(p->n_in, p->pool_size); ++ break; ++ } + } ++ ++ if (p->in_flight <= 0) ++ break; ++ ++ vlc_cond_wait(&p->flight_cond, &p->lock); + } + -+ vlc_mutex_unlock(&p->lock); ++ if (inc_flight) ++ ++p->in_flight; + -+ // Do the free old op outside the mutex in case the free is slow -+ if (deadpool != NULL) -+ free_pool(p, deadpool, dead_n, dead_size); ++ vlc_mutex_unlock(&p->lock); + + if (v == NULL && req_el_size != 0) + v = p->el_alloc_fn(p->alloc_v, req_el_size); @@ -6836,11 +7257,14 @@ + // Tag ref + if (v != NULL) + cma_pool_fixed_ref(p); ++ // Remove flight if we set it and error ++ else if (inc_flight) ++ cma_pool_fixed_dec_in_flight(p); + + return v; +} + -+void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size) ++static void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight) +{ + vlc_mutex_lock(&p->lock); + @@ -6850,29 +7274,123 @@ + p->pool = calloc(p->pool_size, sizeof(void*)); + + p->pool[p->n_out] = v; -+ p->n_out = p->n_out + 1 < p->pool_size ? p->n_out + 1 : 0; ++ p->n_out = inc_mod(p->n_out, p->pool_size); + v = NULL; + } + ++ if (was_in_flight) ++ --p->in_flight; ++ + vlc_mutex_unlock(&p->lock); + ++ vlc_cond_signal(&p->flight_cond); ++ + if (v != NULL) + p->el_free_fn(p->alloc_v, v, el_size); + + cma_pool_fixed_unref(p); +} + ++static int cma_pool_fixed_resize(cma_pool_fixed_t * const p, ++ const unsigned int new_pool_size, const int new_flight_size) ++{ ++ void ** dead_pool = NULL; ++ size_t dead_size = 0; ++ unsigned int dead_n = 0; ++ ++ // This makes this non-reentrant but saves us a lot of time in the normal ++ // "nothing happens" case ++ if (p->pool_size == new_pool_size && p->flight_size == new_flight_size) ++ return 0; ++ ++ vlc_mutex_lock(&p->lock); ++ ++ if (p->pool != NULL && new_pool_size != p->pool_size) ++ { ++ void ** const new_pool = calloc(new_pool_size, sizeof(void*)); ++ unsigned int d, s; ++ dead_pool = p->pool; ++ dead_size = p->el_size; ++ dead_n = p->pool_size; ++ ++ if (new_pool == NULL) ++ { ++ vlc_mutex_unlock(&p->lock); ++ return -1; ++ } ++ ++ for (d = 0, s = p->n_in; d != new_pool_size && (new_pool[d] = dead_pool[s]) != NULL; ++d, s = inc_mod(s, dead_n)) ++ dead_pool[s] = NULL; ++ ++ p->n_out = 0; ++ p->n_in = (d != new_pool_size) ? d : 0; ++ p->pool = new_pool; ++ } ++ ++ p->pool_size = new_pool_size; ++ if (new_flight_size > p->flight_size) ++ vlc_cond_broadcast(&p->flight_cond); // Lock still active so nothing happens till we release it ++ p->in_flight += p->flight_size - new_flight_size; ++ p->flight_size = new_flight_size; ++ ++ vlc_mutex_unlock(&p->lock); ++ ++ free_pool(p, dead_pool, dead_n, dead_size); ++ return 0; ++} ++ ++static int cma_pool_fixed_fill(cma_pool_fixed_t * const p, const size_t el_size) ++{ ++ for (;;) ++ { ++ vlc_mutex_lock(&p->lock); ++ bool done = el_size == p->el_size && p->pool != NULL && p->pool[p->n_out] != NULL; ++ vlc_mutex_unlock(&p->lock); ++ if (done) ++ break; ++ void * buf = cma_pool_fixed_get(p, el_size, false, true); ++ if (buf == NULL) ++ return -ENOMEM; ++ cma_pool_fixed_put(p, buf, el_size, false); ++ } ++ return 0; ++} ++ ++static void cma_pool_fixed_cancel(cma_pool_fixed_t * const p) ++{ ++ vlc_mutex_lock(&p->lock); ++ p->cancel = true; ++ vlc_cond_broadcast(&p->flight_cond); ++ vlc_mutex_unlock(&p->lock); ++} ++ ++static void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p) ++{ ++ vlc_mutex_lock(&p->lock); ++ p->cancel = false; ++ vlc_mutex_unlock(&p->lock); ++} ++ ++ +// Purge pool & unref -+void cma_pool_fixed_kill(cma_pool_fixed_t * const p) ++static void cma_pool_fixed_kill(cma_pool_fixed_t * const p) +{ ++ if (p == NULL) ++ return; ++ + // This flush is not strictly needed but it reclaims what memory we can reclaim asap -+ cma_pool_fixed_get(p, 0); ++ cma_pool_fixed_get(p, 0, false, false); + cma_pool_fixed_unref(p); +} + -+cma_pool_fixed_t* -+cma_pool_fixed_new(const unsigned int pool_size, void * const alloc_v, -+ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn) ++// Create a new pool ++static cma_pool_fixed_t* ++cma_pool_fixed_new(const unsigned int pool_size, ++ const int flight_size, ++ void * const alloc_v, ++ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn, ++ cma_pool_on_delete_fn * const on_delete_fn, ++ const char * const name) +{ + cma_pool_fixed_t* const p = calloc(1, sizeof(cma_pool_fixed_t)); + if (p == NULL) @@ -6880,24 +7398,71 @@ + + atomic_store(&p->ref_count, 1); + vlc_mutex_init(&p->lock); ++ vlc_cond_init(&p->flight_cond); + + p->pool_size = pool_size; ++ p->flight_size = flight_size; ++ p->in_flight = -flight_size; + + p->alloc_v = alloc_v; + p->el_alloc_fn = alloc_fn; + p->el_free_fn = free_fn; ++ p->on_delete_fn = on_delete_fn; ++ p->name = name == NULL ? NULL : strdup(name); ++#if TRACE_ALL ++ p->seq = atomic_fetch_add(&pool_seq, 1); ++#endif + + return p; +} + ++// --------------------------------------------------------------------------- ++// ++// CMA buffer functions - uses cma_pool_fixed for pooling ++ ++struct cma_buf_pool_s { ++ cma_pool_fixed_t * pool; ++ vcsm_init_type_t init_type; ++ ++ bool all_in_flight; ++#if TRACE_ALL ++ size_t alloc_n; ++ size_t alloc_size; ++#endif ++}; ++ ++typedef struct cma_buf_s { ++ atomic_int ref_count; ++ cma_buf_pool_t * cbp; ++ bool in_flight; ++ size_t size; ++ unsigned int vcsm_h; // VCSM handle from initial alloc ++ unsigned int vc_h; // VC handle for ZC mmal buffers ++ unsigned int vc_addr; // VC addr - unused by us but wanted by FFmpeg ++ int fd; // dmabuf handle for GL ++ void * mmap; // ARM mapped address ++ picture_context_t *ctx2; ++} cma_buf_t; + +static void cma_pool_delete(cma_buf_t * const cb) +{ ++ assert(atomic_load(&cb->ref_count) == 0); ++#if TRACE_ALL ++ cb->cbp->alloc_size -= cb->size; ++ --cb->cbp->alloc_n; ++ fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cb->cbp->pool->seq, cb->cbp->pool->name, cb->cbp->alloc_n, cb->cbp->alloc_size); ++#endif ++ + if (cb->ctx2 != NULL) + cb->ctx2->destroy(cb->ctx2); + + if (cb->mmap != MAP_FAILED) -+ munmap(cb->mmap, cb->size); ++ { ++ if (cb->cbp->init_type == VCSM_INIT_CMA) ++ munmap(cb->mmap, cb->size); ++ else ++ vcsm_unlock_hdl(cb->vcsm_h); ++ } + if (cb->fd != -1) + close(cb->fd); + if (cb->vcsm_h != 0) @@ -6915,13 +7480,16 @@ + +static void * cma_pool_alloc_cb(void * v, size_t size) +{ -+ VLC_UNUSED(v); ++ cma_buf_pool_t * const cbp = v; + + cma_buf_t * const cb = malloc(sizeof(cma_buf_t)); + if (cb == NULL) + return NULL; + + *cb = (cma_buf_t){ ++ .ref_count = ATOMIC_VAR_INIT(0), ++ .cbp = cbp, ++ .in_flight = 0, + .size = size, + .vcsm_h = 0, + .vc_h = 0, @@ -6929,18 +7497,57 @@ + .mmap = MAP_FAILED, + .ctx2 = NULL + }; ++#if TRACE_ALL ++ cb->cbp->alloc_size += cb->size; ++ ++cb->cbp->alloc_n; ++ fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cbp->pool->seq, cbp->pool->name, cbp->alloc_n, cbp->alloc_size); ++#endif + -+ if ((cb->vcsm_h = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST, (char*)"VLC frame")) == 0) ++ // 0x80 is magic value to force full ARM-side mapping - otherwise ++ // cache requests can cause kernel crashes ++ if ((cb->vcsm_h = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST | 0x80, "VLC frame")) == 0) ++ { ++#if TRACE_ALL ++ fprintf(stderr, "vcsm_malloc_cache fail\n"); ++#endif + goto fail; ++ } + + if ((cb->vc_h = vcsm_vc_hdl_from_hdl(cb->vcsm_h)) == 0) ++ { ++#if TRACE_ALL ++ fprintf(stderr, "vcsm_vc_hdl_from_hdl fail\n"); ++#endif + goto fail; ++ } + -+ if ((cb->fd = vcsm_export_dmabuf(cb->vcsm_h)) == -1) -+ goto fail; ++ if (cbp->init_type == VCSM_INIT_CMA) ++ { ++ if ((cb->fd = vcsm_export_dmabuf(cb->vcsm_h)) == -1) ++ { ++#if TRACE_ALL ++ fprintf(stderr, "vcsm_export_dmabuf fail\n"); ++#endif ++ goto fail; ++ } + -+ if ((cb->mmap = mmap(NULL, cb->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, cb->fd, 0)) == MAP_FAILED) -+ goto fail; ++ if ((cb->mmap = mmap(NULL, cb->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, cb->fd, 0)) == MAP_FAILED) ++ goto fail; ++ } ++ else ++ { ++ void * arm_addr; ++ if ((arm_addr = vcsm_lock(cb->vcsm_h)) == NULL) ++ { ++#if TRACE_ALL ++ fprintf(stderr, "vcsm_lock fail\n"); ++#endif ++ goto fail; ++ } ++ cb->mmap = arm_addr; ++ } ++ ++ cb->vc_addr = vcsm_vc_addr_from_hdl(cb->vcsm_h); + + return cb; + @@ -6949,188 +7556,258 @@ + return NULL; +} + -+void cma_buf_pool_delete(cma_pool_fixed_t * const p) ++// Pool has died - safe now to exit vcsm ++static void cma_buf_pool_on_delete_cb(void * v) +{ -+ assert(p != NULL); ++ cma_buf_pool_t * const cbp = v; + -+ cma_pool_fixed_kill(p); ++ cma_vcsm_exit(cbp->init_type); ++ free(cbp); +} + -+cma_pool_fixed_t * cma_buf_pool_new(void) ++void cma_buf_pool_cancel(cma_buf_pool_t * const cbp) +{ -+ return cma_pool_fixed_new(5, NULL, cma_pool_alloc_cb, cma_pool_free_cb); ++ if (cbp == NULL || cbp->pool == NULL) ++ return; ++ ++ cma_pool_fixed_cancel(cbp->pool); +} + ++void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp) ++{ ++ if (cbp == NULL || cbp->pool == NULL) ++ return; + -+typedef struct cma_pic_context_s { -+ picture_context_t cmn; ++ cma_pool_fixed_uncancel(cbp->pool); ++} + -+ atomic_int ref_count; -+ cma_pool_fixed_t * p; -+ cma_buf_t * cb; -+} cma_pic_context_t; ++// User finished with pool ++void cma_buf_pool_delete(cma_buf_pool_t * const cbp) ++{ ++ if (cbp == NULL) ++ return; ++ ++ if (cbp->pool != NULL) ++ { ++ // We will call cma_buf_pool_on_delete_cb when the pool finally dies ++ // (might be now) which will free up our env. ++ cma_pool_fixed_kill(cbp->pool); ++ } ++ else ++ { ++ // Had no pool for some reason (error) but must still finish cleanup ++ cma_buf_pool_on_delete_cb(cbp); ++ } ++} + ++int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size) ++{ ++ return cma_pool_fixed_fill(cbp->pool, el_size); ++} + -+static void cma_buf_pic_ctx_ref(cma_pic_context_t * const ctx) ++int cma_buf_pool_resize(cma_buf_pool_t * const cbp, ++ const unsigned int new_pool_size, const int new_flight_size) +{ -+ atomic_fetch_add(&ctx->ref_count, 1); ++ return cma_pool_fixed_resize(cbp->pool, new_pool_size, new_flight_size); +} + -+static void cma_buf_pic_ctx_unref(cma_pic_context_t * const ctx) ++cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size, const bool all_in_flight, const char * const name) +{ -+ if (atomic_fetch_sub(&ctx->ref_count, 1) > 0) -+ return; ++ vcsm_init_type_t const init_type = cma_vcsm_init(); ++ if (init_type == VCSM_INIT_NONE) ++ return NULL; ++ ++ cma_buf_pool_t * const cbp = calloc(1, sizeof(cma_buf_pool_t)); ++ if (cbp == NULL) ++ return NULL; + -+ if (ctx->cb != NULL) -+ cma_pool_fixed_put(ctx->p, ctx->cb, ctx->cb->size); ++ cbp->init_type = init_type; ++ cbp->all_in_flight = all_in_flight; + -+ free(ctx); -+} ++ if ((cbp->pool = cma_pool_fixed_new(pool_size, flight_size, cbp, cma_pool_alloc_cb, cma_pool_free_cb, cma_buf_pool_on_delete_cb, name)) == NULL) ++ goto fail; ++ return cbp; + -+static picture_context_t * cma_buf_pic_ctx_copy(picture_context_t * pic_ctx) -+{ -+ cma_buf_pic_ctx_ref((cma_pic_context_t *)pic_ctx); -+ return pic_ctx; ++fail: ++ cma_buf_pool_delete(cbp); ++ return NULL; +} + -+static void cma_buf_pic_ctx_destroy(picture_context_t * pic_ctx) ++ ++void cma_buf_in_flight(cma_buf_t * const cb) +{ -+ cma_buf_pic_ctx_unref((cma_pic_context_t *)pic_ctx); ++ if (!cb->cbp->all_in_flight) ++ { ++ assert(!cb->in_flight); ++ cb->in_flight = true; ++ cma_pool_fixed_inc_in_flight(cb->cbp->pool); ++ } +} + -+int cma_buf_pic_attach(cma_pool_fixed_t * const p, picture_t * const pic, const size_t size) ++void cma_buf_end_flight(cma_buf_t * const cb) +{ -+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma)) -+ return VLC_EGENERIC; -+ if (pic->context != NULL) -+ return VLC_EBADVAR; -+ -+ cma_buf_t * const cb = cma_pool_fixed_get(p, size); -+ if (cb == NULL) -+ return VLC_ENOMEM; -+ -+ cma_pic_context_t * const ctx = malloc(sizeof(cma_pic_context_t)); -+ if (ctx == NULL) -+ goto fail; ++ if (cb != NULL && !cb->cbp->all_in_flight && cb->in_flight) ++ { ++ cb->in_flight = false; ++ cma_pool_fixed_dec_in_flight(cb->cbp->pool); ++ } ++} + -+ *ctx = (cma_pic_context_t){ -+ .cmn = { -+ .destroy = cma_buf_pic_ctx_destroy, -+ .copy = cma_buf_pic_ctx_copy -+ }, -+ .ref_count = 0, -+ .p = p, -+ .cb = cb -+ }; + -+ pic->context = &ctx->cmn; -+ return VLC_SUCCESS; ++// Return vcsm handle ++unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb) ++{ ++ return cb->vcsm_h; ++} + -+fail: -+ cma_pool_fixed_put(p, cb, size); -+ return VLC_EGENERIC; ++size_t cma_buf_size(const cma_buf_t * const cb) ++{ ++ return cb->size; +} + -+int cma_buf_pic_add_context2(picture_t *const pic, picture_context_t * const ctx2) ++int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2) +{ -+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; -+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL || ctx->cb == NULL || ctx->cb->ctx2 != NULL) ++ if (cb->ctx2 != NULL) + return VLC_EGENERIC; + -+ ctx->cb->ctx2 = ctx2; ++ cb->ctx2 = ctx2; + return VLC_SUCCESS; +} + -+unsigned int cma_buf_pic_vc_handle(const picture_t * const pic) ++unsigned int cma_buf_vc_handle(const cma_buf_t *const cb) +{ -+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; -+ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? 0 : ctx->cb == NULL ? 0 : ctx->cb->vc_h; ++ return cb->vc_h; +} + -+int cma_buf_pic_fd(const picture_t * const pic) ++int cma_buf_fd(const cma_buf_t *const cb) +{ -+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; -+ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? -1 : ctx->cb == NULL ? -1 : ctx->cb->fd; ++ return cb->fd; +} + -+void * cma_buf_pic_addr(const picture_t * const pic) ++void * cma_buf_addr(const cma_buf_t *const cb) +{ -+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; -+ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? NULL : ctx->cb == NULL ? NULL : ctx->cb->mmap; ++ return cb->mmap; +} + -+picture_context_t * cma_buf_pic_context2(const picture_t * const pic) ++unsigned int cma_buf_vc_addr(const cma_buf_t *const cb) +{ -+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; -+ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? NULL : ctx->cb == NULL ? NULL : ctx->cb->ctx2; ++ return cb->vc_addr; +} + -+cma_pic_context_t * cma_buf_pic_context_ref(const picture_t * const pic) ++ ++picture_context_t * cma_buf_context2(const cma_buf_t *const cb) +{ -+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; ++ return cb->ctx2; ++} + -+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL || ctx->cb == NULL) -+ return NULL; + -+ cma_buf_pic_ctx_ref(ctx); -+ return ctx; ++void cma_buf_unref(cma_buf_t * const cb) ++{ ++ if (cb == NULL) ++ return; ++ if (atomic_fetch_sub(&cb->ref_count, 1) <= 1) ++ { ++ const bool was_in_flight = cb->in_flight; ++ cb->in_flight = false; ++ cma_pool_fixed_put(cb->cbp->pool, cb, cb->size, was_in_flight); ++ } +} + -+void cma_buf_pic_context_unref(cma_pic_context_t * const ctx) ++cma_buf_t * cma_buf_ref(cma_buf_t * const cb) +{ -+ if (ctx != NULL) -+ cma_buf_pic_ctx_unref(ctx); ++ if (cb == NULL) ++ return NULL; ++ atomic_fetch_add(&cb->ref_count, 1); ++ return cb; +} + ++cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const cbp, const size_t size) ++{ ++ cma_buf_t *const cb = cma_pool_fixed_get(cbp->pool, size, cbp->all_in_flight, false); ++ ++ if (cb == NULL) ++ return NULL; ++ ++ cb->in_flight = cbp->all_in_flight; ++ // When 1st allocated or retrieved from the pool the block will have a ++ // ref count of 0 so ref here ++ return cma_buf_ref(cb); ++} + --- /dev/null +++ b/modules/hw/mmal/mmal_cma.h -@@ -0,0 +1,45 @@ +@@ -0,0 +1,71 @@ ++#ifndef VLC_MMAL_MMAL_CMA_H_ ++#define VLC_MMAL_MMAL_CMA_H_ ++ + +struct cma_pool_fixed_s; +typedef struct cma_pool_fixed_s cma_pool_fixed_t; + +typedef void * cma_pool_alloc_fn(void * v, size_t size); +typedef void cma_pool_free_fn(void * v, void * el, size_t size); ++typedef void cma_pool_on_delete_fn(void * v); + ++#if 0 +void cma_pool_fixed_unref(cma_pool_fixed_t * const p); +void cma_pool_fixed_ref(cma_pool_fixed_t * const p); -+void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size); -+void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size); ++void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool in_flight); ++void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight); ++void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p); ++void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p); ++void cma_pool_fixed_cancel(cma_pool_fixed_t * const p); ++void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p); +void cma_pool_fixed_kill(cma_pool_fixed_t * const p); -+cma_pool_fixed_t* cma_pool_fixed_new(const unsigned int pool_size, void * const alloc_v, -+ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn); -+ -+void cma_buf_pool_delete(cma_pool_fixed_t * const p); -+cma_pool_fixed_t * cma_buf_pool_new(void); -+ -+int cma_buf_pic_attach(cma_pool_fixed_t * const p, picture_t * const pic, const size_t size); -+int cma_buf_pic_add_context2(picture_t *const pic, picture_context_t * const ctx2); -+unsigned int cma_buf_pic_vc_handle(const picture_t * const pic); -+int cma_buf_pic_fd(const picture_t * const pic); -+void * cma_buf_pic_addr(const picture_t * const pic); -+picture_context_t * cma_buf_pic_context2(const picture_t * const pic); -+struct cma_pic_context_s; -+struct cma_pic_context_s * cma_buf_pic_context_ref(const picture_t * const pic); -+void cma_buf_pic_context_unref(struct cma_pic_context_s * const ctx); -+ -+#include -+ -+static inline bool is_cma_buf_pic_chroma(const uint32_t chroma) -+{ -+ return chroma == VLC_CODEC_MMAL_ZC_RGB32 || chroma == VLC_CODEC_MMAL_ZC_SAND8 || chroma == VLC_CODEC_MMAL_ZC_I420; -+} -+ -+static inline void cma_buf_pool_deletez(cma_pool_fixed_t ** const pp) -+{ -+ cma_pool_fixed_t * const p = *pp; ++int cma_pool_fixed_resize(cma_pool_fixed_t * const p, ++ const unsigned int new_pool_size, const int new_flight_size); ++cma_pool_fixed_t * cma_pool_fixed_new(const unsigned int pool_size, ++ const int flight_size, ++ void * const alloc_v, ++ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn, ++ cma_pool_on_delete_fn * const on_delete_fn, ++ const char * const name); ++#endif ++ ++struct cma_buf_s; ++typedef struct cma_buf_s cma_buf_t; ++ ++void cma_buf_in_flight(cma_buf_t * const cb); ++void cma_buf_end_flight(cma_buf_t * const cb); ++unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb); ++size_t cma_buf_size(const cma_buf_t * const cb); ++int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2); ++unsigned int cma_buf_vc_handle(const cma_buf_t *const cb); ++int cma_buf_fd(const cma_buf_t *const cb); ++void * cma_buf_addr(const cma_buf_t *const cb); ++unsigned int cma_buf_vc_addr(const cma_buf_t *const cb); ++picture_context_t * cma_buf_context2(const cma_buf_t *const cb); ++ ++void cma_buf_unref(cma_buf_t * const cb); ++cma_buf_t * cma_buf_ref(cma_buf_t * const cb); ++ ++struct cma_buf_pool_s; ++typedef struct cma_buf_pool_s cma_buf_pool_t; ++ ++cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const p, const size_t size); ++void cma_buf_pool_cancel(cma_buf_pool_t * const cbp); ++void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp); ++void cma_buf_pool_delete(cma_buf_pool_t * const p); ++int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size); ++int cma_buf_pool_resize(cma_buf_pool_t * const cbp, ++ const unsigned int new_pool_size, const int new_flight_size); ++cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size, ++ const bool all_in_flight, const char * const name); ++ ++static inline void cma_buf_pool_deletez(cma_buf_pool_t ** const pp) ++{ ++ cma_buf_pool_t * const p = *pp; + if (p != NULL) { + *pp = NULL; + cma_buf_pool_delete(p); + } +} + -+ ++#endif // VLC_MMAL_MMAL_CMA_H_ --- /dev/null +++ b/modules/hw/mmal/mmal_gl.h @@ -0,0 +1,45 @@ @@ -7179,9 +7856,117 @@ + return pic_sys->cmabuf->h_vcsm; +} + +--- /dev/null ++++ b/modules/hw/mmal/mmal_piccpy_neon.S +@@ -0,0 +1,105 @@ ++// Copy pix ++ ++ .syntax unified ++ .arm ++// .thumb ++ .text ++ .align 16 ++ .arch armv7-a ++ .fpu neon-vfpv4 ++ ++ ++.macro function name ++ .global \name ++#ifdef __ELF__ ++ .type \name, %function ++#endif ++\name: ++.endm ++ ++ ++.macro piccpy_to_8, bit_depth ++ subs r2, #128 ++ vpush {q4-q7} ++ blt 2f ++1: ++ vldm r1!, {q0-q7} ++ subs r2, #128 ++ vqrshrn.u16 d0, q0, #\bit_depth - 8 ++ vqrshrn.u16 d1, q1, #\bit_depth - 8 ++ vqrshrn.u16 d2, q2, #\bit_depth - 8 ++ vqrshrn.u16 d3, q3, #\bit_depth - 8 ++ vldm r1!, {q8-q15} ++ vqrshrn.u16 d4, q4, #\bit_depth - 8 ++ vqrshrn.u16 d5, q5, #\bit_depth - 8 ++ vqrshrn.u16 d6, q6, #\bit_depth - 8 ++ vqrshrn.u16 d7, q7, #\bit_depth - 8 ++ vqrshrn.u16 d8, q8, #\bit_depth - 8 ++ vqrshrn.u16 d9, q9, #\bit_depth - 8 ++ vqrshrn.u16 d10, q10, #\bit_depth - 8 ++ vqrshrn.u16 d11, q11, #\bit_depth - 8 ++ vqrshrn.u16 d12, q12, #\bit_depth - 8 ++ vqrshrn.u16 d13, q13, #\bit_depth - 8 ++ vqrshrn.u16 d14, q14, #\bit_depth - 8 ++ vqrshrn.u16 d15, q15, #\bit_depth - 8 ++ vstm r0!, {q0-q7} ++ bge 1b ++2: ++ adds r2, #64 ++ blt 1f ++ ++ vldm r1!, {q0-q7} ++ vqrshrn.u16 d0, q0, #\bit_depth - 8 ++ vqrshrn.u16 d1, q1, #\bit_depth - 8 ++ vqrshrn.u16 d2, q2, #\bit_depth - 8 ++ vqrshrn.u16 d3, q3, #\bit_depth - 8 ++ vqrshrn.u16 d4, q4, #\bit_depth - 8 ++ vqrshrn.u16 d5, q5, #\bit_depth - 8 ++ vqrshrn.u16 d6, q6, #\bit_depth - 8 ++ vqrshrn.u16 d7, q7, #\bit_depth - 8 ++ vstm r0!, {q0-q3} ++1: ++ adds r2, #32 ++ blt 1f ++ ++ vldm r1!, {q0-q3} ++ vqrshrn.u16 d0, q0, #\bit_depth - 8 ++ vqrshrn.u16 d1, q1, #\bit_depth - 8 ++ vqrshrn.u16 d2, q2, #\bit_depth - 8 ++ vqrshrn.u16 d3, q3, #\bit_depth - 8 ++ vstm r0!, {q0-q1} ++1: ++ adds r2, #16 ++ blt 1f ++ ++ vldm r1!, {q0-q1} ++ vqrshrn.u16 d0, q0, #\bit_depth - 8 ++ vqrshrn.u16 d1, q1, #\bit_depth - 8 ++ vstm r0!, {q0} ++1: ++ adds r2, #8 ++ blt 1f ++ ++ vldm r1!, {q0} ++ vqrshrn.u16 d0, q0, #\bit_depth - 8 ++ vstr d0, [r0] ++ add r0, #8 ++1: ++ adds r2, #4 ++ blt 1f ++ ++ vldr d0, [r1] ++ vqrshrn.u16 d0, q0, #\bit_depth - 8 ++ vstr s0, [r0] ++1: ++ vpop {q4-q7} ++ bx lr ++.endm ++ ++ ++@ [r0] Dest ++@ [r1] Src ++@ r2 Pels ++function mmal_piccpy_10_to_8_neon ++ piccpy_to_8 10 ++ --- a/modules/hw/mmal/mmal_picture.c +++ b/modules/hw/mmal/mmal_picture.c -@@ -21,25 +21,1034 @@ +@@ -21,25 +21,1509 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ @@ -7194,15 +7979,20 @@ +#include + #include ++#include #include ++ ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wbad-function-cast" ++#include ++#pragma GCC diagnostic pop #include +#include +#include +#include +#include -+ -+#include "mmal_cma.h" // ************** ++#include "mmal_cma.h" #include "mmal_picture.h" -int mmal_picture_lock(picture_t *picture) @@ -7281,6 +8071,17 @@ + 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: + case VLC_CODEC_MMAL_ZC_I420: + return MMAL_ENCODING_I420; + case VLC_CODEC_RGBA: @@ -7296,18 +8097,20 @@ + return MMAL_ENCODING_YUVUV128; + case VLC_CODEC_MMAL_ZC_SAND10: + return MMAL_ENCODING_YUVUV64_10; ++ case VLC_CODEC_MMAL_ZC_SAND30: ++ return MMAL_ENCODING_YUV10_COL; + default: + break; + } + return 0; +} + -+ -+void vlc_to_mmal_video_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc) ++static void vlc_fmt_to_video_format(MMAL_VIDEO_FORMAT_T *const vf_mmal, const video_frame_format_t * const vf_vlc) +{ -+ MMAL_VIDEO_FORMAT_T * const vf_mmal = &es_fmt->es->video; ++ const unsigned int wmask = (vf_vlc->i_chroma == VLC_CODEC_MMAL_ZC_I420 || ++ vf_vlc->i_chroma == VLC_CODEC_I420) ? 31 : 15; + -+ vf_mmal->width = (vf_vlc->i_width + 31) & ~31; ++ 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; @@ -7325,6 +8128,73 @@ + 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 we have a format that might have come from ffmpeg then rework for ++ // a better guess as to layout. All sand stuff is "special" with regards to ++ // width/height vs real layout so leave as is if that ++ if ((pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420 || ++ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_RGB32) && ++ pic->p[0].i_pixel_pitch != 0) ++ { ++ // Now overwrite width/height with a better guess as to actual layout info ++ vf_new->height = pic->p[0].i_lines; ++ vf_new->width = pic->p[0].i_pitch / pic->p[0].i_pixel_pitch; ++ } ++ ++ 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) ++ { ++#if 0 ++ char dbuf0[5], dbuf1[5]; ++ printf("%dx%d (%d,%d %dx%d) par:%d/%d %s -> %dx%d (%d,%d %dx%d) par:%d/%d %s\n", ++ vf_old->width , ++ vf_old->height , ++ vf_old->crop.x , ++ vf_old->crop.y , ++ vf_old->crop.width , ++ vf_old->crop.height , ++ vf_old->par.num , ++ vf_old->par.den , ++ str_fourcc(dbuf0, vf_old->color_space) , ++ vf_new->width , ++ vf_new->height , ++ vf_new->crop.x , ++ vf_new->crop.y , ++ vf_new->crop.width , ++ vf_new->crop.height , ++ vf_new->par.num , ++ vf_new->par.den , ++ str_fourcc(dbuf1, vf_new->color_space) ); ++#endif ++ *vf_old = *vf_new; ++ return true; ++ } ++ return false; ++} ++ ++ +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) +{ @@ -7407,9 +8277,7 @@ + hw_mmal_port_pool_ref_t ** pppr, + MMAL_PORT_T * const port, + const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback) - { -- picture_sys_t *pic_sys = picture->p_sys; -- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer; ++{ + MMAL_STATUS_T status; + + port->userdata = (struct MMAL_PORT_USERDATA_T *)obj; @@ -7444,20 +8312,16 @@ + msg_Err(obj, "Failed to create output pool"); + return status; + } - -- 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; ++ + 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; - } - -- pic_sys->displayed = false; ++ } ++ + return MMAL_SUCCESS; +} + @@ -7468,78 +8332,317 @@ + unsigned int i; + + for (i = 0; i != ctx->buf_count; ++i) { -+ mmal_buffer_header_release(ctx->bufs[i]); ++ if (ctx->bufs[i] != NULL) ++ mmal_buffer_header_release(ctx->bufs[i]); + } ++ ++ cma_buf_end_flight(ctx->cb); ++ cma_buf_unref(ctx->cb); ++ + free(ctx); +} + -+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn) ++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->cb = cma_buf_ref(src_ctx->cb); ++ ++ 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) { ++ hw_mmal_port_pool_ref_acquire(ppr); ++ 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 ++// * Make NEON! ++typedef void piccpy_fn(void * dest, const void * src, size_t n); ++ ++extern piccpy_fn mmal_piccpy_10_to_8_neon; ++ ++static void piccpy_10_to_8_c(void * dest, const void * src, size_t n) + { +- picture_sys_t *pic_sys = picture->p_sys; +- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer; ++ 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 = vlc_CPU_ARM_NEON() ? mmal_piccpy_10_to_8_neon : 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 + +- 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; ++ assert(fmt->encoding == MMAL_ENCODING_I420); ++ ++ switch (pic->format.i_chroma) { ++ case VLC_CODEC_I420: ++ { ++ 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 (cma_vcsm_type() == VCSM_INIT_LEGACY) { // ** CMA is currently always uncached ++ flush_range(dest, length); + } + +- pic_sys->displayed = false; ++ if (pLength != NULL) ++ *pLength = (uint32_t)length; + + return VLC_SUCCESS; + } ++ ++ ++static MMAL_BOOL_T rep_buf_free_cb(MMAL_BUFFER_HEADER_T *header, void *userdata) +{ -+ 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; ++ cma_buf_t * const cb = userdata; ++ VLC_UNUSED(header); + -+ if (dst_ctx == NULL) -+ return NULL; -+ -+ // Copy -+ dst_ctx->cmn = src_ctx->cmn; ++ cma_buf_unref(cb); ++ return MMAL_FALSE; ++} + -+ 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]; -+ mmal_buffer_header_acquire(dst_ctx->bufs[i]); -+ } ++static int cma_buf_buf_attach(MMAL_BUFFER_HEADER_T * const buf, cma_buf_t * const cb) ++{ ++ // Just a CMA buffer - fill in new buffer ++ const uintptr_t vc_h = cma_buf_vc_handle(cb); ++ if (vc_h == 0) ++ return VLC_EGENERIC; + -+ return &dst_ctx->cmn; ++ mmal_buffer_header_reset(buf); ++ buf->data = (uint8_t *)vc_h; ++ buf->alloc_size = cma_buf_size(cb); ++ buf->length = buf->alloc_size; ++ // Ensure cb remains valid for the duration of this buffer ++ mmal_buffer_header_pre_release_cb_set(buf, rep_buf_free_cb, cma_buf_ref(cb)); ++ return VLC_SUCCESS; +} + -+static MMAL_BOOL_T -+buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata) ++MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic, ++ MMAL_POOL_T * const rep_pool, ++ MMAL_PORT_T * const port, ++ cma_buf_pool_t * const cbp) +{ -+ hw_mmal_port_pool_ref_t * const ppr = userdata; ++ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(rep_pool->queue); ++ if (buf == NULL) ++ goto fail0; + -+ // 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 ++ cma_buf_t * const cb = cma_buf_pool_alloc_buf(cbp, port->buffer_size); ++ if (cb == NULL) ++ goto fail1; + -+ // 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 ++ if (cma_buf_buf_attach(buf, cb) != VLC_SUCCESS) ++ goto fail2; + -+ return MMAL_TRUE; ++ pic_to_buf_copy_props(buf, pic); ++ ++ if (hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), &buf->length, port->format, pic) != VLC_SUCCESS) ++ goto fail2; ++ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; ++ ++ cma_buf_unref(cb); ++ return buf; ++ ++fail2: ++ cma_buf_unref(cb); ++fail1: ++ mmal_buffer_header_release(buf); ++fail0: ++ return NULL; +} - -- return VLC_SUCCESS; -+// Buffer belongs to context on successful return from this fn -+// is still valid on failure -+picture_context_t * -+hw_mmal_gen_context(const MMAL_FOURCC_T fmt, MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr) ++ ++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 = calloc(1, sizeof(pic_ctx_mmal_t)); ++ 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 (ctx == NULL) ++ if (rep_buf == NULL) + return NULL; + -+ // If we have an associated ppr then ref & set appropriate callbacks -+ if (ppr != NULL) { -+ hw_mmal_port_pool_ref_acquire(ppr); -+ mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr); -+ buf->user_data = 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 if (ctx->cb != NULL) ++ { ++ // Just a CMA buffer - fill in new buffer ++ if (cma_buf_buf_attach(rep_buf, ctx->cb) != 0) ++ goto fail; ++ } ++ else ++ goto fail; + -+ ctx->cmn.copy = hw_mmal_pic_ctx_copy; -+ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy; -+ -+ ctx->fmt = fmt; -+ ctx->buf_count = 1; -+ ctx->bufs[0] = buf; ++ pic_to_buf_copy_props(rep_buf, pic); ++ return rep_buf; + -+ return &ctx->cmn; ++fail: ++ mmal_buffer_header_release(rep_buf); ++ return NULL; +} + ++ ++ ++ +int hw_mmal_get_gpu_mem(void) { + static int stashed_val = -2; + VCHI_INSTANCE_T vchi_instance; @@ -7642,6 +8745,8 @@ + vlc_mutex_t lock; + + MMAL_POOL_T * buf_pool; ++ ++ vcsm_init_type_t vcsm_init_type; +}; + +typedef struct vzc_subbuf_ent_s @@ -7752,7 +8857,7 @@ + ent = ent->prev; + } + return NULL; - } ++} + +#define POOL_ENT_ALLOC_BLOCK 0x10000 + @@ -7919,10 +9024,17 @@ + +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, mul_rect->width, div_rect->width); -+ d->y = rescale_x(s->y, mul_rect->height, div_rect->height); -+ d->width = rescale_x(s->width, mul_rect->width, div_rect->width); -+ d->height = rescale_x(s->height, mul_rect->height, div_rect->height); ++ 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); ++#if 0 ++ fprintf(stderr, "(%d,%d %dx%d) * (%d,%d %dx%d) / (%d,%d %dx%d) -> (%d,%d %dx%d)\n", ++ s->x, s->y, s->width, s->height, ++ mul_rect->x, mul_rect->y, mul_rect->width, mul_rect->height, ++ div_rect->x, div_rect->y, div_rect->width, div_rect->height, ++ d->x, d->y, d->width, d->height); ++#endif +} + +void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect) @@ -8070,17 +9182,15 @@ + + // 2D copy + { -+ unsigned int i; + uint8_t *d = ent->buf; + const uint8_t *s = pic->p[0].p_pixels + xl * bpp + fmt->i_y_offset * pic->p[0].i_pitch; -+ for (i = 0; i != fmt->i_visible_height; ++i) { -+ memcpy(d, s, dst_stride); -+ d += dst_stride; -+ s += pic->p[0].i_pitch; -+ } ++ ++ mem_copy_2d(d, dst_stride, s, pic->p[0].i_pitch, fmt->i_visible_height, dst_stride); + + // And make sure it is actually in memory -+ flush_range(ent->buf, d - (uint8_t *)ent->buf); ++ if (pc->vcsm_init_type != VCSM_INIT_CMA) { // ** CMA is currently always uncached ++ flush_range(ent->buf, dst_stride * fmt->i_visible_height); ++ } + } + } + } @@ -8114,13 +9224,12 @@ + + vlc_mutex_destroy(&pc->lock); + -+// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash ++ cma_vcsm_exit(pc->vcsm_init_type); + ++// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash + free (pc); + -+ vcsm_exit(); -+ -+// printf(">>> %s\n", __func__); ++ // printf(">>> %s\n", __func__); +} + +void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc) @@ -8171,7 +9280,11 @@ + if (pc == NULL) + return NULL; + -+ vcsm_init(); ++ 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 @@ -8189,24 +9302,170 @@ + return pc; +} + ++//---------------------------------------------------------------------------- ++ ++ ++static const uint8_t shift_00[] = {0,0,0,0}; ++static const uint8_t shift_01[] = {0,1,1,1}; ++ ++int cma_pic_set_data(picture_t * const pic, ++ const MMAL_ES_FORMAT_T * const mm_esfmt, ++ const MMAL_BUFFER_HEADER_T * const buf) ++{ ++ const MMAL_VIDEO_FORMAT_T * const mm_fmt = &mm_esfmt->es->video; ++ const MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T *const buf_vid = (buf == NULL) ? NULL : &buf->type->video; ++ cma_buf_t *const cb = cma_buf_pic_get(pic); ++ unsigned int planes = 1; ++ ++ uint8_t * const data = cma_buf_addr(cb); ++ if (data == NULL) { ++ return VLC_ENOMEM; ++ } ++ ++ const uint8_t * ws = shift_00; ++ const uint8_t * hs = shift_00; ++ int pb = 1; ++ ++ switch (mm_esfmt->encoding) ++ { ++ case MMAL_ENCODING_ARGB: ++ case MMAL_ENCODING_ABGR: ++ case MMAL_ENCODING_RGBA: ++ case MMAL_ENCODING_BGRA: ++ case MMAL_ENCODING_RGB32: ++ case MMAL_ENCODING_BGR32: ++ pb = 4; ++ break; ++ case MMAL_ENCODING_RGB16: ++ pb = 2; ++ break; ++ ++ case MMAL_ENCODING_I420: ++ ws = shift_01; ++ hs = shift_01; ++ planes = 3; ++ break; ++ ++ case MMAL_ENCODING_YUVUV128: ++ hs = shift_01; ++ planes = 2; ++ break; ++ ++ default: ++// msg_Err(p_filter, "%s: Unexpected format", __func__); ++ return VLC_EGENERIC; ++ } ++ ++ // Fix up SAR if unset ++ if (pic->format.i_sar_den == 0 || pic->format.i_sar_num == 0) { ++ pic->format.i_sar_den = mm_fmt->par.den; ++ pic->format.i_sar_num = mm_fmt->par.num; ++ } ++ ++ pic->i_planes = planes; ++ unsigned int offset = 0; ++ for (unsigned int i = 0; i != planes; ++i) { ++ pic->p[i] = (plane_t){ ++ .p_pixels = data + (buf_vid != NULL ? buf_vid->offset[i] : offset), ++ .i_lines = mm_fmt->height >> hs[i], ++ .i_pitch = buf_vid != NULL ? buf_vid->pitch[i] : mm_fmt->width * pb, ++ .i_pixel_pitch = pb, ++ .i_visible_lines = mm_fmt->crop.height >> hs[i], ++ .i_visible_pitch = mm_fmt->crop.width >> ws[i] ++ }; ++ offset += pic->p[i].i_pitch * pic->p[i].i_lines; ++ } ++ return VLC_SUCCESS; ++} ++ ++int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic) ++{ ++ if (!is_cma_buf_pic_chroma(pic->format.i_chroma)) ++ return VLC_EGENERIC; ++ if (pic->context != NULL) ++ return VLC_EBADVAR; ++ ++ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t)); ++ ++ if (ctx == NULL) ++ return VLC_ENOMEM; ++ ++ ctx->cmn.copy = hw_mmal_pic_ctx_copy; ++ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy; ++ ctx->buf_count = 1; // cb takes the place of the 1st buf ++ ctx->cb = cb; ++ ++ cma_buf_in_flight(cb); ++ ++ pic->context = &ctx->cmn; ++ return VLC_SUCCESS; ++} ++ ++cma_buf_t * cma_buf_pic_get(picture_t * const pic) ++{ ++ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context; ++ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? 0 : ctx->cb; ++} ++ + +//---------------------------------------------------------------------------- + ++/* 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) +{ -+ if (vcsm_init_ex(1, -1) == 0) { -+ return VCSM_INIT_CMA; ++ vcsm_init_type_t rv = VCSM_INIT_NONE; ++ // We don't bother locking - taking a copy here should be good enough ++ vcsm_init_type_t try_type = last_vcsm_type; ++ ++ if (try_type == VCSM_INIT_NONE) { ++ if (bcm_host_is_fkms_active()) ++ try_type = VCSM_INIT_CMA; ++ else ++ try_type = VCSM_INIT_LEGACY; ++ } ++ ++ if (try_type == VCSM_INIT_CMA) { ++ if (vcsm_init_ex(1, -1) == 0) ++ rv = VCSM_INIT_CMA; ++ else if (vcsm_init_ex(0, -1) == 0) ++ rv = VCSM_INIT_LEGACY; + } -+ else if (vcsm_init_ex(0, -1) == 0) { -+ return VCSM_INIT_LEGACY; ++ else ++ { ++ if (vcsm_init_ex(0, -1) == 0) ++ rv = VCSM_INIT_LEGACY; ++ else if (vcsm_init_ex(1, -1) == 0) ++ rv = VCSM_INIT_CMA; + } -+ return VCSM_INIT_NONE; ++ ++ // Just in case this affects vcsm init do after that ++ if (rv != VCSM_INIT_NONE) ++ 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) ++ { + vcsm_exit(); ++ 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) @@ -8228,7 +9487,7 @@ + --- a/modules/hw/mmal/mmal_picture.h +++ b/modules/hw/mmal/mmal_picture.h -@@ -24,19 +24,275 @@ +@@ -24,19 +24,298 @@ #ifndef VLC_MMAL_MMAL_PICTURE_H_ #define VLC_MMAL_MMAL_PICTURE_H_ @@ -8237,6 +9496,8 @@ #include #include ++#include "mmal_cma.h" ++ /* Think twice before changing this. Incorrect values cause havoc. */ #define NUM_ACTUAL_OPAQUE_BUFFERS 30 @@ -8264,31 +9525,28 @@ + + +#define CTX_BUFS_MAX 4 -+ +typedef struct pic_ctx_mmal_s { + picture_context_t cmn; // PARENT: Common els at start + -+ MMAL_FOURCC_T fmt; ++ cma_buf_t * cb; + + unsigned int buf_count; + MMAL_BUFFER_HEADER_T * bufs[CTX_BUFS_MAX]; + -+#if 0 -+ MMAL_BUFFER_HEADER_T * buf; -+ hw_mmal_port_pool_ref_t * ppr; -+ -+ MMAL_BUFFER_HEADER_T * sub_bufs; -+ MMAL_BUFFER_HEADER_T * sub_tail; -+ -+ vlc_object_t * obj; -+#endif +} pic_ctx_mmal_t; + +const char * str_fourcc(char * const buf, const unsigned int fcc); + +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 vlc_to_mmal_video_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc); ++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); + +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); @@ -8329,24 +9587,25 @@ + return n + 1 > ctx->buf_count ? NULL : ctx->bufs[n + 1]; +} + -+static inline bool hw_mmal_pic_is_mmal(const picture_t * const pic) ++static inline bool hw_mmal_chroma_is_mmal(const vlc_fourcc_t chroma) +{ -+ return pic->format.i_chroma == VLC_CODEC_MMAL_OPAQUE || -+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8 || -+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND10 || -+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420 || -+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_RGB32; ++ return ++ chroma == VLC_CODEC_MMAL_OPAQUE || ++ chroma == VLC_CODEC_MMAL_ZC_SAND8 || ++ chroma == VLC_CODEC_MMAL_ZC_SAND10 || ++ chroma == VLC_CODEC_MMAL_ZC_SAND30 || ++ chroma == VLC_CODEC_MMAL_ZC_I420 || ++ chroma == VLC_CODEC_MMAL_ZC_RGB32; +} + -+static inline MMAL_FOURCC_T hw_mmal_pic_format(const picture_t *const pic) ++static inline bool hw_mmal_pic_is_mmal(const picture_t * const pic) +{ -+ const pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context; -+ return ctx->fmt; ++ return hw_mmal_chroma_is_mmal(pic->format.i_chroma); +} + +picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn); +void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn); -+picture_context_t * hw_mmal_gen_context(const MMAL_FOURCC_T fmt, ++picture_context_t * hw_mmal_gen_context( + MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr); + +int hw_mmal_get_gpu_mem(void); @@ -8394,6 +9653,7 @@ + return MMAL_SUCCESS; +} + ++ +static inline void pic_to_buf_copy_props(MMAL_BUFFER_HEADER_T * const buf, const picture_t * const pic) +{ + if (!pic->b_progressive) @@ -8432,16 +9692,12 @@ + VLC_TICK_INVALID; +} + -+// Retrieve buf from pic & update with pic props -+// Note that this is a weak pointer - replicate before putting in a Q -+static inline MMAL_BUFFER_HEADER_T * pic_mmal_buffer(const picture_t *const pic) -+{ -+ MMAL_BUFFER_HEADER_T * const buf = ((pic_ctx_mmal_t *)pic->context)->bufs[0]; -+ if (buf != NULL) -+ pic_to_buf_copy_props(buf, pic); ++MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic, ++ MMAL_POOL_T * const rep_pool, ++ MMAL_PORT_T * const port, ++ cma_buf_pool_t * const cbp); + -+ return buf; -+} ++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; @@ -8487,6 +9743,31 @@ + }; +} + ++int cma_pic_set_data(picture_t * const pic, ++ const MMAL_ES_FORMAT_T * const mm_esfmt, ++ const MMAL_BUFFER_HEADER_T * const buf); ++ ++// Attaches cma buf to pic ++// Marks in_flight if not all_in_flight anyway ++int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic); ++// Returns a pointer to the cma_buf attached to the pic ++// Just a pointer - doesn't add a ref ++cma_buf_t * cma_buf_pic_get(picture_t * const pic); ++ ++static inline bool is_cma_buf_pic_chroma(const uint32_t chroma) ++{ ++ return chroma == VLC_CODEC_MMAL_ZC_RGB32 || ++ chroma == VLC_CODEC_MMAL_ZC_SAND8 || ++ chroma == VLC_CODEC_MMAL_ZC_SAND10 || ++ chroma == VLC_CODEC_MMAL_ZC_SAND30 || ++ chroma == VLC_CODEC_MMAL_ZC_I420; ++} ++ ++ ++int rpi_get_model_type(void); ++bool rpi_is_model_pi4(void); ++bool rpi_is_fkms_active(void); ++ +typedef enum vcsm_init_type_e { + VCSM_INIT_NONE = 0, + VCSM_INIT_LEGACY, @@ -8495,6 +9776,7 @@ + +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); + @@ -8625,7 +9907,7 @@ + --- /dev/null +++ b/modules/hw/mmal/subpic.c -@@ -0,0 +1,227 @@ +@@ -0,0 +1,234 @@ +/***************************************************************************** + * mmal.c: MMAL-based decoder plugin for Raspberry Pi + ***************************************************************************** @@ -8693,7 +9975,8 @@ + *spe = (subpic_reg_stash_t){NULL}; +} + -+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, const unsigned int layer) ++MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, ++ const int display_id, const unsigned int layer) +{ + MMAL_STATUS_T err; + @@ -8714,6 +9997,7 @@ + + port->userdata = (void *)p_filter; + spe->port = port; ++ spe->display_id = display_id; + spe->layer = layer; + + return MMAL_SUCCESS; @@ -8752,7 +10036,7 @@ + return -1; + } +#if TRACE_ALL -+ msg_Dbg(p_filter, "Remove pic for sub %d", sub_no); ++ msg_Dbg(p_filter, "Remove pic for sub %d", spe->seq); +#endif + buf->cmd = 0; + buf->data = NULL; @@ -8797,6 +10081,11 @@ + 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; + @@ -8837,7 +10126,7 @@ + if (needs_update) + { +#if TRACE_ALL -+ msg_Dbg(p_filter, "Update pic for sub %d", sub_no); ++ msg_Dbg(p_filter, "Update pic for sub %d", spe->seq); +#endif + if ((err = port_send_replicated(spe->port, spe->pool, sub_buf, pts)) != MMAL_SUCCESS) + { @@ -8855,7 +10144,7 @@ + --- /dev/null +++ b/modules/hw/mmal/subpic.h -@@ -0,0 +1,29 @@ +@@ -0,0 +1,32 @@ +#ifndef VLC_HW_MMAL_SUBPIC_H_ +#define VLC_HW_MMAL_SUBPIC_H_ + @@ -8863,6 +10152,7 @@ +{ + 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; @@ -8881,7 +10171,9 @@ + +void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe); + -+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, const unsigned int layer); ++// If display id is -1 it will be unset ++MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, ++ const int display_id, const unsigned int layer); + +#endif + @@ -9269,7 +10561,7 @@ + --- a/modules/hw/mmal/vout.c +++ b/modules/hw/mmal/vout.c -@@ -27,21 +27,24 @@ +@@ -27,21 +27,27 @@ #endif #include @@ -9282,21 +10574,26 @@ #include +#include - #include "mmal_picture.h" -+#include "subpic.h" - +-#include "mmal_picture.h" +- ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wbad-function-cast" #include ++#pragma GCC diagnostic pop #include #include #include #include -#include + ++#include "mmal_picture.h" ++#include "subpic.h" ++ +#define TRACE_ALL 0 #define MAX_BUFFERS_IN_TRANSIT 1 #define VC_TV_MAX_MODE_IDS 127 -@@ -50,11 +53,6 @@ +@@ -50,10 +56,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.") @@ -9304,11 +10601,16 @@ -#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- 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.") - #define MMAL_ADJUST_REFRESHRATE_LONGTEXT N_("Adjust HDMI refresh rate to the video.") -@@ -68,64 +66,30 @@ +@@ -68,64 +76,33 @@ #define PHASE_OFFSET_TARGET ((double)0.25) #define PHASE_CHECK_INTERVAL 100 @@ -9371,17 +10673,20 @@ - 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; - int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */ - int i_frame_rate; ++ 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; int next_phase_check; /* lowpass for phase check frequency */ int phase_offset; /* currently applied offset to presentation time in ns */ -@@ -136,264 +100,451 @@ +@@ -136,264 +113,485 @@ bool native_interlaced; bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */ bool b_progressive; @@ -9471,36 +10776,22 @@ - return VLC_EGENERIC; +static inline bool want_copy(const vout_display_t * const vd) +{ -+ return (vd->fmt.i_chroma == VLC_CODEC_I420); ++ return (vd->fmt.i_chroma == VLC_CODEC_I420 || vd->fmt.i_chroma == VLC_CODEC_I420_10L); +} - sys = calloc(1, sizeof(struct vout_display_sys_t)); - if (!sys) - return VLC_ENOMEM; - vd->sys = sys; -+// 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 inline 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) ++static inline vlc_fourcc_t req_chroma(const vout_display_t * const vd) +{ -+ if (s_stride == d_stride && s_stride < line_len + 32) -+ { -+ memcpy(d_ptr, s_ptr, s_stride * lines); -+ } -+ else -+ { -+ while (lines-- != 0) { -+ memcpy(d_ptr, s_ptr, line_len); -+ d_ptr += d_stride; -+ s_ptr += s_stride; -+ } -+ } ++ return !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma) && !want_copy(vd) ? ++ VLC_CODEC_I420 : ++ vd->fmt.i_chroma; +} - sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); - bcm_host_init(); - -- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE; +static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc) +{ + switch (fcc){ @@ -9509,29 +10800,33 @@ + case VLC_CODEC_MMAL_ZC_SAND8: + return MMAL_ENCODING_YUVUV128; + case VLC_CODEC_MMAL_ZC_SAND10: -+ return MMAL_ENCODING_YUVUV64_10; // It will be after we've converted it... ++ return MMAL_ENCODING_YUVUV64_10; ++ case VLC_CODEC_MMAL_ZC_SAND30: ++ return MMAL_ENCODING_YUV10_COL; ++ case VLC_CODEC_MMAL_ZC_I420: + case VLC_CODEC_I420: + return MMAL_ENCODING_I420; + default: + break; + } -+ return 0; ++ return MMAL_ENCODING_I420; +} +- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE; ++static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate) ++{ ++ 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; + - 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; -+static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate) -+{ -+ 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 = 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; @@ -9550,7 +10845,6 @@ + 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); -+} - sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; - status = mmal_port_enable(sys->component->control, control_port_cb); @@ -9559,6 +10853,9 @@ - sys->component->control->name, status, mmal_status_to_string(status)); - ret = VLC_EGENERIC; - goto out; ++ msg_Dbg(vd, "WxH: %dx%d, Crop: %dx%d", v_fmt->width, v_fmt->height, v_fmt->crop.width, v_fmt->crop.height); ++} ++ +static void display_src_rect(const vout_display_t * const vd, MMAL_RECT_T *const rect) +{ + const bool wants_isp = want_isp(vd); @@ -9899,14 +11196,14 @@ + isp->output->buffer_size = isp->output->buffer_size_recommended; + isp->output->buffer_num = 2; + isp->output->userdata = (void *)vd; - -- bcm_host_deinit(); ++ + 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; + } -+ + +- bcm_host_deinit(); + mmal_pool_callback_set(isp->out_pool, isp_out_pool_cb, isp); + + if ((err = isp_prepare(vd, isp)) != MMAL_SUCCESS) @@ -9981,12 +11278,12 @@ +#endif +} + -+static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height) ++static int query_resolution(vout_display_t *vd, const int display_id, unsigned *width, unsigned *height) +{ -+ TV_DISPLAY_STATE_T display_state; ++ TV_DISPLAY_STATE_T display_state = {0}; + int ret = 0; + -+ if (vc_tv_get_display_state(&display_state) == 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); @@ -10005,15 +11302,58 @@ + ret = -1; + } + -+ return ret; - } - ++ return ret; ++} ++ ++static inline 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, false); ++ ++ sys->dest_rect = place_to_mmal_rect(place); ++#if TRACE_ALL ++ msg_Dbg(vd, "%s: %dx%d -> %dx%d @ %d,%d", __func__, ++ tcfg.display.width, tcfg.display.height, ++ place.width, place.height, place.x, place.y); ++#endif ++} ++ ++ ++ static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg, const video_format_t *fmt) { - vout_display_sys_t *sys = vd->sys; +- vout_display_place_t place; + vout_display_sys_t * const sys = vd->sys; - vout_display_place_t place; MMAL_DISPLAYREGION_T display_region; MMAL_STATUS_T status; @@ -10027,21 +11367,12 @@ if (fmt) { sys->input->format->es->video.par.num = fmt->i_sar_num; -@@ -412,22 +563,29 @@ +@@ -412,22 +610,17 @@ if (!cfg) cfg = vd->cfg; - vout_display_PlacePicture(&place, fmt, cfg, false); -+ { -+ // Ignore what VLC thinks might be going on with display size -+ vout_display_cfg_t tcfg = *cfg; -+ tcfg.display.width = sys->display_width; -+ tcfg.display.height = sys->display_height; -+ tcfg.is_display_filled = true; -+ vout_display_PlacePicture(&place, fmt, &tcfg, false); -+ -+ msg_Dbg(vd, "%dx%d -> %dx%d @ %d,%d", tcfg.display.width, tcfg.display.height, place.width, place.height, place.x, place.y); -+ } ++ place_dest(vd, sys, cfg, fmt); display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION; display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T); @@ -10050,11 +11381,12 @@ - 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.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.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 | @@ -10063,7 +11395,7 @@ 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)", -@@ -435,7 +593,6 @@ +@@ -435,7 +628,6 @@ return -EINVAL; } @@ -10071,7 +11403,7 @@ 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) { -@@ -446,192 +603,153 @@ +@@ -446,192 +638,161 @@ return 0; } @@ -10135,7 +11467,14 @@ -#ifndef NDEBUG - msg_Dbg(vd, "Creating picture pool with %u pictures", count); +#if TRACE_ALL -+ msg_Dbg(vd, "<<< %s", __func__); ++ { ++ char dbuf0[5]; ++ msg_Dbg(vd, "<<< %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__, ++ str_fourcc(dbuf0, p_pic->format.i_chroma), p_pic->format.i_width, p_pic->format.i_height, ++ p_pic->format.i_x_offset, p_pic->format.i_y_offset, ++ p_pic->format.i_visible_width, p_pic->format.i_visible_height, ++ p_pic->format.i_sar_num, p_pic->format.i_sar_den); ++ } #endif - sys->input->buffer_num = count; @@ -10144,40 +11483,17 @@ - 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; + // If we had subpics then we have attached them to the main pic in prepare + // so all we have to do here is delete the refs + if (subpicture != NULL) { + subpicture_Delete(subpicture); } -- 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); +- 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; -+ 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); -+ } -+ + if (!sys->input->is_enabled && + (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS) + { @@ -10200,6 +11516,27 @@ + } } - +- 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; ++ else if (sys->isp.pending) { ++ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q); ++ sys->isp.pending = false; ++#if TRACE_ALL ++ msg_Dbg(vd, "--- %s: ISP stuff", __func__); ++#endif ++ 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; ++ } + } +- - memset(&picture_res, 0, sizeof(picture_resource_t)); - sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *)); - for (i = 0; i < sys->num_buffers; ++i) { @@ -10212,40 +11549,40 @@ - msg_Err(vd, "Failed to create picture"); - free(picture_res.p_sys); - 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 TRACE_ALL -+ msg_Dbg(vd, "--- %s: ISP stuff", __func__); -+#endif -+ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS) ++ else ++ { ++ MMAL_BUFFER_HEADER_T *const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->pool); ++ if (pic_buf == NULL) + { -+ mmal_buffer_header_release(buf); -+ msg_Err(vd, "Send ISP buffer to render input failed"); ++ msg_Err(vd, "Replicated buffer get fail"); + goto fail; } -- + - 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; -- ++ // 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"); ++ ++ // HVS can deal with on-line dimension changes ++ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS) ++ msg_Warn(vd, "Input format commit failed"); ++ } + - sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg); - if (!sys->picture_pool) { - msg_Err(vd, "Failed to create picture pool"); - goto out; -+ else -+ { -+ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic); -+#if TRACE_ALL -+ msg_Dbg(vd, "--- %s: Buf stuff", __func__); -+#endif -+ if ((err = port_send_replicated(sys->input, sys->pool, pic_buf, pic_buf->pts)) != MMAL_SUCCESS) ++ 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; + } @@ -10314,7 +11651,7 @@ + sub_buf != NULL ? sub_buf : *psub_bufs2++, + &sys->subs[sub_no].sub, + &p_pic->format, -+ &(MMAL_RECT_T){.width = sys->display_width, .height = sys->display_height}, ++ &sys->dest_rect, + p_pic->date)) == 0) + break; + else if (rv < 0) @@ -10377,14 +11714,15 @@ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: case VOUT_DISPLAY_CHANGE_SOURCE_CROP: -@@ -640,11 +758,38 @@ +@@ -640,11 +801,39 @@ break; case VOUT_DISPLAY_RESET_PICTURES: - vlc_assert_unreachable(); + msg_Warn(vd, "Reset Pictures"); + kill_pool(sys); -+ vd->fmt = vd->source; // Take whatever source wants to give us ++ 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 actaully deal with + ret = VLC_SUCCESS; + break; + @@ -10417,14 +11755,15 @@ default: msg_Warn(vd, "Unknown control query %d", query); break; -@@ -661,13 +806,11 @@ +@@ -661,13 +850,11 @@ 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, &width, &height) >= 0) { ++ if (query_resolution(vd, sys->display_id, &width, &height) >= 0) { sys->display_width = width; sys->display_height = height; - vout_display_SendEventDisplaySize(vd, width, height); @@ -10433,7 +11772,7 @@ } sys->need_configure_display = false; -@@ -676,56 +819,164 @@ +@@ -676,56 +863,171 @@ vlc_mutex_unlock(&sys->manage_mutex); } @@ -10466,16 +11805,21 @@ + +#if 0 + char dbuf0[5]; -+ msg_Dbg(vd, " [%p:%p] Pos=%d,%d src=%dx%d/%dx%d, vd=%dx%d/%dx%d, Alpha=%d, Fmt=%s", src, src->p[0].p_pixels, ++ msg_Dbg(vd, " [%p:%p] Pos=%d,%d src=%dx%d/%dx%d, vd->fmt=%dx%d/%dx%d, vd->source=%dx%d/%dx%d, cfg=%dx%d, Alpha=%d, Fmt=%s", src, src->p[0].p_pixels, + sreg->i_x, sreg->i_y, + src->format.i_visible_width, src->format.i_visible_height, + src->format.i_width, src->format.i_height, + vd->fmt.i_visible_width, vd->fmt.i_visible_height, + vd->fmt.i_width, vd->fmt.i_height, ++ vd->source.i_visible_width, vd->source.i_visible_height, ++ vd->source.i_width, vd->source.i_height, ++ vd->cfg->display.width, vd->cfg->display.height, + sreg->i_alpha, + str_fourcc(dbuf0, src->format.i_chroma)); +#endif + ++ // 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}, @@ -10510,8 +11854,22 @@ + + 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 inrto temp ++ // subpic list here - if they turn up here then process into temp + // buffers + if (subpicture != NULL) { + attach_subpics(vd, sys, subpicture); @@ -10527,25 +11885,8 @@ + + MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->copy_pool->queue); + // Copy 2d -+ unsigned int y_size = sys->input->format->es->video.width * sys->input->format->es->video.height; -+ ++ hw_mmal_copy_pic_to_buf(buf->data, &buf->length, sys->input->format, p_pic); + buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; -+ buf->length = sys->input->buffer_size; -+ -+ mem_copy_2d(buf->data, sys->input->format->es->video.width, -+ p_pic->p[0].p_pixels, p_pic->p[0].i_pitch, -+ sys->input->format->es->video.crop.height, -+ sys->input->format->es->video.crop.width); -+ -+ mem_copy_2d(buf->data + y_size, sys->input->format->es->video.width / 2, -+ p_pic->p[1].p_pixels, p_pic->p[1].i_pitch, -+ sys->input->format->es->video.crop.height / 2, -+ sys->input->format->es->video.crop.width / 2); -+ -+ mem_copy_2d(buf->data + y_size + y_size / 4, sys->input->format->es->video.width / 2, -+ p_pic->p[2].p_pixels, p_pic->p[2].i_pitch, -+ sys->input->format->es->video.crop.height / 2, -+ sys->input->format->es->video.crop.width / 2); + + sys->copy_buf = buf; + } @@ -10567,11 +11908,16 @@ + if (isp_prepare(vd, isp) != MMAL_SUCCESS) + return; + -+ buf = pic_mmal_buffer(p_pic); -+ if ((err = port_send_replicated(isp->input, isp->in_pool, -+ buf, buf->pts)) != MMAL_SUCCESS) ++ if ((buf = hw_mmal_pic_buf_replicated(p_pic, isp->in_pool)) == NULL) ++ { ++ msg_Err(vd, "Pic has no attached buffer"); ++ return; ++ } ++ ++ if ((err = mmal_port_send_buffer(isp->input, buf)) != MMAL_SUCCESS) + { + msg_Err(vd, "Send buffer to input failed"); ++ mmal_buffer_header_release(buf); + return; + } + @@ -10632,7 +11978,28 @@ } static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2) -@@ -828,148 +1079,12 @@ +@@ -780,9 +1082,9 @@ + 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) { +@@ -810,7 +1112,7 @@ + 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); + } +@@ -828,148 +1130,12 @@ } } @@ -10782,7 +12149,7 @@ ((double)vd->sys->i_frame_rate / vd->sys->i_frame_rate_base); vout_display_sys_t *sys = vd->sys; -@@ -1012,32 +1127,286 @@ +@@ -1012,32 +1178,317 @@ } } @@ -10886,41 +12253,72 @@ +#endif +} + ++ ++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(vlc_object_t *object) +{ + vout_display_t *vd = (vout_display_t *)object; + vout_display_sys_t *sys; -+ vout_display_place_t place; + MMAL_DISPLAYREGION_T display_region; + MMAL_STATUS_T status; + int ret = VLC_EGENERIC; -+ const MMAL_FOURCC_T enc_in = vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma); ++ // 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); + +#if TRACE_ALL + msg_Dbg(vd, "<<< %s", __func__); +#endif -+ if (enc_in == 0) -+ { -+#if TRACE_ALL -+ char dbuf0[5]; -+ msg_Dbg(vd, ">>> %s: Format %s not MMAL", __func__, str_fourcc(dbuf0, vd->fmt.i_chroma)); -+#endif -+ return VLC_EGENERIC; -+ } + + 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 = var_InheritInteger(vd, "qt-fullscreen-screennumber" ); ++ 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)", @@ -10961,7 +12359,7 @@ + + sys->input->buffer_size = sys->input->buffer_size_recommended; + -+ if (!want_copy(vd)) { ++ if (!needs_copy) { + sys->input->buffer_num = 30; + } + else { @@ -10973,18 +12371,25 @@ + } + } + -+ vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); ++ 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.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.dest_rect = sys->dest_rect; + 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; ++ 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)", @@ -11012,35 +12417,33 @@ + goto fail; + } + -+ { -+ unsigned int i; -+ for (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->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; -+ } ++ 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; + } + } + -+ -+ vlc_mutex_init(&sys->manage_mutex); ++ // If we can't deal with it directly ask for I420 ++ vd->fmt.i_chroma = req_chroma(vd); + + vd->info = (vout_display_info_t){ + .is_slow = false, @@ -11055,13 +12458,6 @@ + vd->display = vd_display; + vd->control = vd_control; + -+ vc_tv_register_callback(tvservice_cb, vd); -+ -+ if (query_resolution(vd, &sys->display_width, &sys->display_height) < 0) -+ { -+ sys->display_width = vd->cfg->display.width; -+ sys->display_height = vd->cfg->display.height; -+ } + + msg_Dbg(vd, ">>> %s: ok", __func__); + return VLC_SUCCESS; @@ -11090,6 +12486,8 @@ + MMAL_ADJUST_REFRESHRATE_LONGTEXT, false) + add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT, + MMAL_NATIVE_INTERLACE_LONGTEXT, false) ++ add_string(MMAL_DISPLAY_NAME, "auto", MMAL_DISPLAY_TEXT, ++ MMAL_DISPLAY_LONGTEXT, false) + set_callbacks(OpenMmalVout, CloseMmalVout) + +vlc_module_end() @@ -11097,7 +12495,7 @@ + --- /dev/null +++ b/modules/hw/mmal/xsplitter.c -@@ -0,0 +1,437 @@ +@@ -0,0 +1,560 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif @@ -11119,16 +12517,97 @@ + +#define TRACE_ALL 0 + ++typedef struct display_desc_s ++{ ++ vout_display_t * vout; ++ unsigned int max_pels; ++} display_desc_t; ++ +typedef struct mmal_x11_sys_s +{ + bool use_mmal; -+ vout_display_t * cur_vout; -+ vout_display_t * mmal_vout; -+ vout_display_t * x_vout; ++ display_desc_t * cur_desc; ++ display_desc_t mmal_desc; ++ display_desc_t x_desc; + uint32_t changed; + vlc_fourcc_t subpicture_chromas[16]; +} mmal_x11_sys_t; + ++#define MAX_GL_PELS (1920*1080) ++#define MAX_MMAL_PELS (4096*4096) // Should never be hit ++ ++#if 0 ++// Gen prog for the following table ++// Not done inline in case we end up pulling in FP libs we don't want ++#include ++#include ++ ++int main(int argc, char *argv[]) ++{ ++ unsigned int i; ++ for (i = 0; i != 64; ++i) ++ { ++ printf(" [%2u]=%5u,", i, (unsigned int)(0.5 + (1/sqrt((i + 5)/4.0) * 65536.0))); ++ if (i % 4 == 3) ++ printf("\n"); ++ } ++} ++#endif ++ ++static const uint16_t sqrt_tab[64] = { ++ [ 0]=58617, [ 1]=53510, [ 2]=49541, [ 3]=46341, ++ [ 4]=43691, [ 5]=41449, [ 6]=39520, [ 7]=37837, ++ [ 8]=36353, [ 9]=35030, [10]=33843, [11]=32768, ++ [12]=31790, [13]=30894, [14]=30070, [15]=29309, ++ [16]=28602, [17]=27945, [18]=27330, [19]=26755, ++ [20]=26214, [21]=25705, [22]=25225, [23]=24770, ++ [24]=24339, [25]=23930, [26]=23541, [27]=23170, ++ [28]=22817, [29]=22479, [30]=22155, [31]=21845, ++ [32]=21548, [33]=21263, [34]=20988, [35]=20724, ++ [36]=20470, [37]=20225, [38]=19988, [39]=19760, ++ [40]=19539, [41]=19326, [42]=19119, [43]=18919, ++ [44]=18725, [45]=18536, [46]=18354, [47]=18176, ++ [48]=18004, [49]=17837, [50]=17674, [51]=17515, ++ [52]=17361, [53]=17211, [54]=17064, [55]=16921, ++ [56]=16782, [57]=16646, [58]=16514, [59]=16384, ++ [60]=16257, [61]=16134, [62]=16013, [63]=15895 ++}; ++#define SQRT_MAX (sizeof(sqrt_tab)/sizeof(sqrt_tab[0]) - 1) ++ ++static bool cpy_fmt_limit_size(const display_desc_t * const dd, ++ video_format_t * const dst, ++ const video_format_t * const src) ++{ ++ const unsigned int src_pel = src->i_visible_width * src->i_visible_height; ++ ++ *dst = *src; ++ ++ if (src_pel <= dd->max_pels) ++ return false; ++ ++ // scaling factor sqrt(max_pel/cur_pel) ++ // sqrt done by lookup & 16 bit fixed-point maths - not exactly accurate but ++ // easily good enough & avoids floating point (which may be slow) ++ // src_pel > max_pel so n >= 0 ++ // Rounding should be such that exact sqrts work and everything else rounds ++ // down ++ unsigned int n = ((src_pel * 4 - 1) / dd->max_pels) - 4; ++ unsigned int scale = sqrt_tab[n >= SQRT_MAX ? SQRT_MAX : n]; ++ ++ // Rescale width - rounding up to 16 ++ unsigned int width = ((src->i_visible_width * scale + (16 << 16) - 1) >> 16) & ~15; ++ // Rescale height based on new width ++ unsigned int height = (src->i_visible_height * width + src->i_visible_width/2) / src->i_visible_width; ++ ++// fprintf(stderr, "%dx%d -> %dx%d\n", src->i_visible_width, src->i_visible_height, width, height); ++ ++ dst->i_width = width; ++ dst->i_visible_width = width; ++ dst->i_height = height; ++ dst->i_visible_height = height; ++ return true; ++} ++ +static void unload_display_module(vout_display_t * const x_vout) +{ + if (x_vout != NULL) { @@ -11149,9 +12628,9 @@ + if (sys == NULL) + return; + -+ unload_display_module(sys->x_vout); ++ unload_display_module(sys->x_desc.vout); + -+ unload_display_module(sys->mmal_vout); ++ unload_display_module(sys->mmal_desc.vout); + + free(sys); + @@ -11192,13 +12671,16 @@ +} + + -+static vout_display_t * load_display_module(vout_display_t * const vd, -+ const char * const cap, const char * const module_name) ++static int load_display_module(vout_display_t * const vd, ++ display_desc_t * const dd, ++ const char * const cap, ++ const char * const module_name) +{ + vout_display_t * const x_vout = vlc_object_create(vd, sizeof(*x_vout)); + ++ dd->vout = NULL; + if (!x_vout) -+ return NULL; ++ return -1; + + x_vout->owner.sys = vd; + x_vout->owner.event = mmal_x11_event; @@ -11206,10 +12688,9 @@ + x_vout->owner.window_del = mmal_x11_window_del; + + x_vout->cfg = vd->cfg; -+ x_vout->source = vd->source; + x_vout->info = vd->info; -+ -+ x_vout->fmt = vd->fmt; ++ cpy_fmt_limit_size(dd, &x_vout->source, &vd->source); ++ cpy_fmt_limit_size(dd, &x_vout->fmt, &vd->fmt); + + if ((x_vout->module = module_need(x_vout, cap, module_name, true)) == NULL) + { @@ -11219,11 +12700,12 @@ + + msg_Dbg(vd, "R/G/B: %08x/%08x/%08x", x_vout->fmt.i_rmask, x_vout->fmt.i_gmask, x_vout->fmt.i_bmask); + -+ return x_vout; ++ dd->vout = x_vout; ++ return 0; + +fail: + vlc_object_release(x_vout); -+ return NULL; ++ return -1; +} + + @@ -11239,7 +12721,7 @@ +static picture_pool_t * mmal_x11_pool(vout_display_t * vd, unsigned count) +{ + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; -+ vout_display_t * const x_vd = sys->cur_vout; ++ vout_display_t * const x_vd = sys->cur_desc->vout; +#if TRACE_ALL + char buf0[5]; + msg_Dbg(vd, "<<< %s (count=%d) %s:%dx%d->%s:%dx%d", __func__, count, @@ -11268,7 +12750,7 @@ +static void mmal_x11_prepare(vout_display_t * vd, picture_t * pic, subpicture_t * sub) +{ + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; -+ vout_display_t * const x_vd = sys->cur_vout; ++ vout_display_t * const x_vd = sys->cur_desc->vout; +#if TRACE_ALL + msg_Dbg(vd, "<<< %s", __func__); +#endif @@ -11289,10 +12771,10 @@ +static void mmal_x11_display(vout_display_t * vd, picture_t * pic, subpicture_t * sub) +{ + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; -+ vout_display_t * const x_vd = sys->cur_vout; -+ const bool is_mmal_pic = hw_mmal_pic_is_mmal(pic); ++ vout_display_t * const x_vd = sys->cur_desc->vout; + +#if TRACE_ALL ++ const bool is_mmal_pic = hw_mmal_pic_is_mmal(pic); + msg_Dbg(vd, "<<< %s: fmt: %dx%d/%dx%d, pic:%dx%d, pts=%lld, mmal=%d/%d", __func__, vd->fmt.i_width, vd->fmt.i_height, x_vd->fmt.i_width, x_vd->fmt.i_height, pic->format.i_width, pic->format.i_height, (long long)pic->date, + is_mmal_pic, sys->use_mmal); +#endif @@ -11312,28 +12794,29 @@ +} + + -+static int vout_display_Control(vout_display_t *vd, int query, ...) ++static int vout_display_Control(display_desc_t * const dd, int query, ...) +{ + va_list args; + int result; + + va_start(args, query); -+ result = vd->control(vd, query, args); ++ result = dd->vout->control(dd->vout, query, args); + va_end(args); + + return result; +} + -+static bool want_mmal_vout(vout_display_t * vd, const mmal_x11_sys_t * const sys) ++static bool want_mmal_vout(vout_display_t * const vd, const mmal_x11_sys_t * const sys) +{ -+ return sys->mmal_vout != NULL && (sys->x_vout == NULL || var_InheritBool(vd, "fullscreen")); ++ return sys->mmal_desc.vout != NULL && ++ (sys->x_desc.vout == NULL || var_InheritBool(vd, "fullscreen")); +} + +/* Control on the module (mandatory) */ +static int mmal_x11_control(vout_display_t * vd, int ctl, va_list va) +{ + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; -+ vout_display_t *x_vd = sys->cur_vout; ++ display_desc_t *x_desc = sys->cur_desc; + int rv; +#if TRACE_ALL + msg_Dbg(vd, "<<< %s[%d] (ctl=%d)", __func__, sys->use_mmal, ctl); @@ -11348,23 +12831,23 @@ + const vout_display_cfg_t * const cfg = va_arg(va, const vout_display_cfg_t *); + const bool want_mmal = want_mmal_vout(vd, sys); + const bool swap_vout = (sys->use_mmal != want_mmal); -+ vout_display_t * const new_vd = want_mmal ? sys->mmal_vout : sys->x_vout; ++ display_desc_t * const new_desc = want_mmal ? &sys->mmal_desc : &sys->x_desc; + + msg_Dbg(vd, "Change size: %d, %d: mmal_vout=%p, want_mmal=%d, fs=%d", -+ cfg->display.width, cfg->display.height, sys->mmal_vout, want_mmal, ++ cfg->display.width, cfg->display.height, sys->mmal_desc.vout, want_mmal, + var_InheritBool(vd, "fullscreen")); + + if (swap_vout) { + if (sys->use_mmal) { -+ vout_display_Control(x_vd, VOUT_DISPLAY_CHANGE_MMAL_HIDE); ++ vout_display_Control(x_desc, VOUT_DISPLAY_CHANGE_MMAL_HIDE); + } + vout_display_SendEventPicturesInvalid(vd); + } + -+ rv = vout_display_Control(new_vd, ctl, cfg); ++ rv = vout_display_Control(new_desc, ctl, cfg); + if (rv == VLC_SUCCESS) { -+ vd->fmt = new_vd->fmt; -+ sys->cur_vout = new_vd; ++ vd->fmt = new_desc->vout->fmt; ++ sys->cur_desc = new_desc; + sys->use_mmal = want_mmal; + } + @@ -11373,43 +12856,50 @@ + const uint32_t changed = sys->changed; + sys->changed = 0; + if ((changed & (1 << VOUT_DISPLAY_CHANGE_DISPLAY_FILLED)) != 0) -+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, vd->cfg); ++ vout_display_Control(new_desc, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, vd->cfg); + if ((changed & (1 << VOUT_DISPLAY_CHANGE_ZOOM)) != 0) -+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg); -+ if ((changed & ((1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP) | (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT))) != 0) -+ new_vd->source = vd->source; ++ vout_display_Control(new_desc, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg); ++ if ((changed & ((1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP) | ++ (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT))) != 0) ++ cpy_fmt_limit_size(new_desc, &new_desc->vout->source, &vd->source); + if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT)) != 0) -+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT); ++ vout_display_Control(new_desc, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT); + if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP)) != 0) -+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP); ++ vout_display_Control(new_desc, VOUT_DISPLAY_CHANGE_SOURCE_CROP); + if ((changed & (1 << VOUT_DISPLAY_CHANGE_VIEWPOINT)) != 0) -+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg); ++ vout_display_Control(new_desc, VOUT_DISPLAY_CHANGE_VIEWPOINT, vd->cfg); + } + + break; + } + + case VOUT_DISPLAY_RESET_PICTURES: -+ msg_Dbg(vd, "<<< %s: Pic reset: fmt: %dx%d<-%dx%d, source: %dx%d/%dx%d", __func__, -+ vd->fmt.i_width, vd->fmt.i_height, x_vd->fmt.i_width, x_vd->fmt.i_height, -+ vd->source.i_width, vd->source.i_height, x_vd->source.i_width, x_vd->source.i_height); ++ { ++ char dbuf0[5], dbuf1[5], dbuf2[5]; ++ msg_Dbg(vd, "<<< %s: Pic reset: fmt: %s,%dx%d<-%s,%dx%d, source: %s,%dx%d/%dx%d", __func__, ++ str_fourcc(dbuf0, vd->fmt.i_chroma), vd->fmt.i_width, vd->fmt.i_height, ++ str_fourcc(dbuf1, x_desc->vout->fmt.i_chroma), x_desc->vout->fmt.i_width, x_desc->vout->fmt.i_height, ++ str_fourcc(dbuf2, vd->source.i_chroma), vd->source.i_width, vd->source.i_height, x_desc->vout->source.i_width, ++ x_desc->vout->source.i_height); ++ } + // If the display doesn't have has_pictures_invalid then it doesn't + // expect RESET_PICTURES -+ if (sys->x_vout->info.has_pictures_invalid) { -+ rv = sys->x_vout->control(sys->x_vout, ctl, va); ++ if (sys->x_desc.vout->info.has_pictures_invalid) { ++ rv = sys->x_desc.vout->control(sys->x_desc.vout, ctl, va); + } -+ if (sys->mmal_vout && sys->mmal_vout->info.has_pictures_invalid) { -+ rv = sys->mmal_vout->control(sys->mmal_vout, ctl, va); ++ if (sys->mmal_desc.vout && sys->mmal_desc.vout->info.has_pictures_invalid) { ++ rv = sys->mmal_desc.vout->control(sys->mmal_desc.vout, ctl, va); + } -+ vd->fmt = x_vd->fmt; ++ vd->fmt = x_desc->vout->fmt; + break; + + case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: + case VOUT_DISPLAY_CHANGE_SOURCE_CROP: -+ x_vd->source = vd->source; ++ cpy_fmt_limit_size(x_desc, &x_desc->vout->source, &vd->source); ++ + /* FALLTHRU */ + default: -+ rv = x_vd->control(x_vd, ctl, va); ++ rv = x_desc->vout->control(x_desc->vout, ctl, va); +// vd->fmt = x_vd->fmt; + break; + } @@ -11426,7 +12916,7 @@ +static void mmal_x11_manage(vout_display_t * vd) +{ + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; -+ vout_display_t * const x_vd = sys->cur_vout; ++ vout_display_t * const x_vd = sys->cur_desc->vout; +#if TRACE_ALL + msg_Dbg(vd, "<<< %s", __func__); +#endif @@ -11453,15 +12943,34 @@ + .subpicture_chromas = NULL + }; + -+ if ((sys->x_vout = load_display_module(vd, "vout display", "opengles2")) != NULL) ++ { ++ char dbuf0[5]; ++ msg_Dbg(vd, ">>> %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__, ++ str_fourcc(dbuf0, vd->fmt.i_chroma), ++ vd->fmt.i_width, vd->fmt.i_height, ++ vd->fmt.i_x_offset, vd->fmt.i_y_offset, ++ vd->fmt.i_visible_width, vd->fmt.i_visible_height, ++ vd->fmt.i_sar_num, vd->fmt.i_sar_den); ++ } ++ ++ sys->x_desc.max_pels = MAX_GL_PELS; ++ sys->mmal_desc.max_pels = MAX_MMAL_PELS; ++ ++ if (load_display_module(vd, &sys->x_desc, "vout display", "opengles2") == 0) ++ { + msg_Dbg(vd, "Opengles2 output found"); -+ else if ((sys->x_vout = load_display_module(vd, "vout display", "xcb_x11")) != NULL) -+ msg_Dbg(vd, "X11 XCB output found"); ++ } ++ else ++ { ++ sys->x_desc.max_pels = MAX_MMAL_PELS; ++ if (load_display_module(vd, &sys->x_desc, "vout display", "xcb_x11") == 0) ++ msg_Dbg(vd, "X11 XCB output found"); ++ } + -+ if ((sys->mmal_vout = load_display_module(vd, "vout display", "mmal_vout")) != NULL) ++ if ((load_display_module(vd, &sys->mmal_desc, "vout display", "mmal_vout")) == 0) + msg_Dbg(vd, "MMAL output found"); + -+ if (sys->mmal_vout == NULL && sys->x_vout == NULL) { ++ if (sys->mmal_desc.vout == NULL && sys->x_desc.vout == NULL) { + char dbuf0[5], dbuf1[5]; + msg_Info(vd, "No valid output found for vout (%s/%s)", str_fourcc(dbuf0, vd->fmt.i_chroma), str_fourcc(dbuf1, vd->source.i_chroma)); + goto fail; @@ -11476,33 +12985,33 @@ +#endif + + if (want_mmal_vout(vd, sys)) { -+ sys->cur_vout = sys->mmal_vout; ++ sys->cur_desc = &sys->mmal_desc; + sys->use_mmal = true; + } + else { -+ sys->cur_vout = sys->x_vout; ++ sys->cur_desc = &sys->x_desc; + sys->use_mmal = false; + } + -+ if (sys->mmal_vout == NULL || sys->x_vout == NULL) { -+ vd->info = sys->cur_vout->info; ++ if (sys->mmal_desc.vout == NULL || sys->x_desc.vout == NULL) { ++ vd->info = sys->cur_desc->vout->info; + vd->info.has_pictures_invalid = true; // Should make this unwanted + } + else { + // We have both - construct a combination + vd->info = (vout_display_info_t){ + .is_slow = false, -+ .has_double_click = sys->mmal_vout->info.has_double_click || sys->x_vout->info.has_double_click, -+ .needs_hide_mouse = sys->mmal_vout->info.needs_hide_mouse || sys->x_vout->info.needs_hide_mouse, ++ .has_double_click = sys->mmal_desc.vout->info.has_double_click || sys->x_desc.vout->info.has_double_click, ++ .needs_hide_mouse = sys->mmal_desc.vout->info.needs_hide_mouse || sys->x_desc.vout->info.needs_hide_mouse, + .has_pictures_invalid = true, + }; + // Construct intersection of subpicture chromas + // sys calloced so no need to add the terminating zero -+ if (sys->mmal_vout->info.subpicture_chromas != NULL && sys->x_vout->info.subpicture_chromas != NULL) { ++ if (sys->mmal_desc.vout->info.subpicture_chromas != NULL && sys->x_desc.vout->info.subpicture_chromas != NULL) { + unsigned int n = 0; + // N^2 - fix if we ever care -+ for (const vlc_fourcc_t * p1 = sys->mmal_vout->info.subpicture_chromas; *p1 != 0 && n != 15; ++p1) { -+ for (const vlc_fourcc_t * p2 = sys->x_vout->info.subpicture_chromas; *p2 != 0; ++p2) { ++ for (const vlc_fourcc_t * p1 = sys->mmal_desc.vout->info.subpicture_chromas; *p1 != 0 && n != 15; ++p1) { ++ for (const vlc_fourcc_t * p2 = sys->x_desc.vout->info.subpicture_chromas; *p2 != 0; ++p2) { + if (*p1 == *p2) { + sys->subpicture_chromas[n++] = *p1; + break; @@ -11513,8 +13022,20 @@ + vd->info.subpicture_chromas = sys->subpicture_chromas; + } + } -+ vd->fmt = sys->cur_vout->fmt; ++ vd->fmt = sys->cur_desc->vout->fmt; + ++#if TRACE_ALL ++ { ++ char dbuf0[5]; ++ msg_Dbg(vd, ">>> %s: (%s) %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__, ++ module_get_name(sys->cur_desc->vout->module, false), ++ str_fourcc(dbuf0, vd->fmt.i_chroma), ++ vd->fmt.i_width, vd->fmt.i_height, ++ vd->fmt.i_x_offset, vd->fmt.i_y_offset, ++ vd->fmt.i_visible_width, vd->fmt.i_visible_height, ++ vd->fmt.i_sar_num, vd->fmt.i_sar_den); ++ } ++#endif + return VLC_SUCCESS; + +fail: @@ -11561,15 +13082,34 @@ const EGLint conf_attr[] = { EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 5, +--- a/src/input/decoder.c ++++ b/src/input/decoder.c +@@ -1995,6 +1995,7 @@ + vlc_mutex_lock( &p_owner->lock ); + p_owner->b_waiting = false; + vlc_cond_signal( &p_owner->wait_request ); ++ vlc_mutex_unlock( &p_owner->lock ); + + /* If the video output is paused or slow, or if the picture pool size was + * under-estimated (e.g. greedy video filter, buggy decoder...), the +@@ -2005,7 +2006,6 @@ + * worker threads (if any) and the decoder thread to terminate. */ + if( p_owner->p_vout != NULL ) + vout_Cancel( p_owner->p_vout, true ); +- vlc_mutex_unlock( &p_owner->lock ); + + vlc_join( p_owner->thread, NULL ); + --- a/src/misc/fourcc.c +++ b/src/misc/fourcc.c -@@ -755,8 +755,12 @@ +@@ -755,8 +755,13 @@ { { VLC_CODEC_VDPAU_VIDEO_420, VLC_CODEC_VDPAU_VIDEO_422, VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT }, FAKE_FMT() }, - { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE, - VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE }, -+ { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE }, ++ { { VLC_CODEC_ANDROID_OPAQUE }, FAKE_FMT() }, ++ { { VLC_CODEC_MMAL_OPAQUE, VLC_CODEC_MMAL_ZC_SAND30 }, + FAKE_FMT() }, + { { VLC_CODEC_MMAL_ZC_I420, VLC_CODEC_MMAL_ZC_SAND8, + VLC_CODEC_MMAL_ZC_SAND10, VLC_CODEC_MMAL_ZC_RGB32 }, @@ -11578,3 +13118,67 @@ FAKE_FMT() }, { { VLC_CODEC_D3D11_OPAQUE_10B, VLC_CODEC_D3D9_OPAQUE_10B }, FAKE_FMT() }, +--- a/src/misc/picture.c ++++ b/src/misc/picture.c +@@ -365,10 +365,30 @@ + p_dst->b_top_field_first = p_src->b_top_field_first; + } + ++static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma) ++{ ++ return i_chroma == VLC_CODEC_MMAL_OPAQUE || ++ i_chroma == VLC_CODEC_MMAL_ZC_I420 || ++ i_chroma == VLC_CODEC_MMAL_ZC_RGB32 || ++ i_chroma == VLC_CODEC_MMAL_ZC_SAND10 || ++ i_chroma == VLC_CODEC_MMAL_ZC_SAND30 || ++ i_chroma == VLC_CODEC_MMAL_ZC_SAND8; ++} ++ + void picture_CopyPixels( picture_t *p_dst, const picture_t *p_src ) + { +- for( int i = 0; i < p_src->i_planes ; i++ ) +- plane_CopyPixels( p_dst->p+i, p_src->p+i ); ++ if( is_zc_chroma(p_src->format.i_chroma) ) ++ { ++ assert(p_dst->i_planes == 0); ++ p_dst->i_planes = p_src->i_planes; ++ for( int i = 0; i < p_src->i_planes; i++ ) ++ p_dst->p[i] = p_src->p[i]; ++ } ++ else ++ { ++ for( int i = 0; i < p_src->i_planes; i++ ) ++ plane_CopyPixels( p_dst->p+i, p_src->p+i ); ++ } + + assert( p_dst->context == NULL ); + +--- a/src/video_output/video_output.c ++++ b/src/video_output/video_output.c +@@ -964,6 +964,17 @@ + return NULL; + } + ++ ++static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma) ++{ ++ return i_chroma == VLC_CODEC_MMAL_OPAQUE || ++ i_chroma == VLC_CODEC_MMAL_ZC_I420 || ++ i_chroma == VLC_CODEC_MMAL_ZC_RGB32 || ++ i_chroma == VLC_CODEC_MMAL_ZC_SAND10 || ++ i_chroma == VLC_CODEC_MMAL_ZC_SAND30 || ++ i_chroma == VLC_CODEC_MMAL_ZC_SAND8; ++} ++ + static int ThreadDisplayRenderPicture(vout_thread_t *vout, bool is_forced) + { + vout_thread_sys_t *sys = vout->p; +@@ -1098,7 +1109,7 @@ + } + + assert(vout_IsDisplayFiltered(vd) == !sys->display.use_dr); +- if (sys->display.use_dr && !is_direct) { ++ if (sys->display.use_dr && !is_direct && !is_zc_chroma(todisplay->format.i_chroma)) { + picture_t *direct = NULL; + if (likely(vout->p->display_pool != NULL)) + direct = picture_pool_Get(vout->p->display_pool); diff --git a/sources b/sources index cc99bb4..3bf1f0a 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (vlc-3.0.9-20191014-0223.tar.xz) = c4585121d67d426d4cc1441f3b7f11be864b2a9e4da100e5b5f3a7116a4bce8e96c809ee9048b9566cf8c1893d2326ca9afad5913696ecfd8b502f80cdfd1a78 +SHA512 (vlc-3.0.9-20200118-0223.tar.xz) = 3ad1e02a4603953fc08e4be7b3a4bb2fb05fe987b26559467e6b19b437dfc01c76ce4d03185fe3bda7d3d6c07cca9b99607a2cc530f266f4eb0f37d05e0f32c9 diff --git a/vlc.spec b/vlc.spec index 2b1ccf9..8e4ec8e 100644 --- a/vlc.spec +++ b/vlc.spec @@ -1,4 +1,4 @@ -%global vlc_date 20191014 +%global vlc_date 20200118 #global vlc_rc -rc9 %global vlc_tag -%{?vlc_date}-0223 %if 0%{?vlc_tag:1} @@ -54,11 +54,11 @@ Summary: The cross-platform open-source multimedia framework, player and server Epoch: 1 Name: vlc Version: 3.0.9 -Release: 27%{?dist} +Release: 28%{?dist} License: GPLv2+ URL: https://www.videolan.org Source0: %{vlc_url}/%{?!vlc_tag:%{version}/}vlc-%{version}%{?vlc_tag}.tar.xz -Patch0: https://github.com/RPi-Distro/vlc/raw/buster-rpt/debian/patches/mmal_10.patch +Patch0: https://github.com/RPi-Distro/vlc/raw/buster-rpt/debian/patches/mmal_16.patch Patch1: libplacebo_patch_1.patch Patch2: Fix_aom_abi_break.patch Patch3: 0001-Use-SYSTEM-wide-ciphers-for-gnutls.patch @@ -129,7 +129,9 @@ BuildRequires: libmtp-devel >= 1.0.0 %{?_with_projectm:BuildRequires: libprojectM-devel} BuildRequires: libproxy-devel BuildRequires: librsvg2-devel >= 2.9.0 +%if ! 0%{?el8} BuildRequires: libssh2-devel +%endif BuildRequires: libsysfs-devel BuildRequires: libshout-devel BuildRequires: libsmbclient-devel @@ -560,6 +562,11 @@ fi || : %changelog +* Sat Jan 18 2020 Nicolas Chauvet - 1:3.0.9-28 +- Update to current snapshot +- Drop libssh2 from el8 - rfbz#5519 +- Update mmal patch + * Sun Dec 22 2019 Leigh Scott - 1:3.0.9-27 - Rebuild for new protobuf version