[vlc-commits] [Git][videolan/vlc][master] test: video_output: add non-regression test

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Sun Dec 12 18:11:31 UTC 2021



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
785d1745 by Alexandre Janniaux at 2021-12-11T17:14:13+01:00
test: video_output: add non-regression test

This commit introduces an integration test infrastructure which mocks
the external components (filters, decoders, window, display) used by the
video pipeline and starts this pipeline using the usual input item
properties and player.

The test will spawn an interface to have an initial vlc_object_t without
going though the private layers of libvlc (avoiding casting the instance
to private type, etc), so as to avoid encouraging code using such casts.

It uses this infrastructure to prevent regression from the ticket #26345.
By ensuring that the second failed decoder update won't crash, we can
check that an already started vout won't lead to double-join/free like
it was fixed in a7c99d05938a86e830498045aa41e4b213a7eb45.

The test itself will later be extended to add more sensible cases.

- - - - -


4 changed files:

- test/Makefile.am
- + test/src/video_output/video_output.c
- + test/src/video_output/video_output.h
- + test/src/video_output/video_output_scenarios.c


Changes:

=====================================
test/Makefile.am
=====================================
@@ -33,6 +33,7 @@ check_PROGRAMS = \
 	test_src_misc_bits \
 	test_src_misc_epg \
 	test_src_misc_keystore \
+	test_src_video_output \
 	test_modules_packetizer_helpers \
 	test_modules_packetizer_hxxx \
 	test_modules_packetizer_h264 \
@@ -78,7 +79,7 @@ check_HEADERS = libvlc/test.h libvlc/libvlc_additions.h libvlc/media_utils.h
 TESTS = $(check_PROGRAMS) check_POTFILES.sh
 
 AM_CFLAGS = -DSRCDIR=\"$(srcdir)\"
-AM_LDFLAGS = -no-install
+AM_LDFLAGS = -no-install $(LDFLAGS_vlc)
 LIBVLCCORE = -L../src/ -lvlccore
 LIBVLC = -L../lib -lvlc
 
@@ -156,6 +157,12 @@ test_modules_playlist_m3u_SOURCES = modules/demux/playlist/m3u.c
 test_modules_playlist_m3u_LDADD = $(LIBVLCCORE) $(LIBVLC)
 
 
+test_src_video_output_SOURCES = \
+	src/video_output/video_output.c \
+	src/video_output/video_output.h \
+	src/video_output/video_output_scenarios.c
+test_src_video_output_LDADD = $(LIBVLCCORE) $(LIBVLC)
+
 checkall:
 	$(MAKE) check_PROGRAMS="$(check_PROGRAMS) $(EXTRA_PROGRAMS)" check
 


=====================================
test/src/video_output/video_output.c
=====================================
@@ -0,0 +1,322 @@
+/*****************************************************************************
+ * video_output.c: test for the video output pipeline
+ *****************************************************************************
+ * Copyright (C) 2021 VideoLabs
+ *
+ * Author: 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
+
+/* Define a builtin module for mocked parts */
+#define MODULE_NAME test_vout_mock
+#define MODULE_STRING "test_vout_mock"
+#undef __PLUGIN__
+
+const char vlc_module_name[] = MODULE_STRING;
+
+#include "../../libvlc/test.h"
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_access.h>
+#include <vlc_demux.h>
+#include <vlc_codec.h>
+#include <vlc_vout_window.h>
+#include <vlc_interface.h>
+#include <vlc_player.h>
+#include <vlc_filter.h>
+#include <vlc_vout_display.h>
+
+#include <limits.h>
+
+#include "video_output.h"
+static size_t current_scenario;
+
+static void DecoderDeviceClose(struct vlc_decoder_device *device)
+    { VLC_UNUSED(device); }
+
+static const struct vlc_decoder_device_operations decoder_device_ops =
+{
+    .close = DecoderDeviceClose,
+};
+
+static int OpenDecoderDevice(
+        struct vlc_decoder_device *device,
+        vout_window_t *window
+) {
+    VLC_UNUSED(window);
+    device->ops = &decoder_device_ops;
+    /* Pick any valid one, we'll not use the module which can make use of
+     * the private parts. */
+    device->type = VLC_DECODER_DEVICE_VAAPI;
+    return VLC_SUCCESS;
+}
+
+static int DecoderDecode(decoder_t *dec, block_t *block)
+{
+    if (block == NULL)
+        return VLC_SUCCESS;
+
+    const picture_resource_t resource = {
+        .p_sys = NULL,
+    };
+    picture_t *pic = picture_NewFromResource(&dec->fmt_out.video, &resource);
+    assert(pic);
+    pic->date = block->i_pts;
+    pic->b_progressive = true;
+    block_Release(block);
+
+    struct vout_scenario *scenario = &vout_scenarios[current_scenario];
+    assert(scenario->decoder_decode != NULL);
+    scenario->decoder_decode(dec, pic);
+
+    return VLC_SUCCESS;
+}
+
+static void CloseDecoder(vlc_object_t *obj)
+{
+    decoder_t *dec = (decoder_t*)obj;
+    struct vlc_video_context *vctx = dec->p_sys;
+    if (vctx)
+        vlc_video_context_Release(vctx);
+}
+
+static int OpenDecoder(vlc_object_t *obj)
+{
+    decoder_t *dec = (decoder_t*)obj;
+
+    struct vlc_decoder_device *device = decoder_GetDecoderDevice(dec);
+    assert(device);
+    vlc_decoder_device_Release(device);
+
+    dec->pf_decode = DecoderDecode;
+    // Necessary ?
+    es_format_Clean(&dec->fmt_out);
+    es_format_Copy(&dec->fmt_out, &dec->fmt_in);
+
+    struct vout_scenario *scenario = &vout_scenarios[current_scenario];
+    assert(scenario->decoder_setup != NULL);
+    scenario->decoder_setup(dec);
+
+    msg_Dbg(obj, "Decoder chroma %4.4s -> %4.4s size %ux%u",
+            (const char *)&dec->fmt_in.i_codec,
+            (const char *)&dec->fmt_out.i_codec,
+            dec->fmt_out.video.i_width, dec->fmt_out.video.i_height);
+
+    return VLC_SUCCESS;
+}
+
+static int OpenFilter(vlc_object_t *obj)
+{
+    filter_t *filter = (filter_t *)obj;
+
+    static const struct vlc_filter_operations ops = {
+        .filter_video = NULL,
+        .close = NULL,
+    };
+    filter->ops = &ops;
+
+    return VLC_SUCCESS;
+}
+
+static picture_t *ConverterFilter(filter_t *filter, picture_t *input)
+{
+    video_format_Clean(&input->format);
+    video_format_Copy(&input->format, &filter->fmt_out.video);
+    return input;
+}
+
+static int OpenConverter(vlc_object_t *obj)
+{
+    filter_t *filter = (filter_t *)obj;
+
+    msg_Dbg(obj, "converter chroma %4.4s -> %4.4s size %ux%u -> %ux%u",
+            (const char *)&filter->fmt_in.i_codec,
+            (const char *)&filter->fmt_out.i_codec,
+            filter->fmt_in.video.i_width, filter->fmt_in.video.i_height,
+            filter->fmt_out.video.i_width, filter->fmt_out.video.i_height);
+
+    struct vout_scenario *scenario = &vout_scenarios[current_scenario];
+    assert(scenario->converter_setup != NULL);
+    scenario->converter_setup(filter);
+
+    static const struct vlc_filter_operations ops = {
+        .filter_video = ConverterFilter,
+        .close = NULL,
+    };
+    filter->ops = &ops;
+
+    return VLC_SUCCESS;
+}
+
+static int OpenWindow(vout_window_t *wnd)
+{
+    static const struct vout_window_operations ops = {
+
+    };
+    wnd->ops = &ops;
+    return VLC_SUCCESS;
+}
+
+static void CloseDisplay(vout_display_t *vd)
+{
+
+}
+
+static int OpenDisplay(vout_display_t *vd, video_format_t *fmtp,
+                       struct vlc_video_context *vctx)
+{
+    static const struct vlc_display_operations ops =
+    {
+        .close = CloseDisplay,
+    };
+    vd->ops = &ops;
+
+    struct vout_scenario *scenario = &vout_scenarios[current_scenario];
+    assert(scenario->display_setup != NULL);
+    int ret = scenario->display_setup(vd, fmtp, vctx);
+
+    msg_Dbg(vd, "vout display chroma %4.4s size %ux%u -> %ux%u",
+            (const char *)&fmtp->i_chroma,
+            fmtp->i_width, fmtp->i_height,
+            fmtp->i_width, fmtp->i_height);
+
+    return ret;
+}
+
+static void play_scenario(intf_thread_t *intf, struct vout_scenario *scenario)
+{
+    vout_scenario_init();
+    input_item_t *media = input_item_New(scenario->source, "dummy");
+    assert(media);
+
+    /* TODO: Codec doesn't seem to have effect in transcode:
+     * - add a test that --codec works?
+     * - do not use --codec at all here? */
+
+    var_Create(intf, "codec", VLC_VAR_STRING);
+    var_SetString(intf, "codec", MODULE_STRING);
+
+    var_Create(intf, "vout", VLC_VAR_STRING);
+    var_SetString(intf, "vout", MODULE_STRING);
+
+    var_Create(intf, "window", VLC_VAR_STRING);
+    var_SetString(intf, "window", MODULE_STRING);
+
+    vlc_player_t *player = vlc_player_New(&intf->obj,
+        VLC_PLAYER_LOCK_NORMAL, NULL, NULL);
+    assert(player);
+
+    vlc_player_Lock(player);
+    vlc_player_SetCurrentMedia(player, media);
+    vlc_player_Start(player);
+    vlc_player_Unlock(player);
+
+    vout_scenario_wait(scenario);
+
+    vlc_player_Delete(player);
+    input_item_Release(media);
+
+    var_Destroy(intf, "vout");
+    var_Destroy(intf, "codec");
+}
+
+static int OpenIntf(vlc_object_t *obj)
+{
+    intf_thread_t *intf = (intf_thread_t*)obj;
+
+    msg_Info(intf, "Starting tests");
+    while (current_scenario < vout_scenarios_count)
+    {
+        msg_Info(intf, " - Running transcode scenario %zu", current_scenario);
+        play_scenario(intf, &vout_scenarios[current_scenario]);
+        current_scenario++;
+    }
+
+    return VLC_SUCCESS;
+}
+
+/**
+ * Inject the mocked modules as a static plugin:
+ *  - access for triggering the correct decoder
+ *  - decoder for generating video format and context
+ *  - filter for generating video format and context
+ *  - encoder to check the previous video format and context
+ **/
+vlc_module_begin()
+    set_callbacks(OpenDecoder, CloseDecoder)
+    set_capability("video decoder", INT_MAX)
+
+    add_submodule()
+        set_callback(OpenDecoderDevice)
+        set_capability("decoder device", 0)
+
+    add_submodule()
+        set_callback(OpenFilter)
+        set_capability("video filter", 0)
+
+    add_submodule()
+        set_callback(OpenConverter)
+        set_capability("video converter", INT_MAX)
+
+    add_submodule()
+        set_callback(OpenWindow)
+        set_capability("vout window", INT_MAX)
+
+    add_submodule()
+        set_callback(OpenDisplay)
+        set_capability("vout display", 0)
+
+    /* Interface module to avoid casting libvlc_instance_t to object */
+    add_submodule()
+        set_callback(OpenIntf)
+        set_capability("interface", 0)
+
+vlc_module_end()
+
+/* Helper typedef for vlc_static_modules */
+typedef int (*vlc_plugin_cb)(vlc_set_cb, void*);
+
+
+VLC_EXPORT const vlc_plugin_cb vlc_static_modules[];
+const vlc_plugin_cb vlc_static_modules[] = {
+    VLC_SYMBOL(vlc_entry),
+    NULL
+};
+
+int main( int argc, char **argv )
+{
+    (void)argc; (void)argv;
+    test_init();
+
+    const char * const args[] = {
+        "-vvv", "--vout=dummy", "--aout=dummy", "--text-renderer=dummy",
+        "--no-auto-preparse", "--dec-dev=" MODULE_STRING,
+        "--no-spu", "--no-osd",
+    };
+
+    libvlc_instance_t *vlc = libvlc_new(ARRAY_SIZE(args), args);
+
+    libvlc_add_intf(vlc, MODULE_STRING);
+    libvlc_playlist_play(vlc);
+
+    libvlc_release(vlc);
+    assert(vout_scenarios_count == current_scenario);
+    return 0;
+}


=====================================
test/src/video_output/video_output.h
=====================================
@@ -0,0 +1,50 @@
+/*****************************************************************************
+ * video_output.h: test for the video output pipeline
+ *****************************************************************************
+ * Copyright (C) 2021 VideoLabs
+ *
+ * Author: 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.
+ *****************************************************************************/
+
+#define MODULE_NAME test_vout_mock
+#define MODULE_STRING "test_vout_mock"
+#undef __PLUGIN__
+
+#include <vlc_fourcc.h>
+#include <vlc_vout_display.h>
+
+
+#define TEST_FLAG_CONVERTER 0x01
+#define TEST_FLAG_FILTER 0x02
+
+struct vout_scenario {
+    const char *source;
+    void (*decoder_setup)(decoder_t *);
+    void (*decoder_decode)(decoder_t *, picture_t *);
+    int  (*display_setup)(vout_display_t *, video_format_t *,
+                          struct vlc_video_context *);
+    void (*display_prepare)(vout_display_t *, picture_t *);
+    void (*display_display)(vout_display_t *, picture_t *);
+    void (*filter_setup)(filter_t *);
+    void (*converter_setup)(filter_t *);
+};
+
+
+void vout_scenario_init(void);
+void vout_scenario_wait(struct vout_scenario *scenario);
+extern size_t vout_scenarios_count;
+extern struct vout_scenario vout_scenarios[];


=====================================
test/src/video_output/video_output_scenarios.c
=====================================
@@ -0,0 +1,163 @@
+/*****************************************************************************
+ * vout_scenario.c: testflight for video output pipeline
+ *****************************************************************************
+ * Copyright (C) 2021 VideoLabs
+ *
+ * Author: 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
+
+#define MODULE_NAME test_vout_mock
+#define MODULE_STRING "test_vout_mock"
+#undef __PLUGIN__
+
+#include <vlc_common.h>
+#include "video_output.h"
+
+#include <vlc_filter.h>
+#include <vlc_vout_display.h>
+
+static struct scenario_data
+{
+    vlc_sem_t wait_stop;
+    struct vlc_video_context *decoder_vctx;
+    unsigned display_picture_count;
+    bool converter_opened;
+    bool display_opened;
+
+    vlc_fourcc_t display_chroma;
+} scenario_data;
+
+static void decoder_fixed_size(decoder_t *dec, vlc_fourcc_t chroma,
+        unsigned width, unsigned height)
+{
+    dec->fmt_out.video.i_chroma
+        = dec->fmt_out.i_codec
+        = chroma;
+    dec->fmt_out.video.i_visible_width
+        = dec->fmt_out.video.i_width
+        = width;
+    dec->fmt_out.video.i_visible_height
+        = dec->fmt_out.video.i_height
+        = height;
+}
+
+static void decoder_rgba_800_600(decoder_t *dec)
+    { decoder_fixed_size(dec, VLC_CODEC_RGBA, 800, 600); }
+
+static void decoder_decode_change_chroma(decoder_t *dec, picture_t *pic)
+{
+    static const vlc_fourcc_t chroma_list[] = {
+        VLC_CODEC_RGBA,
+        VLC_CODEC_I420,
+        VLC_CODEC_NV12,
+    };
+
+    size_t index = 0;
+    while (index < ARRAY_SIZE(chroma_list))
+    {
+        index++;
+        if (chroma_list[index - 1] == dec->fmt_out.video.i_chroma)
+            break;
+    }
+
+    /* Limit to the last chroma */
+    if (index >= ARRAY_SIZE(chroma_list))
+        index = ARRAY_SIZE(chroma_list);
+
+    /* Switch to the new chroma */
+    dec->fmt_out.video.i_chroma
+        = dec->fmt_out.i_codec
+        = chroma_list[index];
+
+    int ret = decoder_UpdateVideoOutput(dec, NULL);
+    //assert(ret == VLC_SUCCESS);
+    if (ret != VLC_SUCCESS)
+    {
+        vlc_sem_post(&scenario_data.wait_stop);
+        return;
+    }
+
+    /* Simulate the chroma change */
+    pic->format.i_chroma = chroma_list[index];
+    decoder_QueueVideo(dec, pic);
+}
+
+static int display_fixed_size(vout_display_t *vd, video_format_t *fmtp,
+        struct vlc_video_context *vctx, vlc_fourcc_t chroma,
+        unsigned width, unsigned height)
+{
+    (void)fmtp; (void)vctx;
+    msg_Info(vd, "Setting up the display %4.4s: %ux%u",
+             (const char *)&chroma, width, height);
+
+    scenario_data.display_opened = true;
+    return VLC_SUCCESS;
+}
+
+static int display_fail_second_time(vout_display_t *vd, video_format_t *fmtp,
+        struct vlc_video_context *vctx, unsigned width, unsigned height)
+{
+    (void)vctx;
+
+    vlc_fourcc_t chroma = fmtp->i_chroma;
+    if (scenario_data.display_opened)
+    {
+        msg_Info(vd, "Failing the display %4.4s: %ux%u",
+                 (const char *)&chroma, width, height);
+        return VLC_EGENERIC;
+    }
+
+    return display_fixed_size(vd, fmtp, vctx, chroma, 800, 600);
+}
+
+static int display_800_600_fail_second_time(
+        vout_display_t *vd, video_format_t *fmtp,
+        struct vlc_video_context *vctx)
+    { return display_fail_second_time(vd, fmtp, vctx, 800, 600); }
+
+const char source_800_600[] = "mock://video_track_count=1;length=100000000000;video_width=800;video_height=600";
+struct vout_scenario vout_scenarios[] =
+{{
+    .source = source_800_600,
+    .decoder_setup = decoder_rgba_800_600,
+    .decoder_decode = decoder_decode_change_chroma,
+    .display_setup = display_800_600_fail_second_time,
+}};
+size_t vout_scenarios_count = ARRAY_SIZE(vout_scenarios);
+
+void vout_scenario_init(void)
+{
+    scenario_data.decoder_vctx = NULL;
+    scenario_data.display_picture_count = 0;
+    scenario_data.converter_opened = false;
+    scenario_data.display_opened = false;
+    vlc_sem_init(&scenario_data.wait_stop, 0);
+}
+
+void vout_scenario_wait(struct vout_scenario *scenario)
+{
+    vlc_sem_wait(&scenario_data.wait_stop);
+    if (scenario->converter_setup != NULL)
+        assert(scenario_data.converter_opened);
+
+    if (scenario->display_setup != NULL)
+        assert(scenario_data.display_opened);
+}



View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/785d174548d5418e0e08d4abcd5dc034ab542200

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/785d174548d5418e0e08d4abcd5dc034ab542200
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list