/*
* media_player.c - libvlc smoke test
*
*/
/**********************************************************************
* Copyright (C) 2007 Rémi Denis-Courmont. *
* This program is free software; you can redistribute and/or modify *
* it under the terms of the GNU General Public License as published *
* by the Free Software Foundation; version 2 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, you can get it from: *
* http://www.gnu.org/copyleft/gpl.html *
**********************************************************************/
#include "test.h"
#include "media_player.h"
#include <vlc_common.h>
static const struct libvlc_media_player_cbs state_cbs = {
.version = 0,
.on_state_changed = mp_on_state_changed,
};
static const struct libvlc_media_player_cbs tracks_cbs = {
.version = 0,
.on_track_list_changed = mp_on_track_list_changed,
.on_track_selection_changed = mp_on_track_selection_changed,
};
static const struct libvlc_media_player_cbs programs_cbs = {
.version = 0,
.on_program_list_changed = mp_on_program_list_changed,
.on_program_selection_changed = mp_on_program_selection_changed,
};
static void play_and_wait(libvlc_media_player_t *mp, struct mp_event_ctx *ctx)
{
libvlc_media_player_play(mp);
test_log("Waiting for playing\n");
mp_event_ctx_wait_state(ctx, libvlc_Playing);
}
static void pause_and_wait(libvlc_media_player_t *mp, struct mp_event_ctx *ctx)
{
libvlc_media_player_set_pause(mp, true);
test_log("Waiting for pause\n");
mp_event_ctx_wait_state(ctx, libvlc_Paused);
}
/* Test a bunch of A/V properties. This most does nothing since the current
* test file contains a dummy audio track. This is a smoke test. */
static void test_audio_video(libvlc_media_player_t *mp)
{
bool fs = libvlc_get_fullscreen(mp);
libvlc_set_fullscreen(mp, true);
assert(libvlc_get_fullscreen(mp));
libvlc_set_fullscreen(mp, false);
assert(!libvlc_get_fullscreen(mp));
libvlc_toggle_fullscreen(mp);
assert(libvlc_get_fullscreen(mp));
libvlc_toggle_fullscreen(mp);
assert(!libvlc_get_fullscreen(mp));
libvlc_set_fullscreen(mp, fs);
assert(libvlc_get_fullscreen(mp) == fs);
assert(libvlc_video_get_scale(mp) == 0.); /* default */
libvlc_video_set_scale(mp, 0.); /* no-op */
libvlc_video_set_scale(mp, 2.5);
assert(libvlc_video_get_scale(mp) == 2.5);
libvlc_video_set_scale(mp, 0.);
libvlc_video_set_scale(mp, 0.); /* no-op */
assert(libvlc_video_get_scale(mp) == 0.);
libvlc_audio_output_device_t *aouts = libvlc_audio_output_device_enum(mp);
for (libvlc_audio_output_device_t *e = aouts; e != NULL; e = e->p_next)
{
libvlc_audio_output_device_set( mp, e->psz_device );
}
libvlc_audio_output_device_list_release( aouts );
}
static void test_role(libvlc_media_player_t *mp)
{
int role;
/* Test default value */
assert(libvlc_media_player_get_role(mp) == libvlc_role_Video);
for (role = 0; libvlc_media_player_set_role(mp, role) == 0; role++)
assert(libvlc_media_player_get_role(mp) == role);
assert(role > libvlc_role_Last);
}
static void test_media_player_set_media(const char** argv, int argc)
{
const char * file = test_default_sample;
test_log ("Testing set_media\n");
libvlc_instance_t *vlc = libvlc_new (argc, argv);
assert (vlc != NULL);
libvlc_media_t *md = libvlc_media_new_location(file);
assert (md != NULL);
struct mp_event_ctx ctx;
mp_event_ctx_init(&ctx);
libvlc_media_player_t *mp =
libvlc_media_player_new (vlc, &state_cbs, &ctx);
assert (mp != NULL);
mp_event_ctx_set_mp(&ctx, mp);
libvlc_media_player_set_media (mp, md);
libvlc_media_release (md);
play_and_wait(mp, &ctx);
libvlc_media_player_stop_async (mp);
libvlc_media_player_release (mp);
libvlc_release (vlc);
mp_event_ctx_destroy(&ctx);
}
struct switch_media_ctx
{
vlc_sem_t playing_sem;
vlc_sem_t media_changed_sem;
};
static void switch_media_on_state_changed(void *opaque, libvlc_state_t state)
{
struct switch_media_ctx *ctx = opaque;
if (state == libvlc_Playing)
vlc_sem_post(&ctx->playing_sem);
}
static void switch_media_on_media_changed(void *opaque, libvlc_media_t *media)
{
struct switch_media_ctx *ctx = opaque;
assert(media != NULL);
vlc_sem_post(&ctx->media_changed_sem);
}
static const struct libvlc_media_player_cbs switch_media_cbs = {
.version = 0,
.on_state_changed = switch_media_on_state_changed,
.on_media_changed = switch_media_on_media_changed,
};
static void test_media_player_switch_media(const char** argv, int argc)
{
/* The track gives the es_out an info category, deleted during the input
* teardown: this sends media meta events referencing the draining media
* after the player switched to the next one. */
const char *file1 = "mock://audio_track_count=1;length=100000000";
const char *file2 = test_default_sample;
test_log ("Testing media switch while playing\n");
libvlc_instance_t *vlc = libvlc_new (argc, argv);
assert (vlc != NULL);
struct switch_media_ctx ctx;
vlc_sem_init(&ctx.playing_sem, 0);
vlc_sem_init(&ctx.media_changed_sem, 0);
libvlc_media_player_t *mp =
libvlc_media_player_new (vlc, &switch_media_cbs, &ctx);
assert (mp != NULL);
libvlc_media_t *md = libvlc_media_new_location(file1);
assert (md != NULL);
libvlc_media_player_set_media (mp, md);
vlc_sem_wait(&ctx.media_changed_sem);
/* The application drops its last reference: the media must stay valid as
* long as the player (and its draining input) can reference it */
libvlc_media_release (md);
libvlc_media_player_play (mp);
test_log ("Waiting for playing\n");
vlc_sem_wait(&ctx.playing_sem);
/* Switch to another media while playing: the first media input drains
* asynchronously and still sends events referencing the first media */
md = libvlc_media_new_location(file2);
assert (md != NULL);
libvlc_media_player_set_media (mp, md);
libvlc_media_release (md);
/* The media change is notified once the first input is destroyed: all the
* teardown events referencing the first media have been sent by then */
test_log ("Waiting for the media change\n");
vlc_sem_wait(&ctx.media_changed_sem);
libvlc_media_player_stop_async (mp);
libvlc_media_player_release (mp);
libvlc_release (vlc);
}
static void test_media_player_play_stop(const char** argv, int argc)
{
libvlc_instance_t *vlc;
libvlc_media_t *md;
libvlc_media_player_t *mi;
const char * file = test_default_sample;
test_log ("Testing play and pause of %s\n", file);
vlc = libvlc_new (argc, argv);
assert (vlc != NULL);
md = libvlc_media_new_location(file);
assert (md != NULL);
struct mp_event_ctx ctx;
mp_event_ctx_init(&ctx);
mi = libvlc_media_player_new_from_media (vlc, md, &state_cbs, &ctx);
assert (mi != NULL);
mp_event_ctx_set_mp(&ctx, mi);
libvlc_media_release (md);
play_and_wait(mi, &ctx);
libvlc_media_player_stop_async (mi);
libvlc_media_player_release (mi);
libvlc_release (vlc);
mp_event_ctx_destroy(&ctx);
}
static void test_media_player_pause_stop(const char** argv, int argc)
{
libvlc_instance_t *vlc;
libvlc_media_t *md;
libvlc_media_player_t *mi;
const char * file = test_default_sample;
test_log ("Testing pause and stop of %s\n", file);
vlc = libvlc_new (argc, argv);
assert (vlc != NULL);
md = libvlc_media_new_location(file);
assert (md != NULL);
struct mp_event_ctx ctx;
mp_event_ctx_init(&ctx);
mi = libvlc_media_player_new_from_media (vlc, md, &state_cbs, &ctx);
assert (mi != NULL);
mp_event_ctx_set_mp(&ctx, mi);
libvlc_media_release (md);
test_audio_video(mi);
test_role(mi);
play_and_wait(mi, &ctx);
test_audio_video(mi);
pause_and_wait(mi, &ctx);
test_audio_video(mi);
libvlc_media_player_stop_async (mi);
test_audio_video(mi);
libvlc_media_player_release (mi);
libvlc_release (vlc);
mp_event_ctx_destroy(&ctx);
}
struct track
{
const char *id;
bool toselect;
bool selected;
bool added;
bool removed;
};
static const libvlc_track_type_t types[] =
{ libvlc_track_audio, libvlc_track_video, libvlc_track_text };
static struct track *get_track_from_id(struct track *tracks, const char *id)
{
while (tracks->id != NULL)
{
if (strcmp(tracks->id, id) == 0)
return tracks;
tracks++;
}
return NULL;
}
static bool tracks_check_all_events(struct track *const *tracks, bool removed)
{
for (size_t i = 0; i < ARRAY_SIZE(types); ++i)
{
libvlc_track_type_t type = types[i];
const struct track *ttracks = tracks[type];
while (ttracks->id != NULL)
{
if (removed)
{
if (!ttracks->removed)
return false;
}
else
{
if (!ttracks->added || ttracks->toselect != ttracks->selected)
return false;
}
ttracks++;
}
}
return true;
}
static void test_media_player_tracks(const char** argv, int argc)
{
test_log ("Testing tracks\n");
const char *file = "mock://video_track_count=3;audio_track_count=3;sub_track_count=3";
struct track atracks[] = {
{ "audio/0", false, false, false, false },
{ "audio/1", false, false, false, false },
{ "audio/2", true, false, false, false },
{ NULL, false, false, false, false },
};
static const char atracks_ids[] = "audio/2";
struct track vtracks[] = {
{ "video/0", false, false, false, false },
{ "video/1", true, false, false, false },
{ "video/2", false, false, false, false },
{ NULL, false, false, false, false },
};
static const char vtracks_ids[] = "video/1";
struct track stracks[] = {
{ "spu/0", true, false, false, false },
{ "spu/1", true, false, false, false },
{ "spu/2", false, false, false, false },
{ NULL, false, false, false, false },
};
static const char stracks_ids[] = "spu/0,spu/1";
struct track *alltracks[] = {
[libvlc_track_audio] = atracks,
[libvlc_track_video] = vtracks,
[libvlc_track_text] = stracks,
};
static const size_t alltracks_count[] = {
[libvlc_track_audio] = 3,
[libvlc_track_video] = 3,
[libvlc_track_text] = 3,
};
/* Avoid leaks from various dlopen... */
const char *new_argv[argc+1];
for (int i = 0; i < argc; ++i)
new_argv[i] = argv[i];
new_argv[argc++] = "--codec=araw,rawvideo,subsdec,none";
/* Load the mock media */
libvlc_instance_t *vlc = libvlc_new (argc, new_argv);
assert (vlc != NULL);
libvlc_media_t *md = libvlc_media_new_location(file);
assert (md != NULL);
struct mp_event_ctx ctx;
mp_event_ctx_init(&ctx);
libvlc_media_player_t *mp =
libvlc_media_player_new (vlc, &tracks_cbs, &ctx);
assert (mp != NULL);
mp_event_ctx_set_mp(&ctx, mp);
libvlc_media_player_set_media (mp, md);
libvlc_media_release (md);
/* Confiture track selection before first play */
libvlc_media_player_select_tracks_by_ids(mp, libvlc_track_audio, atracks_ids);
libvlc_media_player_select_tracks_by_ids(mp, libvlc_track_video, vtracks_ids);
libvlc_media_player_select_tracks_by_ids(mp, libvlc_track_text, stracks_ids);
libvlc_media_track_t *libtrack;
libvlc_media_player_play (mp);
/* Check that all tracks are added and selected according to
* libvlc_media_player_select_tracks_by_ids */
while (!tracks_check_all_events(alltracks, false))
{
struct mp_event *ev = mp_event_ctx_wait_event(&ctx);
switch (ev->type)
{
case EVENT_TYPE_TRACK_LIST:
{
assert(ev->track_list.action == libvlc_list_action_added
|| ev->track_list.action == libvlc_list_action_updated);
libvlc_track_type_t type = ev->track_list.type;
struct track *track =
get_track_from_id(alltracks[type], ev->track_list.id);
assert(track);
if (ev->track_list.action == libvlc_list_action_updated)
assert(track->added);
else
track->added = true;
break;
}
case EVENT_TYPE_TRACK_SELECTION:
{
libvlc_track_type_t type = ev->track_selection.type;
const char *selected_id = ev->track_selection.selected_id;
const char *unselected_id = ev->track_selection.unselected_id;
assert(selected_id);
assert(!unselected_id);
struct track *track = get_track_from_id(alltracks[type],
selected_id);
assert(track);
assert(track->toselect);
track->selected = true;
break;
}
default:
assert(!"Event not expected");
}
mp_event_delete(ev);
}
/* Compare with the tracklists */
for (size_t i = 0; i < ARRAY_SIZE(types); ++i)
{
libvlc_track_type_t type = types[i];
size_t track_count = alltracks_count[i];
struct track *tracks = alltracks[i];
libvlc_media_tracklist_t *tracklist =
libvlc_media_player_get_tracklist(mp, type, false);
assert(tracklist);
assert(libvlc_media_tracklist_count(tracklist) == track_count);
for (size_t j = 0; j < track_count; ++j)
{
struct track *track = &tracks[j];
libtrack = libvlc_media_tracklist_at(tracklist, j);
assert(libtrack);
assert(strcmp(libtrack->psz_id, track->id) == 0);
assert(libtrack->selected == track->selected);
}
libvlc_media_tracklist_delete(tracklist);
tracklist = libvlc_media_player_get_tracklist(mp, type, true);
assert(tracklist);
for (size_t j = 0; j < libvlc_media_tracklist_count(tracklist); ++j)
{
libtrack = libvlc_media_tracklist_at(tracklist, j);
assert(libtrack);
assert(libtrack->selected);
}
libvlc_media_tracklist_delete(tracklist);
}
/* Select (replace) a new audio track */
libtrack = libvlc_media_player_get_track_from_id(mp, "audio/0");
assert(libtrack);
libvlc_media_player_select_track(mp, libtrack);
libvlc_media_track_release(libtrack);
atracks[0].toselect = true;
atracks[2].toselect = false;
/* Add a new video track */
libvlc_media_tracklist_t *tracklist =
libvlc_media_player_get_tracklist(mp, libvlc_track_video, false);
assert(tracklist);
libvlc_media_track_t *vtrack1 =
libvlc_media_track_retain(libvlc_media_tracklist_at(tracklist, 1));
libvlc_media_track_t *vtrack2 =
libvlc_media_track_retain(libvlc_media_tracklist_at(tracklist, 2));
libvlc_media_tracklist_delete(tracklist);
const libvlc_media_track_t *selecttracks[] = {
vtrack1, vtrack2,
};
assert(vtrack1->selected); /* video/1 already selected */
assert(!vtrack2->selected); /* select video/2 */
libvlc_media_player_select_tracks(mp, libvlc_track_video, selecttracks, 2);
vtracks[2].toselect = true;
libvlc_media_track_release(vtrack1);
libvlc_media_track_release(vtrack2);
/* Unselect all spu tracks */
libvlc_media_player_unselect_track_type(mp, libvlc_track_text);
stracks[0].toselect = stracks[1].toselect = false;
/* Check that all tracks are added and selected according to previous
* changes. */
while (!tracks_check_all_events(alltracks, false))
{
struct mp_event *ev = mp_event_ctx_wait_event(&ctx);
if (ev->type == EVENT_TYPE_TRACK_LIST)
{
/* It is possible to receive an update between added and selected */
assert(ev->track_list.action == libvlc_list_action_updated);
mp_event_delete(ev);
continue;
}
assert(ev->type == EVENT_TYPE_TRACK_SELECTION);
libvlc_track_type_t type = ev->track_selection.type;
const char *selected_id = ev->track_selection.selected_id;
const char *unselected_id = ev->track_selection.unselected_id;
if (unselected_id)
{
struct track *track =
get_track_from_id(alltracks[type], unselected_id);
assert(!track->toselect);
assert(track->selected);
track->selected = false;
}
if (selected_id)
{
struct track *track =
get_track_from_id(alltracks[type], selected_id);
assert(track->toselect);
assert(!track->selected);
track->selected = true;
}
mp_event_delete(ev);
}
libvlc_media_player_stop_async (mp);
/* Check that all tracks are removed */
while (!tracks_check_all_events(alltracks, true))
{
struct mp_event *ev = mp_event_ctx_wait_event(&ctx);
if (ev->type == EVENT_TYPE_TRACK_LIST
&& ev->track_list.action == libvlc_list_action_removed)
{
libvlc_track_type_t type = ev->track_list.type;
struct track *track =
get_track_from_id(alltracks[type], ev->track_list.id);
assert(track);
track->removed = true;
}
mp_event_delete(ev);
}
libvlc_media_player_release (mp);
libvlc_release (vlc);
mp_event_ctx_destroy(&ctx);
}
static void test_media_player_programs(const char** argv, int argc)
{
#define PROGRAM_COUNT 9
test_log ("Testing programs\n");
char file[sizeof("mock://audio_track_count=1;program_count=x")];
sprintf(file, "mock://audio_track_count=1;program_count=%1u", PROGRAM_COUNT);
/* Avoid leaks from various dlopen... */
const char *new_argv[argc+1];
for (int i = 0; i < argc; ++i)
new_argv[i] = argv[i];
new_argv[argc++] = "--codec=araw,rawvideo,subsdec,none";
/* Load the mock media */
libvlc_instance_t *vlc = libvlc_new (argc, new_argv);
assert (vlc != NULL);
libvlc_media_t *md = libvlc_media_new_location(file);
assert (md != NULL);
struct mp_event_ctx ctx;
mp_event_ctx_init(&ctx);
libvlc_media_player_t *mp =
libvlc_media_player_new (vlc, &programs_cbs, &ctx);
assert (mp != NULL);
mp_event_ctx_set_mp(&ctx, mp);
libvlc_media_player_set_media (mp, md);
libvlc_media_release (md);
libvlc_media_player_select_program_id(mp, 2);
libvlc_media_player_play (mp);
/* check that all programs are added and that one is selected */
for (int nb_added = 0, nb_selected = 0;
nb_added != PROGRAM_COUNT || nb_selected != 1;)
{
struct mp_event *ev = mp_event_ctx_wait_event(&ctx);
switch (ev->type)
{
case EVENT_TYPE_PROGRAM_LIST:
assert(ev->program_list.action == libvlc_list_action_added
|| ev->program_list.action == libvlc_list_action_updated);
if (ev->program_list.action == libvlc_list_action_added)
{
assert(ev->program_list.group_id == nb_added);
nb_added++;
}
break;
case EVENT_TYPE_PROGRAM_SELECTION:
assert(nb_selected == 0);
assert(ev->program_selection.unselected_group_id == -1);
assert(ev->program_selection.selected_group_id != -1);
nb_selected++;
break;
default:
assert(!"Event not expected");
}
mp_event_delete(ev);
}
libvlc_player_program_t *program;
/* Check that we can fetch the program 0 and that it is enabled */
program = libvlc_media_player_get_selected_program(mp);
assert(program != NULL && program->i_group_id == 2 && program->b_selected);
libvlc_player_program_delete(program);
/* Change the program selection, select the last program */
libvlc_media_player_select_program_id(mp, 8);
/* Wait for the program selection event */
{
struct mp_event *ev = mp_event_ctx_wait_event(&ctx);
assert(ev->type == EVENT_TYPE_PROGRAM_SELECTION);
assert(ev->program_selection.unselected_group_id == 2);
assert(ev->program_selection.selected_group_id == 8);
mp_event_delete(ev);
}
/* Ensure that the new program was selected */
program = libvlc_media_player_get_selected_program(mp);
assert(program != NULL && program->i_group_id == 8 && program->b_selected);
libvlc_player_program_delete(program);
/* And that the old one is unselected */
program = libvlc_media_player_get_program_from_id(mp, 0);
assert(program != NULL && program->i_group_id == 0 && !program->b_selected);
libvlc_player_program_delete(program);
/* Check if the program list is valid */
libvlc_player_programlist_t *list = libvlc_media_player_get_programlist(mp);
assert(list);
for (size_t i = 0; i < libvlc_player_programlist_count(list); ++i)
{
program = libvlc_player_programlist_at(list, i);
assert(program != NULL && program->i_group_id == (int)i);
if (i == 8)
assert(program->b_selected);
else
assert(!program->b_selected);
}
libvlc_player_programlist_delete(list);
libvlc_media_player_stop_async (mp);
/* Check that all programs are removed and unselected */
for (int nb_deleted = 0; nb_deleted != PROGRAM_COUNT;)
{
struct mp_event *ev = mp_event_ctx_wait_event(&ctx);
switch (ev->type)
{
case EVENT_TYPE_PROGRAM_LIST:
if (ev->program_list.action == libvlc_list_action_updated)
break;
assert(ev->program_list.action == libvlc_list_action_removed);
assert(ev->program_list.group_id == nb_deleted);
nb_deleted++;
break;
default:
assert(!"Event not expected");
}
mp_event_delete(ev);
}
libvlc_media_player_release (mp);
libvlc_release (vlc);
mp_event_ctx_destroy(&ctx);
}
/* Regression test when having multiple libvlc instances */
static void test_media_player_multiple_instance(const char** argv, int argc)
{
/* When multiple libvlc instance exist */
libvlc_instance_t *instance1 = libvlc_new(argc, argv);
libvlc_instance_t *instance2 = libvlc_new(argc, argv);
/* ...with the media and the player being on different instances */
libvlc_media_t *media1 = libvlc_media_new_path("foo");
libvlc_media_player_t *player1 =
libvlc_media_player_new(instance1, NULL, NULL);
libvlc_media_player_set_media(player1, media1);
/* ...and both being released */
libvlc_media_release(media1);
libvlc_media_player_release(player1);
/* There is no use-after-free when creating a player on the media instance,
* meaning that the player1 did release the correct libvlc instance.*/
libvlc_media_player_t *player2 =
libvlc_media_player_new(instance2, NULL, NULL);
/* And the libvlc nstances can be released without breaking the
* instance inside the player. */
libvlc_release(instance2);
libvlc_release(instance1);
libvlc_media_player_release(player2);
}
static void test_media_player_video_controls(const char** argv, int argc)
{
libvlc_instance_t *instance = libvlc_new(argc, argv);
/* ...with the media and the player being on different instances */
libvlc_media_t *media1 = libvlc_media_new_path("foo");
libvlc_media_player_t *player1 = libvlc_media_player_new(instance, NULL, NULL);
libvlc_media_player_set_media(player1, media1);
libvlc_video_set_video_stereo_mode(player1, libvlc_VideoStereoSideBySide);
libvlc_media_release(media1);
libvlc_media_player_release(player1);
libvlc_release(instance);
}
int main (void)
{
test_init();
test_media_player_set_media (test_defaults_args, test_defaults_nargs);
test_media_player_switch_media (test_defaults_args, test_defaults_nargs);
test_media_player_play_stop (test_defaults_args, test_defaults_nargs);
test_media_player_pause_stop (test_defaults_args, test_defaults_nargs);
test_media_player_tracks (test_defaults_args, test_defaults_nargs);
test_media_player_programs (test_defaults_args, test_defaults_nargs);
test_media_player_multiple_instance (test_defaults_args, test_defaults_nargs);
test_media_player_video_controls(test_defaults_args, test_defaults_nargs);
return 0;
}
¤ Dauer der Verarbeitung: 0.8 Sekunden
¤
*© Formatika GbR, Deutschland
|
|