[vlc-commits] test: player: test the timer API

Thomas Guillem git at videolan.org
Mon Sep 23 13:36:31 CEST 2019


vlc | branch: master | Thomas Guillem <thomas at gllm.fr> | Wed Aug 21 14:36:09 2019 +0200| [7e31ddf0cd308f493993f0cb490bb53d6407c17d] | committer: Thomas Guillem

test: player: test the timer API

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=7e31ddf0cd308f493993f0cb490bb53d6407c17d
---

 test/src/player/player.c | 382 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 381 insertions(+), 1 deletion(-)

diff --git a/test/src/player/player.c b/test/src/player/player.c
index 69270e1877..86551f9069 100644
--- a/test/src/player/player.c
+++ b/test/src/player/player.c
@@ -120,6 +120,30 @@ struct report_media_subitems
     X(input_item_t *, on_media_epg_changed) \
     X(struct report_media_subitems, on_media_subitems_changed) \
 
+struct report_timer
+{
+    enum
+    {
+        REPORT_TIMER_POINT,
+        REPORT_TIMER_TC,
+        REPORT_TIMER_DISCONTINUITY,
+    } type;
+    union
+    {
+        struct vlc_player_timer_point point;
+        struct vlc_player_timer_smpte_timecode tc;
+        vlc_tick_t discontinuity_date;
+    };
+};
+typedef struct VLC_VECTOR(struct report_timer) vec_report_timer;
+
+struct timer_state
+{
+    vlc_player_timer_id *id;
+    vlc_tick_t delay;
+    vec_report_timer vec;
+};
+
 #define X(type, name) typedef struct VLC_VECTOR(type) vec_##name;
 REPORT_LIST
 #undef X
@@ -1847,6 +1871,362 @@ REPORT_LIST
     assert(ctx->listener);
 }
 
+static void
+timers_on_update(const struct vlc_player_timer_point *point, void *data)
+{
+    struct timer_state *timer = data;
+    struct report_timer report =
+    {
+        .type = REPORT_TIMER_POINT,
+        .point = *point,
+    };
+    bool success = vlc_vector_push(&timer->vec, report);
+    assert(success);
+}
+
+static void
+timers_on_discontinuity(vlc_tick_t system_date, void *data)
+{
+    struct timer_state *timer = data;
+    struct report_timer report =
+    {
+        .type = REPORT_TIMER_DISCONTINUITY,
+        .discontinuity_date = system_date,
+    };
+    bool success = vlc_vector_push(&timer->vec, report);
+    assert(success);
+}
+
+static void
+timers_smpte_on_update(const struct vlc_player_timer_smpte_timecode *tc,
+                       void *data)
+{
+    struct timer_state *timer = data;
+    struct report_timer report =
+    {
+        .type = REPORT_TIMER_TC,
+        .tc = *tc,
+    };
+    bool success = vlc_vector_push(&timer->vec, report);
+    assert(success);
+}
+
+static void
+test_timers_assert_smpte(struct timer_state *timer,
+                         vlc_tick_t duration, unsigned fps, bool drop_frame,
+                         unsigned frame_resolution)
+{
+    /* This test doesn't support drop frame handling */
+    assert(duration < VLC_TICK_FROM_SEC(60));
+
+    vec_report_timer *vec = &timer->vec;
+
+    /* Check that we didn't miss any update points */
+    assert(vec->data[0].tc.frames == 0);
+    for (size_t i = 0; i < vec->size; ++i)
+    {
+        struct report_timer *prev_report = i > 0 ? &vec->data[i - 1] : NULL;
+        struct report_timer *report = &vec->data[i];
+
+        assert(report->tc.seconds == (i / fps));
+        if (prev_report)
+        {
+            if (i % fps == 0)
+            {
+                assert(prev_report->tc.frames == fps - 1);
+                assert(report->tc.frames == 0);
+            }
+            else
+                assert(report->tc.frames == prev_report->tc.frames + 1);
+        }
+
+        assert(report->type == REPORT_TIMER_TC);
+        assert(report->tc.drop_frame == drop_frame);
+        assert(report->tc.frame_resolution == frame_resolution);
+    }
+    assert(VEC_LAST(vec).tc.frames + 1 == fps * duration / VLC_TICK_FROM_SEC(1));
+}
+
+static void
+test_timers_assert_smpte_dropframe(struct timer_state *timer, unsigned minute,
+                                   unsigned fps)
+{
+    assert(fps == 30 || fps == 60);
+    assert(minute > 0);
+
+    vec_report_timer *vec = &timer->vec;
+
+    bool last_second_seen = false, minute_seen = false;
+    for (size_t i = 1; i < vec->size; ++i)
+    {
+        struct report_timer *prev_report = &vec->data[i - 1];
+        struct report_timer *report = &vec->data[i];
+
+        assert(report->tc.drop_frame == true);
+        assert(report->tc.frame_resolution == 2);
+
+        if (prev_report->tc.frames == fps - 1)
+        {
+            if (report->tc.seconds == 59)
+            {
+                /* Last second before the new minute */
+                assert(prev_report->tc.minutes == minute - 1);
+                assert(prev_report->tc.seconds == 58);
+
+                assert(report->tc.minutes == minute - 1);
+                assert(report->tc.frames == 0);
+
+                last_second_seen = true;
+            }
+            else if (report->tc.seconds == 0)
+            {
+                /* The minute just reached, check that 2 or 4 frames are
+                 * dropped every minutes, except every 10 minutes */
+
+                assert(prev_report->tc.minutes == minute - 1);
+                assert(prev_report->tc.seconds == 59);
+
+                assert(report->tc.minutes == minute);
+                if (minute % 10 == 0)
+                    assert(report->tc.frames == 0);
+                else
+                    assert(report->tc.frames == (fps / 30 * 2) /* drop frame */);
+
+                minute_seen = true;
+            }
+
+        }
+        else if (prev_report->tc.minutes != 0 && prev_report->tc.seconds != 0
+              && prev_report->tc.frames != 0)
+            assert(report->tc.frames == prev_report->tc.frames + 1);
+    }
+
+    /* Assert that we've seen the full last second and the new minute */
+    assert(last_second_seen && minute_seen);
+}
+
+#define REGULAR_TIMER_IDX 0
+#define REGULAR_DELAY_TIMER_IDX 1
+#define SMPTE_TIMER_IDX 2
+#define TIMER_COUNT 3
+#define SOURCE_DELAY_TIMER_VALUE (VLC_TICK_FROM_MS(2))
+
+static void
+test_timers_playback(struct ctx *ctx, struct timer_state timers[],
+                     size_t track_count, vlc_tick_t length, unsigned fps,
+                     unsigned rate)
+{
+#define SAMPLE_LENGTH VLC_TICK_FROM_MS(1)
+#define MAX_UPDATE_COUNT (size_t)(length / SAMPLE_LENGTH)
+
+    struct media_params params = DEFAULT_MEDIA_PARAMS(length);
+
+    params.track_count[VIDEO_ES] = track_count;
+    params.track_count[AUDIO_ES] = track_count;
+    params.track_count[SPU_ES] = track_count;
+    params.audio_sample_length = SAMPLE_LENGTH;
+    params.video_frame_rate = fps;
+    params.video_frame_rate_base = 1;
+
+    player_set_current_mock_media(ctx, "media1", &params, false);
+    player_set_rate(ctx, rate);
+    player_start(ctx);
+
+    wait_state(ctx, VLC_PLAYER_STATE_STARTED);
+    wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+
+    /* Common for regular timers */
+    for (size_t timer_idx = 0; timer_idx < SMPTE_TIMER_IDX; ++timer_idx)
+    {
+        struct timer_state *timer = &timers[timer_idx];
+        vec_report_timer *vec = &timer->vec;
+
+        for (size_t i = 1; i < vec->size; ++i)
+        {
+            struct report_timer *prev_report = &vec->data[i - 1];
+            struct report_timer *report = &vec->data[i];
+
+            /* Only the last event should be a discontinuity. We can assume
+             * that since we are not seeking and playing a fake content */
+            if (i < vec->size - 1)
+            {
+                if (i == 1)
+                    assert(prev_report->point.system_date == INT64_MAX);
+
+                assert(report->type == REPORT_TIMER_POINT);
+                /* ts/position should increase, rate should stay to 1.f */
+                assert(report->point.ts >= prev_report->point.ts);
+                assert(report->point.system_date != VLC_TICK_INVALID);
+                assert(report->point.position >= prev_report->point.position);
+                assert(report->point.rate == rate);
+                assert(report->point.length == length);
+            }
+            else
+            {
+                assert(report->type == REPORT_TIMER_DISCONTINUITY);
+                assert(report->discontinuity_date == VLC_TICK_INVALID);
+            }
+        }
+    }
+
+    /* Assertions for the regular timer that received all update points */
+    if (track_count != 0)
+    {
+        struct timer_state *timer = &timers[REGULAR_TIMER_IDX];
+        vec_report_timer *vec = &timer->vec;
+
+        /* Check that we didn't miss any update points */
+        assert(vec->size > 1);
+        size_t point_count = 1;
+        for (size_t i = 1; i < vec->size - 1; ++i)
+        {
+            struct report_timer *prev_report = &vec->data[i - 1];
+            struct report_timer *report = &vec->data[i];
+
+            /* Don't count forced points */
+            if (report->point.ts != prev_report->point.ts)
+            {
+                assert(report->point.ts == prev_report->point.ts + SAMPLE_LENGTH);
+                point_count++;
+            }
+        }
+        assert(vec->data[vec->size - 2].point.ts
+               == length - SAMPLE_LENGTH + VLC_TICK_0);
+        assert(point_count == MAX_UPDATE_COUNT);
+    }
+
+    /* Assertions for the regular filtered timer */
+    {
+        struct timer_state *timer = &timers[REGULAR_DELAY_TIMER_IDX];
+        vec_report_timer *vec = &timer->vec;
+
+        /* It should not receive all update points */
+        assert(vec->size < MAX_UPDATE_COUNT);
+
+        for (size_t i = 1; i < vec->size; ++i)
+        {
+            struct report_timer *prev_report = &vec->data[i - 1];
+            struct report_timer *report = &vec->data[i];
+            if (i < vec->size - 1)
+            {
+                if (i == 1)
+                    assert(prev_report->point.system_date == INT64_MAX);
+                else
+                    assert(report->point.system_date - prev_report->point.system_date
+                           >= timer->delay);
+            }
+        }
+    }
+
+    if (track_count > 0)
+        test_timers_assert_smpte(&timers[SMPTE_TIMER_IDX], length, fps, false, 3);
+    else
+    {
+        struct timer_state *timer = &timers[SMPTE_TIMER_IDX];
+        vec_report_timer *vec = &timer->vec;
+        assert(vec->size == 0);
+    }
+    test_end(ctx);
+
+    for (size_t i = 0; i < TIMER_COUNT; ++i)
+    {
+        struct timer_state *timer = &timers[i];
+        vlc_vector_clear(&timer->vec);
+    }
+}
+
+static void
+test_timers(struct ctx *ctx)
+{
+    test_log("timers\n");
+
+    vlc_player_t *player = ctx->player;
+
+    static const struct vlc_player_timer_cbs cbs =
+    {
+        .on_update = timers_on_update,
+        .on_discontinuity = timers_on_discontinuity,
+    };
+    static const struct vlc_player_timer_smpte_cbs smpte_cbs =
+    {
+        .on_update = timers_smpte_on_update,
+    };
+
+    /* Configure timers */
+    struct timer_state timers[TIMER_COUNT];
+
+    /* Receive all clock update points */
+    timers[REGULAR_TIMER_IDX].delay = VLC_TICK_INVALID;
+
+    /* Filter some points in order to not be flooded */
+    timers[REGULAR_DELAY_TIMER_IDX].delay = SOURCE_DELAY_TIMER_VALUE;
+
+    /* Create all timers */
+    for (size_t i = 0; i < ARRAY_SIZE(timers); ++i)
+    {
+        vlc_vector_init(&timers[i].vec);
+        if (i == SMPTE_TIMER_IDX)
+            timers[i].id = vlc_player_AddSmpteTimer(player, &smpte_cbs,
+                                                    &timers[i]);
+        else
+            timers[i].id = vlc_player_AddTimer(player, timers[i].delay, &cbs,
+                                               &timers[i]);
+        assert(timers[i].id);
+    }
+
+    /* Test all timers using valid tracks */
+    test_timers_playback(ctx, timers, 1, VLC_TICK_FROM_MS(200), 120, 1);
+
+    /* Test all timers without valid tracks */
+    test_timers_playback(ctx, timers, 0, VLC_TICK_FROM_MS(5000), 24, 16);
+
+    /* Test SMPTE 29.97DF and 59.94DF arround 1 minute and 10 minutes to check
+     * if timecodes are dropped every minutes */
+    static const unsigned df_fps_list[] = { 30, 60 };
+    static const unsigned df_min_test_list[] = { 1, 10 };
+
+    for (size_t i = 0; i < ARRAY_SIZE(df_fps_list); ++i)
+    {
+        unsigned fps = df_fps_list[i];
+        for (size_t j = 0; j < ARRAY_SIZE(df_min_test_list); ++j)
+        {
+            unsigned minute = df_min_test_list[j];
+
+            struct media_params params =
+                DEFAULT_MEDIA_PARAMS(minute * VLC_TICK_FROM_SEC(60)
+                                     + VLC_TICK_FROM_MS(400));
+            params.track_count[VIDEO_ES] = 1;
+            params.track_count[AUDIO_ES] = 0;
+            params.track_count[SPU_ES] = 0;
+            params.video_frame_rate = fps * 1000;
+            params.video_frame_rate_base = 1001;
+
+            player_set_current_mock_media(ctx, "media1", &params, false);
+            player_set_rate(ctx, 24);
+
+            vlc_player_SetTime(player, params.length - VLC_TICK_FROM_SEC(2));
+
+            player_start(ctx);
+
+            wait_state(ctx, VLC_PLAYER_STATE_STARTED);
+            wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+
+            test_timers_assert_smpte_dropframe(&timers[SMPTE_TIMER_IDX], minute,
+                                               fps);
+
+            test_end(ctx);
+
+            vlc_vector_clear(&timers[SMPTE_TIMER_IDX].vec);
+        }
+    }
+
+    for (size_t i = 0; i < ARRAY_SIZE(timers); ++i)
+    {
+        struct timer_state *timer = &timers[i];
+        vlc_vector_clear(&timer->vec);
+        vlc_player_RemoveTimer(player, timer->id);
+    }
+}
 
 int
 main(void)
@@ -1859,7 +2239,6 @@ main(void)
     ctx_init(&ctx, false);
     test_no_outputs(&ctx);
     ctx_destroy(&ctx);
-
     ctx_init(&ctx, true);
 
     test_outputs(&ctx); /* Must be the first test */
@@ -1877,6 +2256,7 @@ main(void)
     test_tracks(&ctx, true);
     test_tracks(&ctx, false);
     test_programs(&ctx);
+    test_timers(&ctx);
 
     test_delete_while_playback(VLC_OBJECT(ctx.vlc->p_libvlc_int), true);
     test_delete_while_playback(VLC_OBJECT(ctx.vlc->p_libvlc_int), false);



More information about the vlc-commits mailing list