From 54f3cd52db49df0cb6a2a70eae2198db67707976 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 4 Feb 2015 10:38:00 +0100 Subject: [PATCH] Add video tester that creates its own GTK windows for video display. --- tester/CMakeLists.txt | 8 ++ tester/Makefile.am | 10 +- tester/call_tester.c | 2 +- tester/liblinphone_tester.c | 10 +- tester/liblinphone_tester.h | 2 + tester/tester.c | 11 ++ tester/video_tester.c | 198 ++++++++++++++++++++++++++++++++++++ 7 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 tester/video_tester.c diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt index 015bd579c..34e5d122b 100644 --- a/tester/CMakeLists.txt +++ b/tester/CMakeLists.txt @@ -20,6 +20,8 @@ # ############################################################################ +find_package(GTK2 2.18 COMPONENTS gtk) + set(SOURCE_FILES accountmanager.c call_tester.c @@ -40,8 +42,14 @@ set(SOURCE_FILES tester.c transport_tester.c upnp_tester.c + video_tester.c ) add_executable(liblinphone_tester ${SOURCE_FILES}) target_include_directories(liblinphone_tester PUBLIC ${CUNIT_INCLUDE_DIRS}) target_link_libraries(liblinphone_tester linphone ${CUNIT_LIBRARIES}) +if (GTK2_FOUND) + target_compile_definitions(liblinphone_tester PRIVATE HAVE_GTK) + target_include_directories(liblinphone_tester PUBLIC ${GTK2_INCLUDE_DIRS}) + target_link_libraries(liblinphone_tester linphone ${GTK2_LIBRARIES}) +endif() diff --git a/tester/Makefile.am b/tester/Makefile.am index 6c5ba79bc..5b4328430 100644 --- a/tester/Makefile.am +++ b/tester/Makefile.am @@ -27,7 +27,8 @@ liblinphonetester_la_SOURCES = tester.c \ player_tester.c \ dtmf_tester.c \ accountmanager.c \ - offeranswer_tester.c + offeranswer_tester.c \ + video_tester.c liblinphonetester_la_LDFLAGS= -no-undefined liblinphonetester_la_LIBADD= ../coreapi/liblinphone.la $(CUNIT_LIBS) @@ -35,6 +36,13 @@ liblinphonetester_la_LIBADD= ../coreapi/liblinphone.la $(CUNIT_LIBS) AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/coreapi AM_CFLAGS = $(STRICT_OPTIONS) $(STRICT_OPTIONS_CC) -DIN_LINPHONE $(ORTP_CFLAGS) $(MEDIASTREAMER_CFLAGS) $(CUNIT_CFLAGS) $(BELLESIP_CFLAGS) $(LIBXML2_CFLAGS) $(SQLITE3_CFLAGS) +if BUILD_GTK_UI + +liblinphone_tester_la_LIBADD += $(LIBGTK_LIBS) $(LIBGTKMAC_LIBS) +AM_CFLAGS += $(LIBGTK_CFLAGS) $(LIBGTKMAC_CFLAGS) -DHAVE_GTK + +endif + if !BUILD_IOS noinst_PROGRAMS = liblinphone_tester diff --git a/tester/call_tester.c b/tester/call_tester.c index 96b90f0e8..2d4a662d7 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -740,7 +740,7 @@ void disable_all_audio_codecs_except_one(LinphoneCore *lc, const char *mime, int } #ifdef VIDEO_ENABLED -static void disable_all_video_codecs_except_one(LinphoneCore *lc, const char *mime) { +void disable_all_video_codecs_except_one(LinphoneCore *lc, const char *mime) { const MSList *codecs = linphone_core_get_video_codecs(lc); const MSList *it = NULL; PayloadType *pt = NULL; diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index f04a5c16d..fbba48218 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -24,6 +24,9 @@ #if HAVE_CU_CURSES #include "CUnit/CUCurses.h" #endif +#ifdef HAVE_GTK +#include +#endif extern int liblinphone_tester_use_log_file; @@ -167,6 +170,12 @@ int main (int argc, char *argv[]) char *xml_tmp_file=NULL; int xml = 0; FILE* log_file=NULL; + +#ifdef HAVE_GTK + gdk_threads_init(); + gtk_init(&argc, &argv); +#endif + #if defined(ANDROID) linphone_core_set_log_handler(linphone_android_ortp_log_handler); #elif defined(__QNX__) @@ -246,7 +255,6 @@ int main (int argc, char *argv[]) } liblinphone_tester_enable_xml(xml); - ret = liblinphone_tester_run_tests(suite_name, test_name); liblinphone_tester_uninit(); diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 8111015f0..c8ca02436 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -63,6 +63,7 @@ extern test_suite_t transport_test_suite; extern test_suite_t player_test_suite; extern test_suite_t dtmf_test_suite; extern test_suite_t offeranswer_test_suite; +extern test_suite_t video_test_suite; extern int liblinphone_tester_nb_test_suites(void); @@ -295,6 +296,7 @@ bool_t call_with_test_params(LinphoneCoreManager* caller_mgr bool_t call(LinphoneCoreManager* caller_mgr,LinphoneCoreManager* callee_mgr); void end_call(LinphoneCoreManager *m1, LinphoneCoreManager *m2); void disable_all_audio_codecs_except_one(LinphoneCore *lc, const char *mime, int rate); +void disable_all_video_codecs_except_one(LinphoneCore *lc, const char *mime); stats * get_stats(LinphoneCore *lc); LinphoneCoreManager *get_manager(LinphoneCore *lc); const char *liblinphone_tester_get_subscribe_content(void); diff --git a/tester/tester.c b/tester/tester.c index cbd1bfd44..c10dc0c79 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -25,6 +25,9 @@ #if HAVE_CU_CURSES #include "CUnit/CUCurses.h" #endif +#ifdef HAVE_GTK +#include +#endif static test_suite_t **test_suite = NULL; static int nb_test_suites = 0; @@ -201,6 +204,11 @@ bool_t wait_for_list(MSList* lcs,int* counter,int value,int timeout_ms) { liblinphone_tester_clock_start(&start); while ((counter==NULL || *counternext) { +#ifdef HAVE_GTK + gdk_threads_enter(); + gtk_main_iteration_do(FALSE); + gdk_threads_leave(); +#endif linphone_core_iterate((LinphoneCore*)(iterator->data)); } ms_usleep(20000); @@ -442,6 +450,9 @@ void liblinphone_tester_init(void) { add_test_suite(&transport_test_suite); add_test_suite(&player_test_suite); add_test_suite(&dtmf_test_suite); +#if defined(VIDEO_ENABLED) && defined(HAVE_GTK) + add_test_suite(&video_test_suite); +#endif } void liblinphone_tester_uninit(void) { diff --git a/tester/video_tester.c b/tester/video_tester.c new file mode 100644 index 000000000..88518aff9 --- /dev/null +++ b/tester/video_tester.c @@ -0,0 +1,198 @@ +/* + liblinphone_tester - liblinphone test suite + Copyright (C) 2013 Belledonne Communications SARL + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either 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, see . +*/ + +#if defined(VIDEO_ENABLED) && defined(HAVE_GTK) + +#include +#include "CUnit/Basic.h" +#include "linphonecore.h" +#include "liblinphone_tester.h" +#include "lpconfig.h" +#include "private.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#elif defined(WIN32) +#include +#elif defined(__APPLE__) +extern void *gdk_quartz_window_get_nswindow(GdkWindow *window); +extern void *gdk_quartz_window_get_nsview(GdkWindow *window); +#endif + +#include + + +static unsigned long get_native_handle(GdkWindow *gdkw) { +#ifdef GDK_WINDOWING_X11 + return (unsigned long)GDK_WINDOW_XID(gdkw); +#elif defined(WIN32) + return (unsigned long)GDK_WINDOW_HWND(gdkw); +#elif defined(__APPLE__) + return (unsigned long)gdk_quartz_window_get_nsview(gdkw); +#endif + g_warning("No way to get the native handle from gdk window"); + return 0; +} + +static GtkWidget *create_video_window(LinphoneCall *call) { + GtkWidget *video_window; + GdkColor color; + MSVideoSize vsize = MS_VIDEO_SIZE_CIF; + + video_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_resize(GTK_WINDOW(video_window), vsize.width, vsize.height); + gdk_color_parse("black", &color); + gtk_widget_modify_bg(video_window, GTK_STATE_NORMAL, &color); + gtk_widget_show(video_window); + g_object_set_data(G_OBJECT(video_window), "call", call); + gdk_display_flush(gdk_window_get_display(gtk_widget_get_window(video_window))); + return video_window; +} + +static void show_video_window(LinphoneCall *call) { + GtkWidget *video_window = (GtkWidget *)linphone_call_get_user_data(call); + if (video_window == NULL) { + video_window = create_video_window(call); + linphone_call_set_user_data(call, video_window); + linphone_call_set_native_video_window_id(call, get_native_handle(gtk_widget_get_window(video_window))); + } +} + +static void hide_video_video(LinphoneCall *call) { + GtkWidget *video_window = (GtkWidget *)linphone_call_get_user_data(call); + if (video_window != NULL) { + gtk_widget_destroy(video_window); + linphone_call_set_user_data(call, NULL); + linphone_call_set_native_video_window_id(call, 0); + } +} + +static void video_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *msg) { + switch (cstate) { + case LinphoneCallConnected: + show_video_window(call); + break; + case LinphoneCallEnd: + hide_video_video(call); + break; + default: + break; + } +} + +static bool_t video_call_with_params(LinphoneCoreManager* caller_mgr, LinphoneCoreManager* callee_mgr, const LinphoneCallParams *caller_params, const LinphoneCallParams *callee_params) { + int retry = 0; + stats initial_caller = caller_mgr->stat; + stats initial_callee = callee_mgr->stat; + bool_t result = FALSE; + bool_t did_received_call; + + CU_ASSERT_PTR_NOT_NULL(linphone_core_invite_address_with_params(caller_mgr->lc, callee_mgr->identity, caller_params)); + did_received_call = wait_for(callee_mgr->lc, caller_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallIncomingReceived, initial_callee.number_of_LinphoneCallIncomingReceived + 1); + if (!did_received_call) return 0; + + CU_ASSERT_TRUE(linphone_core_inc_invite_pending(callee_mgr->lc)); + CU_ASSERT_EQUAL(caller_mgr->stat.number_of_LinphoneCallOutgoingProgress, initial_caller.number_of_LinphoneCallOutgoingProgress + 1); + + while (caller_mgr->stat.number_of_LinphoneCallOutgoingRinging != (initial_caller.number_of_LinphoneCallOutgoingRinging + 1) + && caller_mgr->stat.number_of_LinphoneCallOutgoingEarlyMedia != (initial_caller.number_of_LinphoneCallOutgoingEarlyMedia + 1) + && retry++ < 20) { + linphone_core_iterate(caller_mgr->lc); + linphone_core_iterate(callee_mgr->lc); + ms_usleep(100000); + } + + CU_ASSERT_TRUE((caller_mgr->stat.number_of_LinphoneCallOutgoingRinging == initial_caller.number_of_LinphoneCallOutgoingRinging + 1) + || (caller_mgr->stat.number_of_LinphoneCallOutgoingEarlyMedia == initial_caller.number_of_LinphoneCallOutgoingEarlyMedia + 1)); + + CU_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call_remote_address(callee_mgr->lc)); + if(!linphone_core_get_current_call(caller_mgr->lc) || !linphone_core_get_current_call(callee_mgr->lc) || !linphone_core_get_current_call_remote_address(callee_mgr->lc)) { + return 0; + } + + linphone_core_accept_call_with_params(callee_mgr->lc, linphone_core_get_current_call(callee_mgr->lc), callee_params); + + CU_ASSERT_TRUE(wait_for(callee_mgr->lc, caller_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallConnected, initial_callee.number_of_LinphoneCallConnected + 1)); + CU_ASSERT_TRUE(wait_for(callee_mgr->lc, caller_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallConnected, initial_callee.number_of_LinphoneCallConnected + 1)); + result = wait_for(callee_mgr->lc, caller_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallStreamsRunning, initial_caller.number_of_LinphoneCallStreamsRunning + 1) + && wait_for(callee_mgr->lc, caller_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallStreamsRunning, initial_callee.number_of_LinphoneCallStreamsRunning + 1); + return result; +} + + +static void early_media_video_during_video_call_test(void) { + LinphoneCoreManager *marie; + LinphoneCoreManager *pauline; + LinphoneCallParams *marie_params; + LinphoneCallParams *pauline_params; + LinphoneCoreVTable *marie_vtable; + LinphoneCoreVTable *pauline_vtable; + int dummy = 0; + + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); + marie_vtable = linphone_core_v_table_new(); + marie_vtable->call_state_changed = video_call_state_changed; + linphone_core_add_listener(marie->lc, marie_vtable); + linphone_core_set_video_device(marie->lc, "StaticImage: Static picture"); + //linphone_core_set_video_device(marie->lc, "V4L2: /dev/video0"); + linphone_core_enable_video_capture(marie->lc, TRUE); + linphone_core_enable_video_display(marie->lc, TRUE); + linphone_core_set_avpf_mode(marie->lc, LinphoneAVPFEnabled); + marie_params = linphone_core_create_default_call_parameters(marie->lc); + linphone_call_params_enable_video(marie_params, TRUE); + disable_all_video_codecs_except_one(marie->lc, "VP8"); + pauline_vtable = linphone_core_v_table_new(); + pauline_vtable->call_state_changed = video_call_state_changed; + linphone_core_add_listener(pauline->lc, pauline_vtable); + linphone_core_set_video_device(pauline->lc, "StaticImage: Static picture"); + linphone_core_enable_video_capture(pauline->lc, TRUE); + linphone_core_enable_video_display(pauline->lc, TRUE); + pauline_params = linphone_core_create_default_call_parameters(pauline->lc); + linphone_call_params_enable_video(pauline_params, TRUE); + disable_all_video_codecs_except_one(pauline->lc, "VP8"); + + CU_ASSERT_TRUE(video_call_with_params(marie, pauline, marie_params, pauline_params)); + + /* Wait for 3s. */ + wait_for_until(marie->lc, pauline->lc, &dummy, 1, 3000); + + linphone_core_terminate_all_calls(marie->lc); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallEnd, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallReleased, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallReleased, 1)); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + +test_t video_tests[] = { + { "Early-media video during video call", early_media_video_during_video_call_test } +}; + +test_suite_t video_test_suite = { + "Video", + NULL, + NULL, + sizeof(video_tests) / sizeof(video_tests[0]), + video_tests +}; + +#endif /* VIDEO_ENABLED */