vlc | branch: master | Thomas Guillem <[email protected]> | 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", ¶ms, 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", ¶ms, 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); _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
