From 91d983c5825b639ccdf3f91a73c08fc57994d820 Mon Sep 17 00:00:00 2001 From: graysky Date: Sun, 15 Oct 2023 02:26:49 -0400 Subject: [PATCH] alarm/vlc-rpi to 3.0.19-1 Update and sync with extra/vlc but retain lua5.2 due to breakage with lua --- ..._35.patch => 0003-dev-3.0.19-port_1.patch} | 6653 +++++++++++++---- alarm/vlc-rpi/0004-mmal_caca.patch | 15 - alarm/vlc-rpi/0005-mmal_chain.patch | 14 - alarm/vlc-rpi/0006-mmal_exit_fix.patch | 14 - alarm/vlc-rpi/0007-mmal_wayland.patch | 72 - alarm/vlc-rpi/PKGBUILD | 38 +- 6 files changed, 5314 insertions(+), 1492 deletions(-) rename alarm/vlc-rpi/{0003-mmal_35.patch => 0003-dev-3.0.19-port_1.patch} (85%) delete mode 100644 alarm/vlc-rpi/0004-mmal_caca.patch delete mode 100644 alarm/vlc-rpi/0005-mmal_chain.patch delete mode 100644 alarm/vlc-rpi/0006-mmal_exit_fix.patch delete mode 100644 alarm/vlc-rpi/0007-mmal_wayland.patch diff --git a/alarm/vlc-rpi/0003-mmal_35.patch b/alarm/vlc-rpi/0003-dev-3.0.19-port_1.patch similarity index 85% rename from alarm/vlc-rpi/0003-mmal_35.patch rename to alarm/vlc-rpi/0003-dev-3.0.19-port_1.patch index 186c7385c..95b75e27d 100644 --- a/alarm/vlc-rpi/0003-mmal_35.patch +++ b/alarm/vlc-rpi/0003-dev-3.0.19-port_1.patch @@ -1,9 +1,28 @@ +diff --git a/bin/vlc.c b/bin/vlc.c +index 72e0eee428..6d92b95990 100644 +--- a/bin/vlc.c ++++ b/bin/vlc.c +@@ -106,7 +106,10 @@ static void vlc_kill (void *data) + static void exit_timeout (int signum) + { + (void) signum; +- signal (SIGINT, SIG_DFL); ++// This doesn't seem to be strong enough to reliably kill us if we fail to exit ++// in a timely fashion - so upgrade to _exit(). ++// signal (SIGINT, SIG_DFL); ++ _exit(0); + } + + /***************************************************************************** +diff --git a/configure.ac b/configure.ac +index 753d0610e6..2933715dc6 100644 --- a/configure.ac +++ b/configure.ac -@@ -3081,6 +3081,21 @@ dnl OpenGL ES 2: depends on EGL 1.1 +@@ -3083,6 +3083,21 @@ AS_IF([test "${have_gl}" = "yes"], [ + dnl OpenGL ES 2: depends on EGL 1.1 PKG_ENABLE_MODULES_VLC([GLES2], [], [glesv2], [OpenGL ES v2 support], [disabled]) - dnl ++dnl +dnl DRM +dnl +AC_ARG_ENABLE(drm, @@ -18,11 +37,10 @@ +]) +AM_CONDITIONAL([HAVE_DRM], [test "${have_drm}" = "yes"]) + -+dnl + dnl dnl Xlib dnl - -@@ -3444,20 +3459,24 @@ AM_CONDITIONAL([HAVE_KVA], [test "${have +@@ -3447,20 +3462,24 @@ AM_CONDITIONAL([HAVE_KVA], [test "${have_kva}" = "yes"]) dnl dnl MMAL plugin dnl @@ -50,7 +68,7 @@ AS_IF([test "${enable_mmal}" = "yes"], [ AC_MSG_ERROR([Cannot find bcm library...]) ], [ AC_MSG_WARN([Cannot find bcm library...]) ]) -@@ -3469,6 +3488,7 @@ if test "${enable_mmal}" != "no"; then +@@ -3472,6 +3491,7 @@ if test "${enable_mmal}" != "no"; then VLC_RESTORE_FLAGS fi AM_CONDITIONAL([HAVE_MMAL], [test "${have_mmal}" = "yes"]) @@ -58,6 +76,8 @@ dnl dnl evas plugin +diff --git a/include/vlc_fourcc.h b/include/vlc_fourcc.h +index 97827bd4c1..76ad846f2f 100644 --- a/include/vlc_fourcc.h +++ b/include/vlc_fourcc.h @@ -374,6 +374,11 @@ @@ -85,6 +105,8 @@ /* CVPixelBuffer opaque buffer type */ #define VLC_CODEC_CVPX_NV12 VLC_FOURCC('C','V','P','N') #define VLC_CODEC_CVPX_UYVY VLC_FOURCC('C','V','P','Y') +diff --git a/include/vlc_opengl.h b/include/vlc_opengl.h +index fdebfeb2e1..42671f85db 100644 --- a/include/vlc_opengl.h +++ b/include/vlc_opengl.h @@ -68,6 +68,9 @@ struct vlc_gl_t @@ -97,6 +119,8 @@ } egl; /* if ext == VLC_GL_EXT_WGL */ struct +diff --git a/modules/Makefile.am b/modules/Makefile.am +index 53b90a2713..6a29b126e9 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -10,9 +10,7 @@ EXTRA_SUBDIRS = \ @@ -117,9 +141,11 @@ include hw/vaapi/Makefile.am include hw/vdpau/Makefile.am include keystore/Makefile.am +diff --git a/modules/audio_filter/converter/tospdif.c b/modules/audio_filter/converter/tospdif.c +index 0c725d99e7..8d962d6e81 100644 --- a/modules/audio_filter/converter/tospdif.c +++ b/modules/audio_filter/converter/tospdif.c -@@ -484,6 +484,7 @@ static int write_buffer_dtshd( filter_t +@@ -484,6 +484,7 @@ static int write_buffer_dtshd( filter_t *p_filter, block_t *p_in_buf ) p_in_buf->i_buffer ) != VLC_SUCCESS ) return SPDIF_ERROR; unsigned i_period = p_filter->fmt_out.audio.i_rate @@ -127,6 +153,8 @@ * core.i_frame_length / core.i_rate; int i_subtype = dtshd_get_subtype( i_period ); +diff --git a/modules/audio_output/alsa.c b/modules/audio_output/alsa.c +index e10e1eb8c3..b8f190d782 100644 --- a/modules/audio_output/alsa.c +++ b/modules/audio_output/alsa.c @@ -39,6 +39,8 @@ @@ -159,7 +187,7 @@ static int Open (vlc_object_t *); static void Close (vlc_object_t *); -@@ -77,6 +87,22 @@ static const char *const channels_text[] +@@ -77,6 +87,22 @@ static const char *const channels_text[] = { N_("Surround 5.0"), N_("Surround 5.1"), N_("Surround 7.1"), }; @@ -268,7 +296,7 @@ /** Helper for ALSA -> VLC debugging output */ static void Dump (vlc_object_t *obj, const char *msg, int (*cb)(void *, snd_output_t *), void *p) -@@ -285,11 +384,20 @@ static int Start (audio_output_t *aout, +@@ -285,11 +384,20 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt) { aout_sys_t *sys = aout->sys; snd_pcm_format_t pcm_format; /* ALSA sample format */ @@ -280,10 +308,10 @@ + snd_pcm_uframes_t bufferSize; + unsigned int req_rate = fmt->i_rate; + vlc_fourcc_t req_format = fmt->i_format; -+ -+ msg_Dbg(aout, "Start: Format: %.4s, Chans: %d, Rate:%d", (char*)&fmt->i_format, aout_FormatNbChannels(fmt), fmt->i_rate); - if (aout_FormatNbChannels(fmt) == 0) ++ msg_Dbg(aout, "Start: Format: %.4s, Chans: %d, Rate:%d", (char*)&fmt->i_format, aout_FormatNbChannels(fmt), fmt->i_rate); ++ + if (aout_FormatNbChannels(fmt) == 0 && AOUT_FMT_LINEAR(fmt)) return VLC_EGENERIC; @@ -291,7 +319,7 @@ switch (fmt->i_format) { case VLC_CODEC_FL64: -@@ -308,36 +416,88 @@ static int Start (audio_output_t *aout, +@@ -308,36 +416,88 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt) pcm_format = SND_PCM_FORMAT_U8; break; default: @@ -388,7 +416,7 @@ if (!strncmp (device, "iec958", 6)) opt = device + 6; -@@ -364,8 +524,12 @@ static int Start (audio_output_t *aout, +@@ -364,8 +524,12 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt) FS( 44100) /* def. */ FS( 48000) FS( 32000) FS( 22050) FS( 24000) FS( 88200) FS(768000) FS( 96000) @@ -402,7 +430,7 @@ default: aes3 = IEC958_AES3_CON_FS_NOTID; break; -@@ -430,21 +594,27 @@ static int Start (audio_output_t *aout, +@@ -430,21 +594,27 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt) if (snd_pcm_hw_params_test_format (pcm, hw, pcm_format) == 0) ; else @@ -434,7 +462,7 @@ pcm_format = SND_PCM_FORMAT_S16; } else -@@ -459,10 +629,10 @@ static int Start (audio_output_t *aout, +@@ -459,10 +629,10 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt) msg_Err (aout, "cannot set sample format: %s", snd_strerror (val)); goto error; } @@ -447,7 +475,7 @@ { uint16_t map = var_InheritInteger (aout, "alsa-audio-channels"); -@@ -474,7 +644,6 @@ static int Start (audio_output_t *aout, +@@ -474,7 +644,6 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt) else { sys->chans_to_reorder = 0; @@ -455,7 +483,7 @@ } /* By default, ALSA plug will pad missing channels with zeroes, which is -@@ -490,14 +659,26 @@ static int Start (audio_output_t *aout, +@@ -490,14 +659,26 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt) } /* Set sample rate */ @@ -473,18 +501,18 @@ + msg_Warn(aout, "Passthrough requires rate %d, got %d", req_rate, sys->rate); + goto error; + } -+ + + bufferSize = req_rate / 10; // 100ms - bigger than this & truehd goes unhappy? + periodSize = bufferSize / 4; + periodSizeMax = bufferSize / 3; + snd_pcm_hw_params_set_period_size_max(pcm, hw, &periodSizeMax, NULL); - ++ + snd_pcm_hw_params_set_buffer_size_near(pcm, hw, &bufferSize); + snd_pcm_hw_params_set_period_size_near(pcm, hw, &periodSize, NULL); #if 1 /* work-around for period-long latency outputs (e.g. PulseAudio): */ param = AOUT_MIN_PREPARE_TIME; val = snd_pcm_hw_params_set_period_time_near (pcm, hw, ¶m, NULL); -@@ -579,13 +760,12 @@ static int Start (audio_output_t *aout, +@@ -579,13 +760,12 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt) } /* Setup audio_output_t */ @@ -503,7 +531,7 @@ aout->time_get = TimeGet; aout->play = Play; -@@ -616,7 +796,7 @@ static int TimeGet (audio_output_t *aout +@@ -616,7 +796,7 @@ static int TimeGet (audio_output_t *aout, vlc_tick_t *restrict delay) msg_Err (aout, "cannot estimate delay: %s", snd_strerror (val)); return -1; } @@ -512,7 +540,7 @@ return 0; } -@@ -627,6 +807,29 @@ static void Play (audio_output_t *aout, +@@ -627,6 +807,29 @@ static void Play (audio_output_t *aout, block_t *block) { aout_sys_t *sys = aout->sys; @@ -542,7 +570,7 @@ if (sys->chans_to_reorder != 0) aout_ChannelReorder(block->p_buffer, block->i_buffer, sys->chans_to_reorder, sys->chans_table, sys->format); -@@ -792,7 +995,7 @@ static int DeviceSelect (audio_output_t +@@ -792,7 +995,7 @@ static int DeviceSelect (audio_output_t *aout, const char *id) static int Open(vlc_object_t *obj) { audio_output_t *aout = (audio_output_t *)obj; @@ -572,19 +600,23 @@ free (sys->device); free (sys); } +diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am +index 6d9465fdae..5d46e5e95f 100644 --- a/modules/codec/Makefile.am +++ b/modules/codec/Makefile.am -@@ -373,6 +373,7 @@ libavcodec_plugin_la_SOURCES = \ +@@ -378,6 +378,7 @@ libavcodec_plugin_la_SOURCES = \ codec/avcodec/subtitle.c \ codec/avcodec/audio.c \ codec/avcodec/va.c codec/avcodec/va.h \ + codec/avcodec/drm_pic.c codec/avcodec/drm_pic.h \ - codec/avcodec/avcodec.c codec/avcodec/avcodec.h + codec/avcodec/avcodec.c codec/avcodec/avcodec.h \ + packetizer/av1_obu.c packetizer/av1_obu.h packetizer/av1.h if ENABLE_SOUT - libavcodec_plugin_la_SOURCES += codec/avcodec/encoder.c +diff --git a/modules/codec/avcodec/avcodec.c b/modules/codec/avcodec/avcodec.c +index d0780e9a17..13bf0f921a 100644 --- a/modules/codec/avcodec/avcodec.c +++ b/modules/codec/avcodec/avcodec.c -@@ -247,28 +247,51 @@ vlc_module_begin () +@@ -252,28 +252,51 @@ vlc_module_begin () #endif vlc_module_end () @@ -640,7 +672,7 @@ if( !p_codec ) msg_Err( p_dec, "Decoder `%s' not found", psz_decoder ); else if( p_codec->id != i_codec_id ) -@@ -279,7 +302,9 @@ AVCodecContext *ffmpeg_AllocContext( dec +@@ -284,7 +307,9 @@ AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec, } free( psz_decoder ); } @@ -651,7 +683,7 @@ p_codec = avcodec_find_decoder( i_codec_id ); if( !p_codec ) { -@@ -299,6 +324,12 @@ AVCodecContext *ffmpeg_AllocContext( dec +@@ -304,6 +329,12 @@ AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec, return avctx; } @@ -664,9 +696,11 @@ /***************************************************************************** * ffmpeg_OpenCodec: *****************************************************************************/ +diff --git a/modules/codec/avcodec/avcodec.h b/modules/codec/avcodec/avcodec.h +index c14170ebb8..7ee1463463 100644 --- a/modules/codec/avcodec/avcodec.h +++ b/modules/codec/avcodec/avcodec.h -@@ -47,6 +47,7 @@ int InitSubtitleDec( vlc_object_t * ); +@@ -48,6 +48,7 @@ int InitSubtitleDec( vlc_object_t * ); void EndSubtitleDec( vlc_object_t * ); /* Initialize decoder */ @@ -674,6 +708,9 @@ AVCodecContext *ffmpeg_AllocContext( decoder_t *, const AVCodec ** ); int ffmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *, const AVCodec * ); +diff --git a/modules/codec/avcodec/drm_pic.c b/modules/codec/avcodec/drm_pic.c +new file mode 100644 +index 0000000000..8319c1c210 --- /dev/null +++ b/modules/codec/avcodec/drm_pic.c @@ -0,0 +1,62 @@ @@ -739,6 +776,9 @@ + + + +diff --git a/modules/codec/avcodec/drm_pic.h b/modules/codec/avcodec/drm_pic.h +new file mode 100644 +index 0000000000..a15f512c23 --- /dev/null +++ b/modules/codec/avcodec/drm_pic.h @@ -0,0 +1,46 @@ @@ -788,9 +828,11 @@ + +int drm_prime_attach_buf_to_pic(struct picture_t *pic, struct AVFrame *frame); + +diff --git a/modules/codec/avcodec/va.c b/modules/codec/avcodec/va.c +index 0feb03b974..aa3a80f929 100644 --- a/modules/codec/avcodec/va.c +++ b/modules/codec/avcodec/va.c -@@ -57,6 +57,22 @@ vlc_fourcc_t vlc_va_GetChroma(enum Pixel +@@ -57,6 +57,22 @@ vlc_fourcc_t vlc_va_GetChroma(enum PixelFormat hwfmt, enum PixelFormat swfmt) return VLC_CODEC_D3D9_OPAQUE; } break; @@ -813,6 +855,8 @@ #if LIBAVUTIL_VERSION_CHECK(54, 13, 1, 24, 100) case AV_PIX_FMT_D3D11VA_VLD: +diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c +index 55ea1c82fb..c299fe2a38 100644 --- a/modules/codec/avcodec/video.c +++ b/modules/codec/avcodec/video.c @@ -29,6 +29,8 @@ @@ -824,14 +868,16 @@ #include #include #include -@@ -46,11 +48,20 @@ +@@ -46,6 +48,8 @@ #include "avcodec.h" #include "va.h" +#include "drm_pic.h" + + #include "../../packetizer/av1_obu.h" + #include "../../packetizer/av1.h" #include "../codec/cc.h" - +@@ -53,6 +57,13 @@ /***************************************************************************** * decoder_sys_t : decoder descriptor *****************************************************************************/ @@ -845,7 +891,7 @@ struct decoder_sys_t { AVCodecContext *p_context; -@@ -59,6 +70,9 @@ struct decoder_sys_t +@@ -61,6 +72,9 @@ struct decoder_sys_t /* Video decoder specific part */ date_t pts; @@ -855,7 +901,7 @@ /* Closed captions for decoders */ cc_data_t cc; -@@ -88,6 +102,7 @@ struct decoder_sys_t +@@ -91,6 +105,7 @@ struct decoder_sys_t /* VA API */ vlc_va_t *p_va; enum PixelFormat pix_fmt; @@ -863,7 +909,7 @@ int profile; int level; -@@ -356,6 +371,13 @@ static int lavc_CopyPicture(decoder_t *d +@@ -359,6 +374,13 @@ static int lavc_CopyPicture(decoder_t *dec, picture_t *pic, AVFrame *frame) { decoder_sys_t *sys = dec->p_sys; @@ -877,15 +923,17 @@ vlc_fourcc_t fourcc = FindVlcChroma(frame->format); if (!fourcc) { -@@ -417,6 +439,7 @@ static int OpenVideoCodec( decoder_t *p_ +@@ -423,6 +445,9 @@ static int OpenVideoCodec( decoder_t *p_dec ) ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel; p_sys->pix_fmt = AV_PIX_FMT_NONE; + p_sys->sw_pix_fmt = AV_PIX_FMT_NONE; - p_sys->profile = -1; - p_sys->level = -1; ++ p_sys->profile = -1; ++ p_sys->level = -1; cc_Init( &p_sys->cc ); -@@ -458,17 +481,37 @@ static int OpenVideoCodec( decoder_t *p_ + + set_video_color_settings( &p_dec->fmt_in.video, ctx ); +@@ -462,6 +487,32 @@ static int OpenVideoCodec( decoder_t *p_dec ) return 0; } @@ -909,9 +957,95 @@ + hw_fail = *fmt; +} + - /***************************************************************************** - * InitVideo: initialize the video decoder - ***************************************************************************** ++/***************************************************************************** ++ * 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 InitVideoDecCommon( decoder_t *p_dec ) + { + decoder_sys_t *p_sys = p_dec->p_sys; +@@ -587,6 +638,8 @@ static int InitVideoDecCommon( decoder_t *p_dec ) + /* ***** misc init ***** */ + date_Init(&p_sys->pts, 1, 30001); + date_Set(&p_sys->pts, VLC_TICK_INVALID); ++ p_sys->dts0 = VLC_TICK_INVALID; ++ p_sys->dts0_used = false; + p_sys->b_first_frame = true; + p_sys->i_late_frames = 0; + p_sys->b_from_preroll = false; +@@ -610,17 +663,31 @@ static int InitVideoDecCommon( decoder_t *p_dec ) + } else + p_sys->palette_sent = true; + ++ // If we want DRM_PRIME then we need to create the context before Open ++ // * This probably applies to anything that wants device_ctx init ++ { ++ const AVCodecHWConfig * hw_config; ++ for (int i = 0; (hw_config = avcodec_get_hw_config(p_codec, i)) != NULL; ++i) ++ { ++ if ((hw_config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) != 0 && ++ hw_config->device_type == AV_HWDEVICE_TYPE_DRM) ++ { ++ int err; ++ if ((err = av_hwdevice_ctx_create(&p_context->hw_device_ctx, hw_config->device_type, NULL, NULL, 0)) < 0) { ++ msg_Dbg(p_dec, "Failed to create specified HW device: %s", av_err2str(err)); ++ goto error; ++ } ++ break; ++ } ++ } ++ } ++ + /* ***** 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; +- } ++ goto error; + + p_dec->pf_decode = DecodeVideo; + p_dec->pf_flush = Flush; +@@ -631,6 +698,12 @@ static int InitVideoDecCommon( decoder_t *p_dec ) + if( p_context->level != FF_LEVEL_UNKNOWN ) + p_dec->fmt_in.i_level = p_context->level; + return VLC_SUCCESS; ++ ++error: ++ vlc_sem_destroy( &p_sys->sem_mt ); ++ free( p_sys ); ++ avcodec_free_context( &p_context ); ++ return VLC_EGENERIC; + } + + static int ffmpeg_OpenVa(decoder_t *p_dec, AVCodecContext *p_context, +@@ -686,6 +759,10 @@ static int ffmpeg_OpenVa(decoder_t *p_dec, AVCodecContext *p_context, + + static const enum PixelFormat hwfmts[] = + { ++#if OPT_RPI ++ // If Pi then do not bother with stuff we know will fail ++ AV_PIX_FMT_DRM_PRIME, ++#else + #ifdef _WIN32 + #if LIBAVUTIL_VERSION_CHECK(54, 13, 1, 24, 100) + AV_PIX_FMT_D3D11VA_VLD, +@@ -695,6 +772,7 @@ static const enum PixelFormat hwfmts[] = + AV_PIX_FMT_VAAPI, + #if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 4, 0)) + AV_PIX_FMT_VDPAU, ++#endif + #endif + AV_PIX_FMT_NONE, + }; +@@ -814,11 +892,11 @@ failed: * the ffmpeg codec will be opened, some memory allocated. The vout is not yet * opened (done after the first decoded frame). *****************************************************************************/ @@ -925,17 +1059,8 @@ if( p_context == NULL ) return VLC_EGENERIC; -@@ -603,6 +646,8 @@ int InitVideoDec( vlc_object_t *obj ) - /* ***** misc init ***** */ - date_Init(&p_sys->pts, 1, 30001); - date_Set(&p_sys->pts, VLC_TS_INVALID); -+ p_sys->dts0 = VLC_TS_INVALID; -+ p_sys->dts0_used = false; - p_sys->b_first_frame = true; - p_sys->i_late_frames = 0; - p_sys->b_from_preroll = false; -@@ -649,6 +694,27 @@ int InitVideoDec( vlc_object_t *obj ) - return VLC_SUCCESS; +@@ -840,6 +918,27 @@ int InitVideoDec( vlc_object_t *obj ) + return InitVideoDecCommon( p_dec ); } +int InitVideoDec( vlc_object_t *obj ) @@ -962,54 +1087,54 @@ /***************************************************************************** * Flush: *****************************************************************************/ -@@ -658,6 +724,8 @@ static void Flush( decoder_t *p_dec ) +@@ -849,6 +948,8 @@ static void Flush( decoder_t *p_dec ) AVCodecContext *p_context = p_sys->p_context; - date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */ -+ p_sys->dts0 = VLC_TS_INVALID; + date_Set(&p_sys->pts, VLC_TICK_INVALID); /* To make sure we recover properly */ ++ p_sys->dts0 = VLC_TICK_INVALID; + p_sys->dts0_used = false; p_sys->i_late_frames = 0; p_sys->b_draining = false; cc_Flush( &p_sys->cc ); -@@ -685,6 +753,8 @@ static bool check_block_validity( decode +@@ -876,6 +977,8 @@ static bool check_block_validity( decoder_sys_t *p_sys, block_t *block ) if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { - date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */ -+ p_sys->dts0 = VLC_TS_INVALID; + date_Set( &p_sys->pts, VLC_TICK_INVALID ); /* To make sure we recover properly */ ++ p_sys->dts0 = VLC_TICK_INVALID; + p_sys->dts0_used = false; cc_Flush( &p_sys->cc ); p_sys->i_late_frames = 0; -@@ -1025,6 +1095,10 @@ static picture_t *DecodeBlock( decoder_t +@@ -1216,6 +1319,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error } if( b_has_data ) { + /* Remember 1st DTS in case we need to invent a timebase */ -+ if (p_sys->dts0 <= VLC_TS_INVALID) ++ if (p_sys->dts0 <= VLC_TICK_INVALID) + p_sys->dts0 = p_block->i_dts; + 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; -@@ -1131,6 +1205,17 @@ static picture_t *DecodeBlock( decoder_t + pkt->pts = p_block->i_pts > VLC_TICK_INVALID ? p_block->i_pts : AV_NOPTS_VALUE; +@@ -1322,6 +1429,17 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error if( i_pts == AV_NOPTS_VALUE ) i_pts = date_Get( &p_sys->pts ); + /* VLC doesn't like having no pts - but a simple timestamp at the + * start of time is all that is needed to get it going - pick the + * first dts we saw as being in the right general area */ -+ if (i_pts > VLC_TS_INVALID) ++ if (i_pts > VLC_TICK_INVALID) + p_sys->dts0_used = true; -+ else if (p_sys->dts0 > VLC_TS_INVALID && !p_sys->dts0_used) ++ else if (p_sys->dts0 > VLC_TICK_INVALID && !p_sys->dts0_used) + { + i_pts = p_sys->dts0; + p_sys->dts0_used = true; + } + /* Interpolate the next PTS */ - if( i_pts > VLC_TS_INVALID ) + if( i_pts > VLC_TICK_INVALID ) date_Set( &p_sys->pts, i_pts ); -@@ -1183,9 +1268,10 @@ static picture_t *DecodeBlock( decoder_t +@@ -1374,9 +1492,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error { /* 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. */ @@ -1022,107 +1147,92 @@ p_pic = decoder_NewPicture(p_dec); if( !p_pic ) -@@ -1538,7 +1624,7 @@ static enum PixelFormat ffmpeg_GetFormat - video_format_t fmt; - - /* Enumerate available formats */ -- enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt); -+ enum PixelFormat def_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++) -@@ -1561,7 +1647,7 @@ static enum PixelFormat ffmpeg_GetFormat - * 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 (lavc_GetVideoFormat(p_dec, &fmt, p_context, p_sys->pix_fmt, swfmt) != 0) -+ if (lavc_GetVideoFormat(p_dec, &fmt, p_context, p_sys->pix_fmt, p_sys->sw_pix_fmt) != 0) - { - msg_Dbg(p_dec, "get format failed"); - goto no_reuse; -@@ -1583,7 +1669,7 @@ static enum PixelFormat ffmpeg_GetFormat - for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++) - if (pi_fmt[i] == p_sys->pix_fmt) - { -- if (lavc_UpdateVideoFormat(p_dec, p_context, p_sys->pix_fmt, swfmt) == 0) -+ if (lavc_UpdateVideoFormat(p_dec, p_context, p_sys->pix_fmt, p_sys->sw_pix_fmt) == 0) - { - msg_Dbg(p_dec, "reusing decoder output format %d", pi_fmt[i]); - return p_sys->pix_fmt; -@@ -1602,7 +1688,7 @@ no_reuse: - p_sys->level = p_context->level; - - if (!can_hwaccel) -- return swfmt; -+ return def_swfmt; - - #if (LIBAVCODEC_VERSION_MICRO >= 100) \ - && (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 83, 101)) -@@ -1610,7 +1696,7 @@ no_reuse: - { - msg_Warn(p_dec, "thread type %d: disabling hardware acceleration", - p_context->active_thread_type); -- return swfmt; -+ return def_swfmt; +@@ -1769,6 +1888,7 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context, + } + swfmt = defaultfmt; } - #endif ++ p_sys->sw_pix_fmt = swfmt; -@@ -1618,6 +1704,8 @@ no_reuse: - - static const enum PixelFormat hwfmts[] = - { -+ AV_PIX_FMT_DRM_PRIME, -+#if !OPT_RPI // RPI - ignore stuff we know isn't going to work - #ifdef _WIN32 - #if LIBAVUTIL_VERSION_CHECK(54, 13, 1, 24, 100) - AV_PIX_FMT_D3D11VA_VLD, -@@ -1628,12 +1716,14 @@ no_reuse: - #if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 4, 0)) - AV_PIX_FMT_VDPAU, - #endif -+#endif - AV_PIX_FMT_NONE, - }; - - for( size_t i = 0; hwfmts[i] != AV_PIX_FMT_NONE; i++ ) - { - enum PixelFormat hwfmt = AV_PIX_FMT_NONE; -+ enum PixelFormat swfmt = def_swfmt; - 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]; -@@ -1641,6 +1731,14 @@ no_reuse: - if( hwfmt == AV_PIX_FMT_NONE ) - continue; - -+#if OPT_RPI -+ // Kludge to what we know the swfmt is going to be -+ if (hwfmt == AV_PIX_FMT_DRM_PRIME && p_context->codec_id == AV_CODEC_ID_HEVC && def_swfmt == AV_PIX_FMT_YUV420P) -+ swfmt = AV_PIX_FMT_RPI4_8; -+ if (hwfmt == AV_PIX_FMT_DRM_PRIME && p_context->codec_id == AV_CODEC_ID_HEVC && def_swfmt == AV_PIX_FMT_YUV420P10LE) -+ swfmt = AV_PIX_FMT_RPI4_10; + if (p_sys->pix_fmt == AV_PIX_FMT_NONE) + goto no_reuse; +diff --git a/modules/gui/qt/components/interface_widgets.cpp b/modules/gui/qt/components/interface_widgets.cpp +index 4a0a0dae12..17946c58d6 100644 +--- a/modules/gui/qt/components/interface_widgets.cpp ++++ b/modules/gui/qt/components/interface_widgets.cpp +@@ -107,6 +107,36 @@ void VideoWidget::sync( void ) + #ifdef QT5_HAS_X11 + if( QX11Info::isPlatformX11() ) + XSync( QX11Info::display(), False ); +#endif ++ refreshHandles(); ++} + - 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 */ -@@ -1673,12 +1771,14 @@ no_reuse: - - p_sys->p_va = va; - p_sys->pix_fmt = hwfmt; -+ p_sys->sw_pix_fmt = swfmt; - p_context->draw_horiz_band = NULL; - return hwfmt; - } - - post_mt(p_sys); - /* Fallback to default behaviour */ -- p_sys->pix_fmt = swfmt; -- return swfmt; -+ p_sys->pix_fmt = def_swfmt; -+ p_sys->sw_pix_fmt = def_swfmt; -+ return p_sys->pix_fmt; ++/** ++ * The wayland surface may change if the window is hidden which ++ * seems to happen sometimes on resize ++ * In Qt it looks like this happens if the window is hidden ++ **/ ++void VideoWidget::refreshHandles() ++{ ++#ifdef QT5_HAS_WAYLAND ++ if (!p_window || p_window->type != VOUT_WINDOW_TYPE_WAYLAND) ++ return; ++ ++ /* Ensure only the video widget is native (needed for Wayland) */ ++ stable->setAttribute( Qt::WA_DontCreateNativeAncestors, true); ++ ++ QWindow *window = stable->windowHandle(); ++ assert(window != NULL); ++ window->create(); ++ ++ QPlatformNativeInterface *qni = qApp->platformNativeInterface(); ++ assert(qni != NULL); ++ ++ p_window->handle.wl = static_cast( ++ qni->nativeResourceForWindow(QByteArrayLiteral("surface"), ++ window)); ++ p_window->display.wl = static_cast( ++ qni->nativeResourceForIntegration(QByteArrayLiteral("wl_display"))); + #endif } + +@@ -165,21 +195,7 @@ bool VideoWidget::request( struct vout_window_t *p_wnd ) + #ifdef QT5_HAS_WAYLAND + case VOUT_WINDOW_TYPE_WAYLAND: + { +- /* Ensure only the video widget is native (needed for Wayland) */ +- stable->setAttribute( Qt::WA_DontCreateNativeAncestors, true); +- +- QWindow *window = stable->windowHandle(); +- assert(window != NULL); +- window->create(); +- +- QPlatformNativeInterface *qni = qApp->platformNativeInterface(); +- assert(qni != NULL); +- +- p_wnd->handle.wl = static_cast( +- qni->nativeResourceForWindow(QByteArrayLiteral("surface"), +- window)); +- p_wnd->display.wl = static_cast( +- qni->nativeResourceForIntegration(QByteArrayLiteral("wl_display"))); ++ refreshHandles(); + break; + } + #endif +diff --git a/modules/gui/qt/components/interface_widgets.hpp b/modules/gui/qt/components/interface_widgets.hpp +index 583f2d1f50..9dfda5b1de 100644 +--- a/modules/gui/qt/components/interface_widgets.hpp ++++ b/modules/gui/qt/components/interface_widgets.hpp +@@ -88,6 +88,7 @@ private: + bool enable_mouse_events; + + void reportSize(); ++ void refreshHandles(); + + signals: + void sizeChanged( int, int ); +diff --git a/modules/gui/qt/qt.cpp b/modules/gui/qt/qt.cpp +index cefc75830f..3493db6958 100644 --- a/modules/gui/qt/qt.cpp +++ b/modules/gui/qt/qt.cpp @@ -31,6 +31,7 @@ @@ -1142,7 +1252,7 @@ false ) change_private () -@@ -529,6 +530,22 @@ static void *ThreadPlatform( void *obj, +@@ -529,6 +530,22 @@ static void *ThreadPlatform( void *obj, char *platform_name ) QApplication::setAttribute( Qt::AA_UseHighDpiPixmaps ); #endif @@ -1165,6 +1275,9 @@ /* Start the QApplication here */ QVLCApp app( argc, argv ); +diff --git a/modules/hw/drm/Makefile.am b/modules/hw/drm/Makefile.am +new file mode 100644 +index 0000000000..8c3232364e --- /dev/null +++ b/modules/hw/drm/Makefile.am @@ -0,0 +1,26 @@ @@ -1194,6 +1307,9 @@ + libdrm_conv_sand30_plugin.la \ + libdrm_gl_conv_plugin.la +endif +diff --git a/modules/hw/drm/conv_sand30.c b/modules/hw/drm/conv_sand30.c +new file mode 100644 +index 0000000000..c60b5462e5 --- /dev/null +++ b/modules/hw/drm/conv_sand30.c @@ -0,0 +1,212 @@ @@ -1409,6 +1525,9 @@ + set_callbacks(OpenConverterToNv12, CloseConverterToNv12) +vlc_module_end() + +diff --git a/modules/hw/drm/drm_av_deinterlace.c b/modules/hw/drm/drm_av_deinterlace.c +new file mode 100644 +index 0000000000..9e0b569989 --- /dev/null +++ b/modules/hw/drm/drm_av_deinterlace.c @@ -0,0 +1,273 @@ @@ -1685,9 +1804,12 @@ + add_shortcut("deinterlace") +vlc_module_end() + +diff --git a/modules/hw/drm/drm_avcodec.c b/modules/hw/drm/drm_avcodec.c +new file mode 100644 +index 0000000000..b20eaf700e --- /dev/null +++ b/modules/hw/drm/drm_avcodec.c -@@ -0,0 +1,94 @@ +@@ -0,0 +1,80 @@ +/***************************************************************************** + * avcodec.c: VDPAU decoder for libav + ***************************************************************************** @@ -1731,30 +1853,19 @@ + return VLC_SUCCESS; +} + -+static int Open(vlc_va_t *va, AVCodecContext *avctx, enum PixelFormat pix_fmt, ++static int Open(vlc_va_t *va, AVCodecContext *avctx, const AVPixFmtDescriptor *desc, ++ enum PixelFormat pix_fmt, + const es_format_t *fmt, picture_sys_t *p_sys) +{ -+ int err; + VLC_UNUSED(fmt); + VLC_UNUSED(p_sys); ++ VLC_UNUSED(desc); + + msg_Dbg(va, "%s: pix_fmt=%d", __func__, pix_fmt); + + if (pix_fmt != AV_PIX_FMT_DRM_PRIME) + return VLC_EGENERIC; + -+ enum AVHWDeviceType devtype = av_hwdevice_find_type_by_name("drm"); -+ if (devtype == AV_HWDEVICE_TYPE_NONE) { -+ msg_Dbg(va, "No DRM device found in ffmpeg"); -+ return VLC_EGENERIC; -+ } -+ -+ // ctx->hw_device_ctx gets freed when we call avcodec_free_context -+ if ((err = av_hwdevice_ctx_create(&avctx->hw_device_ctx, devtype, NULL, NULL, 0)) < 0) { -+ msg_Err(va, "Failed to create specified HW device: %s", av_err2str(err)); -+ goto error; -+ } -+ + // This gives us whatever the decode requires + 6 frames that will be + // alloced by ffmpeg before it blocks (at least for Pi HEVC) + avctx->extra_hw_frames = 6; @@ -1762,9 +1873,6 @@ + va->description = "DRM Video Accel"; + va->get = drm_va_get; + return VLC_SUCCESS; -+ -+error: -+ return VLC_EGENERIC; +} + +static void Close(vlc_va_t *va, void **hwctx) @@ -1782,6 +1890,9 @@ + set_callbacks(Open, Close) + add_shortcut("drm_prime") +vlc_module_end() +diff --git a/modules/hw/drm/drm_gl_conv.c b/modules/hw/drm/drm_gl_conv.c +new file mode 100644 +index 0000000000..b61b55f4e6 --- /dev/null +++ b/modules/hw/drm/drm_gl_conv.c @@ -0,0 +1,367 @@ @@ -2152,6 +2263,8 @@ + add_shortcut("drm_gl_converter") +vlc_module_end () + +diff --git a/modules/hw/mmal/Makefile.am b/modules/hw/mmal/Makefile.am +index 1dfb3e35ce..f85effd0b5 100644 --- a/modules/hw/mmal/Makefile.am +++ b/modules/hw/mmal/Makefile.am @@ -1,23 +1,60 @@ @@ -2222,6 +2335,9 @@ +mmal_LTLIBRARIES += libmmal_avcodec_plugin.la +endif + +diff --git a/modules/hw/mmal/blend_rgba_neon.S b/modules/hw/mmal/blend_rgba_neon.S +new file mode 100644 +index 0000000000..d17bf6b9aa --- /dev/null +++ b/modules/hw/mmal/blend_rgba_neon.S @@ -0,0 +1,189 @@ @@ -2414,6 +2530,9 @@ + + + +diff --git a/modules/hw/mmal/blend_rgba_neon.h b/modules/hw/mmal/blend_rgba_neon.h +new file mode 100644 +index 0000000000..ac0810855e --- /dev/null +++ b/modules/hw/mmal/blend_rgba_neon.h @@ -0,0 +1,17 @@ @@ -2434,6 +2553,9 @@ + +#endif + +diff --git a/modules/hw/mmal/blend_test.c b/modules/hw/mmal/blend_test.c +new file mode 100644 +index 0000000000..1612a0e07c --- /dev/null +++ b/modules/hw/mmal/blend_test.c @@ -0,0 +1,180 @@ @@ -2617,6 +2739,8 @@ + return 0; +} + +diff --git a/modules/hw/mmal/codec.c b/modules/hw/mmal/codec.c +index 99ff21ca70..a6ee2919d3 100644 --- a/modules/hw/mmal/codec.c +++ b/modules/hw/mmal/codec.c @@ -26,267 +26,448 @@ @@ -2670,7 +2794,10 @@ -static int OpenDecoder(decoder_t *dec); -static void CloseDecoder(decoder_t *dec); -- ++#define MMAL_RESIZE_NAME "mmal-resize" ++#define MMAL_RESIZE_TEXT N_("Use mmal resizer rather than hvs.") ++#define MMAL_RESIZE_LONGTEXT N_("Use mmal resizer rather than isp. This uses less gpu memory than the ISP but is slower.") + -vlc_module_begin() - set_shortname(N_("MMAL decoder")) - set_description(N_("MMAL-based decoder plugin for Raspberry Pi")) @@ -2679,20 +2806,16 @@ - add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false) - set_callbacks(OpenDecoder, CloseDecoder) -vlc_module_end() -+#define MMAL_RESIZE_NAME "mmal-resize" -+#define MMAL_RESIZE_TEXT N_("Use mmal resizer rather than hvs.") -+#define MMAL_RESIZE_LONGTEXT N_("Use mmal resizer rather than isp. This uses less gpu memory than the ISP but is slower.") -+ +#define MMAL_ISP_NAME "mmal-isp" +#define MMAL_ISP_TEXT N_("Use mmal isp rather than hvs.") +#define MMAL_ISP_LONGTEXT N_("Use mmal isp rather than hvs. This may be faster but has no blend.") -+ -+#define MMAL_DECODE_ENABLE_NAME "mmal-decode-enable" -+#define MMAL_DECODE_ENABLE_TEXT N_("Enable mmal decode even if normally disabled") -+#define MMAL_DECODE_ENABLE_LONGTEXT N_("Enable mmal decode even if normally disabled. MMAL decode is normally disabled on Pi4 or later.") -struct decoder_sys_t { - bool opaque; ++#define MMAL_DECODE_ENABLE_NAME "mmal-decode-enable" ++#define MMAL_DECODE_ENABLE_TEXT N_("Enable mmal decode even if normally disabled") ++#define MMAL_DECODE_ENABLE_LONGTEXT N_("Enable mmal decode even if normally disabled. MMAL decode is normally disabled on Pi4 or later.") ++ +typedef struct decoder_sys_t +{ MMAL_COMPONENT_T *component; @@ -2728,22 +2851,10 @@ -static int change_output_format(decoder_t *dec); -static int send_output_buffer(decoder_t *dec); -static void fill_output_port(decoder_t *dec); -- + -/* VLC decoder callback */ -static int decode(decoder_t *dec, block_t *block); -static void flush_decoder(decoder_t *dec); -- --/* MMAL callbacks */ --static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); --static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); --static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); - --static int OpenDecoder(decoder_t *dec) --{ -- int ret = VLC_SUCCESS; -- decoder_sys_t *sys; -- MMAL_PARAMETER_UINT32_T extra_buffers; -- MMAL_STATUS_T status; +typedef struct supported_mmal_enc_s { + struct { + MMAL_PARAMETER_HEADER_T header; @@ -2751,26 +2862,36 @@ + } supported; + int n; +} supported_mmal_enc_t; -+ + +-/* MMAL callbacks */ +-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); +-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); +-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); +#define SUPPORTED_MMAL_ENC_INIT \ +{ \ + {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}}, \ + -1 \ +} +-static int OpenDecoder(decoder_t *dec) +-{ +- int ret = VLC_SUCCESS; +- decoder_sys_t *sys; +- MMAL_PARAMETER_UINT32_T extra_buffers; +- MMAL_STATUS_T status; ++static supported_mmal_enc_t supported_decode_in_enc = SUPPORTED_MMAL_ENC_INIT; + - if (dec->fmt_in.i_codec != VLC_CODEC_MPGV && - dec->fmt_in.i_codec != VLC_CODEC_H264) - return VLC_EGENERIC; -+static supported_mmal_enc_t supported_decode_in_enc = SUPPORTED_MMAL_ENC_INIT; ++static bool is_enc_supported(supported_mmal_enc_t * const support, const MMAL_FOURCC_T fcc) ++{ ++ int i; - sys = calloc(1, sizeof(decoder_sys_t)); - if (!sys) { - ret = VLC_ENOMEM; - goto out; -+static bool is_enc_supported(supported_mmal_enc_t * const support, const MMAL_FOURCC_T fcc) -+{ -+ int i; -+ + if (fcc == 0) + return false; + if (support->n == -1) @@ -2801,17 +2922,9 @@ - MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status)); - ret = VLC_EGENERIC; - goto out; -- } + return is_enc_supported(support, fcc); +} - -- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec; -- status = mmal_port_enable(sys->component->control, control_port_cb); -- if (status != MMAL_SUCCESS) { -- msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)", -- sys->component->control->name, status, mmal_status_to_string(status)); -- ret = VLC_EGENERIC; -- goto out; ++ +static MMAL_FOURCC_T vlc_to_mmal_es_fourcc(const unsigned int fcc) +{ + switch (fcc){ @@ -2848,12 +2961,13 @@ + return 0; +} -- sys->input = sys->component->input[0]; -- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec; -- if (dec->fmt_in.i_codec == VLC_CODEC_MPGV) -- sys->input->format->encoding = MMAL_ENCODING_MP2V; -- else -- sys->input->format->encoding = MMAL_ENCODING_H264; +- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec; +- status = mmal_port_enable(sys->component->control, control_port_cb); +- if (status != MMAL_SUCCESS) { +- msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)", +- sys->component->control->name, status, mmal_status_to_string(status)); +- ret = VLC_EGENERIC; +- goto out; +static MMAL_FOURCC_T pic_to_slice_mmal_fourcc(const MMAL_FOURCC_T fcc) +{ + switch (fcc){ @@ -2883,10 +2997,17 @@ + return MMAL_ENCODING_BGR32_SLICE; + default: + break; -+ } + } + return 0; +} +- sys->input = sys->component->input[0]; +- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec; +- if (dec->fmt_in.i_codec == VLC_CODEC_MPGV) +- sys->input->format->encoding = MMAL_ENCODING_MP2V; +- else +- sys->input->format->encoding = MMAL_ENCODING_H264; +- - if (dec->fmt_in.i_codec == VLC_CODEC_H264) { - if (dec->fmt_in.i_extra > 0) { - status = mmal_format_extradata_alloc(sys->input->format, @@ -3167,19 +3288,17 @@ + d = 1; + } + } - -- if (sys->component && sys->component->is_enabled) -- mmal_component_disable(sys->component); ++ + format->es->video.par = rationalize_sar(n, d); + } -- if (sys->input_pool) -- mmal_pool_destroy(sys->input_pool); +- if (sys->component && sys->component->is_enabled) +- mmal_component_disable(sys->component); + if (sys->output_format != NULL) + mmal_format_free(sys->output_format); -- if (sys->output_format) -- mmal_format_free(sys->output_format); +- if (sys->input_pool) +- mmal_pool_destroy(sys->input_pool); + sys->output_format = format; + } + } @@ -3188,8 +3307,8 @@ + msg_Warn(dec, "Unexpected output cb event: %s", str_fourcc(buf0, buffer->cmd)); + } -- if (sys->output_pool) -- mmal_pool_destroy(sys->output_pool); +- if (sys->output_format) +- mmal_format_free(sys->output_format); + // If we get here then we were flushing (cmd == 0 && len == 0) or + // that was an EVENT - in either case we want to release the buffer + // back to its pool rather than recycle it. @@ -3198,13 +3317,14 @@ + mmal_buffer_header_release(buffer); +} +- if (sys->output_pool) +- mmal_pool_destroy(sys->output_pool); + - if (sys->component) - mmal_component_release(sys->component); - vlc_sem_destroy(&sys->sem); - free(sys); - -- bcm_host_deinit(); +static void fill_output_port(decoder_t *dec) +{ + decoder_sys_t *sys = dec->p_sys; @@ -3219,7 +3339,8 @@ + + return; + } -+ + +- bcm_host_deinit(); + hw_mmal_port_pool_ref_fill(sys->ppr); + return; } @@ -3240,7 +3361,7 @@ if (atomic_load(&sys->started)) { mmal_format_full_copy(sys->output->format, sys->output_format); status = mmal_port_format_commit(sys->output); -@@ -300,7 +481,9 @@ static int change_output_format(decoder_ +@@ -300,7 +481,9 @@ static int change_output_format(decoder_t *dec) } port_reset: @@ -3375,7 +3496,7 @@ - ret = -1; - goto err; - } - +- - p_sys = picture->p_sys; - for (int i = 0; i < picture->i_planes; i++) - buffer_size += picture->p[i].i_lines * picture->p[i].i_pitch; @@ -3403,7 +3524,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) { @@ -3433,6 +3554,19 @@ +static MMAL_STATUS_T decoder_send_extradata(decoder_t * const dec, decoder_sys_t *const sys) { - decoder_sys_t *sys = dec->p_sys; +- +- unsigned max_buffers_in_transit = 0; +- int buffers_available = 0; +- int buffers_to_send = 0; +- int i; +- +- if (sys->output_pool) { +- max_buffers_in_transit = __MAX(sys->output_pool->headers_num, +- MIN_NUM_BUFFERS_IN_TRANSIT); +- buffers_available = mmal_queue_length(sys->output_pool->queue); +- } else { +- max_buffers_in_transit = NUM_DECODER_BUFFER_HEADERS; +- buffers_available = NUM_DECODER_BUFFER_HEADERS - atomic_load(&sys->output_in_transit); + if (dec->fmt_in.i_codec == VLC_CODEC_H264 && + dec->fmt_in.i_extra > 0) + { @@ -3446,19 +3580,7 @@ + buf->length = dec->fmt_in.i_extra; + 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); -- buffers_available = mmal_queue_length(sys->output_pool->queue); -- } else { -- max_buffers_in_transit = NUM_DECODER_BUFFER_HEADERS; -- buffers_available = NUM_DECODER_BUFFER_HEADERS - atomic_load(&sys->output_in_transit); ++ + status = mmal_port_send_buffer(sys->input, buf); + if (status != MMAL_SUCCESS) { + msg_Err(dec, "Failed to send extradata buffer to input port (status=%"PRIx32" %s)", @@ -3467,10 +3589,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)", @@ -3490,15 +3612,14 @@ - MMAL_BUFFER_HEADER_T *buffer; - MMAL_STATUS_T status; + decoder_sys_t *const sys = dec->p_sys; -+ -+#if TRACE_ALL -+ msg_Dbg(dec, "%s: <<<", __func__); -+#endif - msg_Dbg(dec, "Flushing decoder ports..."); - mmal_port_flush(sys->output); - mmal_port_flush(sys->input); -- ++#if TRACE_ALL ++ msg_Dbg(dec, "%s: <<<", __func__); ++#endif + - while (atomic_load(&sys->output_in_transit) || - atomic_load(&sys->input_in_transit)) - vlc_sem_wait(&sys->sem); @@ -3538,7 +3659,7 @@ /* * Configure output port if necessary */ -@@ -541,18 +654,50 @@ static int decode(decoder_t *dec, block_ +@@ -541,18 +654,50 @@ static int decode(decoder_t *dec, block_t *block) msg_Err(dec, "Failed to change output port format"); } @@ -3592,7 +3713,7 @@ if (atomic_load(&sys->started)) fill_output_port(dec); -@@ -563,18 +708,21 @@ static int decode(decoder_t *dec, block_ +@@ -563,18 +708,21 @@ static int decode(decoder_t *dec, block_t *block) if (block->i_flags & BLOCK_FLAG_CORRUPTED) flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED; @@ -3619,7 +3740,7 @@ len = block->i_buffer; if (len > buffer->alloc_size) -@@ -585,94 +733,1835 @@ static int decode(decoder_t *dec, block_ +@@ -585,94 +733,1835 @@ static int decode(decoder_t *dec, block_t *block) block->i_buffer -= len; buffer->length = len; if (block->i_buffer == 0) { @@ -4123,12 +4244,6 @@ - buffer->alloc_size = 0; - buffer->data = NULL; - mmal_buffer_header_release(buffer); -- } -- } -- atomic_fetch_sub(&sys->output_in_transit, 1); -- vlc_sem_post(&sys->sem); -- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { -- fmt = mmal_event_format_changed_get(buffer); + filter_t * const p_filter = (filter_t *)port->userdata; + filter_sys_t * const sys = p_filter->p_sys; + @@ -4139,9 +4254,7 @@ +#endif + if (buf->cmd == 0) { + picture_t * const pic = (picture_t *)buf->user_data; - -- format = mmal_format_alloc(); -- mmal_format_full_copy(format, fmt->format); ++ + if (pic == NULL) { + msg_Err(p_filter, "%s: Buffer has no attached picture", __func__); + } @@ -4154,14 +4267,12 @@ + else + { + buf_to_pic_copy_props(pic, buf); - -- if (sys->opaque) -- format->encoding = MMAL_ENCODING_OPAQUE; ++ + // Set pic data pointers from buf aux info now it has it + if (sys->is_cma) { + if (cma_pic_set_data(pic, sys->output->format, buf) != VLC_SUCCESS) + msg_Err(p_filter, "Failed to set data"); -+ } + } + +// draw_corners(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, pic->p[0].i_visible_pitch / 4, pic->p[0].i_visible_lines); +#if DEBUG_SQUARES @@ -4172,18 +4283,30 @@ + + buf->user_data = NULL; // Responsability for this pic no longer with buffer + conv_out_q_pic(sys, pic); -+ } + } +- atomic_fetch_sub(&sys->output_in_transit, 1); +- vlc_sem_post(&sys->sem); +- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { +- fmt = mmal_event_format_changed_get(buffer); + } -+ + +- format = mmal_format_alloc(); +- mmal_format_full_copy(format, fmt->format); + mmal_buffer_header_release(buf); +} -+ -+ + +- if (sys->opaque) +- format->encoding = MMAL_ENCODING_OPAQUE; + +- sys->output_format = format; +static void slice_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; -+ + +- mmal_buffer_header_release(buffer); +- } else { +- mmal_buffer_header_release(buffer); +#if TRACE_ALL + msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld", __func__, + buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size, (long long)buf->pts); @@ -4259,7 +4382,7 @@ + conv_out_q_pic(sys, pic); + } + } -+ } + } + + // Put back + buf->user_data = NULL; // Zap here to make sure we can't reuse later @@ -4273,7 +4396,7 @@ +fail: + sys->err_stream = MMAL_EIO; + vlc_sem_post(&sys->sem); // If we were waiting then break us out - the flush should fix sem values -+} + } + + +static void conv_flush(filter_t * p_filter) @@ -4779,10 +4902,8 @@ + p_filter->p_sys = NULL; + free(sys); +} - -- sys->output_format = format; - -- mmal_buffer_header_release(buffer); ++ ++ +static inline MMAL_FOURCC_T filter_enc_in(const video_format_t * const fmt) +{ + if (hw_mmal_chroma_is_mmal(fmt->i_chroma)) @@ -4912,13 +5033,12 @@ + sys->is_sliced = false; // Copy directly into filter picture + sys->component_name = MMAL_COMPONENT_ISP_RESIZER; + sys->out_port_cb_fn = conv_output_port_cb; - } else { -- mmal_buffer_header_release(buffer); ++ } else { + sys->resizer_type = FILTER_RESIZER_HVS; + sys->is_sliced = false; // Copy directly into filter picture + sys->component_name = MMAL_COMPONENT_HVS; + sys->out_port_cb_fn = conv_output_port_cb; - } ++ } + sys->is_cma = is_cma_buf_pic_chroma(p_filter->fmt_out.video.i_chroma); + + status = mmal_component_create(sys->component_name, &sys->component); @@ -5105,7 +5225,7 @@ +fail0: + picture_Release(in_pic); + return NULL; - } ++} + +static void to_zc_flush(filter_t * p_filter) +{ @@ -5508,6 +5628,9 @@ +vlc_module_end() + + +diff --git a/modules/hw/mmal/converter_mmal.c b/modules/hw/mmal/converter_mmal.c +new file mode 100644 +index 0000000000..44333c43f8 --- /dev/null +++ b/modules/hw/mmal/converter_mmal.c @@ -0,0 +1,480 @@ @@ -5991,6 +6114,8 @@ + add_shortcut("mmal_gl_converter") +vlc_module_end () + +diff --git a/modules/hw/mmal/deinterlace.c b/modules/hw/mmal/deinterlace.c +index 4b08eee9b6..e535dbe07f 100644 --- a/modules/hw/mmal/deinterlace.c +++ b/modules/hw/mmal/deinterlace.c @@ -26,11 +26,12 @@ @@ -6062,14 +6187,21 @@ MMAL_PORT_T *input; MMAL_PORT_T *output; + MMAL_POOL_T *in_pool; -+ + +- MMAL_QUEUE_T *filtered_pictures; +- vlc_sem_t sem; + MMAL_QUEUE_T * out_q; -+ + +- atomic_bool started; + // Bind this lot somehow into ppr???? + bool is_cma; + cma_buf_pool_t * cma_out_pool; + MMAL_POOL_T * out_pool; -+ + +- /* statistics */ +- int output_in_transit; +- int input_in_transit; +-}; + hw_mmal_port_pool_ref_t *out_ppr; + + bool half_rate; @@ -6078,19 +6210,11 @@ + bool use_passthrough; + unsigned int seq_in; // Seq of next frame to submit (1-15) [Init=1] + unsigned int seq_out; // Seq of last frame received (1-15) [Init=15] - -- MMAL_QUEUE_T *filtered_pictures; -- vlc_sem_t sem; ++ + vcsm_init_type_t vcsm_init_type; - -- atomic_bool started; ++ +} filter_sys_t; -- /* statistics */ -- int output_in_transit; -- int input_in_transit; --}; -- -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); -static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); -static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); @@ -6244,24 +6368,11 @@ - sys->input->name, status, mmal_status_to_string(status)); - ret = VLC_EGENERIC; - goto out; -- } -- sys->input->buffer_size = sys->input->buffer_size_recommended; -- sys->input->buffer_num = sys->input->buffer_num_recommended; - -- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) { -- MMAL_PARAMETER_BOOLEAN_T zero_copy = { -- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, -- 1 -- }; ++ +static MMAL_STATUS_T fill_output_from_q(filter_t * const p_filter, filter_sys_t * const sys, MMAL_QUEUE_T * const q) +{ + MMAL_BUFFER_HEADER_T * out_buf; - -- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr); -- if (status != MMAL_SUCCESS) { -- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", -- sys->input->name, status, mmal_status_to_string(status)); -- goto out; ++ + while ((out_buf = mmal_queue_get(q)) != NULL) + { + MMAL_STATUS_T err; @@ -6270,37 +6381,34 @@ + msg_Err(p_filter, "Send buffer to output failed"); + mmal_queue_put_back(q, out_buf); + return err; - } ++ } } +- sys->input->buffer_size = sys->input->buffer_size_recommended; +- sys->input->buffer_num = sys->input->buffer_num_recommended; + return MMAL_SUCCESS; +} -- status = mmal_port_enable(sys->input, input_port_cb); -- if (status != MMAL_SUCCESS) { -- msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)", -- sys->input->name, status, mmal_status_to_string(status)); -- ret = VLC_EGENERIC; -- goto out; -- } +- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) { +- MMAL_PARAMETER_BOOLEAN_T zero_copy = { +- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, +- 1 +- }; +// 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); -- sys->output = sys->component->output[0]; -- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter; -- mmal_format_full_copy(sys->output->format, sys->input->format); +- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr); +- if (status != MMAL_SUCCESS) { +- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", +- sys->input->name, status, mmal_status_to_string(status)); +- goto out; +- } + 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) { -- msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)", -- sys->input->name, status, mmal_status_to_string(status)); -- ret = VLC_EGENERIC; -- goto out; ++ + return MMAL_FALSE; +} + @@ -6346,8 +6454,14 @@ + sys->input->buffer_num = 30; + sys->input->buffer_size = sys->input->buffer_size_recommended; + mmal_log_dump_format(sys->input->format); -+ } -+ + } + +- status = mmal_port_enable(sys->input, input_port_cb); +- if (status != MMAL_SUCCESS) { +- msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)", +- sys->input->name, status, mmal_status_to_string(status)); +- ret = VLC_EGENERIC; +- goto out; + // Reenable stuff if the last thing we did was flush + // Output should always be enabled + if (!sys->input->is_enabled && @@ -6355,8 +6469,11 @@ + { + msg_Err(p_filter, "Input port reenable failed"); + goto fail; -+ } -+ + } + +- sys->output = sys->component->output[0]; +- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter; +- mmal_format_full_copy(sys->output->format, sys->input->format); + if (!sys->is_cma) + { + // Fill output from anything that has turned up in pool Q @@ -6365,7 +6482,7 @@ + msg_Err(p_filter, "Out port fill fail"); + goto fail; + } - } ++ } + else + { + // We are expecting one in - one out so simply wedge a new bufer @@ -6378,8 +6495,7 @@ + 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); @@ -6403,6 +6519,26 @@ + out_buf->length, out_buf->alloc_size, (long long)out_buf->pts); +#endif +- status = mmal_port_format_commit(sys->output); +- if (status != MMAL_SUCCESS) { +- msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)", +- sys->input->name, status, mmal_status_to_string(status)); +- ret = VLC_EGENERIC; +- goto out; ++ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS) ++ { ++ msg_Err(p_filter, "Send buffer to output failed"); ++ goto fail; ++ } ++ out_buf = NULL; + } + +- sys->output->buffer_num = 3; ++ // Stuff into input ++ // We assume the BH is already set up with values reflecting pic date etc. ++ { ++ MMAL_BUFFER_HEADER_T * const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->in_pool); + - if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) { - MMAL_PARAMETER_UINT32_T extra_buffers = { - { MMAL_PARAMETER_EXTRA_BUFFERS, sizeof(MMAL_PARAMETER_UINT32_T) }, @@ -6413,36 +6549,23 @@ - msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)", - status, mmal_status_to_string(status)); - goto out; -+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS) ++ if (pic_buf == NULL) + { -+ msg_Err(p_filter, "Send buffer to output failed"); ++ msg_Err(p_filter, "Pic has not attached buffer"); + goto fail; } -+ out_buf = NULL; -+ } - MMAL_PARAMETER_BOOLEAN_T zero_copy = { - { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, - 1 - }; -+ // 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; -+ } ++ picture_Release(p_pic); - status = mmal_port_parameter_set(sys->output, &zero_copy.hdr); - if (status != MMAL_SUCCESS) { - msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", - sys->output->name, status, mmal_status_to_string(status)); - goto out; -+ picture_Release(p_pic); -+ + // Add a sequence to the flags so we can track what we have actually + // deinterlaced + pic_buf->flags = (pic_buf->flags & ~(0xfU * MMAL_BUFFER_HEADER_FLAG_USER0)) | (sys->seq_in * (MMAL_BUFFER_HEADER_FLAG_USER0)); @@ -6727,9 +6850,9 @@ + free(sys); +} -+ - bcm_host_deinit(); ++ +static bool is_fmt_valid_in(const vlc_fourcc_t fmt) +{ + return fmt == VLC_CODEC_MMAL_OPAQUE || @@ -6751,30 +6874,56 @@ - picture_t *picture; - int ret = 0; + filter_sys_t *sys; -+ -+ msg_Dbg(filter, "<<< %s", __func__); -+ -+ if (!is_fmt_valid_in(filter->fmt_in.video.i_chroma) || -+ filter->fmt_out.video.i_chroma != filter->fmt_in.video.i_chroma) -+ return VLC_EGENERIC; - if (!sys->output->is_enabled) { - ret = VLC_EGENERIC; - goto out; +- } ++ msg_Dbg(filter, "<<< %s", __func__); + +- 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; ++ 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; + +- buffer = picture->p_sys->buffer; +- buffer->user_data = picture; +- buffer->cmd = 0; + sys = calloc(1, sizeof(filter_sys_t)); + if (!sys) + return VLC_ENOMEM; + filter->p_sys = sys; -+ + +- mmal_picture_lock(picture); + sys->seq_in = 1; + sys->seq_out = 15; + sys->is_cma = is_cma_buf_pic_chroma(filter->fmt_out.video.i_chroma); -+ + +- 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->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { + msg_Err(filter, "VCSM init failed"); + goto fail; -+ } -+ + } + +-out: +- return ret; +-} + if (!rpi_use_qpu_deinterlace()) + { + sys->half_rate = true; @@ -6802,13 +6951,30 @@ + sys->use_passthrough = true; + msg_Warn(filter, "Deinterlace bypassed due to lack of GPU memory"); + } - } ++ } -- picture = filter_NewPicture(filter); -- if (!picture) { -- msg_Warn(filter, "Failed to get new picture"); -- ret = -1; -- goto out; +-static void fill_output_port(filter_t *filter) +-{ +- filter_sys_t *sys = filter->p_sys; +- /* allow at least 2 buffers in transit */ +- unsigned max_buffers_in_transit = __MAX(2, MIN_NUM_BUFFERS_IN_TRANSIT); +- int buffers_available = sys->output->buffer_num - +- atomic_load(&sys->output_in_transit) - +- mmal_queue_length(sys->filtered_pictures); +- int buffers_to_send = max_buffers_in_transit - sys->output_in_transit; +- int i; +- +- if (buffers_to_send > buffers_available) +- buffers_to_send = buffers_available; +- +-#ifndef NDEBUG +- msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)", +- buffers_to_send, buffers_available, sys->output_in_transit, +- sys->output->buffer_num); +-#endif +- for (i = 0; i < buffers_to_send; ++i) { +- if (send_output_buffer(filter) < 0) +- break; + if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU)) + sys->use_qpu = false; + if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV)) @@ -6836,8 +7002,17 @@ + cma_vcsm_exit(sys->vcsm_init_type); + sys->vcsm_init_type = VCSM_INIT_NONE; + return 0; -+ } -+ + } +-} + +-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; + { + 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__, @@ -6852,13 +7027,9 @@ + 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; ++ } -- buffer = picture->p_sys->buffer; -- buffer->user_data = picture; -- buffer->cmd = 0; +- fill_output_port(filter); + 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)", @@ -6866,7 +7037,10 @@ + goto fail; + } -- mmal_picture_lock(picture); +- buffer = picture->p_sys->buffer; +- buffer->user_data = picture; +- buffer->pts = picture->date; +- buffer->cmd = 0; + { + const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = { + { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) }, @@ -6877,111 +7051,10 @@ + { 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); -- if (status != MMAL_SUCCESS) { ++ status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr); + 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); @@ -7011,31 +7084,120 @@ - } else { - msg_Dbg(filter, "Failed waiting for filtered picture"); - break; -- } -+ if ((sys->out_q = mmal_queue_create()) == NULL) -+ { -+ msg_Err(filter, "Failed to create out Q"); -+ goto fail; ++ msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)", ++ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status)); ++ goto fail; + } } - if (out_picture) - out_picture->p_next = NULL; - +- - return ret; -} -- + -static void flush(filter_t *filter) -{ - filter_sys_t *sys = filter->p_sys; - MMAL_BUFFER_HEADER_T *buffer; ++ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter; ++ status = mmal_port_enable(sys->component->control, control_port_cb); ++ if (status != MMAL_SUCCESS) { ++ msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)", ++ sys->component->control->name, status, mmal_status_to_string(status)); ++ goto fail; ++ } + +- msg_Dbg(filter, "flush deinterlace filter"); ++ sys->input = sys->component->input[0]; ++ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter; ++ sys->input->format->encoding = vlc_to_mmal_video_fourcc(&filter->fmt_in.video); ++ hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &filter->fmt_in.video); + +- msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)", +- sys->input_in_transit, sys->output_in_transit); +- mmal_port_flush(sys->output); +- mmal_port_flush(sys->input); ++ es_format_Copy(&filter->fmt_out, &filter->fmt_in); ++ if (!sys->half_rate) ++ filter->fmt_out.video.i_frame_rate *= 2; + +- msg_Dbg(filter, "flush: wait for all buffers to be returned"); +- while (atomic_load(&sys->input_in_transit) || +- atomic_load(&sys->output_in_transit)) +- vlc_sem_wait(&sys->sem); ++ 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; + +- while ((buffer = mmal_queue_get(sys->filtered_pictures))) { +- picture_t *pic = (picture_t *)buffer->user_data; +- msg_Dbg(filter, "flush: release already filtered pic %p", +- (void *)pic); +- picture_Release(pic); ++ 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"); +-} + +-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; ++ } + +- 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; + } + +- mmal_buffer_header_release(buffer); +-} + +-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +-{ +- picture_t *picture = (picture_t *)buffer->user_data; +- filter_t *filter = (filter_t *)port->userdata; +- filter_sys_t *sys = filter->p_sys; ++ if ((sys->out_q = mmal_queue_create()) == NULL) ++ { ++ msg_Err(filter, "Failed to create out Q"); ++ goto fail; ++ } ++ + sys->output = sys->component->output[0]; + mmal_format_full_copy(sys->output->format, sys->input->format); -- msg_Dbg(filter, "flush deinterlace filter"); +- if (picture) { +- picture_Release(picture); +- } else { +- msg_Warn(filter, "Got buffer without picture on input port - OOOPS"); +- mmal_buffer_header_release(buffer); + 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; -+ } + } + else + { + // CMA stuff @@ -7047,24 +7209,8 @@ + goto fail; + } -- msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)", -- sys->input_in_transit, sys->output_in_transit); -- mmal_port_flush(sys->output); -- mmal_port_flush(sys->input); -- -- msg_Dbg(filter, "flush: wait for all buffers to be returned"); -- while (atomic_load(&sys->input_in_transit) || -- atomic_load(&sys->output_in_transit)) -- vlc_sem_wait(&sys->sem); -- -- while ((buffer = mmal_queue_get(sys->filtered_pictures))) { -- picture_t *pic = (picture_t *)buffer->user_data; -- msg_Dbg(filter, "flush: release already filtered pic %p", -- (void *)pic); -- picture_Release(pic); -- } -- atomic_store(&sys->started, false); -- msg_Dbg(filter, "flush: done"); +- atomic_fetch_sub(&sys->input_in_transit, 1); +- vlc_sem_post(&sys->sem); -} + // Rate control done by CMA in flight logic, so have "inexhaustable" pool here + if ((sys->out_pool = mmal_pool_create(30, 0)) == NULL) @@ -7073,33 +7219,38 @@ + goto fail; + } --static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) -{ - filter_t *filter = (filter_t *)port->userdata; -- MMAL_STATUS_T status; +- filter_sys_t *sys = filter->p_sys; +- picture_t *picture; +- +- if (buffer->cmd == 0) { +- if (buffer->length > 0) { +- atomic_store(&sys->started, true); +- mmal_queue_put(sys->filtered_pictures, buffer); +- picture = (picture_t *)buffer->user_data; +- } else { +- picture = (picture_t *)buffer->user_data; +- picture_Release(picture); + 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)); -- } ++ + 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); --} +- atomic_fetch_sub(&sys->output_in_transit, 1); +- vlc_sem_post(&sys->sem); +- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { +- msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled"); +- mmal_buffer_header_release(buffer); +- } else { +- mmal_buffer_header_release(buffer); + 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; ++ + // CB just drops all bufs into out_q + if ((status = mmal_port_enable(sys->output, di_output_port_cb)) != MMAL_SUCCESS) + { @@ -7107,22 +7258,15 @@ + sys->output->name, status, mmal_status_to_string(status)); + goto fail; + } -+ } - -- if (picture) { -- picture_Release(picture); -- } else { -- msg_Warn(filter, "Got buffer without picture on input port - OOOPS"); -- mmal_buffer_header_release(buffer); + } ++ + status = mmal_component_enable(sys->component); + if (status != MMAL_SUCCESS) { + msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)", + sys->component->name, status, mmal_status_to_string(status)); + goto fail; - } - -- atomic_fetch_sub(&sys->input_in_transit, 1); -- vlc_sem_post(&sys->sem); ++ } ++ + filter->pf_video_filter = deinterlace; + filter->pf_flush = di_flush; + return 0; @@ -7131,12 +7275,7 @@ + CloseMmalDeinterlace(filter); + return ret; } - --static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) --{ -- filter_t *filter = (filter_t *)port->userdata; -- filter_sys_t *sys = filter->p_sys; -- picture_t *picture; ++ +vlc_module_begin() + set_shortname(N_("MMAL deinterlace")) + set_description(N_("MMAL-based deinterlace filter plugin")) @@ -7160,26 +7299,10 @@ + +vlc_module_end() + - -- if (buffer->cmd == 0) { -- if (buffer->length > 0) { -- atomic_store(&sys->started, true); -- mmal_queue_put(sys->filtered_pictures, buffer); -- picture = (picture_t *)buffer->user_data; -- } else { -- picture = (picture_t *)buffer->user_data; -- picture_Release(picture); -- } -- -- atomic_fetch_sub(&sys->output_in_transit, 1); -- vlc_sem_post(&sys->sem); -- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { -- msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled"); -- mmal_buffer_header_release(buffer); -- } else { -- mmal_buffer_header_release(buffer); -- } --} ++ +diff --git a/modules/hw/mmal/mmal_avcodec.c b/modules/hw/mmal/mmal_avcodec.c +new file mode 100644 +index 0000000000..e32c2819f4 --- /dev/null +++ b/modules/hw/mmal/mmal_avcodec.c @@ -0,0 +1,2302 @@ @@ -9485,6 +9608,9 @@ + set_callbacks(MmalAvcodecOpenDecoder, MmalAvcodecCloseDecoder) +vlc_module_end() + +diff --git a/modules/hw/mmal/mmal_cma.c b/modules/hw/mmal/mmal_cma.c +new file mode 100644 +index 0000000000..4dfedbc57c --- /dev/null +++ b/modules/hw/mmal/mmal_cma.c @@ -0,0 +1,672 @@ @@ -10160,6 +10286,9 @@ + return cma_buf_ref(cb); +} + +diff --git a/modules/hw/mmal/mmal_cma.h b/modules/hw/mmal/mmal_cma.h +new file mode 100644 +index 0000000000..fd8ea34562 --- /dev/null +++ b/modules/hw/mmal/mmal_cma.h @@ -0,0 +1,43 @@ @@ -10206,6 +10335,9 @@ +} + +#endif // VLC_MMAL_MMAL_CMA_H_ +diff --git a/modules/hw/mmal/mmal_cma_drmprime.c b/modules/hw/mmal/mmal_cma_drmprime.c +new file mode 100644 +index 0000000000..d58bfdd944 --- /dev/null +++ b/modules/hw/mmal/mmal_cma_drmprime.c @@ -0,0 +1,120 @@ @@ -10329,6 +10461,9 @@ +} + + +diff --git a/modules/hw/mmal/mmal_cma_drmprime.h b/modules/hw/mmal/mmal_cma_drmprime.h +new file mode 100644 +index 0000000000..2ca92a72a7 --- /dev/null +++ b/modules/hw/mmal/mmal_cma_drmprime.h @@ -0,0 +1,9 @@ @@ -10341,6 +10476,9 @@ + +#endif // VLC_MMAL_MMAL_CMA_H_ + +diff --git a/modules/hw/mmal/mmal_cma_int.h b/modules/hw/mmal/mmal_cma_int.h +new file mode 100644 +index 0000000000..1ff3fa4fd0 --- /dev/null +++ b/modules/hw/mmal/mmal_cma_int.h @@ -0,0 +1,58 @@ @@ -10402,6 +10540,9 @@ + +#endif + +diff --git a/modules/hw/mmal/mmal_cma_pic.h b/modules/hw/mmal/mmal_cma_pic.h +new file mode 100644 +index 0000000000..7d7d60d3a3 --- /dev/null +++ b/modules/hw/mmal/mmal_cma_pic.h @@ -0,0 +1,49 @@ @@ -10454,6 +10595,9 @@ +} + + +diff --git a/modules/hw/mmal/mmal_piccpy_neon.S b/modules/hw/mmal/mmal_piccpy_neon.S +new file mode 100644 +index 0000000000..6fec851800 --- /dev/null +++ b/modules/hw/mmal/mmal_piccpy_neon.S @@ -0,0 +1,95 @@ @@ -10552,6 +10696,8 @@ +function mmal_piccpy_10_to_8_neon + piccpy_to_8 10 + +diff --git a/modules/hw/mmal/mmal_picture.c b/modules/hw/mmal/mmal_picture.c +index 7ddd5b566c..c7f8b42af0 100644 --- a/modules/hw/mmal/mmal_picture.c +++ b/modules/hw/mmal/mmal_picture.c @@ -21,25 +21,1574 @@ @@ -12138,6 +12284,8 @@ +} + + +diff --git a/modules/hw/mmal/mmal_picture.h b/modules/hw/mmal/mmal_picture.h +index 3539f2cfc8..8d04517e08 100644 --- a/modules/hw/mmal/mmal_picture.h +++ b/modules/hw/mmal/mmal_picture.h @@ -24,19 +24,278 @@ @@ -12425,6 +12573,9 @@ +#define MMAL_COMPONENT_HVS "vc.ril.hvs" #endif +diff --git a/modules/hw/mmal/rpi_prof.h b/modules/hw/mmal/rpi_prof.h +new file mode 100644 +index 0000000000..bae488b623 --- /dev/null +++ b/modules/hw/mmal/rpi_prof.h @@ -0,0 +1,110 @@ @@ -12538,6 +12689,9 @@ + +#endif + +diff --git a/modules/hw/mmal/subpic.c b/modules/hw/mmal/subpic.c +new file mode 100644 +index 0000000000..42770eaac9 --- /dev/null +++ b/modules/hw/mmal/subpic.c @@ -0,0 +1,257 @@ @@ -12798,6 +12952,9 @@ + + + +diff --git a/modules/hw/mmal/subpic.h b/modules/hw/mmal/subpic.h +new file mode 100644 +index 0000000000..4599f8a5da --- /dev/null +++ b/modules/hw/mmal/subpic.h @@ -0,0 +1,33 @@ @@ -12834,6 +12991,9 @@ + +#endif + +diff --git a/modules/hw/mmal/transform_ops.h b/modules/hw/mmal/transform_ops.h +new file mode 100644 +index 0000000000..acfb5c254b --- /dev/null +++ b/modules/hw/mmal/transform_ops.h @@ -0,0 +1,99 @@ @@ -12936,6 +13096,9 @@ + +#endif + +diff --git a/modules/hw/mmal/v7_pmu.S b/modules/hw/mmal/v7_pmu.S +new file mode 100644 +index 0000000000..dc82357bf3 --- /dev/null +++ b/modules/hw/mmal/v7_pmu.S @@ -0,0 +1,263 @@ @@ -13202,6 +13365,9 @@ +/* ------------------------------------------------------------ */ + + +diff --git a/modules/hw/mmal/v7_pmu.h b/modules/hw/mmal/v7_pmu.h +new file mode 100644 +index 0000000000..55c2bdf885 --- /dev/null +++ b/modules/hw/mmal/v7_pmu.h @@ -0,0 +1,113 @@ @@ -13318,6 +13484,8 @@ +// End of v7_pmu.h +// ------------------------------------------------------------ + +diff --git a/modules/hw/mmal/vout.c b/modules/hw/mmal/vout.c +index 76188a457c..b9fef71e3c 100644 --- a/modules/hw/mmal/vout.c +++ b/modules/hw/mmal/vout.c @@ -27,21 +27,28 @@ @@ -13392,7 +13560,8 @@ -static int Open(vlc_object_t *); -static void Close(vlc_object_t *); -- ++#define SUBS_MAX 4 + -vlc_module_begin() - set_shortname(N_("MMAL vout")) - set_description(N_("MMAL-based vout plugin for Raspberry Pi")) @@ -13407,8 +13576,7 @@ - MMAL_NATIVE_INTERLACE_LONGTEXT, false) - set_callbacks(Open, Close) -vlc_module_end() -+#define SUBS_MAX 4 - +- -struct dmx_region_t { - struct dmx_region_t *next; - picture_t *picture; @@ -13455,15 +13623,15 @@ + MMAL_RECT_T win_rect; // Window rect after transform(s) + MMAL_RECT_T display_rect; // Actual shape of display (x, y always 0) + MMAL_RECT_T req_win; // User requested window (w=0 => fullscreen) -+ + +- int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */ +- int i_frame_rate; + MMAL_RECT_T spu_rect; // Output rectangle in cfg coords (for subpic placement) + MMAL_RECT_T dest_rect; // Output rectangle in display coords + MMAL_DISPLAYTRANSFORM_T dest_transform; // Dest window coord transform + MMAL_DISPLAYTRANSFORM_T display_transform; // "Native" display transform + MMAL_DISPLAYTRANSFORM_T video_transform; // Combined config+native transform - -- int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */ -- int i_frame_rate; ++ + unsigned int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */ + unsigned int i_frame_rate; @@ -13478,13 +13646,8 @@ bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */ bool b_progressive; - bool opaque; /* indicated use of opaque picture format (zerocopy) */ --}; + bool force_config; - --static const vlc_fourcc_t subpicture_chromas[] = { -- VLC_CODEC_RGBA, -- 0 --}; ++ + vout_subpic_t subs[SUBS_MAX]; + // Stash for subpics derived from the passed subpicture rather than + // included with the main pic @@ -13501,13 +13664,29 @@ + MMAL_POOL_T * out_pool; + bool pending; + } isp; ++ ++ MMAL_POOL_T * copy_pool; ++ MMAL_BUFFER_HEADER_T * copy_buf; ++ ++ // Subpic blend if we have to do it here ++ vzc_pool_ctl_t * vzc; + }; + +-static const vlc_fourcc_t subpicture_chromas[] = { +- VLC_CODEC_RGBA, +- 0 +-}; -/* Utility functions */ -static inline uint32_t align(uint32_t x, uint32_t y); -static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg, - const video_format_t *fmt); -+ MMAL_POOL_T * copy_pool; -+ MMAL_BUFFER_HEADER_T * copy_buf; ++// ISP setup ++ ++static inline bool want_isp(const vout_display_t * const vd) ++{ ++ return (vd->fmt.i_chroma == VLC_CODEC_MMAL_ZC_SAND10); ++} -/* VLC vout display callbacks */ -static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count); @@ -13517,59 +13696,14 @@ - subpicture_t *subpicture); -static int vd_control(vout_display_t *vd, int query, va_list args); -static void vd_manage(vout_display_t *vd); -- --/* MMAL callbacks */ --static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); --static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); -+ // Subpic blend if we have to do it here -+ vzc_pool_ctl_t * vzc; -+}; - --/* TV service */ --static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height); --static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, -- uint32_t param2); --static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt); --static int set_latency_target(vout_display_t *vd, bool enable); - --/* DispManX */ --static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture); --static void close_dmx(vout_display_t *vd); --static struct dmx_region_t *dmx_region_new(vout_display_t *vd, -- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region); --static void dmx_region_update(struct dmx_region_t *dmx_region, -- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture); --static void dmx_region_delete(struct dmx_region_t *dmx_region, -- DISPMANX_UPDATE_HANDLE_T update); --static void show_background(vout_display_t *vd, bool enable); --static void maintain_phase_sync(vout_display_t *vd); -+// ISP setup - --static int Open(vlc_object_t *object) -+static inline bool want_isp(const vout_display_t * const vd) - { -- vout_display_t *vd = (vout_display_t *)object; -- vout_display_sys_t *sys; -- uint32_t buffer_pitch, buffer_height; -- vout_display_place_t place; -- MMAL_DISPLAYREGION_T display_region; -- MMAL_STATUS_T status; -- int ret = VLC_SUCCESS; -- unsigned i; -+ return (vd->fmt.i_chroma == VLC_CODEC_MMAL_ZC_SAND10); -+} - -- if (vout_display_IsWindowed(vd)) -- return VLC_EGENERIC; +static inline bool want_copy(const vout_display_t * const vd) +{ + return (vd->fmt.i_chroma == VLC_CODEC_I420 || vd->fmt.i_chroma == VLC_CODEC_I420_10L); +} -- sys = calloc(1, sizeof(struct vout_display_sys_t)); -- if (!sys) -- return VLC_ENOMEM; -- vd->sys = sys; +-/* MMAL callbacks */ +-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); +-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); +static inline vlc_fourcc_t req_chroma(const vout_display_t * const vd) +{ + return !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma) && !want_copy(vd) ? @@ -13577,8 +13711,12 @@ + vd->fmt.i_chroma; +} -- sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); -- bcm_host_init(); +-/* TV service */ +-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height); +-static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, +- uint32_t param2); +-static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt); +-static int set_latency_target(vout_display_t *vd, bool enable); +static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc) +{ + switch (fcc){ @@ -13599,19 +13737,23 @@ + return MMAL_ENCODING_I420; +} -- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE; +-/* DispManX */ +-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture); +-static void close_dmx(vout_display_t *vd); +-static struct dmx_region_t *dmx_region_new(vout_display_t *vd, +- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region); +-static void dmx_region_update(struct dmx_region_t *dmx_region, +- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture); +-static void dmx_region_delete(struct dmx_region_t *dmx_region, +- DISPMANX_UPDATE_HANDLE_T update); +-static void show_background(vout_display_t *vd, bool enable); +-static void maintain_phase_sync(vout_display_t *vd); +static 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; ++ + es_fmt->type = MMAL_ES_TYPE_VIDEO; + es_fmt->encoding = is_intermediate ? MMAL_ENCODING_I420 : vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma); + es_fmt->encoding_variant = 0; @@ -13628,24 +13770,23 @@ + } else { + v_fmt->par.num = vd->fmt.i_sar_num; + v_fmt->par.den = vd->fmt.i_sar_den; - } ++ } + v_fmt->frame_rate.num = vd->fmt.i_frame_rate; + v_fmt->frame_rate.den = vd->fmt.i_frame_rate_base; + v_fmt->color_space = vlc_to_mmal_color_space(vd->fmt.space); -- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; -- status = mmal_port_enable(sys->component->control, control_port_cb); -- if (status != MMAL_SUCCESS) { -- msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)", -- sys->component->control->name, status, mmal_status_to_string(status)); -- ret = VLC_EGENERIC; -- goto out; +-static int Open(vlc_object_t *object) + msg_Dbg(vd, "WxH: %dx%d, Crop: %dx%d", v_fmt->width, v_fmt->height, v_fmt->crop.width, v_fmt->crop.height); +} + +static MMAL_RECT_T +display_src_rect(const vout_display_t * const vd, const video_format_t * const src) -+{ + { +- vout_display_t *vd = (vout_display_t *)object; +- vout_display_sys_t *sys; +- uint32_t buffer_pitch, buffer_height; +- vout_display_place_t place; +- MMAL_DISPLAYREGION_T display_region; + const bool wants_isp = want_isp(vd); + + // Scale source derived cropping to actual picture shape @@ -13678,39 +13819,26 @@ +static void isp_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + vout_display_t *vd = (vout_display_t *)port->userdata; -+ MMAL_STATUS_T status; -+ + MMAL_STATUS_T status; +- int ret = VLC_SUCCESS; +- unsigned i; + +- if (vout_display_IsWindowed(vd)) +- return VLC_EGENERIC; + if (buffer->cmd == MMAL_EVENT_ERROR) { + status = *(uint32_t *)buffer->data; + msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); - } ++ } -- sys->input = sys->component->input[0]; -- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd; +- sys = calloc(1, sizeof(struct vout_display_sys_t)); +- if (!sys) +- return VLC_ENOMEM; +- vd->sys = sys; + mmal_buffer_header_release(buffer); +} -- if (sys->opaque) { -- sys->input->format->encoding = MMAL_ENCODING_OPAQUE; -- sys->i_planes = 1; -- sys->buffer_size = sys->input->buffer_size_recommended; -- } else { -- sys->input->format->encoding = MMAL_ENCODING_I420; -- vd->fmt.i_chroma = VLC_CODEC_I420; -- buffer_pitch = align(vd->fmt.i_width, 32); -- buffer_height = align(vd->fmt.i_height, 16); -- sys->i_planes = 3; -- sys->buffer_size = 3 * buffer_pitch * buffer_height / 2; -- } -- -- sys->input->format->es->video.width = vd->fmt.i_width; -- sys->input->format->es->video.height = vd->fmt.i_height; -- sys->input->format->es->video.crop.x = 0; -- sys->input->format->es->video.crop.y = 0; -- sys->input->format->es->video.crop.width = vd->fmt.i_width; -- sys->input->format->es->video.crop.height = vd->fmt.i_height; -- sys->input->format->es->video.par.num = vd->source.i_sar_num; -- sys->input->format->es->video.par.den = vd->source.i_sar_den; +- sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); +- bcm_host_init(); +static void isp_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) +{ + if (buf->cmd == 0 && buf->length != 0) @@ -13719,13 +13847,7 @@ + // but might not on later flushes as we shut down + vout_display_t * const vd = (vout_display_t *)port->userdata; + struct vout_isp_conf_s *const isp = &vd->sys->isp; - -- status = mmal_port_format_commit(sys->input); -- if (status != MMAL_SUCCESS) { -- msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)", -- sys->input->name, status, mmal_status_to_string(status)); -- ret = VLC_EGENERIC; -- goto out; ++ +#if TRACE_ALL + msg_Dbg(vd, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts); +#endif @@ -13733,8 +13855,7 @@ +#if TRACE_ALL + msg_Dbg(vd, ">>> %s: out Q len=%d", __func__, mmal_queue_length(isp->out_q)); +#endif - } -- sys->input->buffer_size = sys->input->buffer_size_recommended; ++ } + else + { + mmal_buffer_header_reset(buf); @@ -13742,34 +13863,20 @@ + } +} -- vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); -- display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION; -- display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T); -- display_region.fullscreen = MMAL_FALSE; -- display_region.src_rect.x = vd->fmt.i_x_offset; -- display_region.src_rect.y = vd->fmt.i_y_offset; -- display_region.src_rect.width = vd->fmt.i_visible_width; -- display_region.src_rect.height = vd->fmt.i_visible_height; -- display_region.dest_rect.x = place.x; -- display_region.dest_rect.y = place.y; -- display_region.dest_rect.width = place.width; -- display_region.dest_rect.height = place.height; -- display_region.layer = sys->layer; -- display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT | -- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER; -- status = mmal_port_parameter_set(sys->input, &display_region.hdr); -- if (status != MMAL_SUCCESS) { -- msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)", -- status, mmal_status_to_string(status)); -- ret = VLC_EGENERIC; -- goto out; +- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE; +static void isp_empty_out_q(struct vout_isp_conf_s * const isp) +{ + MMAL_BUFFER_HEADER_T * buf; + // We can be called as part of error recovery so allow for missing Q + if (isp->out_q == NULL) + return; -+ + +- status = mmal_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; + while ((buf = mmal_queue_get(isp->out_q)) != NULL) + mmal_buffer_header_release(buf); +} @@ -13799,58 +13906,70 @@ + } } -- for (i = 0; i < sys->i_planes; ++i) { -- sys->planes[i].i_lines = buffer_height; -- sys->planes[i].i_pitch = buffer_pitch; -- sys->planes[i].i_visible_lines = vd->fmt.i_visible_height; -- sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width; +- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; +- status = mmal_port_enable(sys->component->control, control_port_cb); +- if (status != MMAL_SUCCESS) { +- msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)", +- sys->component->control->name, status, mmal_status_to_string(status)); +- ret = VLC_EGENERIC; +- goto out; + while ((buf = mmal_queue_get(isp->out_pool->queue)) != NULL) { + if ((err = mmal_port_send_buffer(isp->output, buf)) != MMAL_SUCCESS) + { + msg_Err(vd, "ISP output port stuff failed"); + return err; + } -+ } + } -- if (i > 0) { -- sys->planes[i].i_lines /= 2; -- sys->planes[i].i_pitch /= 2; -- sys->planes[i].i_visible_lines /= 2; -- sys->planes[i].i_visible_pitch /= 2; +- sys->input = sys->component->input[0]; +- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd; + if (!isp->input->is_enabled) { + if ((err = mmal_port_enable(isp->input, isp_input_cb)) != MMAL_SUCCESS) + { + msg_Err(vd, "ISP input port enable failed"); + return err; - } - } ++ } ++ } + return MMAL_SUCCESS; +} -- vlc_mutex_init(&sys->buffer_mutex); -- vlc_cond_init(&sys->buffer_cond); -- vlc_mutex_init(&sys->manage_mutex); +- if (sys->opaque) { +- sys->input->format->encoding = MMAL_ENCODING_OPAQUE; +- sys->i_planes = 1; +- sys->buffer_size = sys->input->buffer_size_recommended; +- } else { +- sys->input->format->encoding = MMAL_ENCODING_I420; +- vd->fmt.i_chroma = VLC_CODEC_I420; +- buffer_pitch = align(vd->fmt.i_width, 32); +- buffer_height = align(vd->fmt.i_height, 16); +- sys->i_planes = 3; +- sys->buffer_size = 3 * buffer_pitch * buffer_height / 2; +- } +- +- sys->input->format->es->video.width = vd->fmt.i_width; +- sys->input->format->es->video.height = vd->fmt.i_height; +- sys->input->format->es->video.crop.x = 0; +- sys->input->format->es->video.crop.y = 0; +- sys->input->format->es->video.crop.width = vd->fmt.i_width; +- sys->input->format->es->video.crop.height = vd->fmt.i_height; +- sys->input->format->es->video.par.num = vd->source.i_sar_num; +- sys->input->format->es->video.par.den = vd->source.i_sar_den; +static void isp_close(vout_display_t * const vd, vout_display_sys_t * const vd_sys) +{ + struct vout_isp_conf_s * const isp = &vd_sys->isp; + VLC_UNUSED(vd); -- vd->pool = vd_pool; -- vd->prepare = vd_prepare; -- vd->display = vd_display; -- vd->control = vd_control; -- vd->manage = vd_manage; +- status = mmal_port_format_commit(sys->input); +- if (status != MMAL_SUCCESS) { +- msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)", +- sys->input->name, status, mmal_status_to_string(status)); +- ret = VLC_EGENERIC; +- goto out; + if (isp->component == NULL) + return; - -- vc_tv_register_callback(tvservice_cb, vd); ++ + isp_flush(isp); - -- if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) { -- vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height); -- } else { -- sys->display_width = vd->cfg->display.width; -- sys->display_height = vd->cfg->display.height; ++ + if (isp->component->control->is_enabled) + mmal_port_disable(isp->component->control); + @@ -13861,54 +13980,80 @@ + mmal_queue_destroy(isp->out_q); + isp->out_q = NULL; } +- sys->input->buffer_size = sys->input->buffer_size_recommended; -- sys->dmx_handle = vc_dispmanx_display_open(0); -- vd->info.subpicture_chromas = subpicture_chromas; +- vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); +- display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION; +- display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T); +- display_region.fullscreen = MMAL_FALSE; +- display_region.src_rect.x = vd->fmt.i_x_offset; +- display_region.src_rect.y = vd->fmt.i_y_offset; +- display_region.src_rect.width = vd->fmt.i_visible_width; +- display_region.src_rect.height = vd->fmt.i_visible_height; +- display_region.dest_rect.x = place.x; +- display_region.dest_rect.y = place.y; +- display_region.dest_rect.width = place.width; +- display_region.dest_rect.height = place.height; +- display_region.layer = sys->layer; +- display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT | +- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER; +- status = mmal_port_parameter_set(sys->input, &display_region.hdr); +- if (status != MMAL_SUCCESS) { +- msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)", +- status, mmal_status_to_string(status)); +- ret = VLC_EGENERIC; +- goto out; + if (isp->out_pool != NULL) { + mmal_port_pool_destroy(isp->output, isp->out_pool); + isp->out_pool = NULL; -+ } + } -- vout_display_DeleteWindow(vd, NULL); +- for (i = 0; i < sys->i_planes; ++i) { +- sys->planes[i].i_lines = buffer_height; +- sys->planes[i].i_pitch = buffer_pitch; +- sys->planes[i].i_visible_lines = vd->fmt.i_visible_height; +- sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width; + isp->input = NULL; + isp->output = NULL; --out: -- if (ret != VLC_SUCCESS) -- Close(object); +- if (i > 0) { +- sys->planes[i].i_lines /= 2; +- sys->planes[i].i_pitch /= 2; +- sys->planes[i].i_visible_lines /= 2; +- sys->planes[i].i_visible_pitch /= 2; +- } + mmal_component_release(isp->component); + isp->component = NULL; - -- return ret; ++ + return; - } - --static void Close(vlc_object_t *object) ++} ++ +// Restuff into output rather than return to pool is we can +static MMAL_BOOL_T isp_out_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata) - { -- vout_display_t *vd = (vout_display_t *)object; -- vout_display_sys_t *sys = vd->sys; -- char response[20]; /* answer is hvs_update_fields=%1d */ -- unsigned i; ++{ + struct vout_isp_conf_s * const isp = userdata; + VLC_UNUSED(pool); + if (isp->output->is_enabled) { + mmal_buffer_header_reset(buffer); + if (mmal_port_send_buffer(isp->output, buffer) == MMAL_SUCCESS) + return MMAL_FALSE; -+ } + } + return MMAL_TRUE; +} -- vc_tv_unregister_callback_full(tvservice_cb, vd); +- vlc_mutex_init(&sys->buffer_mutex); +- vlc_cond_init(&sys->buffer_cond); +- vlc_mutex_init(&sys->manage_mutex); +static MMAL_STATUS_T isp_setup(vout_display_t * const vd, vout_display_sys_t * const vd_sys) +{ + struct vout_isp_conf_s * const isp = &vd_sys->isp; + MMAL_STATUS_T err; -- if (sys->dmx_handle) -- close_dmx(vd); +- vd->pool = vd_pool; +- vd->prepare = vd_prepare; +- vd->display = vd_display; +- vd->control = vd_control; +- vd->manage = vd_manage; + if ((err = mmal_component_create(MMAL_COMPONENT_ISP_RESIZER, &isp->component)) != MMAL_SUCCESS) { + msg_Err(vd, "Cannot create ISP component"); + return err; @@ -13916,63 +14061,44 @@ + isp->input = isp->component->input[0]; + isp->output = isp->component->output[0]; -- if (sys->component && sys->component->control->is_enabled) -- mmal_port_disable(sys->component->control); +- vc_tv_register_callback(tvservice_cb, vd); + isp->component->control->userdata = (void *)vd; + if ((err = mmal_port_enable(isp->component->control, isp_control_port_cb)) != MMAL_SUCCESS) { + msg_Err(vd, "Failed to enable ISP control port"); + goto fail; + } -- if (sys->input && sys->input->is_enabled) -- mmal_port_disable(sys->input); +- if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) { +- vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height); +- } else { +- sys->display_width = vd->cfg->display.width; +- sys->display_height = vd->cfg->display.height; + isp->input->userdata = (void *)vd; + display_set_format(vd, isp->input->format, false); - -- if (sys->component && sys->component->is_enabled) -- mmal_component_disable(sys->component); ++ + if ((err = port_parameter_set_bool(isp->input, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS) + goto fail; - -- if (sys->pool) -- mmal_port_pool_destroy(sys->input, sys->pool); ++ + if ((err = mmal_port_format_commit(isp->input)) != MMAL_SUCCESS) { + msg_Err(vd, "Failed to set ISP input format"); + goto fail; + } - -- if (sys->component) -- mmal_component_release(sys->component); ++ + isp->input->buffer_size = isp->input->buffer_size_recommended; + isp->input->buffer_num = 30; - -- if (sys->picture_pool) -- picture_pool_Release(sys->picture_pool); -- else -- for (i = 0; i < sys->num_buffers; ++i) -- if (sys->pictures[i]) { -- mmal_buffer_header_release(sys->pictures[i]->p_sys->buffer); -- picture_Release(sys->pictures[i]); -- } ++ + if ((isp->in_pool = mmal_pool_create(isp->input->buffer_num, 0)) == NULL) + { + msg_Err(vd, "Failed to create input pool"); + goto fail; + } - -- vlc_mutex_destroy(&sys->buffer_mutex); -- vlc_cond_destroy(&sys->buffer_cond); -- vlc_mutex_destroy(&sys->manage_mutex); ++ + if ((isp->out_q = mmal_queue_create()) == NULL) + { + err = MMAL_ENOMEM; + goto fail; + } - -- if (sys->native_interlaced) { -- if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 || -- response[18] != '0') -- msg_Warn(vd, "Could not reset hvs field mode"); ++ + display_set_format(vd, isp->output->format, true); + + if ((err = port_parameter_set_bool(isp->output, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS) @@ -13981,10 +14107,8 @@ + if ((err = mmal_port_format_commit(isp->output)) != MMAL_SUCCESS) { + msg_Err(vd, "Failed to set ISP input format"); + goto fail; - } - -- free(sys->pictures); -- free(sys); ++ } ++ + isp->output->buffer_size = isp->output->buffer_size_recommended; + isp->output->buffer_num = 2; + isp->output->userdata = (void *)vd; @@ -13999,19 +14123,14 @@ + + if ((err = isp_prepare(vd, isp)) != MMAL_SUCCESS) + goto fail; - -- bcm_host_deinit(); ++ + return MMAL_SUCCESS; + +fail: + isp_close(vd, vd_sys); + return err; - } - --static inline uint32_t align(uint32_t x, uint32_t y) { -- uint32_t mod = x % y; -- if (mod == 0) -- return x; ++} ++ +static MMAL_STATUS_T isp_check(vout_display_t * const vd, vout_display_sys_t * const vd_sys) +{ + struct vout_isp_conf_s *const isp = &vd_sys->isp; @@ -14031,16 +14150,18 @@ + if (mmal_queue_length(isp->out_pool->queue) == isp->output->buffer_num) + isp_close(vd, vd_sys); + } - else -- return x + y - mod; ++ else + { + // ISP closed but we want it + return isp_setup(vd, vd_sys); -+ } -+ + } + +- sys->dmx_handle = vc_dispmanx_display_open(0); +- vd->info.subpicture_chromas = subpicture_chromas; + return MMAL_SUCCESS; +} -+ + +- vout_display_DeleteWindow(vd, NULL); +/* TV service */ +static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, + uint32_t param2); @@ -14049,7 +14170,10 @@ + +// Mmal +static void maintain_phase_sync(vout_display_t *vd); -+ + +-out: +- if (ret != VLC_SUCCESS) +- Close(object); + + +static void vd_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) @@ -14093,13 +14217,18 @@ + msg_Warn(vd, "Failed to query display resolution"); + ret = -1; + } -+ -+ return ret; -+} -+ + + return ret; + } + +-static void Close(vlc_object_t *object) +static inline MMAL_RECT_T +place_to_mmal_rect(const vout_display_place_t place) -+{ + { +- vout_display_t *vd = (vout_display_t *)object; +- vout_display_sys_t *sys = vd->sys; +- char response[20]; /* answer is hvs_update_fields=%1d */ +- unsigned i; + return (MMAL_RECT_T){ + .x = place.x, + .y = place.y, @@ -14107,7 +14236,8 @@ + .height = place.height + }; +} -+ + +- vc_tv_unregister_callback_full(tvservice_cb, vd); +static MMAL_RECT_T +place_out(const vout_display_cfg_t * cfg, + const video_format_t * fmt, @@ -14116,7 +14246,9 @@ + video_format_t tfmt; + vout_display_cfg_t tcfg; + vout_display_place_t place; -+ + +- if (sys->dmx_handle) +- close_dmx(vd); + // Fix SAR if unknown + if (fmt->i_sar_den == 0 || fmt->i_sar_num == 0) { + tfmt = *fmt; @@ -14124,7 +14256,9 @@ + tfmt.i_sar_num = 1; + fmt = &tfmt; + } -+ + +- if (sys->component && sys->component->control->is_enabled) +- mmal_port_disable(sys->component->control); + // Override what VLC thinks might be going on with display size + // if we know better + if (r.width != 0 && r.height != 0) @@ -14134,15 +14268,23 @@ + tcfg.display.height = r.height; + cfg = &tcfg; + } -+ + +- if (sys->input && sys->input->is_enabled) +- mmal_port_disable(sys->input); + vout_display_PlacePicture(&place, fmt, cfg, false); -+ + +- if (sys->component && sys->component->is_enabled) +- mmal_component_disable(sys->component); + place.x += r.x; + place.y += r.y; -+ + +- if (sys->pool) +- mmal_port_pool_destroy(sys->input, sys->pool); + return place_to_mmal_rect(place); +} -+ + +- if (sys->component) +- mmal_component_release(sys->component); +static MMAL_RECT_T +rect_transform(MMAL_RECT_T s, const MMAL_RECT_T c, const MMAL_DISPLAYTRANSFORM_T t) +{ @@ -14154,7 +14296,15 @@ + s = rect_vflip(s, c); + return s; +} -+ + +- if (sys->picture_pool) +- picture_pool_Release(sys->picture_pool); +- else +- for (i = 0; i < sys->num_buffers; ++i) +- if (sys->pictures[i]) { +- mmal_buffer_header_release(sys->pictures[i]->p_sys->buffer); +- picture_Release(sys->pictures[i]); +- } +static void +place_dest_rect(vout_display_t * const vd, + const vout_display_cfg_t * const cfg, @@ -14164,7 +14314,10 @@ + sys->dest_rect = rect_transform(place_out(cfg, fmt, sys->win_rect), + sys->display_rect, sys->dest_transform); +} -+ + +- vlc_mutex_destroy(&sys->buffer_mutex); +- vlc_cond_destroy(&sys->buffer_cond); +- vlc_mutex_destroy(&sys->manage_mutex); +static void +place_spu_rect(vout_display_t * const vd, + const vout_display_cfg_t * const cfg, @@ -14172,7 +14325,11 @@ +{ + vout_display_sys_t * const sys = vd->sys; + static const MMAL_RECT_T r0 = {0}; -+ + +- if (sys->native_interlaced) { +- if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 || +- response[18] != '0') +- msg_Warn(vd, "Could not reset hvs field mode"); + sys->spu_rect = place_out(cfg, fmt, r0); + sys->spu_rect.x = 0; + sys->spu_rect.y = 0; @@ -14183,12 +14340,15 @@ + if (fmt->i_width * fmt->i_height >= (unsigned int)(sys->spu_rect.width * sys->spu_rect.height)) { + sys->spu_rect.width = fmt->i_visible_width; + sys->spu_rect.height = fmt->i_visible_height; -+ } -+ + } + +- free(sys->pictures); +- free(sys); + if (ORIENT_IS_SWAP(fmt->orientation)) + sys->spu_rect = rect_transpose(sys->spu_rect); +} -+ + +- bcm_host_deinit(); +static void +place_rects(vout_display_t * const vd, + const vout_display_cfg_t * const cfg, @@ -14196,8 +14356,14 @@ +{ + place_dest_rect(vd, cfg, fmt); + place_spu_rect(vd, cfg, fmt); -+} -+ + } + +-static inline uint32_t align(uint32_t x, uint32_t y) { +- uint32_t mod = x % y; +- if (mod == 0) +- return x; +- else +- return x + y - mod; +static int +set_input_region(vout_display_t * const vd, const video_format_t * const fmt) +{ @@ -14255,7 +14421,7 @@ if (fmt) { sys->input->format->es->video.par.num = fmt->i_sar_num; -@@ -412,30 +733,14 @@ static int configure_display(vout_displa +@@ -412,30 +733,14 @@ static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg, if (!cfg) cfg = vd->cfg; @@ -14291,20 +14457,12 @@ 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,205 +751,218 @@ static int configure_display(vout_displa +@@ -446,204 +751,217 @@ static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg, return 0; } +-static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count) +static void kill_pool(vout_display_sys_t * const sys) -+{ -+ if (sys->pic_pool != NULL) { -+ picture_pool_Release(sys->pic_pool); -+ sys->pic_pool = NULL; -+ } -+} -+ -+// Actual picture pool for MMAL opaques is just a set of trivial containers - static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count) { - vout_display_sys_t *sys = vd->sys; - picture_resource_t picture_res; @@ -14312,25 +14470,48 @@ - video_format_t fmt = vd->fmt; - MMAL_STATUS_T status; - unsigned i; -+ vout_display_sys_t * const sys = vd->sys; - +- - if (sys->picture_pool) { - if (sys->num_buffers < count) - msg_Warn(vd, "Picture pool with %u pictures requested, but we already have one with %u pictures", - count, sys->num_buffers); +- +- goto out; ++ if (sys->pic_pool != NULL) { ++ picture_pool_Release(sys->pic_pool); ++ sys->pic_pool = NULL; + } ++} + +- if (sys->opaque) { +- if (count <= NUM_ACTUAL_OPAQUE_BUFFERS) +- count = NUM_ACTUAL_OPAQUE_BUFFERS; ++// Actual picture pool for MMAL opaques is just a set of trivial containers ++static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count) ++{ ++ vout_display_sys_t * const sys = vd->sys; + +- MMAL_PARAMETER_BOOLEAN_T zero_copy = { +- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, +- 1 +- }; + msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__, + vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, vd->source.i_width, vd->source.i_height); -- goto out; +- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr); +- if (status != MMAL_SUCCESS) { +- msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", +- sys->input->name, status, mmal_status_to_string(status)); +- goto out; +- } + if (sys->pic_pool == NULL) { + sys->pic_pool = picture_pool_NewFromFormat(&vd->fmt, count); } + return sys->pic_pool; +} -- if (sys->opaque) { -- if (count <= NUM_ACTUAL_OPAQUE_BUFFERS) -- count = NUM_ACTUAL_OPAQUE_BUFFERS; +- if (count < sys->input->buffer_num_recommended) +- count = sys->input->buffer_num_recommended; +static inline bool +check_shape(vout_display_t * const vd, const picture_t * const p_pic) +{ @@ -14340,22 +14521,14 @@ + return false; +} -- MMAL_PARAMETER_BOOLEAN_T zero_copy = { -- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, -- 1 -- }; +-#ifndef NDEBUG +- msg_Dbg(vd, "Creating picture pool with %u pictures", count); +static void vd_display(vout_display_t *vd, picture_t *p_pic, + subpicture_t *subpicture) +{ + vout_display_sys_t * const sys = vd->sys; + MMAL_STATUS_T err; - -- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr); -- if (status != MMAL_SUCCESS) { -- msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", -- sys->input->name, status, mmal_status_to_string(status)); -- goto out; -- } ++ +#if TRACE_ALL + { + char dbuf0[5]; @@ -14365,13 +14538,7 @@ + p_pic->format.i_visible_width, p_pic->format.i_visible_height, + p_pic->format.i_sar_num, p_pic->format.i_sar_den, + sys->dest_rect.width, sys->dest_rect.height, sys->dest_rect.x, sys->dest_rect.y); - } -- -- if (count < sys->input->buffer_num_recommended) -- count = sys->input->buffer_num_recommended; -- --#ifndef NDEBUG -- msg_Dbg(vd, "Creating picture pool with %u pictures", count); ++ } #endif - sys->input->buffer_num = count; @@ -14391,18 +14558,54 @@ - msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)", - sys->component->name, status, mmal_status_to_string(status)); - goto out; +- } +- +- sys->num_buffers = count; +- sys->pool = mmal_port_pool_create(sys->input, sys->num_buffers, +- sys->input->buffer_size); +- if (!sys->pool) { +- msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32, +- count, sys->input->buffer_size); +- goto out; +- } +- +- memset(&picture_res, 0, sizeof(picture_resource_t)); +- sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *)); +- for (i = 0; i < sys->num_buffers; ++i) { +- picture_res.p_sys = calloc(1, sizeof(picture_sys_t)); +- picture_res.p_sys->owner = (vlc_object_t *)vd; +- picture_res.p_sys->buffer = mmal_queue_get(sys->pool->queue); +- +- sys->pictures[i] = picture_NewFromResource(&fmt, &picture_res); +- if (!sys->pictures[i]) { +- msg_Err(vd, "Failed to create picture"); +- free(picture_res.p_sys); +- goto out; +- } +- +- sys->pictures[i]->i_planes = sys->i_planes; +- memcpy(sys->pictures[i]->p, sys->planes, sys->i_planes * sizeof(plane_t)); + if (!check_shape(vd, p_pic)) + { + msg_Err(vd, "Pic/fmt shape mismatch"); + goto fail; -+ } -+ + } + +- memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t)); +- picture_pool_cfg.picture_count = sys->num_buffers; +- picture_pool_cfg.picture = sys->pictures; +- picture_pool_cfg.lock = mmal_picture_lock; +- +- sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg); +- if (!sys->picture_pool) { +- msg_Err(vd, "Failed to create picture pool"); +- goto out; + if (!sys->input->is_enabled && + (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS) + { + msg_Err(vd, "Input port enable failed"); + goto fail; -+ } + } + // Stuff into input + // We assume the BH is already set up with values reflecting pic date etc. + if (sys->copy_buf != NULL) { @@ -14417,15 +14620,7 @@ + msg_Err(vd, "Send copy buffer to render input failed"); + goto fail; + } - } -- -- 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; @@ -14438,20 +14633,7 @@ + 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) { -- picture_res.p_sys = calloc(1, sizeof(picture_sys_t)); -- picture_res.p_sys->owner = (vlc_object_t *)vd; -- picture_res.p_sys->buffer = mmal_queue_get(sys->pool->queue); -- -- sys->pictures[i] = picture_NewFromResource(&fmt, &picture_res); -- if (!sys->pictures[i]) { -- msg_Err(vd, "Failed to create picture"); -- free(picture_res.p_sys); -- goto out; ++ } + else + { + MMAL_BUFFER_HEADER_T *const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->pool); @@ -14459,38 +14641,8 @@ + { + 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; -+ 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; -+ } - } - -out: - return sys->picture_pool; -} @@ -14500,10 +14652,14 @@ -{ - vout_display_sys_t *sys = vd->sys; - picture_sys_t *pic_sys = picture->p_sys; -- + - if (!sys->adjust_refresh_rate || pic_sys->displayed) - return; -- ++ // 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"); + - /* Apply the required phase_offset to the picture, so that vd_display() - * will be called at the corrected time from the core */ - picture->date += sys->phase_offset; @@ -14516,7 +14672,11 @@ - picture_sys_t *pic_sys = picture->p_sys; - MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer; - MMAL_STATUS_T status; -- ++ // HVS can deal with on-line dimension changes ++ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS) ++ msg_Warn(vd, "Input format commit failed"); ++ } + - if (picture->format.i_frame_rate != sys->i_frame_rate || - picture->format.i_frame_rate_base != sys->i_frame_rate_base || - picture->b_progressive != sys->b_progressive || @@ -14526,8 +14686,14 @@ - sys->i_frame_rate = picture->format.i_frame_rate; - sys->i_frame_rate_base = picture->format.i_frame_rate_base; - configure_display(vd, NULL, &picture->format); -- } -- ++ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS) ++ { ++ mmal_buffer_header_release(pic_buf); ++ msg_Err(vd, "Send buffer to input failed"); ++ goto fail; ++ } + } + - if (!pic_sys->displayed || !sys->opaque) { - buffer->cmd = 0; - buffer->length = sys->input->buffer_size; @@ -14639,8 +14805,8 @@ + 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; - ++ break; ++ + case VOUT_DISPLAY_CHANGE_MMAL_HIDE: + { + MMAL_STATUS_T err; @@ -14659,13 +14825,12 @@ + } + sys->force_config = true; + ret = VLC_SUCCESS; -+ break; + break; + } -+ + default: msg_Warn(vd, "Unknown control query %d", query); - break; -@@ -653,79 +971,208 @@ static int vd_control(vout_display_t *vd +@@ -653,30 +971,198 @@ static int vd_control(vout_display_t *vd, int query, va_list args) return ret; } @@ -14695,41 +14860,32 @@ if (sys->need_configure_display) { - close_dmx(vd); - sys->dmx_handle = vc_dispmanx_display_open(0); -- ++ sys->need_configure_display = false; ++ set_display_windows(vd, sys); ++ } ++ ++ vlc_mutex_unlock(&sys->manage_mutex); ++} + - if (query_resolution(vd, &width, &height) >= 0) { - sys->display_width = width; - sys->display_height = height; - vout_display_SendEventDisplaySize(vd, width, height); -- } -- - sys->need_configure_display = false; -+ set_display_windows(vd, sys); - } - - vlc_mutex_unlock(&sys->manage_mutex); - } - --static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) + +static int attach_subpics(vout_display_t * const vd, vout_display_sys_t * const sys, + subpicture_t * const subpicture) - { -- vout_display_t *vd = (vout_display_t *)port->userdata; -- MMAL_STATUS_T status; ++{ + unsigned int n = 0; - -- if (buffer->cmd == MMAL_EVENT_ERROR) { -- status = *(uint32_t *)buffer->data; -- msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); ++ + if (sys->vzc == NULL) { + if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL) + { + msg_Err(vd, "Failed to allocate VZC"); + return VLC_ENOMEM; -+ } - } + } ++ } -- mmal_buffer_header_release(buffer); +- sys->need_configure_display = false; + // Attempt to import the subpics + for (subpicture_t * spic = subpicture; spic != NULL; spic = spic->p_next) + { @@ -14772,11 +14928,10 @@ + if (++n == SUBS_MAX) + return VLC_SUCCESS; + } -+ } + } + return VLC_SUCCESS; - } - --static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) ++} ++ + +static void vd_prepare(vout_display_t *vd, picture_t *p_pic, +#if VLC_VER_3 @@ -14785,8 +14940,7 @@ + subpicture_t *subpicture, vlc_tick_t date +#endif + ) - { -- vout_display_t *vd = (vout_display_t *)port->userdata; ++{ + MMAL_STATUS_T err; + vout_display_sys_t * const sys = vd->sys; + @@ -14867,34 +15021,49 @@ + +#if 0 + VLC_UNUSED(date); - vout_display_sys_t *sys = vd->sys; -- picture_t *picture = (picture_t *)buffer->user_data; ++ vout_display_sys_t *sys = vd->sys; + picture_sys_t *pic_sys = picture->p_sys; - -- if (picture) -- picture_Release(picture); ++ + if (!sys->adjust_refresh_rate || pic_sys->displayed) + return; -- vlc_mutex_lock(&sys->buffer_mutex); -- atomic_fetch_sub(&sys->buffers_in_transit, 1); -- vlc_cond_signal(&sys->buffer_cond); -- vlc_mutex_unlock(&sys->buffer_mutex); +- vlc_mutex_unlock(&sys->manage_mutex); + /* Apply the required phase_offset to the picture, so that vd_display() + * will be called at the corrected time from the core */ + picture->date += sys->phase_offset; +#endif } --static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height) +-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) + +static void vd_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) { + vout_display_t *vd = (vout_display_t *)port->userdata; + MMAL_STATUS_T status; +@@ -689,45 +1175,6 @@ static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) + mmal_buffer_header_release(buffer); + } + +-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +-{ +- vout_display_t *vd = (vout_display_t *)port->userdata; +- vout_display_sys_t *sys = vd->sys; +- picture_t *picture = (picture_t *)buffer->user_data; +- +- if (picture) +- picture_Release(picture); +- +- vlc_mutex_lock(&sys->buffer_mutex); +- atomic_fetch_sub(&sys->buffers_in_transit, 1); +- vlc_cond_signal(&sys->buffer_cond); +- vlc_mutex_unlock(&sys->buffer_mutex); +-} +- +-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height) +-{ - TV_DISPLAY_STATE_T display_state; - int ret = 0; -+ vout_display_t *vd = (vout_display_t *)port->userdata; -+ MMAL_STATUS_T status; - +- - if (vc_tv_get_display_state(&display_state) == 0) { - if (display_state.state & 0xFF) { - *width = display_state.display.hdmi.width; @@ -14909,17 +15078,15 @@ - } else { - msg_Warn(vd, "Failed to query display resolution"); - ret = -1; -+ if (buffer->cmd == MMAL_EVENT_ERROR) { -+ status = *(uint32_t *)buffer->data; -+ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); - } - +- } +- - return ret; -+ mmal_buffer_header_release(buffer); - } - +-} +- static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2) -@@ -777,12 +1224,12 @@ static void adjust_refresh_rate(vout_dis + { + VLC_UNUSED(reason); +@@ -777,12 +1224,12 @@ static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt) int num_modes; double frame_rate = (double)fmt->i_frame_rate / fmt->i_frame_rate_base; int best_id = -1; @@ -14935,7 +15102,7 @@ supported_modes, VC_TV_MAX_MODE_IDS, NULL, NULL); for (i = 0; i < num_modes; ++i) { -@@ -810,7 +1257,7 @@ static void adjust_refresh_rate(vout_dis +@@ -810,7 +1257,7 @@ static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt) if((best_id >= 0) && (display_state.display.hdmi.mode != supported_modes[best_id].code)) { msg_Info(vd, "Setting HDMI refresh rate to %"PRIu32, supported_modes[best_id].frame_rate); @@ -14944,7 +15111,7 @@ supported_modes[best_id].group, supported_modes[best_id].code); } -@@ -828,148 +1275,12 @@ static void adjust_refresh_rate(vout_dis +@@ -828,148 +1275,12 @@ static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt) } } @@ -15094,7 +15261,7 @@ ((double)vd->sys->i_frame_rate / vd->sys->i_frame_rate_base); vout_display_sys_t *sys = vd->sys; -@@ -1012,32 +1323,441 @@ static void maintain_phase_sync(vout_dis +@@ -1012,32 +1323,441 @@ static void maintain_phase_sync(vout_display_t *vd) } } @@ -15284,7 +15451,7 @@ + msg_Info(vd, "Unexpected X rotation value: %#x", cur_rot); + trans = MMAL_DISPLAY_ROT0; + break; - } ++ } + + return trans; +} @@ -15321,7 +15488,7 @@ + +fail: + return (MMAL_RECT_T){0,0,0,0}; - } ++} + +static int OpenMmalVout(vlc_object_t *object) +{ @@ -15503,7 +15670,7 @@ + msg_Dbg(vd, "Failed to enable subpic component %d", i); + goto fail; + } -+ } + } + + // If we can't deal with it directly ask for I420 + vd->fmt.i_chroma = req_chroma(vd); @@ -15531,7 +15698,7 @@ + msg_Dbg(vd, ">>> %s: rv=%d", __func__, ret); + + return ret == VLC_SUCCESS ? VLC_EGENERIC : ret; -+} + } + +vlc_module_begin() + @@ -15562,6 +15729,9 @@ +vlc_module_end() + + +diff --git a/modules/hw/mmal/xsplitter.c b/modules/hw/mmal/xsplitter.c +new file mode 100644 +index 0000000000..65fcc6b41e --- /dev/null +++ b/modules/hw/mmal/xsplitter.c @@ -0,0 +1,662 @@ @@ -16227,9 +16397,27 @@ + set_callbacks(OpenMmalX11, CloseMmalX11) +vlc_module_end() + +diff --git a/modules/video_chroma/chain.c b/modules/video_chroma/chain.c +index 2709425255..5ac384e633 100644 +--- a/modules/video_chroma/chain.c ++++ b/modules/video_chroma/chain.c +@@ -280,8 +280,9 @@ static int BuildTransformChain( filter_t *p_filter ) + return VLC_SUCCESS; + + /* Lets try resize+chroma first, then transform */ +- msg_Dbg( p_filter, "Trying to build chroma+resize" ); +- EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in ); ++ msg_Dbg( p_filter, "Trying to build chroma+resize, then transform" ); ++ es_format_Copy( &fmt_mid, &p_filter->fmt_out ); ++ video_format_TransformTo(&fmt_mid.video, p_filter->fmt_in.video.orientation); + i_ret = CreateChain( p_filter, &fmt_mid ); + es_format_Clean( &fmt_mid ); + if( i_ret == VLC_SUCCESS ) +diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am +index ae48c8e062..270f1a63b8 100644 --- a/modules/video_output/Makefile.am +++ b/modules/video_output/Makefile.am -@@ -182,6 +182,28 @@ vout_LTLIBRARIES += libglx_plugin.la +@@ -187,6 +187,29 @@ vout_LTLIBRARIES += libglx_plugin.la endif endif @@ -16237,6 +16425,7 @@ + +libdrm_vout_plugin_la_SOURCES = video_output/drmu/drm_vout.c \ + video_output/drmu/drmu_vlc.c video_output/drmu/drmu_vlc.h \ ++ video_output/drmu/drmu_vlc_fmts.c video_output/drmu/drmu_vlc_fmts.h \ + video_output/drmu/drmu_fmts.c video_output/drmu/drmu_fmts.h \ + video_output/drmu/drmu_chroma.h \ + video_output/drmu/drmu_log.h video_output/drmu/drmu.c \ @@ -16258,9 +16447,78 @@ ### Wayland ### libwl_shm_plugin_la_SOURCES = video_output/wayland/shm.c +@@ -205,7 +228,38 @@ video_output/wayland/viewporter-client-protocol.h: \ + + video_output/wayland/viewporter-protocol.c: \ + $(WAYLAND_PROTOCOLS)/stable/viewporter/viewporter.xml +- $(AM_V_GEN)$(WAYLAND_SCANNER) code $< $@ ++ $(AM_V_GEN)$(WAYLAND_SCANNER) private-code $< $@ ++ ++video_output/wayland/linux-dmabuf-unstable-v1-client-protocol.h: \ ++ $(WAYLAND_PROTOCOLS)/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml ++ $(AM_V_GEN)$(WAYLAND_SCANNER) client-header $< $@ ++ ++video_output/wayland/linux-dmabuf-unstable-v1-protocol.c: \ ++ $(WAYLAND_PROTOCOLS)/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml ++ $(AM_V_GEN)$(WAYLAND_SCANNER) private-code $< $@ ++ ++libwl_dmabuf_plugin_la_SOURCES = video_output/wayland/dmabuf.c\ ++ video_output/wayland/picpool.c video_output/wayland/picpool.h\ ++ video_output/wayland/dmabuf_alloc.c video_output/wayland/dmabuf_alloc.h\ ++ video_output/wayland/rgba_premul.c video_output/wayland/rgba_premul.h\ ++ video_output/drmu/pollqueue.c video_output/drmu/pollqueue.h\ ++ video_output/drmu/drmu_vlc_fmts.c video_output/drmu/drmu_vlc_fmts.h ++nodist_libwl_dmabuf_plugin_la_SOURCES = \ ++ video_output/wayland/viewporter-client-protocol.h \ ++ video_output/wayland/viewporter-protocol.c \ ++ video_output/wayland/linux-dmabuf-unstable-v1-client-protocol.h \ ++ video_output/wayland/linux-dmabuf-unstable-v1-protocol.c ++libwl_dmabuf_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) \ ++ -I$(builddir)/video_output/wayland -pthread ++libwl_dmabuf_plugin_la_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) -pthread ++libwl_dmabuf_plugin_la_LIBADD = $(WAYLAND_CLIENT_LIBS) ++libwl_dmabuf_plugin_la_LDFLAGS = $(AM_LDFLAGS) -pthread ++if HAVE_ARM64 ++libwl_dmabuf_plugin_la_SOURCES += video_output/wayland/rgba_premul_aarch64.S\ ++ video_output/wayland/rgba_premul_aarch64.h ++libwl_dmabuf_plugin_la_CFLAGS += -DHAVE_AARCH64_ASM=1 ++endif ++CLEANFILES += $(nodist_libwl_dmabuf_plugin_la_SOURCES) + + libwl_shell_plugin_la_SOURCES = video_output/wayland/shell.c + libwl_shell_plugin_la_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) +@@ -244,6 +298,8 @@ libegl_wl_plugin_la_LIBADD = $(EGL_LIBS) $(WAYLAND_EGL_LIBS) + if HAVE_WAYLAND + BUILT_SOURCES += $(nodist_libwl_shm_plugin_la_SOURCES) + vout_LTLIBRARIES += libwl_shm_plugin.la ++BUILT_SOURCES += $(nodist_libwl_dmabuf_plugin_la_SOURCES) ++vout_LTLIBRARIES += libwl_dmabuf_plugin.la + vout_LTLIBRARIES += libwl_shell_plugin.la + BUILT_SOURCES += $(nodist_libxdg_shell_plugin_la_SOURCES) + vout_LTLIBRARIES += libxdg_shell_plugin.la +diff --git a/modules/video_output/caca.c b/modules/video_output/caca.c +index 3440401082..9297823267 100644 +--- a/modules/video_output/caca.c ++++ b/modules/video_output/caca.c +@@ -160,7 +160,11 @@ static int Open(vlc_object_t *object) + } + + const char *driver = NULL; +-#ifdef __APPLE__ ++// RPI: If driver is NULL then if we have X but DISPLAY is unset then somehow ++// the GL module becomes unloaded without anything noticing and that then ++// causes a segfault. ++//#ifdef __APPLE__ ++#if 1 + // Make sure we don't try to open a window. + driver = "ncurses"; + #endif +diff --git a/modules/video_output/drmu/drm_vout.c b/modules/video_output/drmu/drm_vout.c +new file mode 100644 +index 0000000000..728cc0744a --- /dev/null +++ b/modules/video_output/drmu/drm_vout.c -@@ -0,0 +1,1040 @@ +@@ -0,0 +1,1060 @@ +/***************************************************************************** + * drm_vout.c: DRM based output device + ***************************************************************************** @@ -16324,7 +16582,6 @@ + +#define DRM_VOUT_MODE_NAME "drm-vout-mode" +#define DRM_VOUT_MODE_TEXT N_("Set this mode for display") -+ +#define DRM_VOUT_MODE_LONGTEXT N_("arg: x@ Force mode to arg") + +#define DRM_VOUT_NO_MODESET_NAME "drm-vout-no-modeset" @@ -16345,7 +16602,7 @@ +#define DRM_VOUT_DISPLAY_NAME "drm-vout-display" +#define DRM_VOUT_DISPLAY_TEXT N_("Output device for Rpi fullscreen.") +#define DRM_VOUT_DISPLAY_LONGTEXT N_("Output device for Rpi fullscreen. " \ -+"Valid values are HDMI-1,HDMI-2. By default if qt-fullscreen-screennumber " \ ++"Valid values are HDMI-1,HDMI-2 or a drm connector name. By default if qt-fullscreen-screennumber " \ +"is specified (or set by Fullscreen Output Device in Preferences) " \ +"HDMI- will be used, otherwise HDMI-1.") + @@ -16843,7 +17100,6 @@ + subpicture_t *subpicture) +{ + vout_display_sys_t *const sys = vd->sys; -+ VLC_UNUSED(subpicture); + +#if TRACE_ALL + msg_Dbg(vd, "<<< %s", __func__); @@ -16851,6 +17107,8 @@ + + drmu_atomic_queue(&sys->display_set); + ++ if (subpicture) ++ subpicture_Delete(subpicture); + picture_Release(p_pic); + return; +} @@ -16989,7 +17247,8 @@ + + for (i = 0; i != SUBPICS_MAX; ++i) + drmu_plane_unref(sys->subplanes + i); -+ subpic_cache_flush(sys); ++ ++ kill_pool(sys); + + drmu_plane_unref(&sys->dp); + drmu_output_unref(&sys->dout); @@ -17124,9 +17383,15 @@ + .v = vd, + .max_level = DRMU_LOG_LEVEL_ALL + }; -+ if ((sys->du = drmu_env_new_xlease(&log)) == NULL && -+ (sys->du = drmu_env_new_open(var_InheritString(vd, DRM_VOUT_MODULE_NAME), &log)) == NULL) -+ goto fail; ++ ++ sys->du = drmu_env_new_xlease(&log); ++ if (sys->du == NULL) { ++ char * module_name = var_InheritString(vd, DRM_VOUT_MODULE_NAME); ++ sys->du = drmu_env_new_open(module_name, &log); ++ free(module_name); ++ if (sys->du == NULL) ++ goto fail; ++ } + } + + drmu_env_restore_enable(sys->du); @@ -17139,9 +17404,8 @@ + drmu_output_modeset_allow(sys->dout, !var_InheritBool(vd, DRM_VOUT_NO_MODESET_NAME)); + drmu_output_max_bpc_allow(sys->dout, !var_InheritBool(vd, DRM_VOUT_NO_MAX_BPC)); + -+ + { -+ const char *display_name = var_InheritString(vd, DRM_VOUT_DISPLAY_NAME); ++ char * const display_name = var_InheritString(vd, DRM_VOUT_DISPLAY_NAME); + int qt_num = var_InheritInteger(vd, "qt-fullscreen-screennumber"); + const char * conn_name = qt_num == 0 ? "HDMI-A-1" : qt_num == 1 ? "HDMI-A-2" : NULL; + const char * dname; @@ -17151,15 +17415,21 @@ + conn_name = "HDMI-A-1"; + else if (strcasecmp(display_name, "hdmi-2") == 0) + conn_name = "HDMI-A-2"; ++ else ++ conn_name = display_name; + } + + dname = conn_name != NULL ? conn_name : ""; + -+ if ((rv = drmu_output_add_output(sys->dout, conn_name)) != 0) { ++ if ((rv = drmu_output_add_output(sys->dout, conn_name)) != 0) + msg_Err(vd, "Failed to find output %s: %s", dname, strerror(-rv)); ++ else ++ msg_Dbg(vd, "Using conn %s", dname); ++ ++ free(display_name); ++ ++ if (rv != 0) + goto fail; -+ } -+ msg_Dbg(vd, "Using conn %s", dname); + } + + if ((sys->sub_fb_pool = drmu_pool_new(sys->du, 10)) == NULL) @@ -17201,11 +17471,17 @@ + vd->display = vd_drm_display; + vd->control = vd_drm_control; + -+ const char *modestr = var_InheritString(vd, DRM_VOUT_MODE_NAME); + sys->mode_id = -1; + ++ char * mode_name = NULL; ++ const char * modestr; ++ + if (var_InheritBool(vd, DRM_VOUT_SOURCE_MODESET_NAME)) + modestr = "source"; ++ else { ++ mode_name = var_InheritString(vd, DRM_VOUT_MODE_NAME); ++ modestr = mode_name; ++ } + + if (modestr != NULL && strcmp(modestr, "none") != 0) { + drmu_mode_simple_params_t pick = { @@ -17245,6 +17521,7 @@ + mode->sar.num, mode->sar.den, pick.width, pick.height, pick.hz_x_1000 / 1000, pick.hz_x_1000 % 1000); + } + } ++ free(mode_name); + + set_format(vd, sys, fmtp); + @@ -17252,7 +17529,7 @@ +// drmu_ufrac_vlc_to_rational(drmu_crtc_sar(sys->dc))); + + { -+ const char *window_str = var_InheritString(vd, DRM_VOUT_WINDOW_NAME); ++ char * const window_str = var_InheritString(vd, DRM_VOUT_WINDOW_NAME); + if (strcmp(window_str, "fullscreen") == 0) { + /* Leave req_win null */ + msg_Dbg(vd, "Window: fullscreen"); @@ -17266,6 +17543,7 @@ + else + msg_Warn(vd, "Window: '%s': cannot parse (usage: x++) - using fullscreen", window_str); + } ++ free(window_str); + } + + if (src_chroma != vd->fmt.i_chroma) @@ -17301,9 +17579,12 @@ + set_callbacks(OpenDrmVout, CloseDrmVout) +vlc_module_end() + +diff --git a/modules/video_output/drmu/drmu.c b/modules/video_output/drmu/drmu.c +new file mode 100644 +index 0000000000..e090feec5b --- /dev/null +++ b/modules/video_output/drmu/drmu.c -@@ -0,0 +1,3878 @@ +@@ -0,0 +1,3924 @@ +// Needed to ensure we get a 64-bit offset to mmap when mapping BOs +#undef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 @@ -17335,6 +17616,10 @@ + +#define TRACE_PROP_NEW 0 + ++#ifndef OPT_IO_CALLOC ++#define OPT_IO_CALLOC 0 ++#endif ++ +#ifndef DRM_FORMAT_P030 +#define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0') +#endif @@ -17352,6 +17637,18 @@ + return rv2 ? rv2 : rv1; +} + ++// Use io_alloc when allocating arrays to pass into ioctls. ++// ++// When debugging with valgrind use calloc rather than malloc otherwise arrays ++// set by ioctls that valgrind doesn't know about (e.g. all drm ioctls) will ++// still be full of 'undefined'. ++// For normal use malloc should be fine ++#if OPT_IO_CALLOC ++#define io_alloc(p, n) (uintptr_t)((p) = calloc((n), sizeof(*(p)))) ++#else ++#define io_alloc(p, n) (uintptr_t)((p) = malloc((n) * sizeof(*(p)))) ++#endif ++ +// Alloc retry helper +static inline int +retry_alloc_u32(uint32_t ** const pp, uint32_t * const palloc_count, uint32_t const new_count) @@ -17360,7 +17657,7 @@ + return 0; + free(*pp); + *palloc_count = 0; -+ if ((*pp = malloc(sizeof(**pp) * new_count)) == NULL) ++ if (io_alloc(*pp, new_count) == 0) + return -ENOMEM; + *palloc_count = new_count; + return 1; @@ -17561,7 +17858,7 @@ +static int +blob_data_read(drmu_env_t * const du, uint32_t blob_id, void ** const ppdata, size_t * plen) +{ -+ void * data; ++ uint8_t * data; + struct drm_mode_get_blob gblob = {.blob_id = blob_id}; + int rv; + @@ -17577,10 +17874,9 @@ + if (gblob.length == 0) + return 0; + -+ if ((data = malloc(gblob.length)) == NULL) ++ if ((gblob.data = io_alloc(data, gblob.length)) == 0) + return -ENOMEM; + -+ gblob.data = (uintptr_t)data; + if ((rv = drmu_ioctl(du, DRM_IOCTL_MODE_GETPROPBLOB, &gblob)) != 0) { + free(data); + return rv; @@ -17764,7 +18060,7 @@ + free(enums); + + pen->n = prop.count_enum_blobs; -+ if ((enums = malloc(pen->n * sizeof(*enums))) == NULL) ++ if (io_alloc(enums, pen->n) == 0) + goto fail; + } + if (retries >= 8) { @@ -19040,14 +19336,12 @@ + free(propids); + propids = NULL; + n = obj_props.count_props; -+ if ((values = malloc(n * sizeof(*values))) == NULL || -+ (propids = malloc(n * sizeof(*propids))) == NULL) { ++ if ((obj_props.prop_values_ptr = io_alloc(values, n)) == 0 || ++ (obj_props.props_ptr = io_alloc(propids, n)) == 0) { + drmu_err(du, "obj/value array alloc failed"); + rv = -ENOMEM; + goto fail; + } -+ obj_props.prop_values_ptr = (uintptr_t)values; -+ obj_props.props_ptr = (uintptr_t)propids; + } + + *ppValues = values; @@ -19219,16 +19513,17 @@ +} drmu_crtc_t; + +static void -+free_crtc(drmu_crtc_t * const dc) ++crtc_uninit(drmu_crtc_t * const dc) +{ ++ drmu_prop_range_delete(&dc->pid.active); + drmu_blob_unref(&dc->mode_id_blob); -+ free(dc); +} + +static void -+crtc_uninit(drmu_crtc_t * const dc) ++crtc_free(drmu_crtc_t * const dc) +{ -+ (void)dc; ++ crtc_uninit(dc); ++ free(dc); +} + + @@ -19301,7 +19596,7 @@ + return; + *ppdc = NULL; + -+ free_crtc(dc); ++ crtc_free(dc); +} + +drmu_env_t * @@ -19720,7 +20015,7 @@ + + if (modes_req > dn->modes_size) { + free(dn->modes); -+ if ((dn->modes = malloc(modes_req * sizeof(*dn->modes))) == NULL) { ++ if (io_alloc(dn->modes, modes_req) == 0) { + drmu_err(du, "Failed to alloc modes array"); + goto fail; + } @@ -19731,7 +20026,7 @@ + + if (encs_req > dn->enc_ids_size) { + free(dn->enc_ids); -+ if ((dn->enc_ids = malloc(encs_req * sizeof(*dn->enc_ids))) == NULL) { ++ if (io_alloc(dn->enc_ids, encs_req) == 0) { + drmu_err(du, "Failed to alloc encs array"); + goto fail; + } @@ -19924,18 +20219,11 @@ +// Called after an atomic commit has completed +// not called on every vsync, so if we haven't committed anything this won't be called +static void -+drmu_atomic_page_flip_cb(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, unsigned int crtc_id, void *user_data) ++drmu_atomic_page_flip_cb(drmu_env_t * const du, void *user_data) +{ + drmu_atomic_t * const da = user_data; -+ drmu_env_t * const du = drmu_atomic_env(da); + drmu_atomic_q_t * const aq = env_atomic_q(du); + -+ (void)fd; -+ (void)sequence; -+ (void)tv_sec; -+ (void)tv_usec; -+ (void)crtc_id; -+ + // At this point: + // next The atomic we are about to commit + // cur The last atomic we committed, now in use (must be != NULL) @@ -20081,7 +20369,7 @@ + struct drmu_env_s * du; + + pthread_mutex_t lock; -+ int dead; ++ bool dead; + + unsigned int seq; // debug + @@ -20278,8 +20566,10 @@ + return; + *pppool = NULL; + -+ pool->dead = 1; ++ pool->dead = true; ++ pthread_mutex_lock(&pool->lock); + pool_free_pool(pool); ++ pthread_mutex_unlock(&pool->lock); + + drmu_pool_unref(&pool); +} @@ -20566,6 +20856,7 @@ + drmu_prop_enum_delete(&dp->pid.color_range); + drmu_prop_enum_delete(&dp->pid.pixel_blend_mode); + drmu_prop_enum_delete(&dp->pid.rotation); ++ drmu_prop_range_delete(&dp->pid.zpos); + free(dp->formats_in); + dp->formats_in = NULL; +} @@ -20992,20 +21283,56 @@ + return drmu_atomic_merge(du->da_restore, &da); +} + ++#define EVT(p) ((const struct drm_event *)(p)) ++static int ++evt_read(drmu_env_t * const du) ++{ ++ uint8_t buf[128]; ++ const ssize_t rlen = read(drmu_fd(du), buf, sizeof(buf)); ++ size_t i; ++ ++ if (rlen < 0) { ++ const int err = errno; ++ drmu_err(du, "Event read failure: %s", strerror(err)); ++ return -err; ++ } ++ ++ for (i = 0; ++ i + sizeof(struct drm_event) <= (size_t)rlen && EVT(buf + i)->length <= (size_t)rlen - i; ++ i += EVT(buf + i)->length) { ++ switch (EVT(buf + i)->type) { ++ case DRM_EVENT_FLIP_COMPLETE: ++ { ++ const struct drm_event_vblank * const vb = (struct drm_event_vblank*)(buf + i); ++ if (EVT(buf + i)->length < sizeof(*vb)) ++ break; ++ ++ drmu_atomic_page_flip_cb(du, (void *)(uintptr_t)vb->user_data); ++ break; ++ } ++ default: ++ drmu_warn(du, "Unexpected DRM event #%x", EVT(buf + i)->type); ++ break; ++ } ++ } ++ ++ if (i != (size_t)rlen) ++ drmu_warn(du, "Partial event received: len=%zd, processed=%zd", rlen, i); ++ ++ return 0; ++} ++#undef EVT ++ +static void +drmu_env_polltask_cb(void * v, short revents) +{ + drmu_env_t * const du = v; -+ drmEventContext ctx = { -+ .version = DRM_EVENT_CONTEXT_VERSION, -+ .page_flip_handler2 = drmu_atomic_page_flip_cb, -+ }; + + if (revents == 0) { + drmu_debug(du, "%s: Timeout", __func__); + } + else { -+ drmHandleEvent(du->fd, &ctx); ++ evt_read(du); + } + + pollqueue_add_task(du->pt, 1000); @@ -21182,6 +21509,9 @@ +} + + +diff --git a/modules/video_output/drmu/drmu.h b/modules/video_output/drmu/drmu.h +new file mode 100644 +index 0000000000..42782594d9 --- /dev/null +++ b/modules/video_output/drmu/drmu.h @@ -0,0 +1,591 @@ @@ -21776,6 +22106,9 @@ + +#endif + +diff --git a/modules/video_output/drmu/drmu_atomic.c b/modules/video_output/drmu/drmu_atomic.c +new file mode 100644 +index 0000000000..fcf48f2d1f --- /dev/null +++ b/modules/video_output/drmu/drmu_atomic.c @@ -0,0 +1,831 @@ @@ -22610,6 +22943,9 @@ +} + + +diff --git a/modules/video_output/drmu/drmu_chroma.h b/modules/video_output/drmu/drmu_chroma.h +new file mode 100644 +index 0000000000..3e3f7e9735 --- /dev/null +++ b/modules/video_output/drmu/drmu_chroma.h @@ -0,0 +1,47 @@ @@ -22660,6 +22996,9 @@ + +#endif + +diff --git a/modules/video_output/drmu/drmu_fmts.c b/modules/video_output/drmu/drmu_fmts.c +new file mode 100644 +index 0000000000..c5769a2ad5 --- /dev/null +++ b/modules/video_output/drmu/drmu_fmts.c @@ -0,0 +1,249 @@ @@ -22912,6 +23251,9 @@ + +#endif + +diff --git a/modules/video_output/drmu/drmu_fmts.h b/modules/video_output/drmu/drmu_fmts.h +new file mode 100644 +index 0000000000..a2400357e4 --- /dev/null +++ b/modules/video_output/drmu/drmu_fmts.h @@ -0,0 +1,31 @@ @@ -22946,6 +23288,9 @@ + +#endif + +diff --git a/modules/video_output/drmu/drmu_log.h b/modules/video_output/drmu/drmu_log.h +new file mode 100644 +index 0000000000..00fa9c3d12 --- /dev/null +++ b/modules/video_output/drmu/drmu_log.h @@ -0,0 +1,53 @@ @@ -23002,9 +23347,12 @@ + +#endif + +diff --git a/modules/video_output/drmu/drmu_output.c b/modules/video_output/drmu/drmu_output.c +new file mode 100644 +index 0000000000..b4709aeb1e --- /dev/null +++ b/modules/video_output/drmu/drmu_output.c -@@ -0,0 +1,615 @@ +@@ -0,0 +1,616 @@ +#include "drmu_output.h" + +#include "drmu_fmts.h" @@ -23590,6 +23938,7 @@ + unsigned int i; + for (i = 0; i != dout->conn_n; ++i) + drmu_conn_unref(dout->dns + i); ++ free(dout->dns); + drmu_crtc_unref(&dout->dc); + free(dout); +} @@ -23620,6 +23969,9 @@ + return dout; +} + +diff --git a/modules/video_output/drmu/drmu_output.h b/modules/video_output/drmu/drmu_output.h +new file mode 100644 +index 0000000000..c977b24721 --- /dev/null +++ b/modules/video_output/drmu/drmu_output.h @@ -0,0 +1,81 @@ @@ -23704,6 +24056,9 @@ +#endif + + +diff --git a/modules/video_output/drmu/drmu_util.c b/modules/video_output/drmu/drmu_util.c +new file mode 100644 +index 0000000000..12a088216d --- /dev/null +++ b/modules/video_output/drmu/drmu_util.c @@ -0,0 +1,125 @@ @@ -23832,6 +24187,9 @@ +} + + +diff --git a/modules/video_output/drmu/drmu_util.h b/modules/video_output/drmu/drmu_util.h +new file mode 100644 +index 0000000000..9c4ea46e4c --- /dev/null +++ b/modules/video_output/drmu/drmu_util.h @@ -0,0 +1,30 @@ @@ -23865,9 +24223,12 @@ + +#endif + +diff --git a/modules/video_output/drmu/drmu_vlc.c b/modules/video_output/drmu/drmu_vlc.c +new file mode 100644 +index 0000000000..f2efd30c21 --- /dev/null +++ b/modules/video_output/drmu/drmu_vlc.c -@@ -0,0 +1,578 @@ +@@ -0,0 +1,351 @@ +#include "drmu_vlc.h" +#include "drmu_fmts.h" +#include "drmu_log.h" @@ -23884,235 +24245,8 @@ +#include +#include + -+#include -+#include +#include + -+#ifndef DRM_FORMAT_P030 -+#define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0') -+#endif -+ -+// N.B. DRM seems to order its format descriptor names the opposite way round to VLC -+// DRM is hi->lo within a little-endian word, VLC is byte order -+ -+#if HAS_ZC_CMA -+uint32_t -+drmu_format_vlc_to_drm_cma(const vlc_fourcc_t chroma_in) -+{ -+ switch (chroma_in) { -+ case VLC_CODEC_MMAL_ZC_I420: -+ return DRM_FORMAT_YUV420; -+ case VLC_CODEC_MMAL_ZC_SAND8: -+ return DRM_FORMAT_NV12; -+ case VLC_CODEC_MMAL_ZC_SAND30: -+ return DRM_FORMAT_P030; -+ case VLC_CODEC_MMAL_ZC_RGB32: -+ return DRM_FORMAT_RGBX8888; -+ } -+ return 0; -+} -+#endif -+ -+#if HAS_DRMPRIME -+uint32_t -+drmu_format_vlc_to_drm_prime(const vlc_fourcc_t chroma_in, uint64_t * const pmod) -+{ -+ uint32_t fmt = 0; -+ uint64_t mod = DRM_FORMAT_MOD_LINEAR; -+ -+ switch (chroma_in) { -+ case VLC_CODEC_DRM_PRIME_I420: -+ fmt = DRM_FORMAT_YUV420; -+ break; -+ case VLC_CODEC_DRM_PRIME_NV12: -+ fmt = DRM_FORMAT_NV12; -+ break; -+ case VLC_CODEC_DRM_PRIME_SAND8: -+ fmt = DRM_FORMAT_NV12; -+ mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0); -+ break; -+ case VLC_CODEC_DRM_PRIME_SAND30: -+ fmt = DRM_FORMAT_P030; -+ mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0); -+ break; -+ } -+ if (pmod) -+ *pmod = !fmt ? DRM_FORMAT_MOD_INVALID : mod; -+ return fmt; -+} -+#endif -+ -+// Convert chroma to drm - can't cope with RGB32 or RGB16 as they require -+// more info -+uint32_t -+drmu_format_vlc_chroma_to_drm(const vlc_fourcc_t chroma) -+{ -+ switch (chroma) { -+ case VLC_CODEC_RGBA: -+ return DRM_FORMAT_ABGR8888; -+ case VLC_CODEC_BGRA: -+ return DRM_FORMAT_ARGB8888; -+ case VLC_CODEC_ARGB: -+ return DRM_FORMAT_BGRA8888; -+ // VLC_CODEC_ABGR does not exist in VLC -+ case VLC_CODEC_VUYA: -+ return DRM_FORMAT_AYUV; -+ // AYUV appears to be the only DRM YUVA-like format -+ case VLC_CODEC_VYUY: -+ return DRM_FORMAT_YUYV; -+ case VLC_CODEC_UYVY: -+ return DRM_FORMAT_YVYU; -+ case VLC_CODEC_YUYV: -+ return DRM_FORMAT_VYUY; -+ case VLC_CODEC_YVYU: -+ return DRM_FORMAT_UYVY; -+ case VLC_CODEC_NV12: -+ return DRM_FORMAT_NV12; -+ case VLC_CODEC_NV21: -+ return DRM_FORMAT_NV21; -+ case VLC_CODEC_NV16: -+ return DRM_FORMAT_NV16; -+ case VLC_CODEC_NV61: -+ return DRM_FORMAT_NV61; -+ case VLC_CODEC_NV24: -+ return DRM_FORMAT_NV24; -+ case VLC_CODEC_NV42: -+ return DRM_FORMAT_NV42; -+ case VLC_CODEC_P010: -+ return DRM_FORMAT_P010; -+ case VLC_CODEC_J420: -+ case VLC_CODEC_I420: -+ return DRM_FORMAT_YUV420; -+ case VLC_CODEC_YV12: -+ return DRM_FORMAT_YVU420; -+ case VLC_CODEC_J422: -+ case VLC_CODEC_I422: -+ return DRM_FORMAT_YUV422; -+ case VLC_CODEC_J444: -+ case VLC_CODEC_I444: -+ return DRM_FORMAT_YUV444; -+ default: -+ break; -+ } -+ -+#if HAS_ZC_CMA -+ return drmu_format_vlc_to_drm_cma(chroma); -+#else -+ return 0; -+#endif -+} -+ -+uint32_t -+drmu_format_vlc_to_drm(const video_frame_format_t * const vf_vlc) -+{ -+ switch (vf_vlc->i_chroma) { -+ case VLC_CODEC_RGB32: -+ { -+ // VLC RGB32 aka RV32 means we have to look at the mask values -+ const uint32_t r = vf_vlc->i_rmask; -+ const uint32_t g = vf_vlc->i_gmask; -+ const uint32_t b = vf_vlc->i_bmask; -+ if (r == 0xff0000 && g == 0xff00 && b == 0xff) -+ return DRM_FORMAT_XRGB8888; -+ if (r == 0xff && g == 0xff00 && b == 0xff0000) -+ return DRM_FORMAT_XBGR8888; -+ if (r == 0xff000000 && g == 0xff0000 && b == 0xff00) -+ return DRM_FORMAT_RGBX8888; -+ if (r == 0xff00 && g == 0xff0000 && b == 0xff000000) -+ return DRM_FORMAT_BGRX8888; -+ break; -+ } -+ case VLC_CODEC_RGB24: -+ { -+ // VLC RGB24 aka RV24 means we have to look at the mask values -+ const uint32_t r = vf_vlc->i_rmask; -+ const uint32_t g = vf_vlc->i_gmask; -+ const uint32_t b = vf_vlc->i_bmask; -+ if (r == 0xff0000 && g == 0xff00 && b == 0xff) -+ return DRM_FORMAT_RGB888; -+ if (r == 0xff && g == 0xff00 && b == 0xff0000) -+ return DRM_FORMAT_BGR888; -+ 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 DRM_FORMAT_RGB565; -+ if (r == 0x1f && g == 0x7e0 && b == 0xf800) -+ return DRM_FORMAT_BGR565; -+ break; -+ } -+ default: -+ break; -+ } -+ -+ return drmu_format_vlc_chroma_to_drm(vf_vlc->i_chroma); -+} -+ -+vlc_fourcc_t -+drmu_format_vlc_to_vlc(const uint32_t vf_drm) -+{ -+ switch (vf_drm) { -+ case DRM_FORMAT_XRGB8888: -+ case DRM_FORMAT_XBGR8888: -+ case DRM_FORMAT_RGBX8888: -+ case DRM_FORMAT_BGRX8888: -+ return VLC_CODEC_RGB32; -+ case DRM_FORMAT_RGB888: -+ case DRM_FORMAT_BGR888: -+ return VLC_CODEC_RGB24; -+ case DRM_FORMAT_BGR565: -+ case DRM_FORMAT_RGB565: -+ return VLC_CODEC_RGB16; -+ case DRM_FORMAT_ABGR8888: -+ return VLC_CODEC_RGBA; -+ case DRM_FORMAT_ARGB8888: -+ return VLC_CODEC_BGRA; -+ case DRM_FORMAT_BGRA8888: -+ return VLC_CODEC_ARGB; -+ // VLC_CODEC_ABGR does not exist in VLC -+ case DRM_FORMAT_AYUV: -+ return VLC_CODEC_VUYA; -+ case DRM_FORMAT_YUYV: -+ return VLC_CODEC_VYUY; -+ case DRM_FORMAT_YVYU: -+ return VLC_CODEC_UYVY; -+ case DRM_FORMAT_VYUY: -+ return VLC_CODEC_YUYV; -+ case DRM_FORMAT_UYVY: -+ return VLC_CODEC_YVYU; -+ case DRM_FORMAT_NV12: -+ return VLC_CODEC_NV12; -+ case DRM_FORMAT_NV21: -+ return VLC_CODEC_NV21; -+ case DRM_FORMAT_NV16: -+ return VLC_CODEC_NV16; -+ case DRM_FORMAT_NV61: -+ return VLC_CODEC_NV61; -+ case DRM_FORMAT_NV24: -+ return VLC_CODEC_NV24; -+ case DRM_FORMAT_NV42: -+ return VLC_CODEC_NV42; -+ case DRM_FORMAT_P010: -+ return VLC_CODEC_P010; -+ case DRM_FORMAT_YUV420: -+ return VLC_CODEC_I420; -+ case DRM_FORMAT_YVU420: -+ return VLC_CODEC_YV12; -+ case DRM_FORMAT_YUV422: -+ return VLC_CODEC_I422; -+ case DRM_FORMAT_YUV444: -+ return VLC_CODEC_I444; -+ default: -+ break; -+ } -+ return 0; -+} -+ +typedef struct fb_aux_pic_s { + picture_context_t * pic_ctx; +} fb_aux_pic_t; @@ -24446,9 +24580,12 @@ + function_name, fmt + DRMU_LOG_FMT_OFFSET_FMT, vl); +} + +diff --git a/modules/video_output/drmu/drmu_vlc.h b/modules/video_output/drmu/drmu_vlc.h +new file mode 100644 +index 0000000000..5c19141930 --- /dev/null +++ b/modules/video_output/drmu/drmu_vlc.h -@@ -0,0 +1,93 @@ +@@ -0,0 +1,81 @@ +#ifndef _DRMU_DRMU_VLC_H +#define _DRMU_DRMU_VLC_H + @@ -24469,6 +24606,7 @@ +#include + +#include "drmu.h" ++#include "drmu_vlc_fmts.h" + +#ifdef __cplusplus +extern "C" { @@ -24510,23 +24648,10 @@ + return (vlc_rational_t) {.num = x.num, .den = x.den}; +} + -+ -+// Convert chroma to drm - can't cope with RGB32 or RGB16 as they require -+// more info. returns 0 if unknown. -+uint32_t drmu_format_vlc_chroma_to_drm(const vlc_fourcc_t chroma); -+// Convert format to drm fourcc - does cope with RGB32 & RGB16 -+uint32_t drmu_format_vlc_to_drm(const video_frame_format_t * const vf_vlc); -+vlc_fourcc_t drmu_format_vlc_to_vlc(const uint32_t vf_drm); -+ +drmu_fb_t * drmu_fb_vlc_new_pic_attach(drmu_env_t * const du, picture_t * const pic); +plane_t drmu_fb_vlc_plane(drmu_fb_t * const dfb, const unsigned int plane_n); + -+#if HAS_DRMPRIME -+// pmod may be NULL -+uint32_t drmu_format_vlc_to_drm_prime(const vlc_fourcc_t chroma_in, uint64_t * const pmod); -+#endif +#if HAS_ZC_CMA -+uint32_t drmu_format_vlc_to_drm_cma(const vlc_fourcc_t chroma_in); +drmu_fb_t * drmu_fb_vlc_new_pic_cma_attach(drmu_env_t * const du, picture_t * const pic); +#endif + @@ -24542,6 +24667,288 @@ +#endif +#endif + +diff --git a/modules/video_output/drmu/drmu_vlc_fmts.c b/modules/video_output/drmu/drmu_vlc_fmts.c +new file mode 100644 +index 0000000000..335a78e5ac +--- /dev/null ++++ b/modules/video_output/drmu/drmu_vlc_fmts.c +@@ -0,0 +1,229 @@ ++#include "drmu_vlc_fmts.h" ++ ++#include ++ ++#ifndef DRM_FORMAT_P030 ++#define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0') ++#endif ++ ++// N.B. DRM seems to order its format descriptor names the opposite way round to VLC ++// DRM is hi->lo within a little-endian word, VLC is byte order ++ ++#if HAS_ZC_CMA ++uint32_t ++drmu_format_vlc_to_drm_cma(const vlc_fourcc_t chroma_in) ++{ ++ switch (chroma_in) { ++ case VLC_CODEC_MMAL_ZC_I420: ++ return DRM_FORMAT_YUV420; ++ case VLC_CODEC_MMAL_ZC_SAND8: ++ return DRM_FORMAT_NV12; ++ case VLC_CODEC_MMAL_ZC_SAND30: ++ return DRM_FORMAT_P030; ++ case VLC_CODEC_MMAL_ZC_RGB32: ++ return DRM_FORMAT_RGBX8888; ++ } ++ return 0; ++} ++#endif ++ ++#if HAS_DRMPRIME ++uint32_t ++drmu_format_vlc_to_drm_prime(const vlc_fourcc_t chroma_in, uint64_t * const pmod) ++{ ++ uint32_t fmt = 0; ++ uint64_t mod = DRM_FORMAT_MOD_LINEAR; ++ ++ switch (chroma_in) { ++ case VLC_CODEC_DRM_PRIME_I420: ++ fmt = DRM_FORMAT_YUV420; ++ break; ++ case VLC_CODEC_DRM_PRIME_NV12: ++ fmt = DRM_FORMAT_NV12; ++ break; ++ case VLC_CODEC_DRM_PRIME_SAND8: ++ fmt = DRM_FORMAT_NV12; ++ mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0); ++ break; ++ case VLC_CODEC_DRM_PRIME_SAND30: ++ fmt = DRM_FORMAT_P030; ++ mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0); ++ break; ++ } ++ if (pmod) ++ *pmod = !fmt ? DRM_FORMAT_MOD_INVALID : mod; ++ return fmt; ++} ++#endif ++ ++// Convert chroma to drm - can't cope with RGB32 or RGB16 as they require ++// more info ++uint32_t ++drmu_format_vlc_chroma_to_drm(const vlc_fourcc_t chroma) ++{ ++ switch (chroma) { ++ case VLC_CODEC_RGBA: ++ return DRM_FORMAT_ABGR8888; ++ case VLC_CODEC_BGRA: ++ return DRM_FORMAT_ARGB8888; ++ case VLC_CODEC_ARGB: ++ return DRM_FORMAT_BGRA8888; ++ // VLC_CODEC_ABGR does not exist in VLC ++ case VLC_CODEC_VUYA: ++ return DRM_FORMAT_AYUV; ++ // AYUV appears to be the only DRM YUVA-like format ++ case VLC_CODEC_VYUY: ++ return DRM_FORMAT_YUYV; ++ case VLC_CODEC_UYVY: ++ return DRM_FORMAT_YVYU; ++ case VLC_CODEC_YUYV: ++ return DRM_FORMAT_VYUY; ++ case VLC_CODEC_YVYU: ++ return DRM_FORMAT_UYVY; ++ case VLC_CODEC_NV12: ++ return DRM_FORMAT_NV12; ++ case VLC_CODEC_NV21: ++ return DRM_FORMAT_NV21; ++ case VLC_CODEC_NV16: ++ return DRM_FORMAT_NV16; ++ case VLC_CODEC_NV61: ++ return DRM_FORMAT_NV61; ++ case VLC_CODEC_NV24: ++ return DRM_FORMAT_NV24; ++ case VLC_CODEC_NV42: ++ return DRM_FORMAT_NV42; ++ case VLC_CODEC_P010: ++ return DRM_FORMAT_P010; ++ case VLC_CODEC_J420: ++ case VLC_CODEC_I420: ++ return DRM_FORMAT_YUV420; ++ case VLC_CODEC_YV12: ++ return DRM_FORMAT_YVU420; ++ case VLC_CODEC_J422: ++ case VLC_CODEC_I422: ++ return DRM_FORMAT_YUV422; ++ case VLC_CODEC_J444: ++ case VLC_CODEC_I444: ++ return DRM_FORMAT_YUV444; ++ default: ++ break; ++ } ++ ++#if HAS_ZC_CMA ++ return drmu_format_vlc_to_drm_cma(chroma); ++#else ++ return 0; ++#endif ++} ++ ++uint32_t ++drmu_format_vlc_to_drm(const video_frame_format_t * const vf_vlc) ++{ ++ switch (vf_vlc->i_chroma) { ++ case VLC_CODEC_RGB32: ++ { ++ // VLC RGB32 aka RV32 means we have to look at the mask values ++ const uint32_t r = vf_vlc->i_rmask; ++ const uint32_t g = vf_vlc->i_gmask; ++ const uint32_t b = vf_vlc->i_bmask; ++ if (r == 0xff0000 && g == 0xff00 && b == 0xff) ++ return DRM_FORMAT_XRGB8888; ++ if (r == 0xff && g == 0xff00 && b == 0xff0000) ++ return DRM_FORMAT_XBGR8888; ++ if (r == 0xff000000 && g == 0xff0000 && b == 0xff00) ++ return DRM_FORMAT_RGBX8888; ++ if (r == 0xff00 && g == 0xff0000 && b == 0xff000000) ++ return DRM_FORMAT_BGRX8888; ++ break; ++ } ++ case VLC_CODEC_RGB24: ++ { ++ // VLC RGB24 aka RV24 means we have to look at the mask values ++ const uint32_t r = vf_vlc->i_rmask; ++ const uint32_t g = vf_vlc->i_gmask; ++ const uint32_t b = vf_vlc->i_bmask; ++ if (r == 0xff0000 && g == 0xff00 && b == 0xff) ++ return DRM_FORMAT_RGB888; ++ if (r == 0xff && g == 0xff00 && b == 0xff0000) ++ return DRM_FORMAT_BGR888; ++ 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 DRM_FORMAT_RGB565; ++ if (r == 0x1f && g == 0x7e0 && b == 0xf800) ++ return DRM_FORMAT_BGR565; ++ break; ++ } ++ default: ++ break; ++ } ++ ++ return drmu_format_vlc_chroma_to_drm(vf_vlc->i_chroma); ++} ++ ++vlc_fourcc_t ++drmu_format_vlc_to_vlc(const uint32_t vf_drm) ++{ ++ switch (vf_drm) { ++ case DRM_FORMAT_XRGB8888: ++ case DRM_FORMAT_XBGR8888: ++ case DRM_FORMAT_RGBX8888: ++ case DRM_FORMAT_BGRX8888: ++ return VLC_CODEC_RGB32; ++ case DRM_FORMAT_RGB888: ++ case DRM_FORMAT_BGR888: ++ return VLC_CODEC_RGB24; ++ case DRM_FORMAT_BGR565: ++ case DRM_FORMAT_RGB565: ++ return VLC_CODEC_RGB16; ++ case DRM_FORMAT_ABGR8888: ++ return VLC_CODEC_RGBA; ++ case DRM_FORMAT_ARGB8888: ++ return VLC_CODEC_BGRA; ++ case DRM_FORMAT_BGRA8888: ++ return VLC_CODEC_ARGB; ++ // VLC_CODEC_ABGR does not exist in VLC ++ case DRM_FORMAT_AYUV: ++ return VLC_CODEC_VUYA; ++ case DRM_FORMAT_YUYV: ++ return VLC_CODEC_VYUY; ++ case DRM_FORMAT_YVYU: ++ return VLC_CODEC_UYVY; ++ case DRM_FORMAT_VYUY: ++ return VLC_CODEC_YUYV; ++ case DRM_FORMAT_UYVY: ++ return VLC_CODEC_YVYU; ++ case DRM_FORMAT_NV12: ++ return VLC_CODEC_NV12; ++ case DRM_FORMAT_NV21: ++ return VLC_CODEC_NV21; ++ case DRM_FORMAT_NV16: ++ return VLC_CODEC_NV16; ++ case DRM_FORMAT_NV61: ++ return VLC_CODEC_NV61; ++ case DRM_FORMAT_NV24: ++ return VLC_CODEC_NV24; ++ case DRM_FORMAT_NV42: ++ return VLC_CODEC_NV42; ++ case DRM_FORMAT_P010: ++ return VLC_CODEC_P010; ++ case DRM_FORMAT_YUV420: ++ return VLC_CODEC_I420; ++ case DRM_FORMAT_YVU420: ++ return VLC_CODEC_YV12; ++ case DRM_FORMAT_YUV422: ++ return VLC_CODEC_I422; ++ case DRM_FORMAT_YUV444: ++ return VLC_CODEC_I444; ++ default: ++ break; ++ } ++ return 0; ++} ++ +diff --git a/modules/video_output/drmu/drmu_vlc_fmts.h b/modules/video_output/drmu/drmu_vlc_fmts.h +new file mode 100644 +index 0000000000..ebdb0c8499 +--- /dev/null ++++ b/modules/video_output/drmu/drmu_vlc_fmts.h +@@ -0,0 +1,38 @@ ++#ifndef _DRMU_DRMU_VLC_FMTS_H ++#define _DRMU_DRMU_VLC_FMTS_H ++ ++#include "config.h" ++ ++#ifndef HAS_ZC_CMA ++#define HAS_ZC_CMA 0 ++#endif ++#define HAS_DRMPRIME 1 ++ ++#include ++ ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++// Convert chroma to drm - can't cope with RGB32 or RGB16 as they require ++// more info. returns 0 if unknown. ++uint32_t drmu_format_vlc_chroma_to_drm(const vlc_fourcc_t chroma); ++// Convert format to drm fourcc - does cope with RGB32 & RGB16 ++uint32_t drmu_format_vlc_to_drm(const video_frame_format_t * const vf_vlc); ++vlc_fourcc_t drmu_format_vlc_to_vlc(const uint32_t vf_drm); ++ ++#if HAS_DRMPRIME ++// pmod may be NULL ++uint32_t drmu_format_vlc_to_drm_prime(const vlc_fourcc_t chroma_in, uint64_t * const pmod); ++#endif ++#if HAS_ZC_CMA ++uint32_t drmu_format_vlc_to_drm_cma(const vlc_fourcc_t chroma_in); ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++#endif +diff --git a/modules/video_output/drmu/drmu_xlease.c b/modules/video_output/drmu/drmu_xlease.c +new file mode 100644 +index 0000000000..9bf2d7b25b --- /dev/null +++ b/modules/video_output/drmu/drmu_xlease.c @@ -0,0 +1,143 @@ @@ -24688,9 +25095,15 @@ + return drmu_env_new_fd(fd, log); +} + +diff --git a/modules/video_output/drmu/pollqueue.c b/modules/video_output/drmu/pollqueue.c +new file mode 100644 +index 0000000000..912afec9a7 --- /dev/null +++ b/modules/video_output/drmu/pollqueue.c -@@ -0,0 +1,372 @@ +@@ -0,0 +1,417 @@ ++#include "pollqueue.h" ++ ++#include +#include +#include +#include @@ -24705,8 +25118,6 @@ +#include +#include + -+#include "pollqueue.h" -+ +#define request_log(...) fprintf(stderr, __VA_ARGS__) + +struct pollqueue; @@ -24716,6 +25127,7 @@ + POLLTASK_QUEUED, + POLLTASK_RUNNING, + POLLTASK_Q_KILL, ++ POLLTASK_Q_DEAD, + POLLTASK_RUN_KILL, +}; + @@ -24732,12 +25144,12 @@ + void * v; + + uint64_t timeout; /* CLOCK_MONOTONIC time, 0 => never */ -+ sem_t kill_sem; +}; + +struct pollqueue { + atomic_int ref_count; + pthread_mutex_t lock; ++ pthread_cond_t cond; + + struct polltask *head; + struct polltask *tail; @@ -24773,8 +25185,6 @@ + .v = v + }; + -+ sem_init(&pt->kill_sem, 0, 0); -+ + return pt; +} + @@ -24801,10 +25211,22 @@ + +static void polltask_free(struct polltask * const pt) +{ -+ sem_destroy(&pt->kill_sem); + free(pt); +} + ++static void polltask_kill(struct polltask * const pt) ++{ ++ struct pollqueue * pq = pt->q; ++ polltask_free(pt); ++ pollqueue_unref(&pq); ++} ++ ++static void polltask_dead(struct polltask * const pt) ++{ ++ pt->state = POLLTASK_Q_DEAD; ++ pthread_cond_broadcast(&pt->q->cond); ++} ++ +static int pollqueue_prod(const struct pollqueue *const pq) +{ + static const uint64_t one = 1; @@ -24817,29 +25239,56 @@ + struct pollqueue * pq; + enum polltask_state state; + bool prodme; ++ bool inthread; + + if (!pt) + return; + + pq = pt->q; ++ inthread = pthread_equal(pthread_self(), pq->worker); ++ + pthread_mutex_lock(&pq->lock); + state = pt->state; -+ pt->state = (state == POLLTASK_RUNNING) ? POLLTASK_RUN_KILL : POLLTASK_Q_KILL; ++ pt->state = inthread ? POLLTASK_RUN_KILL : POLLTASK_Q_KILL; + prodme = !pq->no_prod; + pthread_mutex_unlock(&pq->lock); + -+ if (state != POLLTASK_UNQUEUED) { -+ if (prodme) -+ pollqueue_prod(pq); -+ while (sem_wait(&pt->kill_sem) && errno == EINTR) -+ /* loop */; -+ } ++ switch (state) { ++ case POLLTASK_UNQUEUED: ++ *ppt = NULL; ++ polltask_kill(pt); ++ break; + -+ // Leave zapping the ref until we have DQed the PT as might well be -+ // legitimately used in it -+ *ppt = NULL; -+ polltask_free(pt); -+ pollqueue_unref(&pq); ++ case POLLTASK_QUEUED: ++ case POLLTASK_RUNNING: ++ { ++ int rv = 0; ++ ++ if (inthread) { ++ // We are in worker thread - kill in main loop to avoid confusion or deadlock ++ *ppt = NULL; ++ break; ++ } ++ ++ if (prodme) ++ pollqueue_prod(pq); ++ ++ pthread_mutex_lock(&pq->lock); ++ while (rv == 0 && pt->state != POLLTASK_Q_DEAD) ++ rv = pthread_cond_wait(&pq->cond, &pq->lock); ++ pthread_mutex_unlock(&pq->lock); ++ ++ // Leave zapping the ref until we have DQed the PT as might well be ++ // legitimately used in it ++ *ppt = NULL; ++ polltask_kill(pt); ++ break; ++ } ++ default: ++ request_log("%s: Unexpected task state: %d\n", __func__, state); ++ *ppt = NULL; ++ break; ++ } +} + +static uint64_t pollqueue_now(int timeout) @@ -24857,9 +25306,10 @@ +{ + bool prodme = false; + struct pollqueue * const pq = pt->q; ++ const uint64_t timeout_time = timeout < 0 ? 0 : pollqueue_now(timeout); + + pthread_mutex_lock(&pq->lock); -+ if (pt->state != POLLTASK_Q_KILL && pt->state != POLLTASK_RUN_KILL) { ++ if (pt->state == POLLTASK_UNQUEUED || pt->state == POLLTASK_RUNNING) { + if (pq->tail) + pq->tail->next = pt; + else @@ -24867,7 +25317,7 @@ + pt->prev = pq->tail; + pt->next = NULL; + pt->state = POLLTASK_QUEUED; -+ pt->timeout = timeout < 0 ? 0 : pollqueue_now(timeout); ++ pt->timeout = timeout_time; + pq->tail = pt; + prodme = !pq->no_prod; + } @@ -24879,11 +25329,10 @@ +static void *poll_thread(void *v) +{ + struct pollqueue *const pq = v; -+ struct pollfd *a = NULL; -+ size_t asize = 0; + + pthread_mutex_lock(&pq->lock); + do { ++ struct pollfd a[POLLQUEUE_MAX_QUEUE]; + unsigned int i, j; + unsigned int nall = 0; + unsigned int npoll = 0; @@ -24900,20 +25349,17 @@ + + if (pt->state == POLLTASK_Q_KILL) { + pollqueue_rem_task(pq, pt); -+ sem_post(&pt->kill_sem); ++ polltask_dead(pt); ++ continue; ++ } ++ if (pt->state == POLLTASK_RUN_KILL) { ++ pollqueue_rem_task(pq, pt); ++ polltask_kill(pt); + continue; + } + + if (pt->fd != -1) { -+ if (npoll >= asize) { -+ asize = asize ? asize * 2 : 4; -+ a = realloc(a, asize * sizeof(*a)); -+ if (!a) { -+ request_log("Failed to realloc poll array to %zd\n", asize); -+ goto fail_locked; -+ } -+ } -+ ++ assert(npoll < POLLQUEUE_MAX_QUEUE); + a[npoll++] = (struct pollfd){ + .fd = pt->fd, + .events = pt->events @@ -24935,9 +25381,9 @@ + } + } + -+ pthread_mutex_lock(&pq->lock); + now = pollqueue_now(0); + ++ pthread_mutex_lock(&pq->lock); + /* Prodding in this loop is pointless and might lead to + * infinite looping + */ @@ -24946,13 +25392,13 @@ + const short r = pt->fd == -1 ? 0 : a[j++].revents; + pt_next = pt->next; + ++ if (pt->state != POLLTASK_QUEUED) ++ continue; ++ + /* Pending? */ + if (r || (pt->timeout && (int64_t)(now - pt->timeout) >= 0)) { + pollqueue_rem_task(pq, pt); -+ if (pt->state == POLLTASK_QUEUED) -+ pt->state = POLLTASK_RUNNING; -+ if (pt->state == POLLTASK_Q_KILL) -+ pt->state = POLLTASK_RUN_KILL; ++ pt->state = POLLTASK_RUNNING; + pthread_mutex_unlock(&pq->lock); + + /* This can add new entries to the Q but as @@ -24965,17 +25411,22 @@ + if (pt->state == POLLTASK_RUNNING) + pt->state = POLLTASK_UNQUEUED; + if (pt->state == POLLTASK_RUN_KILL) -+ sem_post(&pt->kill_sem); ++ polltask_kill(pt); + } + } + pq->no_prod = false; + + } while (!pq->kill); + -+fail_locked: + pthread_mutex_unlock(&pq->lock); +fail_unlocked: -+ free(a); ++ ++ polltask_free(pq->prod_pt); ++ pthread_cond_destroy(&pq->cond); ++ pthread_mutex_destroy(&pq->lock); ++ close(pq->prod_fd); ++ free(pq); ++ + return NULL; +} + @@ -24997,6 +25448,7 @@ + *pq = (struct pollqueue){ + .ref_count = ATOMIC_VAR_INIT(0), + .lock = PTHREAD_MUTEX_INITIALIZER, ++ .cond = PTHREAD_COND_INITIALIZER, + .head = NULL, + .tail = NULL, + .kill = false, @@ -25027,18 +25479,21 @@ + +static void pollqueue_free(struct pollqueue *const pq) +{ -+ void *rv; ++ const pthread_t worker = pq->worker; + -+ pthread_mutex_lock(&pq->lock); -+ pq->kill = true; -+ pollqueue_prod(pq); -+ pthread_mutex_unlock(&pq->lock); -+ -+ pthread_join(pq->worker, &rv); -+ polltask_free(pq->prod_pt); -+ pthread_mutex_destroy(&pq->lock); -+ close(pq->prod_fd); -+ free(pq); ++ if (pthread_equal(worker, pthread_self())) { ++ pq->kill = true; ++ pollqueue_prod(pq); ++ pthread_detach(worker); ++ } ++ else ++ { ++ pthread_mutex_lock(&pq->lock); ++ pq->kill = true; ++ pollqueue_prod(pq); ++ pthread_mutex_unlock(&pq->lock); ++ pthread_join(worker, NULL); ++ } +} + +struct pollqueue * pollqueue_ref(struct pollqueue *const pq) @@ -25063,9 +25518,12 @@ + + + +diff --git a/modules/video_output/drmu/pollqueue.h b/modules/video_output/drmu/pollqueue.h +new file mode 100644 +index 0000000000..922b6e3d0b --- /dev/null +++ b/modules/video_output/drmu/pollqueue.h -@@ -0,0 +1,51 @@ +@@ -0,0 +1,61 @@ +#ifndef POLLQUEUE_H_ +#define POLLQUEUE_H_ + @@ -25074,6 +25532,9 @@ +struct polltask; +struct pollqueue; + ++// Max number of tasks that can be Qed ++#define POLLQUEUE_MAX_QUEUE 128 ++ +// Create a new polltask +// Holds a reference on the pollqueue until the polltask is deleted +// @@ -25097,9 +25558,13 @@ +// It is safe to call whilst a polltask is queued (and may be triggered) +// Callback may occur whilst this is in progress but will not occur +// once it is done. (*ppt is nulled only once the callback can not occur) -+// DO NOT CALL in a polltask callback ++// May be called in a polltask callback ++// If called from outside the polltask thread and this causes the pollqueue ++// to be deleted then it will wait for the polltask thread to terminate ++// before returning. +void polltask_delete(struct polltask **const ppt); + ++// Queue a polltask +// timeout_ms == -1 => never +// May be called from the polltask callback +// May only be added once (currently) @@ -25111,12 +25576,17 @@ + +// Unref a pollqueue +// Will be deleted once all polltasks (Qed or otherwise) are deleted too ++// If called from outside the polltask thread and this causes the pollqueue ++// to be deleted then it will wait for the polltask thread to terminate ++// before returning. +void pollqueue_unref(struct pollqueue **const ppq); + +// Add a reference to a pollqueue +struct pollqueue * pollqueue_ref(struct pollqueue *const pq); + +#endif /* POLLQUEUE_H_ */ +diff --git a/modules/video_output/opengl/display.c b/modules/video_output/opengl/display.c +index ec671109bc..46f693a13a 100644 --- a/modules/video_output/opengl/display.c +++ b/modules/video_output/opengl/display.c @@ -48,7 +48,7 @@ vlc_module_begin () @@ -25128,6 +25598,8 @@ set_callbacks (Open, Close) add_shortcut ("opengles2", "gles2") add_module ("gles2", "opengl es2", NULL, +diff --git a/modules/video_output/opengl/egl.c b/modules/video_output/opengl/egl.c +index ab20fb4c9c..dbff2e5efe 100644 --- a/modules/video_output/opengl/egl.c +++ b/modules/video_output/opengl/egl.c @@ -43,6 +43,8 @@ @@ -25147,7 +25619,7 @@ } vlc_gl_sys_t; static int MakeCurrent (vlc_gl_t *gl) -@@ -129,6 +132,15 @@ static bool DestroyImageKHR(vlc_gl_t *gl +@@ -129,6 +132,15 @@ static bool DestroyImageKHR(vlc_gl_t *gl, void *image) return sys->eglDestroyImageKHR(sys->display, image); } @@ -25163,7 +25635,7 @@ static bool CheckToken(const char *haystack, const char *needle) { size_t len = strlen(needle); -@@ -371,6 +383,14 @@ static int Open (vlc_object_t *obj, cons +@@ -371,6 +383,14 @@ static int Open (vlc_object_t *obj, const struct gl_api *api) goto error; } @@ -25178,7 +25650,7 @@ const EGLint conf_attr[] = { EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 5, -@@ -427,6 +447,9 @@ static int Open (vlc_object_t *obj, cons +@@ -427,6 +447,9 @@ static int Open (vlc_object_t *obj, const struct gl_api *api) gl->egl.createImageKHR = CreateImageKHR; gl->egl.destroyImageKHR = DestroyImageKHR; } @@ -25197,19 +25669,3493 @@ set_callbacks (OpenGLES2, Close) add_shortcut ("egl") +diff --git a/modules/video_output/wayland/dmabuf.c b/modules/video_output/wayland/dmabuf.c +new file mode 100644 +index 0000000000..03b71f7d7f +--- /dev/null ++++ b/modules/video_output/wayland/dmabuf.c +@@ -0,0 +1,1554 @@ ++/** ++ * @file shm.c ++ * @brief Wayland shared memory video output module for VLC media player ++ */ ++/***************************************************************************** ++ * Copyright © 2014, 2017 Rémi Denis-Courmont ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Lesser General Public License as published by ++ * the Free Software Foundation; either version 2.1 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. ++ *****************************************************************************/ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include "viewporter-client-protocol.h" ++#include "linux-dmabuf-unstable-v1-client-protocol.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++// *** Avoid this include if possible ++#include ++ ++#include "dmabuf_alloc.h" ++#include "picpool.h" ++#include "rgba_premul.h" ++#include "../drmu/drmu_vlc_fmts.h" ++#include "../drmu/pollqueue.h" ++#include "../../codec/avcodec/drm_pic.h" ++#include ++ ++#define TRACE_ALL 0 ++ ++#define MAX_PICTURES 4 ++#define MAX_SUBPICS 6 ++ ++#define VIDEO_ON_SUBSURFACE 1 ++ ++typedef struct fmt_ent_s { ++ uint32_t fmt; ++ int32_t pri; ++ uint64_t mod; ++} fmt_ent_t; ++ ++typedef struct fmt_list_s { ++ fmt_ent_t * fmts; ++ unsigned int size; ++ unsigned int len; ++} fmt_list_t; ++ ++typedef struct subplane_s { ++ struct wl_surface * surface; ++ struct wl_subsurface * subsurface; ++ struct wp_viewport * viewport; ++} subplane_t; ++ ++typedef struct subpic_ent_s { ++ struct wl_buffer * wb; ++ struct dmabuf_h * dh; ++ picture_t * pic; ++ int alpha; ++ vout_display_place_t dst_rect; ++ vout_display_place_t src_rect; ++ bool update; ++} subpic_ent_t; ++ ++typedef struct video_dmabuf_release_env_ss ++{ ++ struct picture_context_t * ctx; ++ unsigned int rel_count; ++ unsigned int pt_count; ++ struct polltask * pt[AV_DRM_MAX_PLANES]; ++} video_dmabuf_release_env_t; ++ ++struct vout_display_sys_t ++{ ++ vout_window_t *embed; /* VLC window */ ++ struct wp_viewporter *viewporter; ++ struct wp_viewport *viewport; ++ struct zwp_linux_dmabuf_v1 * linux_dmabuf_v1; ++ struct wl_compositor *compositor; ++ struct wl_subcompositor *subcompositor; ++ ++ picture_pool_t *vlc_pic_pool; /* picture pool */ ++ ++ struct wl_surface * last_embed_surface; ++ ++ int x; ++ int y; ++ bool video_attached; ++ bool viewport_set; ++ ++ vout_display_place_t spu_rect; // Window that subpic coords orignate from ++ vout_display_place_t dst_rect; // Window in the display size that holds the video ++ ++ video_format_t curr_aspect; ++ ++#if VIDEO_ON_SUBSURFACE ++ struct wl_surface * video_surface; ++ struct wl_subsurface * video_subsurface; ++ ++ struct wp_viewport * bkg_viewport; ++ unsigned int bkg_w; ++ unsigned int bkg_h; ++#endif ++ ++ struct pollqueue * pollq; ++ ++ picpool_ctl_t * subpic_pool; ++ subplane_t subplanes[MAX_SUBPICS]; ++ subpic_ent_t subpics[MAX_SUBPICS]; ++ subpic_ent_t piccpy; ++ video_dmabuf_release_env_t * pic_vdre; ++ vlc_fourcc_t * subpic_chromas; ++ ++ fmt_list_t dmabuf_fmts; ++}; ++ ++static inline struct wl_display * ++video_display(const vout_display_sys_t * const sys) ++{ ++ return sys->embed->display.wl; ++} ++ ++static inline struct wl_surface * ++video_surface(const vout_display_sys_t * const sys) ++{ ++#if VIDEO_ON_SUBSURFACE ++ return sys->video_surface; ++#else ++ return sys->embed->handle.wl; ++#endif ++} ++ ++static inline struct wl_compositor * ++video_compositor(const vout_display_sys_t * const sys) ++{ ++ return sys->compositor; ++} ++ ++#if VIDEO_ON_SUBSURFACE ++static inline struct wl_surface * ++bkg_surface(const vout_display_sys_t * const sys) ++{ ++ return sys->embed->handle.wl; ++} ++#endif ++ ++static int ++check_embed(vout_display_t * const vd, vout_display_sys_t * const sys, const char * const func) ++{ ++ if (!sys->embed) { ++ msg_Err(vd, "%s: Embed NULL", func); ++ return -1; ++ } ++ if (sys->embed->handle.wl != sys->last_embed_surface) { ++ msg_Warn(vd, "%s: Embed surface changed %p->%p", func, sys->last_embed_surface, sys->embed->handle.wl); ++ sys->last_embed_surface = sys->embed->handle.wl; ++ return 1; ++ } ++ return 0; ++} ++ ++static inline void ++roundtrip(const vout_display_sys_t * const sys) ++{ ++ wl_display_roundtrip(video_display(sys)); ++} ++ ++static void ++buffer_destroy(struct wl_buffer ** ppbuffer) ++{ ++ struct wl_buffer * const buffer = *ppbuffer; ++ if (buffer == NULL) ++ return; ++ *ppbuffer = NULL; ++ wl_buffer_destroy(buffer); ++} ++ ++static void ++subsurface_destroy(struct wl_subsurface ** const ppsubsurface) ++{ ++ if (*ppsubsurface == NULL) ++ return; ++ wl_subsurface_destroy(*ppsubsurface); ++ *ppsubsurface = NULL; ++} ++ ++static void ++surface_destroy(struct wl_surface ** const ppsurface) ++{ ++ if (*ppsurface == NULL) ++ return; ++ wl_surface_destroy(*ppsurface); ++ *ppsurface = NULL; ++} ++ ++static void ++viewport_destroy(struct wp_viewport ** const ppviewport) ++{ ++ if (*ppviewport == NULL) ++ return; ++ wp_viewport_destroy(*ppviewport); ++ *ppviewport = NULL; ++} ++ ++static inline int_fast32_t ++place_rescale_1s(int_fast32_t x, uint_fast32_t mul, uint_fast32_t div) ++{ ++ const int_fast64_t m = x * (int_fast64_t)mul; ++ const uint_fast32_t d2 = div/2; ++ return div == 0 ? (int_fast32_t)m : ++ m >= 0 ? (int_fast32_t)(((uint_fast64_t)m + d2) / div) : ++ -(int_fast32_t)(((uint_fast64_t)(-m) + d2) / div); ++} ++ ++static inline uint_fast32_t ++place_rescale_1u(uint_fast32_t x, uint_fast32_t mul, uint_fast32_t div) ++{ ++ const uint_fast64_t m = x * (uint_fast64_t)mul; ++ return (uint_fast32_t)(div == 0 ? m : (m + div/2) / div); ++} ++ ++static inline vout_display_place_t ++place_rescale(const vout_display_place_t s, const vout_display_place_t mul, const vout_display_place_t div) ++{ ++ return (vout_display_place_t){ ++ .x = place_rescale_1s(s.x - div.x, mul.width, div.width) + mul.x, ++ .y = place_rescale_1s(s.y - div.y, mul.height, div.height) + mul.y, ++ .width = place_rescale_1u(s.width, mul.width, div.width), ++ .height = place_rescale_1u(s.height, mul.height, div.height) ++ }; ++} ++ ++ ++// MMAL headers comment these (getting 2 a bit wrong) but do not give ++// defines ++#define VXF_H_SHIFT 0 // Hflip ++#define VXF_V_SHIFT 1 // Vflip ++#define VXF_T_SHIFT 2 // Transpose ++#define VXF_H_BIT (1 << VXF_H_SHIFT) ++#define VXF_V_BIT (1 << VXF_V_SHIFT) ++#define VXF_T_BIT (1 << VXF_T_SHIFT) ++ ++static inline bool ++is_vxf_transpose(const video_transform_t t) ++{ ++ return ((unsigned int)t & VXF_T_BIT) != 0; ++} ++ ++static inline bool ++is_vxf_hflip(const video_transform_t t) ++{ ++ return ((unsigned int)t & VXF_H_BIT) != 0; ++} ++ ++static inline bool ++is_vxf_vflip(const video_transform_t t) ++{ ++ return ((unsigned int)t & VXF_V_BIT) != 0; ++} ++ ++static inline video_transform_t ++swap_vxf_hv(const video_transform_t x) ++{ ++ return (((x >> VXF_H_SHIFT) & 1) << VXF_V_SHIFT) | ++ (((x >> VXF_V_SHIFT) & 1) << VXF_H_SHIFT) | ++ (x & VXF_T_BIT); ++} ++ ++static inline video_transform_t ++vxf_inverse(const video_transform_t x) ++{ ++ return is_vxf_transpose(x) ? swap_vxf_hv(x) : x; ++} ++ ++// Transform generated by A then B ++// All ops are self inverse so can simply be XORed on their own ++// H & V flips after a transpose need to be swapped ++static inline video_transform_t ++combine_vxf(const video_transform_t a, const video_transform_t b) ++{ ++ return a ^ (is_vxf_transpose(a) ? swap_vxf_hv(b) : b); ++} ++ ++static inline vout_display_place_t ++vplace_transpose(const vout_display_place_t s) ++{ ++ return (vout_display_place_t){ ++ .x = s.y, ++ .y = s.x, ++ .width = s.height, ++ .height = s.width ++ }; ++} ++ ++// hflip s in c ++static inline vout_display_place_t vplace_hflip(const vout_display_place_t s, const vout_display_place_t c) ++{ ++ return (vout_display_place_t){ ++ .x = c.x + (c.x + c.width) - (s.x + s.width), ++ .y = s.y, ++ .width = s.width, ++ .height = s.height ++ }; ++} ++ ++// vflip s in c ++static inline vout_display_place_t vplace_vflip(const vout_display_place_t s, const vout_display_place_t c) ++{ ++ return (vout_display_place_t){ ++ .x = s.x, ++ .y = (c.y + c.height) - (s.y - c.y) - s.height, ++ .width = s.width, ++ .height = s.height ++ }; ++} ++ ++static vout_display_place_t ++place_out(const vout_display_cfg_t * cfg, ++ const video_format_t * fmt, ++ const vout_display_place_t r) ++{ ++ video_format_t tfmt; ++ vout_display_cfg_t tcfg; ++ vout_display_place_t place; ++ ++ // 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; ++ } ++ ++ // Override what VLC thinks might be going on with display size ++ // if we know better ++ if (r.width != 0 && r.height != 0) ++ { ++ tcfg = *cfg; ++ tcfg.display.width = r.width; ++ tcfg.display.height = r.height; ++ cfg = &tcfg; ++ } ++ ++ vout_display_PlacePicture(&place, fmt, cfg, false); ++ ++ place.x += r.x; ++ place.y += r.y; ++ ++ return place; ++} ++ ++#if 0 ++static vout_display_place_t ++rect_transform(vout_display_place_t s, const vout_display_place_t c, const video_transform_t t) ++{ ++ if (is_vxf_transpose(t)) ++ s = vplace_transpose(s); ++ if (is_vxf_hflip(t)) ++ s = vplace_hflip(s, c); ++ if (is_vxf_vflip(t) != 0) ++ s = vplace_vflip(s, c); ++ return s; ++} ++ ++static void ++place_dest_rect(vout_display_t * const vd, ++ const vout_display_cfg_t * const cfg, ++ const video_format_t * fmt) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ sys->dest_rect = rect_transform(place_out(cfg, fmt, sys->win_rect), ++ sys->display_rect, sys->dest_transform); ++} ++#endif ++ ++static void ++place_spu_rect(vout_display_t * const vd, ++ const vout_display_cfg_t * const cfg, ++ const video_format_t * fmt) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ static const vout_display_place_t r0 = {0}; ++ ++ sys->spu_rect = place_out(cfg, fmt, r0); ++ sys->spu_rect.x = 0; ++ sys->spu_rect.y = 0; ++ ++ // Copy place override logic for spu pos from video_output.c ++ // This info doesn't appear to reside anywhere natively ++ ++ if (fmt->i_width * fmt->i_height >= (unsigned int)(sys->spu_rect.width * sys->spu_rect.height)) { ++ sys->spu_rect.width = fmt->i_visible_width; ++ sys->spu_rect.height = fmt->i_visible_height; ++ } ++ ++ if (ORIENT_IS_SWAP(fmt->orientation)) ++ sys->spu_rect = vplace_transpose(sys->spu_rect); ++} ++ ++ ++static int ++fmt_list_add(fmt_list_t * const fl, uint32_t fmt, uint64_t mod, int32_t pri) ++{ ++ if (fl->len >= fl->size) ++ { ++ unsigned int n = fl->len == 0 ? 64 : fl->len * 2; ++ fmt_ent_t * t = realloc(fl->fmts, n * sizeof(*t)); ++ if (t == NULL) ++ return VLC_ENOMEM; ++ fl->fmts = t; ++ fl->size = n; ++ } ++ fl->fmts[fl->len++] = (fmt_ent_t){ ++ .fmt = fmt, ++ .pri = pri, ++ .mod = mod ++ }; ++ return 0; ++} ++ ++static int ++fmt_sort_cb(const void * va, const void * vb) ++{ ++ const fmt_ent_t * const a = va; ++ const fmt_ent_t * const b = vb; ++ return a->fmt < b->fmt ? -1 : a->fmt != b->fmt ? 1 : ++ a->mod < b->mod ? -1 : a->mod != b->mod ? 1 : 0; ++} ++ ++static void ++fmt_list_sort(fmt_list_t * const fl) ++{ ++ unsigned int n = 0; ++ if (fl->len <= 1) ++ return; ++ qsort(fl->fmts, fl->len, sizeof(*fl->fmts), fmt_sort_cb); ++ // Dedup - in case we have multiple working callbacks ++ for (unsigned int i = 1; i != fl->len; ++i) ++ { ++ if (fl->fmts[i].fmt != fl->fmts[n].fmt || fl->fmts[i].mod != fl->fmts[n].mod) ++ fl->fmts[n++] = fl->fmts[i]; ++ } ++ fl->len = n + 1; ++} ++ ++static int ++fmt_list_find(const fmt_list_t * const fl, uint32_t fmt, uint64_t mod) ++{ ++ const fmt_ent_t x = { ++ .fmt = fmt, ++ .mod = mod ++ }; ++ const fmt_ent_t * const fe = (fl->len == 0) ? NULL : ++ bsearch(&x, fl->fmts, fl->len, sizeof(x), fmt_sort_cb); ++ return fe == NULL ? -1 : fe->pri; ++} ++ ++static void ++fmt_list_uninit(fmt_list_t * const fl) ++{ ++ free(fl->fmts); ++ fl->fmts = NULL; ++ fl->size = 0; ++ fl->len = 0; ++} ++ ++static int ++fmt_list_init(fmt_list_t * const fl, const size_t initial_size) ++{ ++ fl->size = 0; ++ fl->len = 0; ++ if ((fl->fmts = malloc(initial_size * sizeof(*fl->fmts))) == NULL) ++ return VLC_ENOMEM; ++ fl->size = initial_size; ++ return VLC_SUCCESS; ++} ++ ++static void ++chequerboard(uint32_t *const data, unsigned int stride, const unsigned int width, const unsigned int height) ++{ ++ stride /= sizeof(uint32_t); ++ ++ /* Draw checkerboxed background */ ++ for (unsigned int y = 0; y < height; ++y) { ++ for (unsigned int x = 0; x < width; ++x) { ++ if ((x + y / 8 * 8) % 16 < 8) ++ data[y * stride + x] = 0xFF666666; ++ else ++ data[y * stride + x] = 0xFFEEEEEE; ++ } ++ } ++} ++ ++/* Sent by the compositor when it's no longer using this buffer */ ++static void ++subpic_buffer_release(void *data, struct wl_buffer *wl_buffer) ++{ ++ struct dmabuf_h * dh = data; ++ ++ buffer_destroy(&wl_buffer); ++ dmabuf_unref(&dh); ++} ++ ++static const struct wl_buffer_listener subpic_buffer_listener = { ++ .release = subpic_buffer_release, ++}; ++ ++static inline size_t cpypic_plane_alloc_size(const plane_t * const p) ++{ ++ return p->i_pitch * p->i_lines; ++} ++ ++static int ++copy_subpic_to_w_buffer(vout_display_t *vd, vout_display_sys_t * const sys, picture_t * const src, ++ int alpha, ++ struct dmabuf_h ** pDmabuf_h, struct wl_buffer ** pW_buffer) ++{ ++ unsigned int w = src->format.i_width; ++ unsigned int h = src->format.i_height; ++ struct zwp_linux_buffer_params_v1 *params = NULL; ++ const uint32_t drm_fmt = drmu_format_vlc_to_drm(&src->format); ++ size_t total_size = 0; ++ size_t offset = 0; ++ struct dmabuf_h * dh = NULL; ++ int i; ++ ++ for (i = 0; i != src->i_planes; ++i) ++ total_size += cpypic_plane_alloc_size(src->p + i); ++ ++ *pW_buffer = NULL; ++ *pDmabuf_h = NULL; ++ ++ if ((dh = picpool_get(sys->subpic_pool, total_size)) == NULL) ++ { ++ msg_Warn(vd, "Failed to alloc dmabuf for subpic"); ++ goto error; ++ } ++ *pDmabuf_h = dh; ++ ++ if ((params = zwp_linux_dmabuf_v1_create_params(sys->linux_dmabuf_v1)) == NULL) ++ { ++ msg_Err(vd, "zwp_linux_dmabuf_v1_create_params FAILED"); ++ goto error; ++ } ++ ++ dmabuf_write_start(dh); ++ for (i = 0; i != src->i_planes; ++i) ++ { ++ const size_t stride = src->p[i].i_pitch; ++ const size_t size = cpypic_plane_alloc_size(src->p + i); ++ ++ if (src->format.i_chroma == VLC_CODEC_RGBA || ++ src->format.i_chroma == VLC_CODEC_BGRA) ++ copy_frame_xxxa_with_premul(dmabuf_map(dh), stride, src->p[i].p_pixels, src->p[i].i_pitch, w, h, alpha); ++ else ++ memcpy((char *)dmabuf_map(dh) + offset, src->p[i].p_pixels, size); ++ ++ zwp_linux_buffer_params_v1_add(params, dmabuf_fd(dh), i, offset, stride, 0, 0); ++ ++ offset += size; ++ } ++ dmabuf_write_end(dh); ++ ++ if ((*pW_buffer = zwp_linux_buffer_params_v1_create_immed(params, w, h, drm_fmt, 0)) == NULL) ++ { ++ msg_Err(vd, "zwp_linux_buffer_params_v1_create_immed FAILED"); ++ goto error; ++ } ++ ++ zwp_linux_buffer_params_v1_destroy(params); ++ wl_buffer_add_listener(*pW_buffer, &subpic_buffer_listener, dh); ++ ++ return VLC_SUCCESS; ++ ++error: ++ if (params) ++ zwp_linux_buffer_params_v1_destroy(params); ++ dmabuf_unref(pDmabuf_h); ++ return VLC_EGENERIC; ++} ++ ++ ++static void kill_pool(vout_display_sys_t * const sys) ++{ ++ if (sys->vlc_pic_pool != NULL) ++ { ++ picture_pool_Release(sys->vlc_pic_pool); ++ sys->vlc_pic_pool = NULL; ++ } ++} ++ ++// Actual picture pool for dmabufs is just a set of trivial containers ++static picture_pool_t *vd_dmabuf_pool(vout_display_t * const vd, unsigned count) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__, ++ vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, vd->source.i_width, vd->source.i_height); ++#endif ++ ++ if (sys->vlc_pic_pool == NULL) ++ sys->vlc_pic_pool = picture_pool_NewFromFormat(&vd->fmt, count); ++ return sys->vlc_pic_pool; ++} ++ ++static video_dmabuf_release_env_t * ++vdre_new(struct picture_context_t * ctx) ++{ ++ video_dmabuf_release_env_t * const vdre = calloc(1, sizeof(*vdre)); ++ if ((vdre->ctx = ctx->copy(ctx)) == NULL) ++ { ++ free(vdre); ++ return NULL; ++ } ++ return vdre; ++} ++ ++static void ++vdre_free(video_dmabuf_release_env_t * const vdre) ++{ ++ unsigned int i; ++ vdre->ctx->destroy(vdre->ctx); ++ for (i = 0; i != vdre->pt_count; ++i) ++ polltask_delete(vdre->pt + i); ++ free(vdre); ++} ++ ++static void ++vdre_delete(video_dmabuf_release_env_t ** const ppvdre) ++{ ++ video_dmabuf_release_env_t * const vdre = *ppvdre; ++ if (vdre == NULL) ++ return; ++ *ppvdre = NULL; ++ vdre_free(vdre); ++} ++ ++static void ++w_ctx_release(void * v, short revents) ++{ ++ video_dmabuf_release_env_t * const vdre = v; ++ VLC_UNUSED(revents); ++ // Wait for all callbacks to come back before releasing buffer ++ if (++vdre->rel_count >= vdre->pt_count) ++ vdre_free(vdre); ++} ++ ++static void ++vdre_add_pt(video_dmabuf_release_env_t * const vdre, struct pollqueue * pq, int fd) ++{ ++ assert(vdre->pt_count < AV_DRM_MAX_PLANES); ++ vdre->pt[vdre->pt_count++] = polltask_new(pq, fd, POLLOUT, w_ctx_release, vdre); ++} ++ ++// Avoid use of vd here as there's a possibility this will be called after ++// it has gone ++static void ++w_buffer_release(void *data, struct wl_buffer *wl_buffer) ++{ ++ video_dmabuf_release_env_t * const vdre = data; ++ unsigned int i = vdre->pt_count; ++ ++ /* Sent by the compositor when it's no longer using this buffer */ ++ buffer_destroy(&wl_buffer); ++ ++ // Whilst we can happily destroy the buffer that doesn't mean we can reuse ++ // the dmabufs yet - we have to wait for them to be free of fences. ++ // We don't want to wait in this callback so do the waiting in pollqueue ++ while (i-- != 0) ++ pollqueue_add_task(vdre->pt[i], 1000); ++} ++ ++static const struct wl_buffer_listener w_buffer_listener = { ++ .release = w_buffer_release, ++}; ++ ++static int ++do_display_dmabuf(vout_display_t * const vd, vout_display_sys_t * const sys, picture_t * const pic, ++ video_dmabuf_release_env_t ** const pVdre, struct wl_buffer ** const pWbuffer) ++{ ++ struct zwp_linux_buffer_params_v1 *params; ++ const AVDRMFrameDescriptor * const desc = drm_prime_get_desc(pic); ++ const uint32_t format = desc->layers[0].format; ++ const unsigned int width = pic->format.i_visible_width; ++ const unsigned int height = pic->format.i_visible_height; ++ unsigned int n = 0; ++ unsigned int flags = 0; ++ int i; ++ struct wl_buffer * w_buffer; ++ video_dmabuf_release_env_t * const vdre = vdre_new(pic->context); ++ ++ assert(*pWbuffer == NULL); ++ assert(*pVdre == NULL); ++ ++ if (vdre == NULL) { ++ msg_Err(vd, "Failed to create vdre"); ++ return VLC_ENOMEM; ++ } ++ ++ for (i = 0; i != desc->nb_objects; ++i) ++ vdre_add_pt(vdre, sys->pollq, desc->objects[i].fd); ++ ++ /* Creation and configuration of planes */ ++ params = zwp_linux_dmabuf_v1_create_params(sys->linux_dmabuf_v1); ++ if (!params) ++ { ++ msg_Err(vd, "zwp_linux_dmabuf_v1_create_params FAILED"); ++ goto error; ++ } ++ ++ for (i = 0; i < desc->nb_layers; ++i) ++ { ++ int j; ++ for (j = 0; j < desc->layers[i].nb_planes; ++j) ++ { ++ const AVDRMPlaneDescriptor *const p = desc->layers[i].planes + j; ++ const AVDRMObjectDescriptor *const obj = desc->objects + p->object_index; ++ ++ zwp_linux_buffer_params_v1_add(params, obj->fd, n++, p->offset, p->pitch, ++ (unsigned int)(obj->format_modifier >> 32), ++ (unsigned int)(obj->format_modifier & 0xFFFFFFFF)); ++ } ++ } ++ ++ if (!pic->b_progressive) ++ { ++ flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED; ++ if (!pic->b_top_field_first) ++ flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST; ++ } ++ ++ /* Request buffer creation */ ++ if ((w_buffer = zwp_linux_buffer_params_v1_create_immed(params, width, height, format, flags)) == NULL) ++ { ++ msg_Err(vd, "zwp_linux_buffer_params_v1_create_immed FAILED"); ++ goto error; ++ } ++ ++ zwp_linux_buffer_params_v1_destroy(params); ++ ++ wl_buffer_add_listener(w_buffer, &w_buffer_listener, vdre); ++ ++ *pVdre = vdre; ++ *pWbuffer = w_buffer; ++ return VLC_SUCCESS; ++ ++error: ++ vdre_free(vdre); ++ return VLC_EGENERIC; ++} ++ ++static void ++subpic_ent_flush(subpic_ent_t * const spe) ++{ ++ if (spe->pic != NULL) { ++ picture_Release(spe->pic); ++ spe->pic = NULL; ++ } ++ buffer_destroy(&spe->wb); ++ dmabuf_unref(&spe->dh); ++} ++ ++static void ++subpic_ent_attach(struct wl_surface * const surface, subpic_ent_t * const spe) ++{ ++ wl_surface_attach(surface, spe->wb, 0, 0); ++ spe->dh = NULL; ++ spe->wb = NULL; ++} ++ ++static void ++mark_all_surface_opaque(struct wl_compositor * compositor, struct wl_surface * surface) ++{ ++ struct wl_region * region_all = wl_compositor_create_region(compositor); ++ wl_region_add(region_all, 0, 0, INT32_MAX, INT32_MAX); ++ wl_surface_set_opaque_region(surface, region_all); ++ wl_region_destroy(region_all); ++} ++ ++static int ++make_video_surface(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++ VLC_UNUSED(vd); ++ ++ if (sys->viewport) ++ return VLC_SUCCESS; ++ ++#if VIDEO_ON_SUBSURFACE ++ // Make a new subsurface to use for video ++ sys->video_surface = wl_compositor_create_surface(video_compositor(sys)); ++ sys->video_subsurface = wl_subcompositor_get_subsurface(sys->subcompositor, sys->video_surface, bkg_surface(sys)); ++ wl_subsurface_place_above(sys->video_subsurface, bkg_surface(sys)); ++ wl_subsurface_set_desync(sys->video_subsurface); // Video update can be desync from main window ++#endif ++ ++ struct wl_surface * const surface = video_surface(sys); ++ ++ // Video is opaque ++ mark_all_surface_opaque(video_compositor(sys), surface); ++ ++ sys->viewport = wp_viewporter_get_viewport(sys->viewporter, surface); ++ ++ /* Determine our pixel format */ ++ static const enum wl_output_transform transforms[8] = { ++ [ORIENT_TOP_LEFT] = WL_OUTPUT_TRANSFORM_NORMAL, ++ [ORIENT_TOP_RIGHT] = WL_OUTPUT_TRANSFORM_FLIPPED, ++ [ORIENT_BOTTOM_LEFT] = WL_OUTPUT_TRANSFORM_FLIPPED_180, ++ [ORIENT_BOTTOM_RIGHT] = WL_OUTPUT_TRANSFORM_180, ++ [ORIENT_LEFT_TOP] = WL_OUTPUT_TRANSFORM_FLIPPED_270, ++ [ORIENT_LEFT_BOTTOM] = WL_OUTPUT_TRANSFORM_90, ++ [ORIENT_RIGHT_TOP] = WL_OUTPUT_TRANSFORM_270, ++ [ORIENT_RIGHT_BOTTOM] = WL_OUTPUT_TRANSFORM_FLIPPED_90, ++ }; ++ ++ wl_surface_set_buffer_transform(surface, transforms[vd->fmt.orientation]); ++ return VLC_SUCCESS; ++} ++ ++static int ++make_subpic_surfaces(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++ unsigned int i; ++ struct wl_surface * const surface = video_surface(sys); ++ struct wl_surface * below = surface; ++ VLC_UNUSED(vd); ++ ++ if (sys->subplanes[0].surface) ++ return VLC_SUCCESS; ++ ++ for (i = 0; i != MAX_SUBPICS; ++i) ++ { ++ subplane_t *plane = sys->subplanes + i; ++ plane->surface = wl_compositor_create_surface(video_compositor(sys)); ++ plane->subsurface = wl_subcompositor_get_subsurface(sys->subcompositor, plane->surface, surface); ++ wl_subsurface_place_above(plane->subsurface, below); ++ below = plane->surface; ++ wl_subsurface_set_sync(plane->subsurface); ++ plane->viewport = wp_viewporter_get_viewport(sys->viewporter, plane->surface); ++ } ++ return VLC_SUCCESS; ++} ++ ++static int ++make_background(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++#if !VIDEO_ON_SUBSURFACE ++ VLC_UNUSED(vd); ++ VLC_UNUSED(sys); ++ return VLC_SUCCESS; ++#else ++ // Build a background ++ // This would be a perfect use of the single_pixel_surface extension ++ // However we don't seem to support it ++ struct dmabuf_h * dh = NULL; ++ ++ if (!sys->bkg_viewport) ++ { ++ unsigned int width = 640; ++ unsigned int height = 480; ++ unsigned int stride = 640 * 4; ++ struct zwp_linux_buffer_params_v1 *params; ++ struct wl_buffer * w_buffer; ++ ++ if ((dh = picpool_get(sys->subpic_pool, stride * height)) == NULL) { ++ msg_Err(vd, "Failed to get DmaBuf for background"); ++ goto error; ++ } ++ ++ dmabuf_write_start(dh); ++ chequerboard(dmabuf_map(dh), stride, width, height); ++ dmabuf_write_end(dh); ++ ++ params = zwp_linux_dmabuf_v1_create_params(sys->linux_dmabuf_v1); ++ if (!params) { ++ msg_Err(vd, "zwp_linux_dmabuf_v1_create_params FAILED"); ++ goto error; ++ } ++ zwp_linux_buffer_params_v1_add(params, dmabuf_fd(dh), 0, 0, stride, 0, 0); ++ w_buffer = zwp_linux_buffer_params_v1_create_immed(params, width, height, DRM_FORMAT_XRGB8888, 0); ++ zwp_linux_buffer_params_v1_destroy(params); ++ if (!w_buffer) { ++ msg_Err(vd, "Failed to create background buffer"); ++ goto error; ++ } ++ ++ sys->bkg_viewport = wp_viewporter_get_viewport(sys->viewporter, bkg_surface(sys)); ++ ++ wl_buffer_add_listener(w_buffer, &subpic_buffer_listener, dh); ++ wl_surface_attach(bkg_surface(sys), w_buffer, 0, 0); ++ dh = NULL; ++ ++ wp_viewport_set_destination(sys->bkg_viewport, sys->bkg_w, sys->bkg_h); ++ mark_all_surface_opaque(video_compositor(sys), bkg_surface(sys)); ++ ++ wl_surface_commit(bkg_surface(sys)); ++ } ++ return VLC_SUCCESS; ++ ++error: ++ dmabuf_unref(&dh); ++ return VLC_ENOMEM; ++#endif ++} ++ ++static void ++set_video_viewport(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++ video_format_t fmt; ++ ++ if (!sys->video_attached || sys->viewport_set) ++ return; ++ ++ sys->viewport_set = true; ++ ++ video_format_ApplyRotation(&fmt, &vd->source); ++ wp_viewport_set_source(sys->viewport, ++ wl_fixed_from_int(fmt.i_x_offset), ++ wl_fixed_from_int(fmt.i_y_offset), ++ wl_fixed_from_int(fmt.i_visible_width), ++ wl_fixed_from_int(fmt.i_visible_height)); ++ wp_viewport_set_destination(sys->viewport, ++ sys->dst_rect.width, sys->dst_rect.height); ++ wl_subsurface_set_position(sys->video_subsurface, sys->dst_rect.x, sys->dst_rect.y); ++} ++ ++static void Prepare(vout_display_t *vd, picture_t *pic, subpicture_t *subpic) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ unsigned int n = 0; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "<<< %s: Surface: %p", __func__, sys->embed->handle.wl); ++#endif ++ check_embed(vd, sys, __func__); ++ ++ if (drmu_format_vlc_to_drm_prime(pic->format.i_chroma, NULL) == 0) { ++ copy_subpic_to_w_buffer(vd, sys, pic, 0xff, &sys->piccpy.dh, &sys->piccpy.wb); ++ } ++ else { ++ do_display_dmabuf(vd, sys, pic, &sys->pic_vdre, &sys->piccpy.wb); ++ } ++ ++ // Attempt to import the subpics ++ for (subpicture_t * spic = subpic; spic != NULL; spic = spic->p_next) ++ { ++ for (subpicture_region_t *sreg = spic->p_region; sreg != NULL; sreg = sreg->p_next) { ++ picture_t * const src = sreg->p_picture; ++ subpic_ent_t * const dst = sys->subpics + n; ++ ++ // If the same picture then assume the same contents ++ // We keep a ref to the previous pic to ensure that the same picture ++ // structure doesn't get reused and confuse us. ++ if (src != dst->pic || sreg->i_alpha != dst->alpha) { ++ subpic_ent_flush(dst); ++ ++ if (copy_subpic_to_w_buffer(vd, sys, src, sreg->i_alpha, &dst->dh, &dst->wb) != 0) ++ continue; ++ ++ dst->pic = picture_Hold(src); ++ dst->alpha = sreg->i_alpha; ++ dst->update = true; ++ } ++ ++ dst->src_rect = (vout_display_place_t) { ++ .x = sreg->fmt.i_x_offset, ++ .y = sreg->fmt.i_y_offset, ++ .width = sreg->fmt.i_visible_width, ++ .height = sreg->fmt.i_visible_height, ++ }; ++ dst->dst_rect = place_rescale( ++ (vout_display_place_t) { ++ .x = sreg->i_x, ++ .y = sreg->i_y, ++ .width = sreg->fmt.i_visible_width, ++ .height = sreg->fmt.i_visible_height, ++ }, ++ (vout_display_place_t) { ++ .x = 0, ++ .y = 0, ++ .width = sys->dst_rect.width, ++ .height = sys->dst_rect.height, ++ }, ++ sys->spu_rect); ++ ++ if (++n == MAX_SUBPICS) ++ goto subpics_done; ++ } ++ } ++subpics_done: ++ ++ // Clear any other entries ++ for (; n != MAX_SUBPICS; ++n) { ++ subpic_ent_t * const dst = sys->subpics + n; ++ ++ if (dst->dh != NULL) ++ dst->update = true; ++ subpic_ent_flush(dst); ++ } ++ ++ (void)pic; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s: Surface: %p", __func__, sys->embed->handle.wl); ++#endif ++} ++ ++static void Display(vout_display_t *vd, picture_t *pic, subpicture_t *subpic) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "<<< %s: Surface: %p", __func__, sys->embed->handle.wl); ++#endif ++ ++ check_embed(vd, sys, __func__); ++ ++ make_video_surface(vd, sys); ++ make_subpic_surfaces(vd, sys); ++ make_background(vd, sys); ++ ++ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) ++ { ++ subpic_ent_t * const spe = sys->subpics + i; ++ ++ if (!spe->update) ++ continue; ++ ++ msg_Dbg(vd, "%s: Update subpic %i: wb=%p alpha=%d", __func__, i, spe->wb, spe->alpha); ++ subpic_ent_attach(sys->subplanes[i].surface, spe); ++ ++ wl_subsurface_set_position(sys->subplanes[i].subsurface, spe->dst_rect.x, spe->dst_rect.y); ++ wp_viewport_set_source(sys->subplanes[i].viewport, ++ wl_fixed_from_int(spe->src_rect.x), wl_fixed_from_int(spe->src_rect.y), ++ wl_fixed_from_int(spe->src_rect.width), wl_fixed_from_int(spe->src_rect.height)); ++ wp_viewport_set_destination(sys->subplanes[i].viewport, spe->dst_rect.width, spe->dst_rect.height); ++ wl_surface_damage(sys->subplanes[i].surface, 0, 0, INT32_MAX, INT32_MAX); ++ ++ wl_surface_commit(sys->subplanes[i].surface); ++ spe->update = false; ++ } ++ ++ if (!sys->piccpy.wb) { ++ msg_Warn(vd, "Display called but prepared pic buffer"); ++ } ++ else { ++ subpic_ent_attach(video_surface(sys), &sys->piccpy); ++ sys->video_attached = true; ++ // Now up to the buffer callback to free stuff ++ sys->pic_vdre = NULL; ++ } ++ ++ set_video_viewport(vd, sys); ++ ++ wl_surface_damage(video_surface(sys), 0, 0, INT32_MAX, INT32_MAX); ++ wl_surface_commit(video_surface(sys)); ++ ++ // With VIDEO_ON_SUBSURFACE we need a commit on the background here ++ // too if the video surface isn't desync. Desync is set, but wayland ++ // can force sync if the bkg surface is a sync subsurface. ++ // Try adding a bkg surface commit here if things freeze with ++ // VIDEO_ON_SUBSURFACE set but don't with it unset ++ ++ wl_display_flush(video_display(sys)); ++// roundtrip(sys); ++ ++ if (subpic) ++ subpicture_Delete(subpic); ++ picture_Release(pic); ++ ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s: Surface: %p", __func__, sys->embed->handle.wl); ++#endif ++} ++ ++static void ResetPictures(vout_display_t *vd) ++{ ++ vout_display_sys_t *sys = vd->sys; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "<<< %s", __func__); ++#endif ++ check_embed(vd, sys, __func__); ++ ++ kill_pool(sys); ++ ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s", __func__); ++#endif ++} ++ ++static int Control(vout_display_t *vd, int query, va_list ap) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "<<< %s: Query=%d", __func__, query); ++#endif ++ check_embed(vd, sys, __func__); ++ ++ switch (query) ++ { ++ case VOUT_DISPLAY_RESET_PICTURES: ++ { ++ vout_display_place_t place; ++ video_format_t src; ++ ++ assert(sys->viewport == NULL); ++ ++ vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); ++ video_format_ApplyRotation(&src, &vd->source); ++ ++ vd->fmt.i_width = src.i_width * place.width ++ / src.i_visible_width; ++ vd->fmt.i_height = src.i_height * place.height ++ / src.i_visible_height; ++ vd->fmt.i_visible_width = place.width; ++ vd->fmt.i_visible_height = place.height; ++ vd->fmt.i_x_offset = src.i_x_offset * place.width ++ / src.i_visible_width; ++ vd->fmt.i_y_offset = src.i_y_offset * place.height ++ / src.i_visible_height; ++ ResetPictures(vd); ++ sys->curr_aspect = vd->source; ++ break; ++ } ++ ++ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: ++ case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED: ++ case VOUT_DISPLAY_CHANGE_ZOOM: ++ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: ++ case VOUT_DISPLAY_CHANGE_SOURCE_CROP: ++ { ++ const vout_display_cfg_t *cfg; ++ ++ if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT ++ || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) ++ { ++ cfg = vd->cfg; ++ } ++ else ++ { ++ cfg = va_arg(ap, const vout_display_cfg_t *); ++ } ++ ++#if !VIDEO_ON_SUBSURFACE ++ vout_display_place_t place; ++ ++ vout_display_PlacePicture(&place, &sys->curr_aspect, vd->cfg, false); ++ sys->x += place.width / 2; ++ sys->y += place.height / 2; ++ ++ vout_display_PlacePicture(&sys->dst_rect, &vd->source, cfg, false); ++ sys->x -= sys->dst_rect.width / 2; ++ sys->y -= sys->dst_rect.height / 2; ++#else ++ vout_display_PlacePicture(&sys->dst_rect, &vd->source, cfg, true); ++#endif ++ ++ place_spu_rect(vd, cfg, &vd->fmt); ++ sys->viewport_set = false; ++ ++ if (sys->viewport) ++ set_video_viewport(vd, sys); ++ ++#if VIDEO_ON_SUBSURFACE ++ if (sys->bkg_viewport != NULL && (cfg->display.width != sys->bkg_w || cfg->display.height != sys->bkg_h)) ++ { ++ sys->bkg_w = cfg->display.width; ++ sys->bkg_h = cfg->display.height; ++ ++ msg_Dbg(vd, "Resize background: %dx%d; surface=%p", cfg->display.width, cfg->display.height, bkg_surface(sys)); ++ wp_viewport_set_destination(sys->bkg_viewport, cfg->display.width, cfg->display.height); ++ wl_surface_commit(bkg_surface(sys)); ++ } ++#endif ++ ++ sys->curr_aspect = vd->source; ++ break; ++ } ++ default: ++ msg_Err(vd, "unknown request %d", query); ++ return VLC_EGENERIC; ++ } ++ ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s: Surface: %p", __func__, sys->embed->handle.wl); ++#endif ++ return VLC_SUCCESS; ++} ++ ++static void linux_dmabuf_v1_listener_format(void *data, ++ struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, ++ uint32_t format) ++{ ++ // Superceeded by _modifier ++ vout_display_t * const vd = data; ++ vout_display_sys_t * const sys = vd->sys; ++ (void)zwp_linux_dmabuf_v1; ++ ++ msg_Dbg(vd, "%s[%p], %.4s", __func__, (void*)vd, (const char *)&format); ++ ++ fmt_list_add(&sys->dmabuf_fmts, format, DRM_FORMAT_MOD_LINEAR, 0); ++} ++ ++static void ++linux_dmabuf_v1_listener_modifier(void *data, ++ struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, ++ uint32_t format, ++ uint32_t modifier_hi, ++ uint32_t modifier_lo) ++{ ++ vout_display_t * const vd = data; ++ vout_display_sys_t * const sys = vd->sys; ++ (void)zwp_linux_dmabuf_v1; ++ ++ msg_Dbg(vd, "%s[%p], %.4s %08x%08x", __func__, (void*)vd, (const char *)&format, modifier_hi, modifier_lo); ++ ++ fmt_list_add(&sys->dmabuf_fmts, format, modifier_lo | ((uint64_t)modifier_hi << 32), 0); ++} ++ ++static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = { ++ .format = linux_dmabuf_v1_listener_format, ++ .modifier = linux_dmabuf_v1_listener_modifier, ++}; ++ ++ ++static void registry_global_cb(void *data, struct wl_registry *registry, ++ uint32_t name, const char *iface, uint32_t vers) ++{ ++ vout_display_t * const vd = data; ++ vout_display_sys_t * const sys = vd->sys; ++ ++ msg_Dbg(vd, "global %3"PRIu32": %s version %"PRIu32, name, iface, vers); ++ ++ if (strcmp(iface, wl_subcompositor_interface.name) == 0) ++ sys->subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); ++ else ++ if (!strcmp(iface, "wp_viewporter")) ++ sys->viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1); ++ else ++ if (!strcmp(iface, "wl_compositor")) ++ { ++ if (vers >= 4) ++ sys->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); ++ else ++ msg_Warn(vd, "Interface %s wanted v 4 got v %d", wl_compositor_interface.name, vers); ++ } ++ else ++ if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0) ++ { ++ if (vers >= 3) ++ sys->linux_dmabuf_v1 = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, 3); ++ else ++ msg_Warn(vd, "Interface %s wanted v 3 got v %d", zwp_linux_dmabuf_v1_interface.name, vers); ++ } ++ ++} ++ ++static void registry_global_remove_cb(void *data, struct wl_registry *registry, ++ uint32_t name) ++{ ++ vout_display_t *vd = data; ++ ++ msg_Dbg(vd, "global remove %3"PRIu32, name); ++ (void) registry; ++} ++ ++static const struct wl_registry_listener registry_cbs = ++{ ++ registry_global_cb, ++ registry_global_remove_cb, ++}; ++ ++ ++static void ++clear_surface_buffer(struct wl_surface * surface) ++{ ++ if (surface == NULL) ++ return; ++ wl_surface_attach(surface, NULL, 0, 0); ++ wl_surface_commit(surface); ++} ++ ++static void ++clear_all_buffers(vout_display_sys_t *sys) ++{ ++ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) ++ { ++ subpic_ent_t * const spe = sys->subpics + i; ++ subpic_ent_flush(spe); ++ ++ clear_surface_buffer(sys->subplanes[i].surface); ++ } ++ ++ clear_surface_buffer(video_surface(sys)); ++ sys->video_attached = false; ++ ++#if VIDEO_ON_SUBSURFACE ++ clear_surface_buffer(bkg_surface(sys)); ++#endif ++ ++ subpic_ent_flush(&sys->piccpy); ++ vdre_delete(&sys->pic_vdre); ++} ++ ++ ++static void Close(vlc_object_t *obj) ++{ ++ vout_display_t * const vd = (vout_display_t *)obj; ++ vout_display_sys_t * const sys = vd->sys; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "<<< %s", __func__); ++#endif ++ ++ if (sys == NULL) ++ return; ++ ++ if (sys->embed == NULL) ++ goto no_window; ++ ++ check_embed(vd, sys, __func__); ++ ++ clear_all_buffers(sys); ++ ++ // Free subpic resources ++ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) ++ { ++ subplane_t * const spl = sys->subplanes + i; ++ ++ viewport_destroy(&spl->viewport); ++ subsurface_destroy(&spl->subsurface); ++ surface_destroy(&spl->surface); ++ } ++ ++ viewport_destroy(&sys->viewport); ++ ++#if VIDEO_ON_SUBSURFACE ++ subsurface_destroy(&sys->video_subsurface); ++ surface_destroy(&sys->video_surface); ++ ++ viewport_destroy(&sys->bkg_viewport); ++#endif ++ ++ wl_display_flush(video_display(sys)); ++ ++ if (sys->viewporter != NULL) ++ wp_viewporter_destroy(sys->viewporter); ++ if (sys->linux_dmabuf_v1 != NULL) ++ zwp_linux_dmabuf_v1_destroy(sys->linux_dmabuf_v1); ++ if (sys->subcompositor != NULL) ++ wl_subcompositor_destroy(sys->subcompositor); ++ if (sys->compositor != NULL) ++ wl_compositor_destroy(sys->compositor); ++ ++ wl_display_flush(video_display(sys)); ++ ++ vout_display_DeleteWindow(vd, sys->embed); ++ sys->embed = NULL; ++ ++ kill_pool(sys); ++ picpool_unref(&sys->subpic_pool); ++ pollqueue_unref(&sys->pollq); ++ ++ free(sys->subpic_chromas); ++ ++no_window: ++ fmt_list_uninit(&sys->dmabuf_fmts); ++ free(sys); ++ ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s", __func__); ++#endif ++} ++ ++static int Open(vlc_object_t *obj) ++{ ++ vout_display_t * const vd = (vout_display_t *)obj; ++ vout_display_sys_t *sys; ++ uint32_t pic_drm_fmt = 0; ++ uint64_t pic_drm_mod = DRM_FORMAT_MOD_LINEAR; ++ ++ msg_Info(vd, "<<< %s: %.4s %dx%d, cfg.display: %dx%d", __func__, ++ (const char*)&vd->fmt.i_chroma, vd->fmt.i_width, vd->fmt.i_height, ++ vd->cfg->display.width, vd->cfg->display.height); ++ ++ if (!(pic_drm_fmt = drmu_format_vlc_to_drm(&vd->fmt)) && ++ !(pic_drm_fmt = drmu_format_vlc_to_drm_prime(vd->fmt.i_chroma, &pic_drm_mod))) ++ return VLC_EGENERIC; ++ ++ sys = calloc(1, sizeof(*sys)); ++ if (unlikely(sys == NULL)) ++ return VLC_ENOMEM; ++ ++ vd->sys = sys; ++ ++ if (fmt_list_init(&sys->dmabuf_fmts, 128)) { ++ msg_Err(vd, "Failed to allocate format list!"); ++ goto error; ++ } ++ ++ /* Get window */ ++ sys->embed = vout_display_NewWindow(vd, VOUT_WINDOW_TYPE_WAYLAND); ++ if (sys->embed == NULL) ++ goto error; ++ sys->last_embed_surface = sys->embed->handle.wl; ++ ++ { ++ struct wl_registry * const registry = wl_display_get_registry(video_display(sys)); ++ if (registry == NULL) { ++ msg_Err(vd, "Cannot get registry for display"); ++ goto error; ++ } ++ ++ wl_registry_add_listener(registry, ®istry_cbs, vd); ++ roundtrip(sys); ++ wl_registry_destroy(registry); ++ } ++ ++ if (sys->compositor == NULL) { ++ msg_Warn(vd, "Interface %s missing", wl_compositor_interface.name); ++ goto error; ++ } ++ if (sys->subcompositor == NULL) { ++ msg_Warn(vd, "Interface %s missing", wl_subcompositor_interface.name); ++ goto error; ++ } ++ if (sys->viewporter == NULL) { ++ msg_Warn(vd, "Interface %s missing", wp_viewporter_interface.name); ++ goto error; ++ } ++ if (sys->linux_dmabuf_v1 == NULL) { ++ msg_Warn(vd, "Interface %s missing", zwp_linux_dmabuf_v1_interface.name); ++ goto error; ++ } ++ ++ // And again for registering formats ++ zwp_linux_dmabuf_v1_add_listener(sys->linux_dmabuf_v1, &linux_dmabuf_v1_listener, vd); ++ ++ roundtrip(sys); ++ ++ fmt_list_sort(&sys->dmabuf_fmts); ++ ++ // Check PIC DRM format here ++ if (fmt_list_find(&sys->dmabuf_fmts, pic_drm_fmt, pic_drm_mod) < 0) { ++ msg_Warn(vd, "Could not find %.4s mod %#"PRIx64" in supported formats", (char*)&pic_drm_fmt, pic_drm_mod); ++ goto error; ++ } ++ ++ { ++ static vlc_fourcc_t const tryfmts[] = { ++ VLC_CODEC_RGBA, ++ VLC_CODEC_BGRA, ++ }; ++ unsigned int n = 0; ++ ++ if ((sys->subpic_chromas = calloc(ARRAY_SIZE(tryfmts) + 1, sizeof(vlc_fourcc_t))) == NULL) ++ goto error; ++ for (unsigned int i = 0; i != ARRAY_SIZE(tryfmts); ++i) ++ { ++ uint32_t drmfmt = drmu_format_vlc_chroma_to_drm(tryfmts[i]); ++ msg_Dbg(vd, "Look for %.4s", (char*)&drmfmt); ++ if (fmt_list_find(&sys->dmabuf_fmts, drmfmt, DRM_FORMAT_MOD_LINEAR) >= 0) ++ sys->subpic_chromas[n++] = tryfmts[i]; ++ } ++ ++ if (n == 0) ++ msg_Warn(vd, "No compatible subpic formats found"); ++ } ++ ++ { ++ struct dmabufs_ctl * dbsc = dmabufs_ctl_new(); ++ if (dbsc == NULL) ++ { ++ msg_Err(vd, "Failed to create dmabuf ctl"); ++ goto error; ++ } ++ sys->subpic_pool = picpool_new(dbsc); ++ dmabufs_ctl_unref(&dbsc); ++ if (sys->subpic_pool == NULL) ++ { ++ msg_Err(vd, "Failed to create picpool"); ++ goto error; ++ } ++ } ++ ++ if ((sys->pollq = pollqueue_new()) == NULL) ++ { ++ msg_Err(vd, "Failed to create pollqueue"); ++ goto error; ++ } ++ ++ sys->curr_aspect = vd->source; ++ sys->bkg_w = vd->cfg->display.width; ++ sys->bkg_h = vd->cfg->display.height; ++ ++ vd->info.has_pictures_invalid = sys->viewport == NULL; ++ vd->info.subpicture_chromas = sys->subpic_chromas; ++ ++ vd->pool = vd_dmabuf_pool; ++ vd->prepare = Prepare; ++ vd->display = Display; ++ vd->control = Control; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s: OK", __func__); ++#endif ++ return VLC_SUCCESS; ++ ++error: ++ Close(obj); ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s: ERROR", __func__); ++#endif ++ return VLC_EGENERIC; ++} ++ ++vlc_module_begin() ++ set_shortname(N_("WL DMABUF")) ++ set_description(N_("Wayland dmabuf video output")) ++ set_category(CAT_VIDEO) ++ set_subcategory(SUBCAT_VIDEO_VOUT) ++ set_capability("vout display", 0) ++ set_callbacks(Open, Close) ++ add_shortcut("wl-dmabuf") ++vlc_module_end() +diff --git a/modules/video_output/wayland/dmabuf_alloc.c b/modules/video_output/wayland/dmabuf_alloc.c +new file mode 100644 +index 0000000000..31578c3098 +--- /dev/null ++++ b/modules/video_output/wayland/dmabuf_alloc.c +@@ -0,0 +1,417 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dmabuf_alloc.h" ++ ++#define DMABUF_NAME1 "/dev/dma_heap/linux,cma" ++#define DMABUF_NAME2 "/dev/dma_heap/reserved" ++ ++#define TRACE_ALLOC 0 ++ ++#define request_log(...) ++#define request_debug(...) ++ ++struct dmabufs_ctl; ++struct dmabuf_h; ++ ++struct dmabuf_fns { ++ int (*buf_alloc)(struct dmabufs_ctl * dbsc, struct dmabuf_h * dh, size_t size); ++ void (*buf_free)(struct dmabuf_h * dh); ++ int (*ctl_new)(struct dmabufs_ctl * dbsc); ++ void (*ctl_free)(struct dmabufs_ctl * dbsc); ++}; ++ ++struct dmabufs_ctl { ++ atomic_int ref_count; ++ int fd; ++ size_t page_size; ++ void * v; ++ const struct dmabuf_fns * fns; ++}; ++ ++struct dmabuf_h { ++ atomic_int ref_count; ++ int fd; ++ size_t size; ++ size_t len; ++ void * mapptr; ++ void * v; ++ const struct dmabuf_fns * fns; ++ ++ void * predel_v; ++ int (* predel_fn)(struct dmabuf_h * dh, void * v); ++}; ++ ++#if TRACE_ALLOC ++static unsigned int total_bufs = 0; ++static size_t total_size = 0; ++#endif ++ ++struct dmabuf_h * dmabuf_import_mmap(void * mapptr, size_t size) ++{ ++ struct dmabuf_h *dh; ++ ++ if (mapptr == MAP_FAILED) ++ return NULL; ++ ++ dh = malloc(sizeof(*dh)); ++ if (!dh) ++ return NULL; ++ ++ *dh = (struct dmabuf_h) { ++ .fd = -1, ++ .size = size, ++ .mapptr = mapptr ++ }; ++ ++ return dh; ++} ++ ++struct dmabuf_h * dmabuf_import(int fd, size_t size) ++{ ++ struct dmabuf_h *dh; ++ ++ fd = dup(fd); ++ if (fd < 0 || size == 0) ++ return NULL; ++ ++ dh = malloc(sizeof(*dh)); ++ if (!dh) { ++ close(fd); ++ return NULL; ++ } ++ ++ *dh = (struct dmabuf_h) { ++ .fd = fd, ++ .size = size, ++ .mapptr = MAP_FAILED ++ }; ++ ++#if TRACE_ALLOC ++ ++total_bufs; ++ total_size += dh->size; ++ request_log("%s: Import: %zd, total=%zd, bufs=%d\n", __func__, dh->size, total_size, total_bufs); ++#endif ++ ++ return dh; ++} ++ ++void dmabuf_free(struct dmabuf_h * dh) ++{ ++ if (!dh) ++ return; ++ ++#if TRACE_ALLOC ++ --total_bufs; ++ total_size -= dh->size; ++ request_log("%s: Free: %zd, total=%zd, bufs=%d\n", __func__, dh->size, total_size, total_bufs); ++#endif ++ ++ dh->fns->buf_free(dh); ++ ++ if (dh->mapptr != MAP_FAILED && dh->mapptr != NULL) ++ munmap(dh->mapptr, dh->size); ++ if (dh->fd != -1) ++ while (close(dh->fd) == -1 && errno == EINTR) ++ /* loop */; ++ free(dh); ++} ++ ++void dmabuf_unref(struct dmabuf_h ** const ppdh) ++{ ++ struct dmabuf_h * dh = *ppdh; ++ int n; ++ ++ if (dh == NULL) ++ return; ++ *ppdh = NULL; ++ ++ n = atomic_fetch_sub(&dh->ref_count, 1); ++// fprintf(stderr, "%s[%p]: Ref: %d\n", __func__, dh, n); ++ if (n != 0) ++ return; ++ ++ if (dh->predel_fn && dh->predel_fn(dh, dh->predel_v) != 0) ++ return; ++ ++ dmabuf_free(dh); ++} ++ ++struct dmabuf_h * dmabuf_ref(struct dmabuf_h * const dh) ++{ ++ if (dh != NULL) ++ { ++ int n = atomic_fetch_add(&dh->ref_count, 1); ++// fprintf(stderr, "%s[%p]: Ref: %d\n", __func__, dh, n); ++ (void)n; ++ } ++ return dh; ++} ++ ++void dmabuf_predel_cb_set(struct dmabuf_h * const dh, ++ int (* const predel_fn)(struct dmabuf_h * dh, void * v), void * const predel_v) ++{ ++ dh->predel_fn = predel_fn; ++ dh->predel_v = predel_v; ++} ++ ++struct dmabuf_h * dmabuf_realloc(struct dmabufs_ctl * dbsc, struct dmabuf_h * old, size_t size) ++{ ++ struct dmabuf_h * dh; ++ if (old != NULL) { ++ if (old->size >= size) { ++ return old; ++ } ++ dmabuf_free(old); ++ } ++ ++ if (size == 0 || ++ (dh = malloc(sizeof(*dh))) == NULL) ++ return NULL; ++ ++ *dh = (struct dmabuf_h){ ++ .fd = -1, ++ .mapptr = MAP_FAILED, ++ .fns = dbsc->fns ++ }; ++ ++ if (dh->fns->buf_alloc(dbsc, dh, size) != 0) ++ goto fail; ++ ++ ++#if TRACE_ALLOC ++ ++total_bufs; ++ total_size += dh->size; ++ request_log("%s: Alloc: %zd, total=%zd, bufs=%d\n", __func__, dh->size, total_size, total_bufs); ++#endif ++ ++ return dh; ++ ++fail: ++ free(dh); ++ return NULL; ++} ++ ++int dmabuf_sync(struct dmabuf_h * const dh, unsigned int flags) ++{ ++ struct dma_buf_sync sync = { ++ .flags = flags ++ }; ++ if (dh->fd == -1) ++ return 0; ++ while (ioctl(dh->fd, DMA_BUF_IOCTL_SYNC, &sync) == -1) { ++ const int err = errno; ++ if (errno == EINTR) ++ continue; ++ request_log("%s: ioctl failed: flags=%#x\n", __func__, flags); ++ return -err; ++ } ++ return 0; ++} ++ ++int dmabuf_write_start(struct dmabuf_h * const dh) ++{ ++ return dmabuf_sync(dh, DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE); ++} ++ ++int dmabuf_write_end(struct dmabuf_h * const dh) ++{ ++ return dmabuf_sync(dh, DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE); ++} ++ ++int dmabuf_read_start(struct dmabuf_h * const dh) ++{ ++ if (!dmabuf_map(dh)) ++ return -1; ++ return dmabuf_sync(dh, DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ); ++} ++ ++int dmabuf_read_end(struct dmabuf_h * const dh) ++{ ++ return dmabuf_sync(dh, DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ); ++} ++ ++ ++void * dmabuf_map(struct dmabuf_h * const dh) ++{ ++ if (!dh) ++ return NULL; ++ if (dh->mapptr != MAP_FAILED) ++ return dh->mapptr; ++ dh->mapptr = mmap(NULL, dh->size, ++ PROT_READ | PROT_WRITE, ++ MAP_SHARED | MAP_POPULATE, ++ dh->fd, 0); ++ if (dh->mapptr == MAP_FAILED) { ++ request_log("%s: Map failed\n", __func__); ++ return NULL; ++ } ++ return dh->mapptr; ++} ++ ++int dmabuf_fd(const struct dmabuf_h * const dh) ++{ ++ if (!dh) ++ return -1; ++ return dh->fd; ++} ++ ++size_t dmabuf_size(const struct dmabuf_h * const dh) ++{ ++ if (!dh) ++ return 0; ++ return dh->size; ++} ++ ++size_t dmabuf_len(const struct dmabuf_h * const dh) ++{ ++ if (!dh) ++ return 0; ++ return dh->len; ++} ++ ++void dmabuf_len_set(struct dmabuf_h * const dh, const size_t len) ++{ ++ dh->len = len; ++} ++ ++static struct dmabufs_ctl * dmabufs_ctl_new2(const struct dmabuf_fns * const fns) ++{ ++ struct dmabufs_ctl * dbsc = calloc(1, sizeof(*dbsc)); ++ ++ if (!dbsc) ++ return NULL; ++ ++ dbsc->fd = -1; ++ dbsc->fns = fns; ++ dbsc->page_size = (size_t)sysconf(_SC_PAGE_SIZE); ++ ++ if (fns->ctl_new(dbsc) != 0) ++ goto fail; ++ ++ return dbsc; ++ ++fail: ++ free(dbsc); ++ return NULL; ++} ++ ++static void dmabufs_ctl_free(struct dmabufs_ctl * const dbsc) ++{ ++ request_debug(NULL, "Free dmabuf ctl\n"); ++ ++ dbsc->fns->ctl_free(dbsc); ++ ++ free(dbsc); ++} ++ ++void dmabufs_ctl_unref(struct dmabufs_ctl ** const pDbsc) ++{ ++ struct dmabufs_ctl * const dbsc = *pDbsc; ++ ++ if (!dbsc) ++ return; ++ *pDbsc = NULL; ++ ++ if (atomic_fetch_sub(&dbsc->ref_count, 1) != 0) ++ return; ++ ++ dmabufs_ctl_free(dbsc); ++} ++ ++struct dmabufs_ctl * dmabufs_ctl_ref(struct dmabufs_ctl * const dbsc) ++{ ++ atomic_fetch_add(&dbsc->ref_count, 1); ++ return dbsc; ++} ++ ++//----------------------------------------------------------------------------- ++// ++// Alloc dmabuf via CMA ++ ++static int ctl_cma_new(struct dmabufs_ctl * dbsc) ++{ ++ while ((dbsc->fd = open(DMABUF_NAME1, O_RDWR)) == -1 && ++ errno == EINTR) ++ /* Loop */; ++ ++ if (dbsc->fd == -1) { ++ while ((dbsc->fd = open(DMABUF_NAME2, O_RDWR)) == -1 && ++ errno == EINTR) ++ /* Loop */; ++ if (dbsc->fd == -1) { ++ request_log("Unable to open either %s or %s\n", ++ DMABUF_NAME1, DMABUF_NAME2); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static void ctl_cma_free(struct dmabufs_ctl * dbsc) ++{ ++ if (dbsc->fd != -1) ++ while (close(dbsc->fd) == -1 && errno == EINTR) ++ /* loop */; ++ ++} ++ ++static int buf_cma_alloc(struct dmabufs_ctl * const dbsc, struct dmabuf_h * dh, size_t size) ++{ ++ struct dma_heap_allocation_data data = { ++ .len = (size + dbsc->page_size - 1) & ~(dbsc->page_size - 1), ++ .fd = 0, ++ .fd_flags = O_RDWR, ++ .heap_flags = 0 ++ }; ++ ++ while (ioctl(dbsc->fd, DMA_HEAP_IOCTL_ALLOC, &data)) { ++ int err = errno; ++ request_log("Failed to alloc %" PRIu64 " from dma-heap(fd=%d): %d (%s)\n", ++ (uint64_t)data.len, ++ dbsc->fd, ++ err, ++ strerror(err)); ++ if (err == EINTR) ++ continue; ++ return -err; ++ } ++ ++ dh->fd = data.fd; ++ dh->size = (size_t)data.len; ++ ++// fprintf(stderr, "%s: size=%#zx, ftell=%#zx\n", __func__, ++// dh->size, (size_t)lseek(dh->fd, 0, SEEK_END)); ++ ++ return 0; ++} ++ ++static void buf_cma_free(struct dmabuf_h * dh) ++{ ++ (void)dh; ++ // Nothing needed ++} ++ ++static const struct dmabuf_fns dmabuf_cma_fns = { ++ .buf_alloc = buf_cma_alloc, ++ .buf_free = buf_cma_free, ++ .ctl_new = ctl_cma_new, ++ .ctl_free = ctl_cma_free, ++}; ++ ++struct dmabufs_ctl * dmabufs_ctl_new(void) ++{ ++ request_debug(NULL, "Dmabufs using CMA\n");; ++ return dmabufs_ctl_new2(&dmabuf_cma_fns); ++} +diff --git a/modules/video_output/wayland/dmabuf_alloc.h b/modules/video_output/wayland/dmabuf_alloc.h +new file mode 100644 +index 0000000000..4ca02e0567 +--- /dev/null ++++ b/modules/video_output/wayland/dmabuf_alloc.h +@@ -0,0 +1,52 @@ ++#ifndef _WAYLAND_DMABUF_ALLOC_H ++#define _WAYLAND_DMABUF_ALLOC_H ++ ++#include ++ ++struct dmabufs_ctl; ++struct dmabuf_h; ++ ++struct dmabufs_ctl * dmabufs_ctl_new(void); ++void dmabufs_ctl_unref(struct dmabufs_ctl ** const pdbsc); ++struct dmabufs_ctl * dmabufs_ctl_ref(struct dmabufs_ctl * const dbsc); ++ ++// Need not preserve old contents ++// On NULL return old buffer is freed ++struct dmabuf_h * dmabuf_realloc(struct dmabufs_ctl * dbsc, struct dmabuf_h *, size_t size); ++ ++static inline struct dmabuf_h * dmabuf_alloc(struct dmabufs_ctl * dbsc, size_t size) { ++ return dmabuf_realloc(dbsc, NULL, size); ++} ++/* Create from existing fd - dups(fd) */ ++struct dmabuf_h * dmabuf_import(int fd, size_t size); ++/* Import an MMAP - return NULL if mapptr = MAP_FAIL */ ++struct dmabuf_h * dmabuf_import_mmap(void * mapptr, size_t size); ++ ++void * dmabuf_map(struct dmabuf_h * const dh); ++ ++/* flags from linux/dmabuf.h DMA_BUF_SYNC_xxx */ ++int dmabuf_sync(struct dmabuf_h * const dh, unsigned int flags); ++ ++int dmabuf_write_start(struct dmabuf_h * const dh); ++int dmabuf_write_end(struct dmabuf_h * const dh); ++int dmabuf_read_start(struct dmabuf_h * const dh); ++int dmabuf_read_end(struct dmabuf_h * const dh); ++ ++int dmabuf_fd(const struct dmabuf_h * const dh); ++/* Allocated size */ ++size_t dmabuf_size(const struct dmabuf_h * const dh); ++/* Bytes in use */ ++size_t dmabuf_len(const struct dmabuf_h * const dh); ++/* Set bytes in use */ ++void dmabuf_len_set(struct dmabuf_h * const dh, const size_t len); ++ ++void dmabuf_predel_cb_set(struct dmabuf_h * const dh, ++ int (* const predel_fn)(struct dmabuf_h * dh, void * v), void * const predel_v); ++static inline void dmabuf_predel_cb_unset(struct dmabuf_h * const dh) {dmabuf_predel_cb_set(dh, 0, NULL);} ++ ++void dmabuf_unref(struct dmabuf_h ** const ppdh); ++struct dmabuf_h * dmabuf_ref(struct dmabuf_h * const dh); ++ ++void dmabuf_free(struct dmabuf_h * dh); ++ ++#endif +diff --git a/modules/video_output/wayland/picpool.c b/modules/video_output/wayland/picpool.c +new file mode 100644 +index 0000000000..fb3b8e78c1 +--- /dev/null ++++ b/modules/video_output/wayland/picpool.c +@@ -0,0 +1,368 @@ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "picpool.h" ++#include "dmabuf_alloc.h" ++ ++// =========================================================================== ++ ++typedef struct pool_ent_s ++{ ++ struct pool_ent_s * next; ++ struct pool_ent_s * prev; ++ ++ atomic_int ref_count; ++ unsigned int seq; ++ ++ struct picpool_ctl_s * pc; ++ struct dmabuf_h * db; ++ ++ size_t size; ++ unsigned int width; ++ unsigned int height; ++} pool_ent_t; ++ ++ ++typedef struct ent_list_hdr_s ++{ ++ pool_ent_t * ents; ++ pool_ent_t * tail; ++ unsigned int n; ++} ent_list_hdr_t; ++ ++#define ENT_LIST_HDR_INIT (ent_list_hdr_t){ \ ++ .ents = NULL, \ ++ .tail = NULL, \ ++ .n = 0 \ ++} ++ ++struct picpool_ctl_s ++{ ++ atomic_int ref_count; ++ ++ ent_list_hdr_t ent_pool; ++ ent_list_hdr_t ents_cur; ++ ent_list_hdr_t ents_prev; ++ ++ unsigned int max_n; ++ unsigned int seq; ++ ++ vlc_mutex_t lock; ++ struct dmabufs_ctl * dbsc; ++}; ++ ++ ++static pool_ent_t * ent_extract(ent_list_hdr_t * const elh, pool_ent_t * const ent) ++{ ++// printf("List %p [%d]: Ext %p\n", elh, elh->n, ent); ++ ++ if (ent == NULL) ++ return NULL; ++ ++ if (ent->next == NULL) ++ elh->tail = ent->prev; ++ else ++ ent->next->prev = ent->prev; ++ ++ if (ent->prev == NULL) ++ elh->ents = ent->next; ++ else ++ ent->prev->next = ent->next; ++ ++ ent->prev = ent->next = NULL; ++ ++ --elh->n; ++ ++ return ent; // For convienience ++} ++ ++static inline pool_ent_t * ent_extract_tail(ent_list_hdr_t * const elh) ++{ ++ return ent_extract(elh, elh->tail); ++} ++ ++static void ent_add_head(ent_list_hdr_t * const elh, pool_ent_t * const ent) ++{ ++// printf("List %p [%d]: Add %p\n", elh, elh->n, ent); ++ ++ if ((ent->next = elh->ents) == NULL) ++ elh->tail = ent; ++ else ++ ent->next->prev = ent; ++ ++ ent->prev = NULL; ++ elh->ents = ent; ++ ++elh->n; ++} ++ ++static void ent_free(pool_ent_t * const ent) ++{ ++// printf("Free ent: %p\n", ent); ++ if (ent != NULL) ++ { ++ // If we still have a ref to a buffer - kill it now ++ dmabuf_free(ent->db); ++ free(ent); ++ } ++} ++ ++static void ent_free_list(ent_list_hdr_t * const elh) ++{ ++ pool_ent_t * ent = elh->ents; ++ ++// printf("Free list: %p [%d]\n", elh, elh->n); ++ ++ *elh = ENT_LIST_HDR_INIT; ++ ++ while (ent != NULL) { ++ pool_ent_t * const t = ent; ++ ent = t->next; ++ ent_free(t); ++ } ++} ++ ++#if 0 ++static void ent_list_move(ent_list_hdr_t * const dst, ent_list_hdr_t * const src) ++{ ++// printf("Move %p->%p\n", src, dst); ++ ++ *dst = *src; ++ *src = ENT_LIST_HDR_INIT; ++} ++#endif ++ ++#if 0 ++// Scans "backwards" as that should give us the fastest match if we are ++// presented with pics in the same order each time ++static pool_ent_t * ent_list_extract_pic_ent(ent_list_hdr_t * const elh, picture_t * const pic) ++{ ++ pool_ent_t *ent = elh->tail; ++ ++// printf("Find list: %p [%d]; pic:%p\n", elh, elh->n, pic); ++ ++ while (ent != NULL) { ++// printf("Check ent: %p, pic:%p\n", ent, ent->pic); ++ ++ if (ent->pic == pic) ++ return ent_extract(elh, ent); ++ ent = ent->prev; ++ } ++ return NULL; ++} ++#endif ++ ++#define POOL_ENT_ALLOC_BLOCK 0x10000 ++ ++static pool_ent_t * pool_ent_alloc_new(picpool_ctl_t * const pc, size_t req_size) ++{ ++ pool_ent_t * ent = calloc(1, sizeof(*ent)); ++ const size_t alloc_size = (req_size + POOL_ENT_ALLOC_BLOCK - 1) & ~(POOL_ENT_ALLOC_BLOCK - 1); ++ ++ if (ent == NULL) ++ return NULL; ++ ++ ent->next = ent->prev = NULL; ++ ++ // Alloc ++ if ((ent->db = dmabuf_realloc(pc->dbsc, NULL, alloc_size)) == NULL) ++ goto fail1; ++// fprintf(stderr, "%s: ent %p db %p req=%zd size=%zd\n", __func__, ent, ent->db, req_size, alloc_size); ++ ent->size = dmabuf_size(ent->db); ++ return ent; ++ ++fail1: ++ free(ent); ++ return NULL; ++} ++ ++static inline pool_ent_t * pool_ent_ref(pool_ent_t * const ent) ++{ ++// int n = atomic_fetch_add(&ent->ref_count, 1) + 1; ++// printf("Ref: %p: %d\n", ent, n); ++ atomic_fetch_add(&ent->ref_count, 1); ++ return ent; ++} ++ ++static void pool_recycle(picpool_ctl_t * const pc, pool_ent_t * const ent) ++{ ++ pool_ent_t * xs = NULL; ++ int n; ++ ++ if (ent == NULL) ++ return; ++ ++ n = atomic_fetch_sub(&ent->ref_count, 1) - 1; ++ ++// fprintf(stderr, "%s: Pool: %p: Ent: %p: %d dh: %p\n", __func__, &pc->ent_pool, ent, n, ent->db); ++ ++ if (n != 0) ++ return; ++ ++ vlc_mutex_lock(&pc->lock); ++ ++ // If we have a full pool then extract the LRU and free it ++ // Free done outside mutex ++ if (pc->ent_pool.n >= pc->max_n) ++ xs = ent_extract_tail(&pc->ent_pool); ++ ++ ent_add_head(&pc->ent_pool, ent); ++ ++ vlc_mutex_unlock(&pc->lock); ++ ++ ent_free(xs); ++} ++ ++// * This could be made more efficient, but this is easy ++static void pool_recycle_list(picpool_ctl_t * const pc, ent_list_hdr_t * const elh) ++{ ++ pool_ent_t * ent; ++ while ((ent = ent_extract_tail(elh)) != NULL) { ++ pool_recycle(pc, ent); ++ } ++} ++ ++static int pool_predel_cb(struct dmabuf_h * dh, void * v) ++{ ++ pool_ent_t * const ent = v; ++ picpool_ctl_t * pc = ent->pc; ++ ++ assert(ent->db == dh); ++ ++ ent->pc = NULL; ++ dmabuf_ref(dh); ++ dmabuf_predel_cb_unset(dh); ++ pool_recycle(pc, ent); ++ picpool_unref(&pc); ++ return 1; // Do not delete ++} ++ ++struct dmabuf_h * picpool_get(picpool_ctl_t * const pc, size_t req_size) ++{ ++ pool_ent_t * best = NULL; ++ ++ vlc_mutex_lock(&pc->lock); ++ ++ { ++ pool_ent_t * ent = pc->ent_pool.ents; ++ ++ // Simple scan ++ while (ent != NULL) { ++ if (ent->size >= req_size && ent->size <= req_size * 2 + POOL_ENT_ALLOC_BLOCK && ++ (best == NULL || best->size > ent->size)) ++ best = ent; ++ ent = ent->next; ++ } ++ ++ // extract best from chain if we've found it ++ ent_extract(&pc->ent_pool, best); ++ } ++ ++ vlc_mutex_unlock(&pc->lock); ++ ++ if (best == NULL) { ++ if ((best = pool_ent_alloc_new(pc, req_size)) == NULL) ++ return NULL; ++ } ++ ++ if ((best->seq = ++pc->seq) == 0) ++ best->seq = ++pc->seq; // Never allow to be zero ++ ++ atomic_store(&best->ref_count, 1); ++ best->pc = picpool_ref(pc); ++ dmabuf_predel_cb_set(best->db, pool_predel_cb, best); ++// fprintf(stderr, "%s: find ent %p db %p size %zd\n", __func__, best, best->db, best->size); ++ return best->db; ++} ++ ++void picpool_flush(picpool_ctl_t * const pc) ++{ ++ pool_recycle_list(pc, &pc->ents_prev); ++ pool_recycle_list(pc, &pc->ents_cur); ++} ++ ++static void picpool_delete(picpool_ctl_t * const pc) ++{ ++ ++// printf("<<< %s\n", __func__); ++ ++ picpool_flush(pc); ++ ++ ent_free_list(&pc->ent_pool); ++ ++ dmabufs_ctl_unref(&pc->dbsc); ++ ++ vlc_mutex_destroy(&pc->lock); ++ ++// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash ++ free (pc); ++ ++ // printf(">>> %s\n", __func__); ++} ++ ++void picpool_unref(picpool_ctl_t ** const pppc) ++{ ++ int n; ++ picpool_ctl_t * const pc = *pppc; ++ ++ if (pc == NULL) ++ return; ++ *pppc = NULL; ++ ++ n = atomic_fetch_sub(&pc->ref_count, 1) - 1; ++ ++ if (n != 0) ++ return; ++ ++ picpool_delete(pc); ++} ++ ++picpool_ctl_t * picpool_ref(picpool_ctl_t * const pc) ++{ ++ atomic_fetch_add(&pc->ref_count, 1); ++ return pc; ++} ++ ++#if 0 ++static MMAL_BOOL_T vcz_pool_release_cb(MMAL_POOL_T * buf_pool, MMAL_BUFFER_HEADER_T *buf, void *userdata) ++{ ++ picpool_ctl_t * const pc = userdata; ++ vzc_subbuf_ent_t * const sb = buf->user_data; ++ ++ VLC_UNUSED(buf_pool); ++ ++// printf("<<< %s\n", __func__); ++ ++ if (sb != NULL) { ++ buf->user_data = NULL; ++ pool_recycle(pc, sb->ent); ++ picpool_release(pc); ++ free(sb); ++ } ++ ++// printf(">>> %s\n", __func__); ++ ++ return MMAL_TRUE; ++} ++#endif ++ ++picpool_ctl_t * picpool_new(struct dmabufs_ctl * dbsc) ++{ ++ picpool_ctl_t * pc; ++ ++ if (dbsc == NULL) ++ return NULL; ++ ++ pc = calloc(1, sizeof(*pc)); ++ if (pc == NULL) ++ return NULL; ++ ++ pc->max_n = 8; ++ pc->dbsc = dmabufs_ctl_ref(dbsc); ++ atomic_store(&pc->ref_count, 1); ++ ++ return pc; ++} +diff --git a/modules/video_output/wayland/picpool.h b/modules/video_output/wayland/picpool.h +new file mode 100644 +index 0000000000..d188c8b416 +--- /dev/null ++++ b/modules/video_output/wayland/picpool.h +@@ -0,0 +1,24 @@ ++#ifndef _WAYLAND_PICPOOL_H ++#define _WAYLAND_PICPOOL_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct dmabuf_h; ++struct dmabufs_ctl; ++struct picpool_ctl_s; ++typedef struct picpool_ctl_s picpool_ctl_t; ++ ++struct dmabuf_h * picpool_get(picpool_ctl_t * const pc, size_t req_size); ++ ++void picpool_flush(picpool_ctl_t * const pc); ++void picpool_unref(picpool_ctl_t ** const pppc); ++picpool_ctl_t * picpool_ref(picpool_ctl_t * const pc); ++picpool_ctl_t * picpool_new(struct dmabufs_ctl * dbsc); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/modules/video_output/wayland/rgba_premul.c b/modules/video_output/wayland/rgba_premul.c +new file mode 100644 +index 0000000000..a60bf78a1c +--- /dev/null ++++ b/modules/video_output/wayland/rgba_premul.c +@@ -0,0 +1,243 @@ ++#include ++ ++#ifdef MAKE_TEST ++#define vlc_CPU_ARM64_NEON() (1) ++#define HAVE_AARCH64_ASM 1 ++#else ++#include ++#include ++#endif ++ ++#include "rgba_premul.h" ++ ++#ifdef HAVE_AARCH64_ASM ++#include "rgba_premul_aarch64.h" ++#endif ++ ++// x, y src offset, not dest ++// This won't be bit exact with aarch64 asm which has slightly different ++// rounding (this is faster when done in C) ++static void ++copy_xxxa_with_premul_c(void * restrict dst_data, int dst_stride, ++ const void * restrict src_data, int src_stride, ++ const unsigned int w, const unsigned int h, ++ const unsigned int global_alpha) ++{ ++ uint8_t * dst = (uint8_t*)dst_data; ++ const uint8_t * src = (uint8_t*)src_data; ++ const int src_inc = src_stride - (int)w * 4; ++ const int dst_inc = dst_stride - (int)w * 4; ++ ++ for (unsigned int i = 0; i != h; ++i) ++ { ++ for (unsigned int j = 0; j != w; ++j, src+=4, dst += 4) ++ { ++ unsigned int a = src[3] * global_alpha * 258; ++ const unsigned int k = 0x800000; ++ dst[0] = (src[0] * a + k) >> 24; ++ dst[1] = (src[1] * a + k) >> 24; ++ dst[2] = (src[2] * a + k) >> 24; ++ dst[3] = (src[3] * global_alpha * 257 + 0x8000) >> 16; ++ } ++ src += src_inc; ++ dst += dst_inc; ++ } ++} ++ ++void ++copy_xxxa_with_premul(void * dst_data, int dst_stride, ++ const void * src_data, int src_stride, ++ const unsigned int w, const unsigned int h, ++ const unsigned int global_alpha) ++{ ++#ifdef HAVE_AARCH64_ASM ++ if (vlc_CPU_ARM64_NEON()) ++ copy_xxxa_with_premul_aarch64(dst_data, dst_stride, src_data, src_stride, w, h, global_alpha); ++ else ++#endif ++ copy_xxxa_with_premul_c(dst_data, dst_stride, src_data, src_stride, w, h, global_alpha); ++} ++ ++// Has the optimization of copying as a single lump if strides are the same ++// and the width is fairly close to the stride ++// at the expense of possibly overwriting some bytes outside the active area ++// (but within the frame) ++void ++copy_frame_xxxa_with_premul(void * dst_data, int dst_stride, ++ const void * src_data, int src_stride, ++ const unsigned int w, const unsigned int h, ++ const unsigned int global_alpha) ++{ ++ if (dst_stride == src_stride && (dst_stride & 3) == 0 && (int)w * 4 <= dst_stride && (int)w * 4 + 64 >= dst_stride) ++ copy_xxxa_with_premul(dst_data, dst_stride, src_data, src_stride, h * dst_stride / 4, 1, global_alpha); ++ else ++ copy_xxxa_with_premul(dst_data, dst_stride, src_data, src_stride, w, h, global_alpha); ++} ++ ++//============================================================================ ++#ifdef MAKE_TEST ++#include ++#include ++#include ++#include ++#include ++ ++static bool verbose = false; ++static bool checkfail = false; ++ ++static uint64_t ++utime(void) ++{ ++ struct timespec ts; ++ clock_gettime(CLOCK_MONOTONIC, &ts); ++ return ts.tv_nsec / 1000 + (uint64_t)ts.tv_sec * 1000000; ++} ++ ++// What the ASM is meant to do exactly ++static void ++copy_xxxa_with_premul_c_asm(void * restrict dst_data, int dst_stride, ++ const void * restrict src_data, int src_stride, ++ const unsigned int w, const unsigned int h, ++ const uint8_t global_alpha) ++{ ++ uint8_t * dst = (uint8_t*)dst_data; ++ const uint8_t * src = (uint8_t*)src_data; ++ const int src_inc = src_stride - (int)w * 4; ++ const int dst_inc = dst_stride - (int)w * 4; ++ ++ for (unsigned int i = 0; i != h; ++i) ++ { ++ for (unsigned int j = 0; j != w; ++j, src+=4, dst += 4) ++ { ++ // What the ASM is meant to do exactly ++ unsigned int a = global_alpha * 257; ++ const unsigned int k = 0x800000; ++ dst[0] = (((src[0] * src[3] * 257) >> 8) * a + k) >> 24; ++ dst[1] = (((src[1] * src[3] * 257) >> 8) * a + k) >> 24; ++ dst[2] = (((src[2] * src[3] * 257) >> 8) * a + k) >> 24; ++ dst[3] = (src[3] * global_alpha * 257 + 0x8000) >> 16; ++ } ++ src += src_inc; ++ dst += dst_inc; ++ } ++} ++ ++#define ALIGN_SIZE 128 ++#define ALIGN_PTR(p) ((uint8_t*)(((uintptr_t)p + (ALIGN_SIZE -1)) & ~(ALIGN_SIZE - 1))) ++ ++static void ++timetest(const unsigned int w, const unsigned int h, const int stride, bool use_c) ++{ ++ uint64_t now; ++ uint64_t done; ++ size_t dsize = h * stride + ALIGN_SIZE; ++ ++ uint8_t * src = malloc(dsize); ++ uint8_t * dst = malloc(dsize); ++ uint8_t * s = ALIGN_PTR(src); ++ uint8_t * d = ALIGN_PTR(dst); ++ ++ memset(src, 0x80, dsize); ++ memset(dst, 0xff, dsize); ++ ++ now = utime(); ++ for (unsigned int i = 0; i != 10; ++i) ++ { ++ if (use_c) ++ copy_xxxa_with_premul_c(d, stride, s, stride, w, h, 0xba); ++ else ++ copy_xxxa_with_premul(d, stride, s, stride, w, h, 0xba); ++ } ++ done = utime(); ++ ++ printf("Time %3s: %dx%d stride %d: %6dus\n", use_c ? "C" : "Asm", w, h, stride, ++ (int)((done - now)/10)); ++ ++ free(src); ++ free(dst); ++} ++ ++static int ++docheck(const uint8_t * const a, const uint8_t * const b, const size_t n) ++{ ++ int t = 0; ++ ++ if (!verbose) ++ return memcmp(a, b, n); ++ ++ for (size_t i = 0; i != n && t < 128; ++i) ++ { ++ if (a[i] != b[i]) ++ { ++ printf("@ %zd: %02x %02x\n", i, a[i], b[i]); ++ ++t; ++ } ++ } ++ return t; ++} ++ ++static void ++checktest(const unsigned int w, const unsigned int h, const int stride, const int offset) ++{ ++ size_t dsize = ((h + 3) * stride + ALIGN_SIZE); ++ ++ uint8_t * src = malloc(dsize); ++ uint8_t * dst = malloc(dsize); ++ uint8_t * dst2 = malloc(dsize); ++ uint8_t * s = ALIGN_PTR(src + stride); ++ uint8_t * d = ALIGN_PTR(dst + stride); ++ uint8_t * d2 = ALIGN_PTR(dst2 + stride); ++ ++ for (unsigned int i = 0; i != dsize; ++i) ++ src[i] = rand(); ++ ++ memset(dst2, 0xff, dsize); ++ memset(dst, 0xff, dsize); ++ ++ copy_xxxa_with_premul_c_asm(d + offset, stride, s, stride, w, h, 0xba); ++ copy_xxxa_with_premul(d2 + offset, stride, s, stride, w, h, 0xba); ++ ++ if (docheck(d - stride, d2 - stride, (h + 2) * stride) != 0) ++ { ++ printf("Check: %dx%d stride %d offset %d: FAIL\n", w, h, stride, offset); ++ checkfail = true; ++ } ++ else if (verbose) ++ { ++ printf("Check: %dx%d stride %d offset %d: ok\n", w, h, stride, offset); ++ } ++ ++ free(src); ++ free(dst); ++ free(dst2); ++} ++ ++int ++main (int argc, char *argv[]) ++{ ++ if (argc >= 2 && strcmp(argv[1], "-v") == 0) ++ verbose = true; ++ ++ timetest(1920, 1080, 1920 * 4, true); ++ timetest(1920, 1080, 1920 * 4, false); ++ timetest(1917, 1080, 1920 * 4, false); ++ timetest(1917, 1080, 1917 * 4, false); ++ timetest(1920 * 1080, 1, 1920 * 1080 * 4, false); ++ ++ checktest(1920, 1080, 1920 * 4, 0); ++ ++ // Stride of 65pel will rotate alignment vertically ++ for (unsigned int i = 1; i != 64; ++i) ++ { ++ checktest(i, 32, 65 * 4, 0); ++ } ++ ++ if (!checkfail) ++ { ++ printf("All chacks passed\n"); ++ } ++ ++ return checkfail; ++} ++ ++#endif +diff --git a/modules/video_output/wayland/rgba_premul.h b/modules/video_output/wayland/rgba_premul.h +new file mode 100644 +index 0000000000..14edfcd173 +--- /dev/null ++++ b/modules/video_output/wayland/rgba_premul.h +@@ -0,0 +1,18 @@ ++#ifndef _WAYLAND_RGBA_PREMUL_H ++#define _WAYLAND_RGBA_PREMUL_H ++ ++void copy_xxxa_with_premul(void * dst_data, int dst_stride, ++ const void * src_data, int src_stride, ++ const unsigned int w, const unsigned int h, ++ const unsigned int global_alpha); ++ ++// Has the optimization of copying as a single lump if strides are the same ++// and the width is fairly close to the stride ++// at the expense of possibly overwriting some bytes outside the active area ++// (but within the frame) ++void copy_frame_xxxa_with_premul(void * dst_data, int dst_stride, ++ const void * src_data, int src_stride, ++ const unsigned int w, const unsigned int h, ++ const unsigned int global_alpha); ++ ++#endif +diff --git a/modules/video_output/wayland/rgba_premul_aarch64.S b/modules/video_output/wayland/rgba_premul_aarch64.S +new file mode 100644 +index 0000000000..7e2ed01503 +--- /dev/null ++++ b/modules/video_output/wayland/rgba_premul_aarch64.S +@@ -0,0 +1,170 @@ ++#include "../../arm_neon/asm.S" ++ ++// copy_xxxa_with_premul( ++// void * dst_data, // x0 ++// int dst_stride, // w1 ++// const void * src_data, // x2 ++// int src_stride, // w3 ++// unsigned int w, // w4 ++// unsigned int h, // w5 ++// unsigned int global_alpha) // w6 ++ ++// rC R, G or B In/Out ++// rA A In ++// sG Global alpha *257 as h scalar (v7.h[1]) In ++// rT1 tmp ++// rT2 tmp ++// ++// vC = (vC * vA * 257/256 * sG + 0x800000) >> 24 ++.macro mul_rgb vC, vA, sG, vT1, vT2 ++ ++ umull \vT2\().8h, \vC\().8b, \vA\().8b ++ umull2 \vT1\().8h, \vC\().16b, \vA\().16b ++ ++ usra \vT2\().8h, \vT2\().8h, #8 ++ usra \vT1\().8h, \vT1\().8h, #8 ++ ++ umull \vC\().4s, \vT2\().4h, \sG ++ umull2 \vT2\().4s, \vT2\().8h, \sG ++ uzp2 \vC\().8h, \vC\().8h, \vT2\().8h ++ ++ umull \vT2\().4s, \vT1\().4h, \sG ++ umull2 \vT1\().4s, \vT1\().8h, \sG ++ uzp2 \vT2\().8h, \vT2\().8h, \vT1\().8h ++ ++ uqrshrn \vC\().8b, \vC\().8h, #8 ++ uqrshrn2 \vC\().16b, \vT2\().8h, #8 ++.endm ++ ++// rA A In/Out ++// vG Global alpha duped bytewise into vector In ++// rT1 tmp ++// ++// vA = (vA * vG *257/256 + 0x80) >> 8 ++.macro mul_a vA, vG, vT1, vT2 ++ umull \vT1\().8h, \vA\().8b, \vG\().8b // Alpha ++ umull2 \vT2\().8h, \vA\().16b, \vG\().16b ++ usra \vT1\().8h, \vT1\().8h, #8 // * 257/256 ++ usra \vT2\().8h, \vT2\().8h, #8 ++ uqrshrn \vA\().8b, \vT1\().8h, #8 ++ uqrshrn2 \vA\().16b, \vT2\().8h, #8 ++.endm ++ ++function copy_xxxa_with_premul_aarch64 ++ mov x15, lr // Stash return addr ++ // Sanity check w & h ++ cbz w4, 90f ++ cbz w5, 90f ++ ++ // Put alpha values somewhere we can use them ++ mov w7, #257 // Would like 258 but 258*255 overflows h ++ mul w7, w7, w6 ++ dup v6.16b, w6 ++ mov v7.h[1], w7 ++ ++ // Calc EOL stride add ++ sub w1, w1, w4, LSL #2 ++ sub w3, w3, w4, LSL #2 ++ ++ // Deal with very short stuff separately ++ // Saves annoying conditionals later ++ cmp w4, #16 ++ bge 22f ++ ++ // Loop for w < 16 ++ mov w6, w4 ++1: ++ bl 50f ++ add x2, x2, w3, SXTW ++ add x0, x0, w1, SXTW ++ subs w5, w5, #1 ++ bne 1b ++ b 90f ++ ++ // Top of height loop ++20: ++ add x2, x2, w3, SXTW ++ add x0, x0, w1, SXTW ++ subs w5, w5, #1 ++ beq 90f ++ ++22: ++ // Align destination before main loop ++ tst x0, #63 ++ mov w6, w4 ++ beq 1f ++ ++ mov w7, #16 ++ ubfm w6, w0, #2, #5 ++ sub w6, w7, w6 ++ bl 50f ++ sub w6, w4, w6 ++1: ++ // If w % 16 != 0 then -16 so the main loop runs 1 fewer times with ++ // the remainder done in the tail ++ tst w6, #15 ++ bne 15f ++ ++ // Top of width loop ++10: ++ ld4 {v0.16b, v1.16b, v2.16b, v3.16b}, [x2], #64 ++ ++ mul_rgb v0, v3, v7.h[1], v16, v17 ++ mul_rgb v1, v3, v7.h[1], v16, v17 ++ mul_rgb v2, v3, v7.h[1], v16, v17 ++ mul_a v3, v6, v16, v17 ++ ++ st4 {v0.16b, v1.16b, v2.16b, v3.16b}, [x0], #64 ++ ++15: ++ subs w6, w6, #16 ++ bgt 10b ++ beq 20b // No tail ++ ++ // Tail ++ bl 50f ++ b 20b ++ ++ // Return ++90: ++ mov lr, x15 ++ ret ++ ++// Tail & Head core ++// ++// w6 Noof pixels to convert ++// Only bottom 3 bits considered, left unchanged on exit ++50: ++ tbz w6, #3, 1f ++ ld4 {v0.8b, v1.8b, v2.8b, v3.8b}, [x2], #32 ++1: tbz w6, #2, 1f ++ ld4 {v0.b, v1.b, v2.b, v3.b}[8], [x2], #4 ++ ld4 {v0.b, v1.b, v2.b, v3.b}[9], [x2], #4 ++ ld4 {v0.b, v1.b, v2.b, v3.b}[10], [x2], #4 ++ ld4 {v0.b, v1.b, v2.b, v3.b}[11], [x2], #4 ++1: tbz w6, #1, 1f ++ ld4 {v0.b, v1.b, v2.b, v3.b}[12], [x2], #4 ++ ld4 {v0.b, v1.b, v2.b, v3.b}[13], [x2], #4 ++1: tbz w6, #0, 1f ++ ld4 {v0.b, v1.b, v2.b, v3.b}[14], [x2], #4 ++1: ++ ++ mul_rgb v0, v3, v7.h[1], v16, v17 ++ mul_rgb v1, v3, v7.h[1], v16, v17 ++ mul_rgb v2, v3, v7.h[1], v16, v17 ++ mul_a v3, v6, v16, v17 ++ ++ tbz w6, #3, 1f ++ st4 {v0.8b, v1.8b, v2.8b, v3.8b}, [x0], #32 ++1: tbz w6, #2, 1f ++ st4 {v0.b, v1.b, v2.b, v3.b}[8], [x0], #4 ++ st4 {v0.b, v1.b, v2.b, v3.b}[9], [x0], #4 ++ st4 {v0.b, v1.b, v2.b, v3.b}[10], [x0], #4 ++ st4 {v0.b, v1.b, v2.b, v3.b}[11], [x0], #4 ++1: tbz w6, #1, 1f ++ st4 {v0.b, v1.b, v2.b, v3.b}[12], [x0], #4 ++ st4 {v0.b, v1.b, v2.b, v3.b}[13], [x0], #4 ++1: tbz w6, #0, 1f ++ st4 {v0.b, v1.b, v2.b, v3.b}[14], [x0], #4 ++1: ++ ret +diff --git a/modules/video_output/wayland/rgba_premul_aarch64.h b/modules/video_output/wayland/rgba_premul_aarch64.h +new file mode 100644 +index 0000000000..829a5d1014 +--- /dev/null ++++ b/modules/video_output/wayland/rgba_premul_aarch64.h +@@ -0,0 +1,9 @@ ++#ifndef _WAYLAND_RGBA_PREMUL_AARCH64_H ++#define _WAYLAND_RGBA_PREMUL_AARCH64_H ++ ++void copy_xxxa_with_premul_aarch64(void * dst_data, int dst_stride, ++ const void * src_data, int src_stride, ++ const unsigned int w, const unsigned int h, ++ const unsigned int global_alpha); ++ ++#endif +diff --git a/modules/video_output/wayland/shm.c b/modules/video_output/wayland/shm.c +index 08ad5022f9..b1feb622d8 100644 +--- a/modules/video_output/wayland/shm.c ++++ b/modules/video_output/wayland/shm.c +@@ -509,7 +509,7 @@ vlc_module_begin() + set_description(N_("Wayland shared memory video output")) + set_category(CAT_VIDEO) + set_subcategory(SUBCAT_VIDEO_VOUT) +- set_capability("vout display", 170) ++ set_capability("vout display", 0) + set_callbacks(Open, Close) + add_shortcut("wl") + vlc_module_end() +diff --git a/pi-util/README.txt b/pi-util/README.txt +new file mode 100644 +index 0000000000..59a8655955 +--- /dev/null ++++ b/pi-util/README.txt +@@ -0,0 +1,100 @@ ++Release notes ++============= ++ ++This version should run with gpu-mem=64 with the default switches. Having ++said that this will only allow for 1 stream. If you are playing >1 stream ++(even transiently) then you will need more (say gpu_mem=128) and you will ++need to set the --mmal-decoders option to the desired max number. The code ++should give up cleanly if it cannot allocate a h/w video decoder and give ++the stream to old-style ffmpeg decode, but as it stands in many cases it ++thinks it has allocated a decoder cleanly only to find that it fails when ++it tries to use it. ++ ++Needs firmware from "Sep 13 2016 17:01:56" or later to work properly ++("vcgencmd version" will give the date). ++ ++There are a few command-line switches - in general you shouldn't use ++them! ++ ++ ++Decode and resizer options ++-------------------------- ++ ++--mmal-decode-opaque Set the decoder to use opaque frames between ++decoder and resizer. This should be faster than i420 but doesn't work ++with old firmware. This is the default with newer firmware (>= ++2016-11-01). (see --mmal-decode-i420) ++ ++--mmal-decode-i420 Set the decoder to use I420 frames between ++decoder and resizer. This generates an unnecessary conversion but works ++with all firmware. This is the default with older firmware (< ++2016-11-01). (see --mmal-decode-opaque) ++ ++--mmal-low-delay Force "low-delay" mode on the decoder pipe. This ++reduces the number of buffered ES frames before the decoder. It isn't ++exactly low-delay but is definitely lower than otherwise. May have a ++slight performance penalty and increase the risk of stuttering. This mode ++will be automatically set by Chrome for some streams. ++ ++--mmal-resize-isp Use ISP resize rather than resizer. Is noticably ++faster but requires --mmal-frame-copy or --mmal-zero-copy and newer ++firmware. This is the default with newer firmware (>= 2016-11-01) and ++enough gpu memory to support --mmal-frame-copy. ++ ++--mmal-resize-resizer Use resizer rather than ISP. Slower than ISP ++resize but supports older firmware and --mmal-slice-copy which may be ++needed if GPU memory is very limited (as will be the case on a Pi1 with a ++default setup). ++ ++Copy-modes ++---------- ++ ++These are modes for getting frames out of mmal. Current default is ++--mmal-frame-copy if --mmal-resize-isp is the default resizer or it looks ++like the firmware doesn't support --mmal-slice-copy otherwise ++--mmal-slice-copy is the default. Explicit use of a copy mode option will ++override the default regardless of whether or not we think the firmware ++supports the selected option. Only use one of of these flags. ++ ++--mmal-zero-copy Pass gpu frames directly to chrome. Chrome ++buffers some frames and stalls if it doesn't get them. So this option ++needs 6+ gpu frames allocated. This is now a legacy and testing option as ++--mmal-frame-copy is faster and you probably want to have gpu_mem=192 if ++you are going to use it. Default frame-buffers = 6 (8M each) ++ ++--mmal-frame-copy Copy frame at a time out of mmal to chrome. ++Currently the fastest option. Needs 2+ gpu frames for plausible ++performance. Default frame-buffers = 2 (8M each). You probably want ++gpu-mem=80 for 1 decoder with this option. ++ ++--mmal-slice-copy Copy frames out in 16-line slices. Has the ++lowest memory overhead, but the highest CPU load. If this is selected ++then --mmal-frame-buffers is the number of slice buffers. Default frame ++buffers = 16 (~122k each). ++ ++Misc options ++------------ ++ ++--enable-logging=stderr This is a standard option for chrome but worth ++noting as the mmal code will print out its interpretation of the command ++line options passed to it along with how much GPU memory it has detected ++and the firmware date. ++ ++--pi-patch-version Print out the versions of Chromium and Pi ++patches. Chrome will then terminate ++ ++--mmal-decoders= Set the number of mmal decoders we wil try to ++create simultainiously. Default=1. If this number is exceeded then decoder ++init will fail and chrome will fallback to ffmpeg decode. There is no ++panalty for setting this to a large number if you wish to have "unlimited" ++decoders. However if it is set too big and there isn't the gpu mem to ++satisfy the requirements of the decode it may fail cleanly and revert to ++software (ffmpeg) decode or init may appear to succeed and decode then ++fails in an undefined manner. ++ ++--mmal-frame-buffers= Set the number of gpu "frame" buffers (see ++--mmal-xxx-copy). Change with care. ++ ++--mmal-red-pixel Puts a red square in the top left of a frame ++decoded by mmal so you can tell that it is active. Doesn't work if ++zero-copy is set. +diff --git a/pi-util/conf.sh b/pi-util/conf.sh +new file mode 100755 +index 0000000000..f3b52b81d2 +--- /dev/null ++++ b/pi-util/conf.sh +@@ -0,0 +1,48 @@ ++BASE=`pwd`/.. ++A=arm-linux-gnueabihf ++TOOLS=$BASE/tools/arm-bcm2708/gcc-arm-8.2-2019.01-x86_64-arm-linux-gnueabihf ++SYSROOT2=$BASE/tools/arm-bcm2708/sysroot-glibc-8.2-2019.01-x86_64-arm-linux-gnueabihf ++#TOOLS=$BASE/tools/arm-bcm2708/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf ++#SYSROOT2=$BASE/tools/arm-bcm2708/sysroot-glibc-linaro-2.23-2017.05-arm-linux-gnueabihf ++SYSROOT=`pwd`/sysroot/raspian_stretch_pi1-sysroot ++HLIB3=/lib/$A ++HLIB1=/usr/lib/$A ++HLIB2=/usr/lib ++HLIB4=/opt/vc/lib ++HLIB5=/usr/local/lib/$A ++ ++LIB2=$SYSROOT$HLIB2 ++LIB1=$SYSROOT$HLIB1 ++LIB3=$SYSROOT$HLIB3 ++LIB4=$SYSROOT$HLIB4 ++LIB5=$SYSROOT$HLIB5 ++ ++#INCLUDES="-I$SYSROOT/usr/include -I$SYSROOT/usr/include/$A -I$SYSROOT/opt/vc/include -I$SYSROOT/usr/lib/arm-linux-gnueabihf/dbus-1.0/include -I$SYSROOT/usr/include/libdrm" ++INCLUDES="-I$SYSROOT/usr/include -I$SYSROOT/usr/include/$A -I$SYSROOT/usr/local/include -I$SYSROOT/opt/vc/include -I$SYSROOT/usr/lib/arm-linux-gnueabihf/dbus-1.0/include" ++#DEFINES="-ggdb -D__VCCOREVER__=0x04000000 -DQT_WARNING_DISABLE_DEPRECATED=\"\"" ++DEFINES="-ggdb -D__VCCOREVER__=0x04000000" ++ARCH="-march=armv7-a -mfpu=neon-vfpv4" ++PREFIX=$TOOLS/bin/$A- ++ ++ ++PATH="$TOOLS/bin:$PATH" \ ++ PKG_CONFIG="pkg-config --define-prefix" \ ++ PKG_CONFIG_PATH="$SYSROOT/usr/lib/pkgconfig:$SYSROOT/usr/local/lib/$A/pkgconfig:$LIB3/pkgconfig" \ ++ CC=${PREFIX}gcc \ ++ CFLAGS="$ARCH $INCLUDES" \ ++ CPP=${PREFIX}cpp \ ++ CPPFLAGS="$ARCH $INCLUDES $DEFINES" \ ++ CXX=${PREFIX}g++ \ ++ CXXFLAGS="$ARCH $INCLUDES" \ ++ LDFLAGS="-ggdb -L$TOOLS/lib -L$LIB1 -L$LIB2 -L$LIB3 -L$LIB4 -L$LIB5 -Wl,-rpath=$HLIB1,-rpath-link=$LIB1,-rpath=$HLIB2,-rpath-link=$LIB2,-rpath=$HLIB3,-rpath-link=$LIB3,-rpath=$HLIB4,-rpath-link=$LIB4,-rpath=$HLIB5,-rpath-link=$LIB5,-rpath-link=`pwd`/src/.libs" \ ++ MOC="`which moc` -qt=5" \ ++ UIC="`which uic` -qt=5" \ ++ RCC="`which rcc` -qt=5" \ ++ ./configure --host=$A --enable-mmal-avcodec --disable-vdpau --disable-libva --enable-debug --disable-lua --disable-chromecast --disable-wayland --enable-gles2 --disable-opencv --enable-dav1d --disable-aom ++ ++# ./configure --host=$A --enable-debug --disable-lua --disable-qt --disable-vdpau --disable-chromecast --disable-wayland --disable-bluray --disable-opencv ++# ./configure --host=$A --enable-debug --disable-wayland ++ ++ ++ ++ +diff --git a/pi-util/conf_native.sh b/pi-util/conf_native.sh +new file mode 100755 +index 0000000000..54674fa8d0 +--- /dev/null ++++ b/pi-util/conf_native.sh +@@ -0,0 +1,96 @@ ++set -e ++BASE=`pwd` ++ ++DO_BOOTSTRAP= ++DO_MAKE= ++DO_CONFIGURE=1 ++ ++while [ "$1" != "" ] ; do ++ case $1 in ++ --make) ++ DO_MAKE=1 ++ DO_CONFIGURE= ++ ;; ++ --bootstrap) ++ DO_BOOTSTRAP=1 ++ ;; ++ *) ++ echo "Usage $0: [--bootstrap] [--make]" ++ echo " bootstrap Do bootstrap before configure" ++ echo " (will always bootstrap if clean)" ++ echo " make Do make after configure" ++ exit 1 ++ ;; ++ esac ++ shift ++done ++ ++if [ ! -f $BASE/configure ]; then ++ echo "configure not found - will bootstrap" ++ DO_BOOTSTRAP=1 ++fi ++ ++CONF_MMAL=--disable-mmal ++ ++# uname -m gives kernel type which may not have the same ++# 32/64bitness as userspace :-( getconf shoudl provide the answer ++# but use uname to check we are on the right processor ++MC=`uname -m` ++LB=`getconf LONG_BIT` ++if [ "$MC" == "armv7l" ] || [ "$MC" == "aarch64" ]; then ++ if [ "$LB" == "32" ]; then ++ # CONF_MMAL=--enable-mmal-avcodec ++ CONF_MMAL= ++ A=arm-linux-gnueabihf ++ ARM=armv7 ++ elif [ "$LB" == "64" ]; then ++ A=aarch64-linux-gnu ++ ARM=arm64 ++ else ++ echo "Unknown LONG_BIT name: $LB" ++ exit 1 ++ fi ++else ++ echo "Unknown machine name: $MC" ++ exit 1 ++fi ++OUT=$BASE/out/$ARM-`lsb_release -sc`-rel ++ ++if [ $DO_BOOTSTRAP ]; then ++ echo "==== Bootstrapping & cleaning $OUT" ++ rm -rf $OUT ++ ./bootstrap ++fi ++ ++if [ ! -f $OUT/Makefile ]; then ++ DO_CONFIGURE=1 ++fi ++ ++USR_PREFIX=$OUT/install ++LIB_PREFIX=$USR_PREFIX/lib/$A ++INC_PREFIX=$USR_PREFIX/include/$A ++ ++echo "==== Configuring in $OUT" ++mkdir -p $OUT ++# Nothing under here need worry git - including this .gitignore! ++echo "**" > $BASE/out/.gitignore ++ ++cd $OUT ++if [ $DO_CONFIGURE ]; then ++ $BASE/configure \ ++ --build=$A \ ++ --prefix=$USR_PREFIX\ ++ --libdir=$LIB_PREFIX\ ++ --includedir=$INC_PREFIX\ ++ --disable-vdpau\ ++ --enable-wayland\ ++ --enable-gles2\ ++ $CONF_MMAL ++ echo "==== Configured in $OUT" ++fi ++ ++if [ $DO_MAKE ]; then ++ echo "==== Making $OUT" ++ make -j8 ++ echo "==== Made $OUT" ++fi +diff --git a/pi-util/genpatch.sh b/pi-util/genpatch.sh +new file mode 100755 +index 0000000000..13265447cd +--- /dev/null ++++ b/pi-util/genpatch.sh +@@ -0,0 +1,68 @@ ++set -e ++ ++NOTAG= ++if [ "$1" == "--notag" ]; then ++ shift ++ NOTAG=1 ++fi ++ ++if [ "$1" == "" ]; then ++ echo Usage: $0 [--notag] \ ++ echo e.g.: $0 mmal_4 ++ exit 1 ++fi ++REF=$1 ++ ++VERSION=`awk '/AC_INIT/{match($0,/[0-9]+(\.[0-9]+)+/);print substr($0,RSTART,RLENGTH)}' configure.ac` ++if [ "$VERSION" == "" ]; then ++ echo Can\'t find version in configure.ac ++ exit 1 ++fi ++ ++if [ $NOTAG ]; then ++ echo Not tagged ++else ++ # Only continue if we are all comitted ++ git diff --name-status --exit-code ++ ++ PATCHTAG=pi/$VERSION/$REF ++ echo Tagging: $PATCHTAG ++ ++ git tag $PATCHTAG ++fi ++ ++DSTDIR=.. ++PATCHNAME=vlc-$VERSION-$REF ++DIFFBASE=$DSTDIR/$PATCHNAME ++ZIPNAME=$PATCHNAME-patch.zip ++ ++# We seem to sometimes gain add ++echo Generating patches in: $DSTDIR/$ZIPNAME ++REFNAME=refs/tags/$VERSION ++git diff $REFNAME -- \ ++ configure.ac \ ++ include \ ++ modules/Makefile.am \ ++ modules/audio_filter \ ++ modules/audio_output \ ++ modules/codec \ ++ modules/gui/qt/qt.cpp \ ++ modules/hw/drm \ ++ modules/hw/mmal \ ++ modules/video_output/Makefile.am \ ++ modules/video_output/drmu \ ++ modules/video_output/opengl \ ++ modules/video_output/wayland \ ++ src/audio_output \ ++ src/input \ ++ src/misc \ ++ > $DIFFBASE-001-rpi.patch ++git diff $REFNAME -- modules/video_chroma/chain.c > $DIFFBASE-002-chain.patch ++git diff $REFNAME -- bin/vlc.c > $DIFFBASE-003-vlc.patch ++git diff $REFNAME -- modules/video_output/caca.c > $DIFFBASE-004-caca.patch ++git diff $REFNAME -- modules/gui/qt/components/interface_widgets.* > $DIFFBASE-005-qt-wayland.patch ++cd $DSTDIR ++zip -m $ZIPNAME $PATCHNAME-*.patch ++ ++#echo Copying patch to arm-build ++#scp $PATCHFILE john@arm-build:patches/0002-vlc-3.0.6-mmal_test_4.patch +diff --git a/pi-util/host-install-dev.sh b/pi-util/host-install-dev.sh +new file mode 100755 +index 0000000000..e4706312ce +--- /dev/null ++++ b/pi-util/host-install-dev.sh +@@ -0,0 +1,4 @@ ++lua5.2 ++protobuf-compiler ++# Unsure exactly which gt5 targets are needed ??libqt5-dev-bin ++sudo apt install qt5-default +diff --git a/pi-util/mmvlc.sh b/pi-util/mmvlc.sh +new file mode 100755 +index 0000000000..a5f503d88a +--- /dev/null ++++ b/pi-util/mmvlc.sh +@@ -0,0 +1,8 @@ ++DSTBASE=/usr/lib/arm-linux-gnueabihf ++DSTPLUGINS=$DSTBASE/vlc/plugins ++DSTDIR=$DSTPLUGINS/mmal ++sudo mkdir -p $DSTDIR ++sudo cp modules/hw/mmal/.libs/*.so $DSTDIR/ ++#sudo cp modules/.libs/libxcb_x11_plugin.so $DSTPLUGINS/video_output/ ++#sudo cp src/.libs/libvlccore.so.9.0.0 $DSTBASE/ ++vlc --no-plugins-cache $* +diff --git a/pi-util/pi-install-dev.sh b/pi-util/pi-install-dev.sh +new file mode 100755 +index 0000000000..e99f872065 +--- /dev/null ++++ b/pi-util/pi-install-dev.sh +@@ -0,0 +1,75 @@ ++# Install set to build appropriate root on a clean pi ++ ++sudo apt-get install \ ++ libprotobuf-dev \ ++ libepoxy-dev \ ++ libavutil-dev \ ++ libavcodec-dev \ ++ libavformat-dev \ ++ libswscale-dev \ ++ libva-dev \ ++ libpostproc-dev \ ++ libtwolame-dev \ ++ liba52-dev \ ++ libflac-dev \ ++ libmpeg2-4-dev \ ++ libass-dev \ ++ libaribb24-dev \ ++ libzvbi-dev \ ++ libkate-dev \ ++ libogg-dev \ ++ libdca-dev \ ++ libxcb-keysyms1-dev \ ++ libsdl2-dev \ ++ librsvg2-dev \ ++ libsystemd-dev \ ++ libarchive-dev \ ++ libnfs-dev \ ++ libssh2-1-dev \ ++ libopencv-dev \ ++ libsmbclient-dev \ ++ libmodplug-dev \ ++ libshine-dev \ ++ libvorbis-dev \ ++ libxml2-dev \ ++comerr-dev \ ++libasound2-dev \ ++libatk1.0-dev \ ++libcap-dev \ ++libcups2-dev \ ++libexif-dev \ ++libffi-dev \ ++libgconf2-dev \ ++libgl1-mesa-dev \ ++libgnome-keyring-dev \ ++libgnutls28-dev \ ++libidn11-dev \ ++libjpeg-dev \ ++libkrb5-dev \ ++libnspr4-dev \ ++libnss3-dev \ ++libpam0g-dev \ ++libpango1.0-dev \ ++libpci-dev \ ++libpcre3-dev \ ++libssl-dev \ ++libudev-dev \ ++libx11-dev \ ++libx11-xcb-dev \ ++libxcb1-dev \ ++libxcb-shm0-dev \ ++libxcb-composite0-dev \ ++libxcb-xv0-dev \ ++libxss-dev \ ++libxt-dev \ ++libxtst-dev \ ++mesa-common-dev ++ ++# Pulse (hopefully) disabled ++# libpulse-dev \ ++ ++# Obviously replace paths appropriately below ++# Now run pi-util/syncroot.sh on the compile m/c to grab the appropriate ++# bits of the root and fix up the paths. ++# e.g. ON COMPILE M/C in src dir: ++# pi-util/syncroot.sh my-pi: raspian_jessie_pi1 +diff --git a/pi-util/rebase_liblinks.py b/pi-util/rebase_liblinks.py +new file mode 100755 +index 0000000000..1f143ed2f6 +--- /dev/null ++++ b/pi-util/rebase_liblinks.py +@@ -0,0 +1,37 @@ ++#!/usr/bin/env python3 ++ ++import os, sys ++from stat import * ++ ++def walktree(top, callback, n, prefix): ++ '''recursively descend the directory tree rooted at top, ++ calling the callback function for each regular file''' ++ ++ for f in os.listdir(top): ++ pathname = os.path.join(top, f) ++ mode = os.lstat(pathname).st_mode ++ if S_ISDIR(mode): ++ # It's a directory, recurse into it ++ walktree(pathname, callback, n+1, prefix) ++ elif S_ISLNK(mode): ++ # It's a file, call the callback function ++ callback(pathname, os.readlink(pathname), n, prefix) ++ ++def visitfile(file, linkname, n, prefix): ++ if (linkname.startswith(prefix + 'lib/')): ++ newlink = "../" * n + linkname[len(prefix):] ++ print('relinking', file, "->", newlink) ++ os.remove(file) ++ os.symlink(newlink, file) ++ ++if __name__ == '__main__': ++ argc = len(sys.argv) ++ if argc == 2: ++ walktree(sys.argv[1], visitfile, 0, "/") ++ elif argc == 3: ++ walktree(sys.argv[1], visitfile, 0, sys.argv[2]) ++ else: ++ print("rebase_liblinks.py []") ++ ++ ++ +diff --git a/pi-util/syncroot.sh b/pi-util/syncroot.sh +new file mode 100755 +index 0000000000..aedaa28323 +--- /dev/null ++++ b/pi-util/syncroot.sh +@@ -0,0 +1,51 @@ ++set -e ++ ++if [ "$1" == "" ]; then ++ echo Usage: $0 \ [\] ++ echo src_dir is a source for rsync so may contain m/c name. ++ echo rootname will be set to \"raspian_stretch_pi1\" if missing ++ echo e.g.: pi-util/syncroot.sh my-pi: raspian_stretch_pi1 ++ exit 1 ++fi ++ ++SYSROOT_NAME=$2 ++if [ "$SYSROOT_NAME" == "" ]; then ++ SYSROOT_NAME=raspian_stretch_pi1 ++fi ++ ++DST_ROOT=`pwd` ++DST=$DST_ROOT/sysroot/$SYSROOT_NAME-sysroot ++SRC=$1 ++ ++RPI_BASE=$DST_ROOT/.. ++TOOL_BASE=$RPI_BASE/tools ++#FIRMWARE_BASE=$RPI_BASE/firmware4 ++ ++echo Sync src: $SRC ++echo Sync dest: $DST ++ ++mkdir -p $DST/lib ++mkdir -p $DST/opt ++mkdir -p $DST/usr/share ++mkdir -p $DST/usr/local/include ++mkdir -p $DST/usr/local/lib ++ ++#ln -sf $FIRMWARE_BASE/opt/vc $DST/opt ++rsync -rl $SRC/opt/vc $DST/opt ++rsync -rl $SRC/lib/arm-linux-gnueabihf $DST/lib ++rsync -rl --exclude "*/cups/backend/*" $SRC/usr/lib $DST/usr ++rsync -rl $SRC/usr/include $DST/usr ++rsync -rl $SRC/usr/share/pkgconfig $DST/usr/share ++rsync -rl $SRC/usr/local/include $DST/usr/local ++rsync -rl $SRC/usr/local/lib $DST/usr/local ++ ++rm $DST/usr/lib/arm-linux-gnueabihf/libpthread* ++rm $DST/usr/lib/arm-linux-gnueabihf/libc.* ++ ++PUSHDIR=`pwd` ++cd $DST/usr/lib/pkgconfig ++ln -sf ../arm-linux-gnueabihf/pkgconfig/* . ++cd $PUSHDIR ++pi-util/rebase_liblinks.py $DST ++ ++ +diff --git a/src/audio_output/dec.c b/src/audio_output/dec.c +index 7eca0de2fd..67aad79855 100644 --- a/src/audio_output/dec.c +++ b/src/audio_output/dec.c -@@ -217,20 +217,27 @@ static void aout_DecSilence (audio_outpu +@@ -217,20 +217,27 @@ static void aout_DecSilence (audio_output_t *aout, vlc_tick_t length, vlc_tick_t { aout_owner_t *owner = aout_owner (aout); const audio_sample_format_t *fmt = &owner->mixer_format; - size_t frames = (fmt->i_rate * length) / CLOCK_FREQ; -+ size_t frame_count = (fmt->i_rate * length) / CLOCK_FREQ; - +- - block_t *block = block_Alloc (frames * fmt->i_bytes_per_frame - / fmt->i_frame_length); - if (unlikely(block == NULL)) - return; /* uho! */ +- +- msg_Dbg (aout, "inserting %zu zeroes", frames); +- memset (block->p_buffer, 0, block->i_buffer); +- block->i_nb_samples = frames; +- block->i_pts = pts; +- block->i_dts = pts; +- block->i_length = length; +- aout_OutputPlay (aout, block); ++ size_t frame_count = (fmt->i_rate * length) / CLOCK_FREQ; ++ + msg_Dbg (aout, "inserting %zu zero frames", frame_count); + while (frame_count != 0) + { @@ -25220,14 +29166,7 @@ + / fmt->i_frame_length)); + if (unlikely(block == NULL)) + return; /* uho! */ - -- msg_Dbg (aout, "inserting %zu zeroes", frames); -- memset (block->p_buffer, 0, block->i_buffer); -- block->i_nb_samples = frames; -- block->i_pts = pts; -- block->i_dts = pts; -- block->i_length = length; -- aout_OutputPlay (aout, block); ++ + memset (block->p_buffer, 0, block->i_buffer); + block->i_nb_samples = frames; + block->i_pts = pts; @@ -25238,10 +29177,12 @@ + } } - static void aout_DecSynchronize (audio_output_t *aout, mtime_t dec_pts, + static void aout_DecSynchronize (audio_output_t *aout, vlc_tick_t dec_pts, +diff --git a/src/input/decoder.c b/src/input/decoder.c +index 69488a681c..57858a8020 100644 --- a/src/input/decoder.c +++ b/src/input/decoder.c -@@ -2000,6 +2000,7 @@ void input_DecoderDelete( decoder_t *p_d +@@ -2000,6 +2000,7 @@ void input_DecoderDelete( decoder_t *p_dec ) vlc_mutex_lock( &p_owner->lock ); p_owner->b_waiting = false; vlc_cond_signal( &p_owner->wait_request ); @@ -25249,7 +29190,7 @@ /* 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 -@@ -2010,7 +2011,6 @@ void input_DecoderDelete( decoder_t *p_d +@@ -2010,7 +2011,6 @@ void input_DecoderDelete( decoder_t *p_dec ) * worker threads (if any) and the decoder thread to terminate. */ if( p_owner->p_vout != NULL ) vout_Cancel( p_owner->p_vout, true ); @@ -25257,9 +29198,11 @@ vlc_join( p_owner->thread, NULL ); +diff --git a/src/misc/fourcc.c b/src/misc/fourcc.c +index ebb7707a34..cd202ed74a 100644 --- a/src/misc/fourcc.c +++ b/src/misc/fourcc.c -@@ -416,6 +416,10 @@ static const vlc_fourcc_t p_D3D11_OPAQUE +@@ -416,6 +416,10 @@ static const vlc_fourcc_t p_D3D11_OPAQUE_10B_fallback[] = { VLC_CODEC_D3D11_OPAQUE_10B, VLC_CODEC_P010, VLC_CODEC_I420_10L, 0, }; @@ -25270,7 +29213,7 @@ static const vlc_fourcc_t p_I440_fallback[] = { VLC_CODEC_I440, VLC_CODEC_YUV_PLANAR_420, -@@ -506,6 +510,7 @@ static const vlc_fourcc_t *pp_YUV_fallba +@@ -506,6 +510,7 @@ static const vlc_fourcc_t *pp_YUV_fallback[] = { p_D3D9_OPAQUE_10B_fallback, p_D3D11_OPAQUE_fallback, p_D3D11_OPAQUE_10B_fallback, @@ -25278,7 +29221,7 @@ NULL, }; -@@ -537,6 +542,15 @@ static const vlc_fourcc_t p_list_YUV[] = +@@ -537,6 +542,15 @@ static const vlc_fourcc_t p_list_YUV[] = { VLC_CODEC_D3D9_OPAQUE_10B, VLC_CODEC_D3D11_OPAQUE, VLC_CODEC_D3D11_OPAQUE_10B, @@ -25316,9 +29259,11 @@ { { VLC_CODEC_CVPX_NV12, VLC_CODEC_CVPX_UYVY, VLC_CODEC_CVPX_I420, VLC_CODEC_CVPX_BGRA }, +diff --git a/src/misc/picture.c b/src/misc/picture.c +index 892d5b7dac..3c75b6450f 100644 --- a/src/misc/picture.c +++ b/src/misc/picture.c -@@ -365,10 +365,30 @@ void picture_CopyProperties( picture_t * +@@ -365,10 +365,30 @@ void picture_CopyProperties( picture_t *p_dst, const picture_t *p_src ) p_dst->b_top_field_first = p_src->b_top_field_first; } diff --git a/alarm/vlc-rpi/0004-mmal_caca.patch b/alarm/vlc-rpi/0004-mmal_caca.patch deleted file mode 100644 index 6d94e51be..000000000 --- a/alarm/vlc-rpi/0004-mmal_caca.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- a/modules/video_output/caca.c -+++ b/modules/video_output/caca.c -@@ -160,7 +160,11 @@ static int Open(vlc_object_t *object) - } - - const char *driver = NULL; --#ifdef __APPLE__ -+// RPI: If driver is NULL then if we have X but DISPLAY is unset then somehow -+// the GL module becomes unloaded without anything noticing and that then -+// causes a segfault. -+//#ifdef __APPLE__ -+#if 1 - // Make sure we don't try to open a window. - driver = "ncurses"; - #endif diff --git a/alarm/vlc-rpi/0005-mmal_chain.patch b/alarm/vlc-rpi/0005-mmal_chain.patch deleted file mode 100644 index 03e4acb41..000000000 --- a/alarm/vlc-rpi/0005-mmal_chain.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- a/modules/video_chroma/chain.c -+++ b/modules/video_chroma/chain.c -@@ -280,8 +280,9 @@ static int BuildTransformChain( filter_t - return VLC_SUCCESS; - - /* Lets try resize+chroma first, then transform */ -- msg_Dbg( p_filter, "Trying to build chroma+resize" ); -- EsFormatMergeSize( &fmt_mid, &p_filter->fmt_out, &p_filter->fmt_in ); -+ msg_Dbg( p_filter, "Trying to build chroma+resize, then transform" ); -+ es_format_Copy( &fmt_mid, &p_filter->fmt_out ); -+ video_format_TransformTo(&fmt_mid.video, p_filter->fmt_in.video.orientation); - i_ret = CreateChain( p_filter, &fmt_mid ); - es_format_Clean( &fmt_mid ); - if( i_ret == VLC_SUCCESS ) diff --git a/alarm/vlc-rpi/0006-mmal_exit_fix.patch b/alarm/vlc-rpi/0006-mmal_exit_fix.patch deleted file mode 100644 index 1b1126eec..000000000 --- a/alarm/vlc-rpi/0006-mmal_exit_fix.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- a/bin/vlc.c -+++ b/bin/vlc.c -@@ -106,7 +106,10 @@ static void vlc_kill (void *data) - static void exit_timeout (int signum) - { - (void) signum; -- signal (SIGINT, SIG_DFL); -+// This doesn't seem to be strong enough to reliably kill us if we fail to exit -+// in a timely fashion - so upgrade to _exit(). -+// signal (SIGINT, SIG_DFL); -+ _exit(0); - } - - /***************************************************************************** diff --git a/alarm/vlc-rpi/0007-mmal_wayland.patch b/alarm/vlc-rpi/0007-mmal_wayland.patch deleted file mode 100644 index 325c70065..000000000 --- a/alarm/vlc-rpi/0007-mmal_wayland.patch +++ /dev/null @@ -1,72 +0,0 @@ ---- a/modules/gui/qt/components/interface_widgets.cpp -+++ b/modules/gui/qt/components/interface_widgets.cpp -@@ -108,6 +108,36 @@ void VideoWidget::sync( void ) - if( QX11Info::isPlatformX11() ) - XSync( QX11Info::display(), False ); - #endif -+ refreshHandles(); -+} -+ -+/** -+ * The wayland surface may change if the window is hidden which -+ * seems to happen sometimes on resize -+ * In Qt it looks like this happens if the window is hidden -+ **/ -+void VideoWidget::refreshHandles() -+{ -+#ifdef QT5_HAS_WAYLAND -+ if (!p_window || p_window->type != VOUT_WINDOW_TYPE_WAYLAND) -+ return; -+ -+ /* Ensure only the video widget is native (needed for Wayland) */ -+ stable->setAttribute( Qt::WA_DontCreateNativeAncestors, true); -+ -+ QWindow *window = stable->windowHandle(); -+ assert(window != NULL); -+ window->create(); -+ -+ QPlatformNativeInterface *qni = qApp->platformNativeInterface(); -+ assert(qni != NULL); -+ -+ p_window->handle.wl = static_cast( -+ qni->nativeResourceForWindow(QByteArrayLiteral("surface"), -+ window)); -+ p_window->display.wl = static_cast( -+ qni->nativeResourceForIntegration(QByteArrayLiteral("wl_display"))); -+#endif - } - - /** -@@ -165,21 +195,7 @@ bool VideoWidget::request( struct vout_w - #ifdef QT5_HAS_WAYLAND - case VOUT_WINDOW_TYPE_WAYLAND: - { -- /* Ensure only the video widget is native (needed for Wayland) */ -- stable->setAttribute( Qt::WA_DontCreateNativeAncestors, true); -- -- QWindow *window = stable->windowHandle(); -- assert(window != NULL); -- window->create(); -- -- QPlatformNativeInterface *qni = qApp->platformNativeInterface(); -- assert(qni != NULL); -- -- p_wnd->handle.wl = static_cast( -- qni->nativeResourceForWindow(QByteArrayLiteral("surface"), -- window)); -- p_wnd->display.wl = static_cast( -- qni->nativeResourceForIntegration(QByteArrayLiteral("wl_display"))); -+ refreshHandles(); - break; - } - #endif ---- a/modules/gui/qt/components/interface_widgets.hpp -+++ b/modules/gui/qt/components/interface_widgets.hpp -@@ -88,6 +88,7 @@ private: - bool enable_mouse_events; - - void reportSize(); -+ void refreshHandles(); - - signals: - void sizeChanged( int, int ); diff --git a/alarm/vlc-rpi/PKGBUILD b/alarm/vlc-rpi/PKGBUILD index 4927470fc..26dc83080 100644 --- a/alarm/vlc-rpi/PKGBUILD +++ b/alarm/vlc-rpi/PKGBUILD @@ -6,11 +6,11 @@ pkgname=vlc-rpi _pkgname=vlc -_vlcver=3.0.18 +_vlcver=3.0.19 # optional fixup version including hyphen _vlcfixupver= pkgver=${_vlcver}${_vlcfixupver//-/.r} -pkgrel=7 +pkgrel=1 pkgdesc='Multi-platform MPEG, VCD/DVD, and DivX player with hw accel for RPi 3/4/400' url='https://www.videolan.org/vlc/' arch=(aarch64) @@ -21,7 +21,7 @@ depends=('a52dec' 'libdvbpsi' 'libxpm' 'libdca' 'libproxy' 'lua52' 'libidn' 'libmpeg2' 'xcb-util-keysyms' 'libtar' 'libxinerama' 'libsecret' 'libupnp' 'libixml.so' 'libupnp.so' 'libarchive' 'qt5-base' 'qt5-x11extras' 'qt5-svg' 'freetype2' 'fribidi' 'harfbuzz' - 'fontconfig' 'libxml2' 'gnutls' 'libplacebo' 'wayland-protocols' + 'fontconfig' 'libxml2' 'gnutls' 'libplacebo' 'aribb24' 'linux-rpi' 'libomxil-bellagio') makedepends=('gst-plugins-base-libs' 'live-media' 'libnotify' 'libbluray' 'flac' 'libdc1394' 'libavc1394' 'libcaca' 'gtk3' @@ -33,7 +33,7 @@ makedepends=('gst-plugins-base-libs' 'live-media' 'libnotify' 'libbluray' 'libx265.so' 'libx264.so' 'zvbi' 'libass' 'libkate' 'libtiger' 'sdl_image' 'libpulse' 'alsa-lib' 'jack' 'libsamplerate' 'libsoxr' 'lirc' 'libgoom2' 'projectm' 'aom' 'srt' 'dav1d' 'libomxil-bellagio' - 'aribb24' 'aribb25' 'pcsclite' 'debugedit') + 'aribb25' 'pcsclite' 'wayland-protocols') # 'chromaprint: Chromaprint audio fingerprinter' optdepends=('avahi: service discovery using bonjour protocol' @@ -97,44 +97,36 @@ optdepends=('avahi: service discovery using bonjour protocol' 'ncurses: ncurses interface' 'libnotify: notification plugin' 'gtk3: notification plugin' - 'aribb24: aribsub support' 'aribb25: aribcam support' - 'pcsclite: aribcam support') + 'pcsclite: aribcam support' + 'live-media: streaming over RTSP') conflicts=('vlc-plugin' 'vlc') provides=('vlc') replaces=('vlc-plugin') -options=('debug' '!emptydirs') +options=('!emptydirs') install='vlc.install' source=(https://download.videolan.org/${_pkgname}/${_vlcver}/${_pkgname}-${_vlcver}${_vlcfixupver}.tar.xz{,.asc} 99-vlc.rules vlc.config.txt 0001-vlc-live-media-2021.patch 0002-libplacebo-5.patch - # credit to RPi-Distro maintainers for this work + # credit to jc and RPi-Distro maintainers for this work, see: + # https://github.com/jc-kynesim/vlc.git # https://github.com/RPi-Distro/vlc/tree/bullseye-rpt/debian/patches - 0003-mmal_35.patch - 0004-mmal_caca.patch - 0005-mmal_chain.patch - 0006-mmal_exit_fix.patch - 0007-mmal_wayland.patch + 0003-dev-3.0.19-port_1.patch update-vlc-plugin-cache.hook) -sha256sums=('57094439c365d8aa8b9b41fa3080cc0eef2befe6025bb5cef722accc625aedec' +sha256sums=('643e3294bafe922324663ca499515b7564f2794575fd7d2b7992d20896381745' 'SKIP' '61125ab0da600d813f1aebd8445fcf03e176389cfb8aa28591f8225a13043089' 'be970a020695fdc4d0f968021f057a1cb625eeb6ee62995560e532d61ffb52dc' '753517a8b88c5950d516f0fe57a3ef169e0665ba7817d4b8d9976c666829a291' 'c47ecb0e8e8c03f8c5451aa12fc2e38e380364c38c411a13aa38b7b41def6989' - '1560f80b52b7d88bd1a4f1c7fb74633aa30e4302a772c739eecbd58b200e7060' - '53613a6eee1c215a7becd9a8b97d0ed9a034684a586b9437f35f215a5c859d1a' - 'a06d62bc579405588f5730a707af602d68f17d764a061f74958135aab34e4d92' - '1371c4fa43c8c7097aad21f3ac959aaea988a447ac30f9b96979c34bb0601316' - '1fcf65188a220905acea6da9cad256ceae405e9ebf5a7d079051984a8e602ba9' + '6d0e7b14ae4cd2e490271a8c00cd97027af01eb6e65629f761562bcbd2869980' 'b98043683dd90d3f5a3f501212dfc629839b661100de5ac79fd30cb7b4a06f13') validpgpkeys=('65F7C6B4206BD057A7EB73787180713BE58D1ADC') # VideoLAN Release Signing Key prepare() { cd ${_pkgname}-${_vlcver} - sed -e 's:truetype/ttf-dejavu:TTF:g' -i modules/visualization/projectm.cpp sed -e 's|-Werror-implicit-function-declaration||g' -i configure sed 's|whoami|echo builduser|g' -i configure @@ -147,7 +139,6 @@ prepare() { echo "Applying patch $src..." patch -Np1 < "../$src" done - autoreconf -vf } @@ -156,8 +147,9 @@ build() { export CFLAGS+=" -I/usr/include/samba-4.0 -ffat-lto-objects -I/usr/include/ffmpeg-rpi" export CPPFLAGS+=" -I/usr/include/samba-4.0" - export CXXFLAGS+=" -std=c++11 -I/usr/include/ffmpeg-rpi" - # upstream doesn't support lua 5.4 yet: https://trac.videolan.org/vlc/ticket/25036 + # building with lua ends in errors whereas lua5.2 builds and runs + export CXXFLAGS+=" -std=c++17 -I/usr/include/ffmpeg-rpi" + export PKG_CONFIG_PATH="/usr/lib/ffmpeg-rpi/pkgconfig" export LUAC=/usr/bin/luac5.2 export LUA_LIBS="$(pkg-config --libs lua5.2)" export RCC=/usr/bin/rcc-qt5