[vlc-commits] [Git][videolan/vlc][master] 2 commits: codec: substext: workaround scaling mode change
Jean-Baptiste Kempf (@jbk)
gitlab at videolan.org
Mon Jul 13 19:48:59 UTC 2026
Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC
Commits:
16fe55ca by François Cartegnie at 2026-07-13T21:23:17+02:00
codec: substext: workaround scaling mode change
refs #29965
- - - - -
b1a44a4d by Alexandre Janniaux at 2026-07-13T21:23:17+02:00
tests: spu: test the placing and external scaling
- - - - -
4 changed files:
- modules/codec/substext.h
- test/Makefile.am
- test/src/meson.build
- + test/src/video_output/spu.c
Changes:
=====================================
modules/codec/substext.h
=====================================
@@ -61,6 +61,7 @@ typedef struct
{
/* a min of one region */
substext_updater_region_t region;
+ uint64_t scaling_modes_flags; /* see #29965 */
/* styling */
text_style_t *p_default_style; /* decoder (full or partial) defaults */
@@ -100,6 +101,17 @@ static inline void SubpictureUpdaterSysRegionAdd(substext_updater_region_t *p_pr
*pp_next = p_new;
}
+static uint64_t GetScalingModesAsFlags(const substext_updater_region_t *r)
+{
+ uint64_t in_window_flags = 0;
+ for (int i=0; i<63 && r; i++)
+ {
+ in_window_flags |= (!r->b_in_window) << i;
+ r = r->p_next;
+ }
+ return in_window_flags;
+}
+
static void SubpictureTextUpdate(subpicture_t *subpic,
const struct vlc_spu_updater_configuration *cfg)
{
@@ -122,12 +134,16 @@ static void SubpictureTextUpdate(subpicture_t *subpic,
prev_render_fmt.i_sar_num = prev_render_fmt.i_sar_den = 1;
}
+ uint64_t scaling_modes_flags = GetScalingModesAsFlags(p_updtregion);
+
if (render_fmt.i_visible_width == prev_render_fmt.i_visible_width &&
render_fmt.i_visible_height == prev_render_fmt.i_visible_height &&
video_format_IsSimilar(cfg->previous.video_dst, fmt_dst) &&
+ sys->scaling_modes_flags == scaling_modes_flags &&
(sys->i_next_update == VLC_TICK_INVALID || sys->i_next_update > cfg->pts))
return;
+ sys->scaling_modes_flags = scaling_modes_flags;
if (!(p_updtregion->flags & UPDT_REGION_FIXED_DONE) &&
p_updtregion->b_absolute && !vlc_spu_regions_is_empty(&subpic->regions) &&
=====================================
test/Makefile.am
=====================================
@@ -69,6 +69,7 @@ check_PROGRAMS = \
test_src_misc_viewpoint \
test_src_video_output \
test_src_video_output_opengl \
+ test_src_video_output_spu \
test_modules_lua_extension \
test_modules_lua_playlist_parser \
test_modules_misc_medialibrary \
@@ -311,6 +312,8 @@ test_src_video_output_SOURCES = \
test_src_video_output_LDADD = $(LIBVLCCORE) $(LIBVLC)
test_src_video_output_opengl_SOURCES = src/video_output/opengl.c
test_src_video_output_opengl_LDADD = $(LIBVLCCORE) $(LIBVLC)
+test_src_video_output_spu_SOURCES = src/video_output/spu.c
+test_src_video_output_spu_LDADD = $(LIBVLCCORE) $(LIBVLC)
test_src_input_decoder_SOURCES = \
src/input/decoder/input_decoder.c \
=====================================
test/src/meson.build
=====================================
@@ -401,3 +401,10 @@ vlc_tests += {
'suite' : ['src', 'test_src'],
'link_with' : [libvlc, libvlccore],
}
+
+vlc_tests += {
+ 'name' : 'test_src_video_output_spu',
+ 'sources' : files('video_output/spu.c'),
+ 'suite' : ['src', 'test_src'],
+ 'link_with' : [libvlc, libvlccore],
+}
=====================================
test/src/video_output/spu.c
=====================================
@@ -0,0 +1,1111 @@
+/*****************************************************************************
+ * test/src/video_output/spu.c
+ *****************************************************************************
+ * Copyright (C) 2019-2026 VideoLabs, VideoLAN and VLC Authors
+ *
+ * Authors: Alexandre Janniaux <ajanni at videolabs.io>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "../../libvlc/test.h"
+#include <assert.h>
+#include <vlc_common.h>
+#include <vlc_spu.h>
+#include <vlc_codec.h>
+#include <vlc_subpicture.h>
+#define MODULE_NAME saletype
+#undef VLC_DYNAMIC_PLUGIN
+#include <vlc_plugin.h>
+#include <vlc_filter.h>
+#include <vlc_vout_display.h>
+#include "../lib/libvlc_internal.h"
+#include "../../../modules/codec/substext.h"
+
+#define PRINTDIMFSTR "%dx%d+%d,%d sar=%d:%d"
+#define PRINTDIMARGS(x) (x).i_visible_width,(x).i_visible_height,(x).i_x_offset,(x).i_y_offset,(x).i_sar_num,(x).i_sar_den
+#define PRINTPLACESTR "%dx%d+%d,%d"
+#define PRINTPLACEARGS(p) (p).width,(p).height,(p).x,(p).y
+
+#define BAILOUT(run) { fprintf(stderr, "FAILED %s:%d %s\n", __FUNCTION__, __LINE__, run); \
+ goto failed; }
+#define EXPECT(foo) do { if(!(foo)) BAILOUT(__FUNCTION__); } while (0)
+#define EXPECT_EQ_INT(foo, expected) do { \
+ int a__ = (foo); \
+ int e__ = (expected); \
+ if (a__ != e__) { \
+ fprintf(stderr, "FAILED %s:%d: %s == %s, got %d expected %d\n", \
+ __FUNCTION__, __LINE__, #foo, #expected, a__, e__); \
+ goto failed; \
+ } \
+} while (0)
+
+enum
+{
+ INTERNAL_SCALED,
+ EXTERNAL_SCALED,
+};
+
+/*
+ * Main tested issues:
+ *
+ * + Check that calling spu_Render multiple time for the same PTS won't move
+ * the subtitle (with or without external scaling).
+ *
+ * + Check that calling spu_Render with or without external scaling gives the
+ * same values.
+ */
+
+const char vlc_module_name[] = "spu_test";
+
+static const char *libvlc_argv[] = {
+ "-v",
+ "--ignore-config",
+ "-Idummy",
+ "--no-media-library",
+ "--text-renderer=saletype"
+};
+
+static subpicture_t *subpicture_new(decoder_t *dec,
+ const subpicture_updater_t *p_dyn)
+{
+ (void)dec;
+ return subpicture_New(p_dyn);
+}
+
+static const struct decoder_owner_callbacks dec_subpicture_cbs =
+{
+ .spu.buffer_new = subpicture_new,
+};
+
+static const es_format_t dec_fmt_in =
+{
+ .i_cat = SPU_ES,
+};
+
+static decoder_t dec_subpicture =
+{
+ .fmt_in = &dec_fmt_in,
+ .cbs = &dec_subpicture_cbs,
+};
+
+static subpicture_t *create_subtitle(unsigned w, unsigned h, int nb_regions)
+{
+ video_format_t fmt;
+ video_format_Init(&fmt, 0);
+ video_format_Setup(&fmt, VLC_CODEC_RGBA, w, h, w, h, 1, 1);
+
+ subpicture_t *sub = decoder_NewSubpicture(&dec_subpicture, NULL);
+ if(!sub)
+ return NULL;
+
+ while(nb_regions--)
+ {
+ subpicture_region_t *region = subpicture_region_New(&fmt);
+ if(!region)
+ {
+ subpicture_Delete(sub);
+ return NULL;
+ }
+ vlc_spu_regions_push(&sub->regions, region);
+
+ region->i_x = 0;
+ region->i_y = 0;
+ }
+
+ sub->i_start = VLC_TICK_0;
+ sub->i_stop = VLC_TICK_0 + vlc_tick_from_sec(1);
+ sub->b_subtitle = true;
+
+ return sub;
+}
+
+/*************************************************************************************************
+ * !Text Updater
+ ************************************************************************************************/
+
+static subpicture_t *create_text_subtitle(int nb_regions)
+{
+ video_format_t fmt;
+ video_format_Init(&fmt, VLC_CODEC_TEXT);
+
+ subpicture_t *sub = decoder_NewSubpictureText(&dec_subpicture);
+ if(!sub)
+ return NULL;
+
+ subtext_updater_sys_t *substextupdt = sub->updater.sys;
+
+ substextupdt->margin_ratio = 0.0;
+ substextupdt->region.p_segments = text_segment_New("100x100");
+
+ while(--nb_regions)
+ {
+ substext_updater_region_t *region = SubpictureUpdaterSysRegionNew();
+ if(!region)
+ {
+ subpicture_Delete(sub);
+ return NULL;
+ }
+ SubpictureUpdaterSysRegionAdd(&substextupdt->region, region);
+ region->b_absolute = true;
+ }
+
+ sub->b_subtitle = true;
+
+ return sub;
+}
+
+static const int aligns_sequence[] = {
+ 0,
+ SUBPICTURE_ALIGN_TOP,
+ SUBPICTURE_ALIGN_BOTTOM,
+ SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT,
+ SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT,
+ SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_RIGHT,
+ SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_RIGHT,
+ SUBPICTURE_ALIGN_LEFT,
+ SUBPICTURE_ALIGN_RIGHT,
+};
+
+const vlc_fourcc_t chroma_list[] =
+{
+ VLC_CODEC_RGBA, 0
+};
+
+static void test_reset_SPU(spu_t *spu)
+{
+ video_format_t fmt;
+ video_format_Init(&fmt, VLC_CODEC_RGBA);
+ assert(spu_Render(spu, NULL, &fmt, &fmt, true, NULL, INT64_MAX, INT64_MAX, false) == NULL);
+}
+
+static int spu_test_scale_x(int v, const video_format_t *fmt_out)
+{
+ return v * fmt_out->i_sar_den / fmt_out->i_sar_num;
+}
+
+static int spu_test_unscale_output_w(const video_format_t *fmt_out)
+{
+ return fmt_out->i_visible_width * fmt_out->i_sar_num /
+ fmt_out->i_sar_den;
+}
+
+static int spu_test_place_x(int x, int w, const video_format_t *fmt_out)
+{
+ int scaled_x = spu_test_scale_x(x, fmt_out);
+ // Because A/R can make the SPU exceed the display size, it needs to be clamped
+ int clamped_x = spu_test_scale_x(spu_test_unscale_output_w(fmt_out) - w,
+ fmt_out);
+
+ return __MIN(scaled_x, clamped_x);
+}
+
+static int spu_test_aligned_x(int align, int x, int w, int output_w)
+{
+ if (align & SUBPICTURE_ALIGN_LEFT)
+ return x;
+ if (align & SUBPICTURE_ALIGN_RIGHT)
+ return output_w - w - x;
+ return (output_w - w) / 2 + x;
+}
+
+static int spu_test_aligned_y(int align, int y, int h, int output_h)
+{
+ if (align & SUBPICTURE_ALIGN_TOP)
+ return y;
+ if (align & SUBPICTURE_ALIGN_BOTTOM)
+ return output_h - h - y;
+ return (output_h - h) / 2 + y;
+}
+
+static int expect_rendered_region(const struct subpicture_region_rendered *region,
+ const video_format_t *fmt_out,
+ int align,
+ int x, int y, int w, int h,
+ float xratio, float yratio,
+ bool internal_scaled,
+ int place_dx, int place_dy)
+{
+ const video_format_t *fmt = ®ion->p_picture->format;
+
+ // effective transformations
+ const unsigned fmt_sar_num = internal_scaled ? fmt_out->i_sar_num : 1;
+ const unsigned fmt_sar_den = internal_scaled ? fmt_out->i_sar_den : 1;
+ // original width/height scaling factor
+ const float fmt_ratio_x = internal_scaled ? xratio : 1;
+ const float fmt_ratio_y = internal_scaled ? yratio : 1;
+
+ const int logical_w = w / xratio;
+ const int logical_h = h / yratio;
+ const int logical_x = spu_test_aligned_x(align, x, w, fmt_out->i_visible_width) / xratio;
+ const int logical_y = spu_test_aligned_y(align, y, h, fmt_out->i_visible_height) / yratio;
+
+ EXPECT_EQ_INT(fmt->i_visible_width, w / fmt_ratio_x * fmt_sar_den / fmt_sar_num);
+ EXPECT_EQ_INT(fmt->i_visible_height, h / fmt_ratio_y);
+ EXPECT_EQ_INT(fmt->i_x_offset, 0);
+ EXPECT_EQ_INT(fmt->i_y_offset, 0);
+
+ EXPECT_EQ_INT(region->place.width, logical_w * fmt_out->i_sar_den / fmt_out->i_sar_num);
+ EXPECT_EQ_INT(region->place.height, logical_h);
+
+ EXPECT_EQ_INT(region->place.x, spu_test_place_x(logical_x, logical_w, fmt_out) + place_dx);
+ EXPECT_EQ_INT(region->place.y, logical_y + place_dy);
+
+ return VLC_SUCCESS;
+
+failed:
+ return VLC_EGENERIC;
+}
+
+#define CHECK_REGION(...) \
+ EXPECT(expect_rendered_region(__VA_ARGS__) == VLC_SUCCESS)
+
+static int test_multiple_spu_render(spu_t *spu)
+{
+ video_format_t fmt_in, fmt_out;
+ video_format_Init(&fmt_in, VLC_CODEC_RGBA);
+ video_format_Init(&fmt_out, VLC_CODEC_RGBA);
+ fmt_in.i_width = fmt_in.i_visible_width = 1920;
+ fmt_in.i_height = fmt_in.i_visible_height = 1080;
+ fmt_in.i_sar_num = fmt_in.i_sar_den = 1;
+ video_format_Copy(&fmt_out, &fmt_in);
+
+ /* render multiple regions with alignment */
+ subpicture_t *subpic = create_subtitle(100, 100, 2);
+ if(!subpic)
+ return 1;
+ subpic->i_start = VLC_TICK_0;
+ subpic->i_stop = VLC_TICK_0 + vlc_tick_from_sec(1);
+ {
+ struct subpicture_region_t *region = vlc_spu_regions_first_or_null(&subpic->regions);
+ region->i_align = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_RIGHT;
+ region->b_absolute = false;
+ region = vlc_list_first_entry_or_null(®ion->node, subpicture_region_t, node);
+ region->i_align = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_RIGHT;
+ region->b_absolute = false;
+ }
+ spu_PutSubpicture(spu, subpic);
+
+ const struct
+ {
+ unsigned num;
+ unsigned den;
+ } aspect_ratios[] = {
+ { 1, 1 },
+ { 4, 3 },
+ { 9, 16 }, // vertical!
+ };
+
+ vlc_render_subpicture *output = NULL;
+ for(unsigned aridx = 0; aridx < ARRAY_SIZE(aspect_ratios); aridx++)
+ {
+ fmt_out.i_sar_num = aspect_ratios[aridx].num;
+ fmt_out.i_sar_den = aspect_ratios[aridx].den;
+
+ for(int i = INTERNAL_SCALED; i<=EXTERNAL_SCALED; i++)
+ {
+ fprintf(stderr,"\n\ttesting %s scaling\n", i==INTERNAL_SCALED ? "internal" : "external");
+ output = spu_Render(spu, i == INTERNAL_SCALED ? NULL : chroma_list,
+ &fmt_out, &fmt_in, true, NULL,
+ VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output->regions.size == 2);
+ const struct subpicture_region_rendered *region0 = output->regions.data[0];
+ const video_format_t *fmt0 = ®ion0->p_picture->format;
+ const struct subpicture_region_rendered *region1 = output->regions.data[1];
+ const video_format_t *fmt1 = ®ion1->p_picture->format;
+
+ fprintf(stderr, "region0 " PRINTDIMFSTR "\n", PRINTDIMARGS(*fmt0));
+ fprintf(stderr, " at place " PRINTPLACESTR "\n", PRINTPLACEARGS(region0->place));
+ fprintf(stderr, "region1 " PRINTDIMFSTR "\n", PRINTDIMARGS(*fmt1));
+ fprintf(stderr, " at place " PRINTPLACESTR "\n", PRINTPLACEARGS(region1->place));
+
+ CHECK_REGION(region0, &fmt_out,
+ SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_RIGHT,
+ 0, 0, 100, 100,
+ 1.0, 1.0, (i == INTERNAL_SCALED),
+ +0, +0);
+
+ CHECK_REGION(region1, &fmt_out,
+ SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_RIGHT,
+ 0, 0, 100, 100,
+ 1.0, 1.0, (i == INTERNAL_SCALED),
+ +0, -100); // displacement because packed against region 0
+
+ vlc_render_subpicture_Delete(output);
+ output = NULL;
+ }
+ }
+
+ return 0;
+
+failed:
+ if(output)
+ vlc_render_subpicture_Delete(output);
+ return 1;
+}
+
+static int test_spu_scaling(spu_t *spu)
+{
+ video_format_t fmt_in, fmt_out;
+ video_format_Init(&fmt_in, VLC_CODEC_RGBA);
+ video_format_Init(&fmt_out, VLC_CODEC_RGBA);
+ fmt_in.i_width = 2000;
+ fmt_in.i_visible_width = 1920;
+ fmt_in.i_height = 1100;
+ fmt_in.i_visible_height = 1080;
+ fmt_in.i_sar_num = fmt_in.i_sar_den = 1;
+ video_format_Copy(&fmt_out, &fmt_in);
+
+ const vout_display_place_t video_position = {fmt_out.i_x_offset,
+ fmt_out.i_y_offset,
+ fmt_out.i_visible_width,
+ fmt_out.i_visible_height};
+
+ vlc_render_subpicture *output0 = NULL;
+ vlc_render_subpicture *output1 = NULL;
+
+ {
+ subpicture_t *subpic = create_subtitle(100, 100, 1);
+ EXPECT(subpic);
+ spu_PutSubpicture(spu, subpic);
+ output0 = spu_Render(spu, NULL, &fmt_out, &fmt_in,
+ true, &video_position,
+ VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output0);
+ EXPECT(output0->regions.size == 1);
+
+ test_reset_SPU(spu);
+
+ subpic = create_subtitle(100, 100, 1);
+ EXPECT(subpic);
+ spu_PutSubpicture(spu, subpic);
+ output1 = spu_Render(spu, chroma_list, &fmt_out, &fmt_in,
+ true, &video_position,
+ VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output1);
+ EXPECT(output1->regions.size == 1);
+
+ const struct subpicture_region_rendered *region1, *region2;
+ region1 = output0->regions.data[0];
+ region2 = output1->regions.data[0];
+
+ /* Without external scaling, a non-1:1 aspect ratio would scale the image to
+ * have a 1:1 aspect ratio, as outputs can't handle scale.
+ * However, aspect ratio can be different that 1:1 with external scaling, as
+ * subtitles can be rasterized at a different aspect ratio.
+ * See substext.h for example. */
+ const video_format_t *fmt1 = ®ion1->p_picture->format;
+ const video_format_t *fmt2 = ®ion2->p_picture->format;
+ EXPECT(fmt1->i_sar_num == fmt2->i_sar_den);
+
+ fprintf(stderr, "place " PRINTPLACESTR, PRINTPLACEARGS(region1->place));
+ fprintf(stderr, "output0 " PRINTDIMFSTR, PRINTDIMARGS(*fmt1));
+ fprintf(stderr, "place " PRINTPLACESTR, PRINTPLACEARGS(region2->place) );
+ fprintf(stderr, "output1 " PRINTDIMFSTR, PRINTDIMARGS(*fmt2));
+
+ /* There should be no differences for subpicture rendering whether external
+ * scaling is enabled or not. */
+ EXPECT(fmt1->i_x_offset == fmt2->i_x_offset && fmt1->i_y_offset == fmt2->i_y_offset);
+ EXPECT(region1->place.x == region2->place.x && region1->place.y == region2->place.y);
+
+ vlc_render_subpicture_Delete(output0);
+ output0 = NULL;
+ vlc_render_subpicture_Delete(output1);
+ output1 = NULL;
+ }
+
+ test_reset_SPU(spu);
+
+ /*
+ * Check that original picture dimensions (the ones the SPU were designed against).
+ * The SPU can come from other source, or the video can be encoded to the non native res.
+ */
+ const struct
+ {
+ unsigned num;
+ unsigned den;
+ } aspect_ratios[] = {
+ { 1, 1 },
+ { 4, 3 },
+ { 16, 9 },
+ };
+
+ for(unsigned aridx = 0; aridx < ARRAY_SIZE(aspect_ratios); aridx++)
+ {
+ float xratio = 0.5, yratio = 0.5;
+
+ fmt_out.i_sar_num = aspect_ratios[aridx].num;
+ fmt_out.i_sar_den = aspect_ratios[aridx].den;
+
+ for(int i = INTERNAL_SCALED; i<=EXTERNAL_SCALED; i++)
+ {
+ subpicture_t *subpic = create_subtitle(100, 100, 1);
+ EXPECT(subpic);
+ subpic->i_original_picture_width = fmt_in.i_visible_width * xratio;
+ subpic->i_original_picture_height = fmt_in.i_visible_height * yratio;
+
+ struct subpicture_region_t *srcregion = vlc_spu_regions_first_or_null(&subpic->regions);
+ if(!srcregion)
+ {
+ subpicture_Delete(subpic);
+ goto failed;
+ }
+ srcregion->i_align = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT;
+ srcregion->i_x = 10;
+ srcregion->i_y = 10;
+
+ spu_PutSubpicture(spu, subpic);
+
+ fprintf(stderr,"\n\ttesting %s original picture dimensions\n",
+ i==INTERNAL_SCALED ? "internal" : "external");
+ output0 = spu_Render(spu, i == INTERNAL_SCALED ? NULL : chroma_list,
+ &fmt_out, &fmt_in, true, NULL,
+ VLC_TICK_0, VLC_TICK_0, false);
+ const struct subpicture_region_rendered *region = output0->regions.data[0];
+ const video_format_t *fmt = ®ion->p_picture->format;
+
+ fprintf(stderr, PRINTDIMFSTR "\n", PRINTDIMARGS(*fmt));
+ fprintf(stderr, " at place " PRINTPLACESTR "\n", PRINTPLACEARGS(region->place));
+
+ CHECK_REGION(region, &fmt_out,
+ SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT,
+ 10, 10, 100, 100,
+ xratio, yratio, (i == INTERNAL_SCALED),
+ +0, +0);
+
+ vlc_render_subpicture_Delete(output0);
+ output0 = NULL;
+ test_reset_SPU(spu);
+ }
+
+ }
+
+ /* Check that destination forced A/R is applied, ie, we compensate */
+ fprintf(stderr, "\nchecking destination A/R\n");
+
+ for(unsigned aridx = 1; aridx < ARRAY_SIZE(aspect_ratios); aridx++)
+ {
+ float xratio = 1.0, yratio = 1.0;
+
+ fmt_out.i_sar_num = aspect_ratios[aridx].num;
+ fmt_out.i_sar_den = aspect_ratios[aridx].den;
+
+ for(int i = INTERNAL_SCALED; i<=EXTERNAL_SCALED; i++)
+ {
+ subpicture_t *subpic = create_subtitle(100, 100, 1);
+ EXPECT(subpic);
+
+ struct subpicture_region_t *srcregion = vlc_spu_regions_first_or_null(&subpic->regions);
+ if(!srcregion)
+ {
+ subpicture_Delete(subpic);
+ goto failed;
+ }
+ srcregion->i_align = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT;
+ srcregion->i_x = 10;
+ srcregion->i_y = 10;
+
+ spu_PutSubpicture(spu, subpic);
+
+ fprintf(stderr,"\n\ttesting %s scaling\n", i==INTERNAL_SCALED ? "internal" : "external");
+ output0 = spu_Render(spu, i == INTERNAL_SCALED ? NULL : chroma_list,
+ &fmt_out, &fmt_in, true, NULL,
+ VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output0);
+ const struct subpicture_region_rendered *region = output0->regions.data[0];
+ const video_format_t *fmt = ®ion->p_picture->format;
+
+ fprintf(stderr, "\t" PRINTDIMFSTR "\n", PRINTDIMARGS(*fmt));
+ fprintf(stderr, "\t at place " PRINTPLACESTR "\n", PRINTPLACEARGS(region->place));
+
+ CHECK_REGION(region, &fmt_out,
+ SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT,
+ 10, 10, 100, 100,
+ xratio, yratio, (i == INTERNAL_SCALED),
+ +0, +0);
+
+ vlc_render_subpicture_Delete(output0);
+ output0 = NULL;
+ test_reset_SPU(spu);
+ }
+
+ }
+
+ return 0;
+
+failed:
+ if(output0)
+ vlc_render_subpicture_Delete(output0);
+ if(output1)
+ vlc_render_subpicture_Delete(output1);
+ return 1;
+}
+
+static int test_spu_align(spu_t *spu)
+{
+ video_format_t fmt_in, fmt_out;
+ video_format_Init(&fmt_in, VLC_CODEC_RGBA);
+ video_format_Init(&fmt_out, VLC_CODEC_RGBA);
+ fmt_in.i_width = fmt_in.i_visible_width = 1920;
+ fmt_in.i_height = fmt_in.i_visible_height = 1080;
+ fmt_in.i_sar_num = fmt_in.i_sar_den = 1;
+ video_format_Copy(&fmt_out, &fmt_in);
+
+ /* Check all aligns */
+ vlc_render_subpicture *output0 = NULL;
+ fmt_out.i_sar_num = 1; fmt_out.i_sar_den = 1;
+ for(unsigned j = 0; j<ARRAY_SIZE(aligns_sequence); j++)
+ {
+ fprintf(stderr, "\n\nchecking align flags %x\n", aligns_sequence[j]);
+ for(int i = INTERNAL_SCALED; i<=EXTERNAL_SCALED; i++)
+ {
+ subpicture_t *subpic = create_subtitle(100, 100, 1);
+ EXPECT(subpic);
+
+ unsigned offset_x = j % 3 ? 10 : 0, offset_y = j % 4 ? 10 : 0;
+
+ struct subpicture_region_t *srcregion = vlc_spu_regions_first_or_null(&subpic->regions);
+ if(!srcregion)
+ {
+ subpicture_Delete(subpic);
+ return 1;
+ }
+ srcregion->b_absolute = false;
+ srcregion->i_align = aligns_sequence[j]; /* change our align */
+ srcregion->i_x = offset_x;
+ srcregion->i_y = offset_y;
+
+ spu_PutSubpicture(spu, subpic);
+
+ fprintf(stderr,"\ttesting %s scaling\n", i==INTERNAL_SCALED ? "internal" : "external");
+ output0 = spu_Render(spu, i == INTERNAL_SCALED ? NULL : chroma_list,
+ &fmt_out, &fmt_in, true, NULL,
+ VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output0);
+
+ const struct subpicture_region_rendered *region = output0->regions.data[0];
+ const video_format_t *fmt = ®ion->p_picture->format;
+
+ fprintf(stderr, "\t" PRINTDIMFSTR "\n", PRINTDIMARGS(*fmt));
+ fprintf(stderr, "\t at place " PRINTPLACESTR "\n", PRINTPLACEARGS(region->place));
+
+ CHECK_REGION(region, &fmt_out,
+ aligns_sequence[j],
+ offset_x, offset_y, 100, 100,
+ 1.0, 1.0, (i == INTERNAL_SCALED),
+ +0, +0);
+
+ vlc_render_subpicture_Delete(output0);
+ output0 = NULL;
+ test_reset_SPU(spu);
+ }
+ }
+
+ return 0;
+
+failed:
+ if(output0)
+ vlc_render_subpicture_Delete(output0);
+ return 1;
+}
+
+static int test_text_rendering(spu_t *spu)
+{
+ video_format_t fmt_in, fmt_out;
+ video_format_Init(&fmt_in, VLC_CODEC_RGBA);
+ video_format_Init(&fmt_out, VLC_CODEC_RGBA);
+ fmt_in.i_width = fmt_in.i_visible_width = 1920;
+ fmt_in.i_height = fmt_in.i_visible_height = 1080;
+ fmt_in.i_sar_num = fmt_in.i_sar_den = 1;
+ video_format_Copy(&fmt_out, &fmt_in);
+
+ vlc_render_subpicture *output0 = NULL;
+ fmt_out.i_sar_num = 1; fmt_out.i_sar_den = 1;
+ for(unsigned j = 0; j<ARRAY_SIZE(aligns_sequence); j++)
+ {
+ for(int i = INTERNAL_SCALED; i<=EXTERNAL_SCALED; i++)
+ {
+ subpicture_t *subpic = create_text_subtitle(1);
+ EXPECT(subpic);
+ subpic->i_start = VLC_TICK_0;
+ subpic->i_stop = VLC_TICK_0 + vlc_tick_from_sec(1);
+ subtext_updater_sys_t *updtsys = subpic->updater.sys;
+ substext_updater_region_t *updtregion = &updtsys->region;
+
+ /* change our align */
+ updtregion->align = aligns_sequence[j];
+ updtregion->origin.x = j % 3 ? 10 : 0;
+ updtregion->origin.y = j % 4 ? 10 : 0;
+
+ fprintf(stderr, "\n\nchecking with updater align flags %x origin %f %f\n", updtregion->align, updtregion->origin.x, updtregion->origin.y);
+
+ /* Ensure it will be re-rendered by updater */
+ vlc_spu_regions_Clear(&subpic->regions);
+ updtregion->flags &= ~UPDT_REGION_FIXED_DONE;
+ updtregion->b_absolute = false;
+
+ spu_PutSubpicture(spu, subpic);
+
+ fprintf(stderr,"\t%d origin %f %f absolute %d\n", __LINE__, updtregion->origin.x, updtregion->origin.y, updtregion->b_absolute);
+ fprintf(stderr,"\ttesting %s scaling\n", i==INTERNAL_SCALED ? "internal" : "external");
+ output0 = spu_Render(spu, i == INTERNAL_SCALED ? NULL : chroma_list,
+ &fmt_out, &fmt_in, true, NULL,
+ VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output0);
+ EXPECT(output0->regions.size);
+ fprintf(stderr,"\t%d origin %f %f\n", __LINE__, updtregion->origin.x, updtregion->origin.y);
+ const struct subpicture_region_rendered *region0 = output0->regions.data[0];
+ const video_format_t *fmt0 = ®ion0->p_picture->format;
+
+ fprintf(stderr, "\tregion0 " PRINTDIMFSTR "\n", PRINTDIMARGS(*fmt0));
+ fprintf(stderr, "\t at place " PRINTPLACESTR "\n", PRINTPLACEARGS(region0->place));
+
+ CHECK_REGION(region0, &fmt_out,
+ aligns_sequence[j],
+ j % 3 ? 10 : 0, j % 4 ? 10 : 0, 100, 100,
+ 1.0, 1.0, (i == INTERNAL_SCALED),
+ +0, +0);
+
+ vlc_render_subpicture_Delete(output0);
+ output0 = NULL;
+ test_reset_SPU(spu);
+ }
+ }
+
+ return 0;
+
+failed:
+ if(output0)
+ vlc_render_subpicture_Delete(output0);
+ return 1;
+}
+
+static int test_spu_rescaling(spu_t *spu)
+{
+ video_format_t fmt_in, fmt_out;
+ video_format_Init(&fmt_in, VLC_CODEC_RGBA);
+ video_format_Init(&fmt_out, VLC_CODEC_RGBA);
+ fmt_in.i_width = 2000;
+ fmt_in.i_visible_width = 1920;
+ fmt_in.i_height = 1200;
+ fmt_in.i_visible_height = 1080;
+ fmt_in.i_sar_num = fmt_in.i_sar_den = 1;
+ video_format_Copy(&fmt_out, &fmt_in);
+
+ vlc_render_subpicture *output0 = NULL;
+ vlc_render_subpicture *output1 = NULL;
+ {
+ subpicture_t *subpic = create_subtitle(100, 100, 1);
+ EXPECT(subpic);
+ struct subpicture_region_t *srcregion = vlc_spu_regions_first_or_null(&subpic->regions);
+ if(!srcregion)
+ {
+ subpicture_Delete(subpic);
+ return 1;
+ }
+ srcregion->i_x = 50;
+ srcregion->i_y = 50;
+ spu_PutSubpicture(spu, subpic);
+
+ output0 = spu_Render(spu, NULL, &fmt_out, &fmt_in,
+ true, NULL, VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output0);
+ EXPECT(output0->regions.size);
+
+ fmt_out.i_visible_width = 1920/10;
+ fmt_out.i_visible_height = 1080/10;
+
+ output1 = spu_Render(spu, NULL, &fmt_out, &fmt_in,
+ true, NULL, VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output1);
+ EXPECT(output1->regions.size);
+
+ vlc_render_subpicture_Delete(output0);
+ output0 = NULL;
+ vlc_render_subpicture_Delete(output1);
+ output1 = NULL;
+ }
+
+ return 0;
+
+failed:
+ if(output0)
+ vlc_render_subpicture_Delete(output0);
+ if(output1)
+ vlc_render_subpicture_Delete(output1);
+ return 1;
+}
+
+static int test_spu_rescaling_text(spu_t *spu)
+{
+ video_format_t fmt_in, fmt_out;
+ video_format_Init(&fmt_in, VLC_CODEC_RGBA);
+ video_format_Init(&fmt_out, VLC_CODEC_RGBA);
+ fmt_in.i_width = 1100;
+ fmt_in.i_visible_width = 1000;
+ fmt_in.i_height = 600;
+ fmt_in.i_visible_height = 500;
+ fmt_in.i_sar_num = fmt_in.i_sar_den = 1;
+ video_format_Copy(&fmt_out, &fmt_in);
+ fmt_out.i_width = 2100;
+ fmt_out.i_visible_width = 2000;
+ fmt_out.i_height = 1100;
+ fmt_out.i_visible_height = 1000;
+
+ vlc_render_subpicture *output0 = NULL;
+ vlc_render_subpicture *output1 = NULL;
+
+ /* Rescaling for text is tricky as the rendered region size will only be
+ * decided by the text_renderer.
+ * - Text is usually rendered at *destination* size if bigger than source
+ * - Otherwise it is rendered at source resolution, but rescaled (we're <= source size)
+ * Scaling then does not apply to the text_renderer output size difference but it should
+ * apply for A/R compensation with the target display. (but A/R can depend on size diff :/)
+ */
+ {
+ subpicture_t *subpic = create_text_subtitle(1);
+ assert(subpic);
+ subpic->i_start = VLC_TICK_0;
+ subpic->i_stop = VLC_TICK_0 + vlc_tick_from_sec(1);
+ subtext_updater_sys_t *updtsys = subpic->updater.sys;
+ substext_updater_region_t *updtregion = &updtsys->region;
+ updtregion->align = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT;
+ updtregion->origin.x = .05;
+ updtregion->origin.y = .05;
+ updtregion->flags |= UPDT_REGION_ORIGIN_X_IS_RATIO|UPDT_REGION_ORIGIN_Y_IS_RATIO;
+ updtregion->b_absolute = false;
+
+ spu_PutSubpicture(spu, subpic);
+
+ output0 = spu_Render(spu, NULL, &fmt_out, &fmt_in,
+ true, NULL, VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output0);
+ EXPECT(output0->regions.size);
+ const struct subpicture_region_rendered *region0 = output0->regions.data[0];
+
+ EXPECT_EQ_INT(region0->place.x, fmt_out.i_visible_width * 0.05);
+ EXPECT_EQ_INT(region0->place.y, fmt_out.i_visible_height * 0.05);
+
+ // resize of dest window
+ fmt_out.i_visible_width /= 4;
+ fmt_out.i_visible_height /= 4;
+
+ output1 = spu_Render(spu, NULL, &fmt_out, &fmt_in,
+ true, NULL, VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output1);
+ EXPECT(output1->regions.size);
+ const struct subpicture_region_rendered *region1 = output1->regions.data[0];
+
+ EXPECT_EQ_INT(region1->place.x, fmt_out.i_visible_width * 0.05);
+ EXPECT_EQ_INT(region1->place.y, (unsigned)(fmt_out.i_visible_height * 0.05));
+ EXPECT_EQ_INT(region1->place.x, region0->place.x /4);
+ EXPECT_EQ_INT(region1->place.y, region0->place.y /4);
+ EXPECT_EQ_INT(region1->place.width, region0->place.width);
+ EXPECT_EQ_INT(region1->place.height, region0->place.height);
+
+ vlc_render_subpicture_Delete(output0);
+ output0 = NULL;
+ vlc_render_subpicture_Delete(output1);
+ output1 = NULL;
+ }
+
+ {
+ /* Render on a wider area / video_position + in_window */
+ const vout_display_place_t video_position = {10,
+ 10,
+ fmt_out.i_visible_width * 2,
+ fmt_out.i_visible_height * 2};
+
+ subpicture_t *subpic = create_text_subtitle(1);
+ assert(subpic);
+ subpic->i_start = VLC_TICK_0;
+ subpic->i_stop = VLC_TICK_0 + vlc_tick_from_sec(1);
+ subtext_updater_sys_t *updtsys = subpic->updater.sys;
+ substext_updater_region_t *updtregion = &updtsys->region;
+ updtregion->align = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT;
+ updtregion->origin.x = .05;
+ updtregion->origin.y = .05;
+ updtregion->flags |= UPDT_REGION_ORIGIN_X_IS_RATIO|UPDT_REGION_ORIGIN_Y_IS_RATIO;
+ updtregion->b_absolute = false;
+
+ spu_PutSubpicture(spu, subpic);
+
+ updtregion->b_in_window = true;
+ output0 = spu_Render(spu, NULL, &fmt_out, &fmt_in,
+ true, &video_position, VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output0);
+ EXPECT(output0->regions.size);
+ const struct subpicture_region_rendered *region0 = output0->regions.data[0];
+
+ EXPECT_EQ_INT(region0->place.x, 10 + (unsigned)(fmt_out.i_visible_width * 0.05) * 2);
+ EXPECT_EQ_INT(region0->place.y, 10 + (unsigned)(fmt_out.i_visible_height * 0.05) * 2);
+ EXPECT_EQ_INT(region0->place.width, 100 * 2);
+ EXPECT_EQ_INT(region0->place.height, 100 * 2);
+
+ vlc_render_subpicture_Delete(output0);
+ output0 = NULL;
+
+ /* video_position but not in_window */
+ updtregion->b_in_window = false;
+ output1 = spu_Render(spu, NULL, &fmt_out, &fmt_in,
+ true, &video_position, VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output1);
+ EXPECT(output1->regions.size);
+ const struct subpicture_region_rendered *region1 = output1->regions.data[0];
+
+ EXPECT_EQ_INT(region1->place.x, 10 + video_position.width * 5/100);
+ EXPECT_EQ_INT(region1->place.y, 10 + video_position.height * 5/100);
+ EXPECT_EQ_INT(region1->place.width, 100);
+ EXPECT_EQ_INT(region1->place.height, 100);
+
+ vlc_render_subpicture_Delete(output1);
+ output1 = NULL;
+ }
+
+ return 0;
+
+failed:
+ if(output0)
+ vlc_render_subpicture_Delete(output0);
+ if(output1)
+ vlc_render_subpicture_Delete(output1);
+ return 1;
+}
+
+static int test_spu_crop(spu_t *spu)
+{
+ video_format_t fmt_in, fmt_out;
+ video_format_Init(&fmt_in, VLC_CODEC_RGBA);
+ video_format_Init(&fmt_out, VLC_CODEC_RGBA);
+ fmt_in.i_width = fmt_in.i_visible_width = 1000;
+ fmt_in.i_height = fmt_in.i_visible_height = 500;
+ fmt_in.i_sar_num = fmt_in.i_sar_den = 1;
+ video_format_Copy(&fmt_out, &fmt_in);
+
+ vlc_render_subpicture *output0 = NULL;
+ vlc_render_subpicture *output1 = NULL;
+
+ {
+ subpicture_t *subpic = create_subtitle(100, 100, 1);
+ EXPECT(subpic);
+
+ struct subpicture_region_t *srcregion =
+ vlc_spu_regions_first_or_null(&subpic->regions);
+ if(!srcregion)
+ {
+ subpicture_Delete(subpic);
+ goto failed;
+ }
+ srcregion->i_align = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT;
+ srcregion->i_x = 20;
+ srcregion->i_y = 30;
+ srcregion->i_max_width = 40;
+ srcregion->i_max_height = 25;
+
+ spu_PutSubpicture(spu, subpic);
+
+ output0 = spu_Render(spu, NULL, &fmt_out, &fmt_in,
+ true, NULL, VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output0);
+ EXPECT(output0->regions.size == 1);
+
+ const struct subpicture_region_rendered *region = output0->regions.data[0];
+ const video_format_t *fmt = ®ion->p_picture->format;
+
+ EXPECT_EQ_INT(fmt->i_x_offset, 0);
+ EXPECT_EQ_INT(fmt->i_y_offset, 0);
+ EXPECT_EQ_INT(fmt->i_visible_width, 40);
+ EXPECT_EQ_INT(fmt->i_visible_height, 25);
+ EXPECT_EQ_INT(region->place.x, 20);
+ EXPECT_EQ_INT(region->place.y, 30);
+ EXPECT_EQ_INT(region->place.width, 40);
+ EXPECT_EQ_INT(region->place.height, 25);
+
+ vlc_render_subpicture_Delete(output0);
+ output0 = NULL;
+ }
+
+ test_reset_SPU(spu);
+
+ {
+ fmt_in.i_width = fmt_in.i_visible_width = 200;
+ fmt_in.i_height = fmt_in.i_visible_height = 100;
+ video_format_Copy(&fmt_out, &fmt_in);
+ fmt_out.i_width = fmt_out.i_visible_width = 400;
+ fmt_out.i_height = fmt_out.i_visible_height = 200;
+
+ const vout_display_place_t video_position = {10, 20, 200, 100};
+
+ subpicture_t *subpic = create_subtitle(20, 10, 1);
+ EXPECT(subpic);
+
+ struct subpicture_region_t *srcregion =
+ vlc_spu_regions_first_or_null(&subpic->regions);
+ if(!srcregion)
+ {
+ subpicture_Delete(subpic);
+ goto failed;
+ }
+ srcregion->i_align = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT;
+ srcregion->i_x = 5;
+ srcregion->i_y = 6;
+ srcregion->b_in_window = false;
+
+ spu_PutSubpicture(spu, subpic);
+
+ output0 = spu_Render(spu, NULL, &fmt_out, &fmt_in,
+ true, &video_position,
+ VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output0);
+ EXPECT(output0->regions.size == 1);
+ const struct subpicture_region_rendered *region0 = output0->regions.data[0];
+
+ EXPECT_EQ_INT(region0->place.x, 15);
+ EXPECT_EQ_INT(region0->place.y, 26);
+ EXPECT_EQ_INT(region0->place.width, 20);
+ EXPECT_EQ_INT(region0->place.height, 10);
+
+ srcregion->b_in_window = true;
+
+ output1 = spu_Render(spu, NULL, &fmt_out, &fmt_in,
+ true, &video_position,
+ VLC_TICK_0, VLC_TICK_0, false);
+ EXPECT(output1);
+ EXPECT(output1->regions.size == 1);
+ const struct subpicture_region_rendered *region1 = output1->regions.data[0];
+
+ EXPECT_EQ_INT(region1->place.x, 10);
+ EXPECT_EQ_INT(region1->place.y, 12);
+ EXPECT_EQ_INT(region1->place.width, 40);
+ EXPECT_EQ_INT(region1->place.height, 20);
+ }
+
+ vlc_render_subpicture_Delete(output0);
+ vlc_render_subpicture_Delete(output1);
+ return 0;
+
+failed:
+ if(output0)
+ vlc_render_subpicture_Delete(output0);
+ if(output1)
+ vlc_render_subpicture_Delete(output1);
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ test_init();
+
+ (void)argc; (void)argv;
+
+ libvlc_instance_t *libvlc = libvlc_new(ARRAY_SIZE(libvlc_argv), libvlc_argv);
+ assert(libvlc);
+
+ spu_t *spu = spu_Create(&libvlc->p_libvlc_int->obj, NULL);
+ assert(spu);
+
+ int ret = test_spu_scaling(spu);
+
+ /* flush SPU before running other test */
+ test_reset_SPU(spu);
+
+ ret |= test_multiple_spu_render(spu);
+
+ /* flush SPU before running other test */
+ test_reset_SPU(spu);
+
+ ret |= test_text_rendering(spu);
+
+ /* flush SPU before running other test */
+ test_reset_SPU(spu);
+
+ ret |= test_spu_align(spu);
+
+ /* flush SPU before running other test */
+ test_reset_SPU(spu);
+
+ ret |= test_spu_rescaling(spu);
+
+ /* flush SPU before running other test */
+ test_reset_SPU(spu);
+
+ ret |= test_spu_rescaling_text(spu);
+
+ /* flush SPU before running other test */
+ test_reset_SPU(spu);
+
+ ret |= test_spu_crop(spu);
+
+ spu_Destroy(spu);
+
+ libvlc_release(libvlc);
+
+ return ret;
+}
+
+/*************************************************************************************************
+ * Fake Renderer
+ ************************************************************************************************/
+
+static void renderer_Destroy( filter_t *p_filter )
+{
+ VLC_UNUSED(p_filter);
+}
+
+static subpicture_region_t *renderer_Render( filter_t *p_filter,
+ const subpicture_region_t *p_region_in,
+ const vlc_fourcc_t *p_chroma_list )
+{
+ VLC_UNUSED(p_filter); VLC_UNUSED(p_chroma_list);
+ unsigned w,h;
+ assert(sscanf(p_region_in->p_text->psz_text, "%ux%u", &w, &h) == 2);
+
+ video_format_t fmt;
+ video_format_Init(&fmt, 0);
+ video_format_Setup(&fmt, VLC_CODEC_RGBA, w, h, w, h, 1, 1);
+ subpicture_region_t *r = subpicture_region_New(&fmt);
+ r->b_absolute = p_region_in->b_absolute;
+ r->i_x = p_region_in->i_x;
+ r->i_y = p_region_in->i_y;
+ r->i_align = p_region_in->i_align;
+ return r;
+}
+
+static const struct vlc_filter_operations renderer_ops =
+{
+ .render = renderer_Render, .close = renderer_Destroy,
+};
+
+static int renderer_Create( filter_t *p_filter )
+{
+ p_filter->ops = &renderer_ops;
+ return VLC_SUCCESS;
+}
+
+vlc_module_begin()
+ set_subcategory(SUBCAT_VIDEO_SUBPIC)
+ add_shortcut("saletype")
+ set_callback_text_renderer(renderer_Create, 0)
+ add_submodule ()
+vlc_module_end()
+
+VLC_EXPORT const vlc_plugin_cb vlc_static_modules[] = {
+ VLC_SYMBOL(vlc_entry),
+ NULL
+};
+
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/c9628afc4b221b171ec2d9e028782b9eb1247426...b1a44a4da2fc55ddb7796141c19e38838354dbe6
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/c9628afc4b221b171ec2d9e028782b9eb1247426...b1a44a4da2fc55ddb7796141c19e38838354dbe6
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help
More information about the vlc-commits
mailing list