diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index a359970e9..4949efae7 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -60,6 +60,7 @@ liblinphone_la_SOURCES=\ remote_provisioning.c \ quality_reporting.c quality_reporting.h\ call_log.c \ + player.c \ $(GITVERSION_FILE) if BUILD_UPNP diff --git a/coreapi/conference.c b/coreapi/conference.c index 7bee313dd..69cecbecb 100644 --- a/coreapi/conference.c +++ b/coreapi/conference.c @@ -41,6 +41,7 @@ static void conference_check_init(LinphoneConference *ctx, int samplerate){ MSAudioConferenceParams params; params.samplerate=samplerate; ctx->conf=ms_audio_conference_new(¶ms); + ctx->terminated=FALSE; } } @@ -74,7 +75,7 @@ void linphone_core_conference_check_uninit(LinphoneCore *lc){ if (ctx->conf){ int remote_count=remote_participants_count(ctx); ms_message("conference_check_uninit(): size=%i",linphone_conference_get_size(ctx)); - if (remote_count==1){ + if (remote_count==1 && !ctx->terminated){ convert_conference_to_call(lc); } if (remote_count==0){ @@ -251,7 +252,6 @@ static int remove_from_conference(LinphoneCore *lc, LinphoneCall *call, bool_t a ms_message("Pausing call to actually remove from conference"); err=_linphone_core_pause_call(lc,call); } - return err; } @@ -388,6 +388,9 @@ int linphone_core_add_all_to_conference(LinphoneCore *lc) { **/ int linphone_core_terminate_conference(LinphoneCore *lc) { MSList *calls=lc->calls; + LinphoneConference *conf=&lc->conf_ctx; + conf->terminated=TRUE; + while (calls) { LinphoneCall *call=(LinphoneCall*)calls->data; calls=calls->next; diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 4fc12d737..b96efab44 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -3240,3 +3240,9 @@ void linphone_call_set_contact_op(LinphoneCall* call) { linphone_address_destroy(contact); } } + +LinphonePlayer *linphone_call_get_player(LinphoneCall *call){ + if (call->player==NULL) + call->player=linphone_call_build_player(call); + return call->player; +} diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 0a35554c4..c37b87aec 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -645,6 +645,27 @@ LINPHONE_PUBLIC uint64_t linphone_call_stats_get_late_packets_cumulative_number( /** Callback prototype */ typedef void (*LinphoneCallCbFunc)(LinphoneCall *call,void * user_data); +/** + * Player interface. + * @ingroup call_control +**/ +typedef struct _LinphonePlayer LinphonePlayer; + +/** + * Callback for notifying end of play (file). + * @param obj the LinphonePlayer + * @param user_data the user_data provided when calling linphone_player_open(). + * @ingroup call_control +**/ +typedef void (*LinphonePlayerEofCallback)(struct _LinphonePlayer *obj, void *user_data); + +int linphone_player_open(LinphonePlayer *obj, const char *filename, LinphonePlayerEofCallback, void *user_data); +int linphone_player_start(LinphonePlayer *obj); +int linphone_player_pause(LinphonePlayer *obj); +int linphone_player_seek(LinphonePlayer *obj, int time_ms); +MSPlayerState linphone_player_get_state(LinphonePlayer *obj); +void linphone_player_close(LinphonePlayer *obj); + /** * LinphoneCallState enum represents the different state a call can reach into. * The application is notified of state changes through the LinphoneCoreVTable::call_state_changed callback. @@ -758,6 +779,7 @@ LINPHONE_PUBLIC LinphoneCallState linphone_call_get_transfer_state(LinphoneCall LINPHONE_PUBLIC void linphone_call_zoom_video(LinphoneCall* call, float zoom_factor, float* cx, float* cy); LINPHONE_PUBLIC void linphone_call_start_recording(LinphoneCall *call); LINPHONE_PUBLIC void linphone_call_stop_recording(LinphoneCall *call); +LINPHONE_PUBLIC LinphonePlayer * linphone_call_get_player(LinphoneCall *call); /** * Return TRUE if this call is currently part of a conference diff --git a/coreapi/player.c b/coreapi/player.c new file mode 100644 index 000000000..df1641886 --- /dev/null +++ b/coreapi/player.c @@ -0,0 +1,168 @@ + +/* +linphone +Copyright (C) 2014 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, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "private.h" + +/** + * Open a new source on this player. + * @param obj the player + * @param filename file to open. + * @param cb a callback used to notify end of play. + * @param user_data a user-data provided in the callback to help the application to retrieve its context. + * @return 0 if successful, -1 otherwise +**/ +int linphone_player_open(LinphonePlayer *obj, const char *filename, LinphonePlayerEofCallback cb, void *user_data){ + obj->user_data=user_data; + obj->cb=cb; + return obj->open(obj->impl,filename); +} + +/** + * Start a play operation. The player must have been open previously with linphone_player_open(). + * @param obj the player. + * @return 0 if successful, -1 otherwise +**/ +int linphone_player_start(LinphonePlayer *obj){ + return obj->start(obj->impl); +} + +/** + * Suspend a play operation. The player must have been started previously with linphone_player_start(). + * @param obj the player. + * @return 0 if successful, -1 otherwise +**/ +int linphone_player_pause(LinphonePlayer *obj){ + return obj->pause(obj->impl); +} + +/** + * Seek at a given position given in milliseconds. The player must be in the paused state. + * @param obj the player. + * @param time_ms the position to seek to. + * @return 0 if successful, -1 otherwise +**/ +int linphone_player_seek(LinphonePlayer *obj, int time_ms){ + return obj->seek(obj->impl,time_ms); +} + +/** + * Get the state of play operation. + * @param obj the player. + * @return the state of the player within MSPlayerClosed, MSPlayerStarted, MSPlayerPaused. +**/ +MSPlayerState linphone_player_get_state(LinphonePlayer *obj){ + return obj->get_state(obj->impl); +} + +/** + * Close the player. + * @param obj the player. +**/ +void linphone_player_close(LinphonePlayer *obj){ + return obj->close(obj->impl); +} + + +/* + * Call player implementation below. + */ + + +static bool_t call_player_check_state(LinphonePlayer *player, bool_t check_player){ + LinphoneCall *call=(LinphoneCall*)player->impl; + if (call->state!=LinphoneCallStreamsRunning){ + ms_warning("Call [%p]: in-call player not usable in state [%s]",call,linphone_call_state_to_string(call->state)); + return FALSE; + } + if (call->audiostream==NULL) { + ms_error("call_player_check_state(): no audiostream."); + return FALSE; + } + if (check_player && call->audiostream->av_player.player==NULL){ + ms_error("call_player_check_state(): no player."); + return FALSE; + } + return TRUE; +} + +static void on_eof(void *user_data, MSFilter *f, unsigned int event_id, void *arg){ + LinphonePlayer *player=(LinphonePlayer *)user_data; + if (player->cb) player->cb(player,user_data); +} + +static int call_player_open(LinphonePlayer* player, const char *filename){ + LinphoneCall *call=(LinphoneCall*)player->impl; + MSFilter *filter; + if (!call_player_check_state(player,FALSE)) return -1; + filter=audio_stream_open_remote_play(call->audiostream,filename); + if (!filter) return -1; + ms_filter_add_notify_callback(filter,&on_eof,player,FALSE); + return 0; +} + +static int call_player_start(LinphonePlayer *player){ + LinphoneCall *call=(LinphoneCall*)player->impl; + if (!call_player_check_state(player,TRUE)) return -1; + return ms_filter_call_method_noarg(call->audiostream->av_player.player,MS_PLAYER_START); +} + +static int call_player_pause(LinphonePlayer *player){ + LinphoneCall *call=(LinphoneCall*)player->impl; + if (!call_player_check_state(player,TRUE)) return -1; + return ms_filter_call_method_noarg(call->audiostream->av_player.player,MS_PLAYER_PAUSE); +} + +static MSPlayerState call_player_get_state(LinphonePlayer *player){ + LinphoneCall *call=(LinphoneCall*)player->impl; + MSPlayerState state=MSPlayerClosed; + if (!call_player_check_state(player,TRUE)) return MSPlayerClosed; + ms_filter_call_method(call->audiostream->av_player.player,MS_PLAYER_GET_STATE,&state); + return state; +} + +static int call_player_seek(LinphonePlayer *player, int time_ms){ + LinphoneCall *call=(LinphoneCall*)player->impl; + if (!call_player_check_state(player,TRUE)) return -1; + return ms_filter_call_method(call->audiostream->av_player.player,MS_PLAYER_SEEK_MS,&time_ms); +} + +static void call_player_close(LinphonePlayer *player){ + LinphoneCall *call=(LinphoneCall*)player->impl; + if (!call_player_check_state(player,TRUE)) return; + ms_filter_call_method_noarg(call->audiostream->av_player.player,MS_PLAYER_CLOSE); + +} + +static void on_call_destroy(void *obj, belle_sip_object_t *call_being_destroyed){ + ms_free(obj); +} + +LinphonePlayer *linphone_call_build_player(LinphoneCall *call){ + LinphonePlayer *obj=ms_new0(LinphonePlayer,1); + obj->open=call_player_open; + obj->close=call_player_close; + obj->start=call_player_start; + obj->seek=call_player_seek; + obj->pause=call_player_pause; + obj->get_state=call_player_get_state; + obj->impl=call; + belle_sip_object_weak_ref(call,on_call_destroy,obj); + return obj; +} diff --git a/coreapi/private.h b/coreapi/private.h index cbcaa3421..afec02fd5 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -230,6 +230,7 @@ struct _LinphoneCall LinphoneCall *referer; /*when this call is the result of a transfer, referer is set to the original call that caused the transfer*/ LinphoneCall *transfer_target;/*if this call received a transfer request, then transfer_target points to the new call created to the refer target */ int localdesc_changed;/*not a boolean, contains a mask representing changes*/ + LinphonePlayer *player; bool_t refer_pending; bool_t expect_media_in_ack; @@ -262,6 +263,7 @@ LinphoneCallLog * linphone_call_log_new(LinphoneCallDir dir, LinphoneAddress *lo void linphone_call_log_completed(LinphoneCall *call); void linphone_call_log_destroy(LinphoneCallLog *cl); void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state); +LinphonePlayer *linphone_call_build_player(LinphoneCall*call); void linphone_auth_info_write_config(struct _LpConfig *config, LinphoneAuthInfo *obj, int pos); @@ -629,6 +631,7 @@ struct _LinphoneConference{ MSAudioEndpoint *record_endpoint; RtpProfile *local_dummy_profile; bool_t local_muted; + bool_t terminated; }; @@ -878,6 +881,23 @@ void linphone_configuring_terminated(LinphoneCore *lc, LinphoneConfiguringState int linphone_remote_provisioning_download_and_apply(LinphoneCore *lc, const char *remote_provisioning_uri); +/***************************************************************************** + * Player interface + ****************************************************************************/ + +struct _LinphonePlayer{ + int (*open)(struct _LinphonePlayer* player, const char *filename); + int (*start)(struct _LinphonePlayer* player); + int (*pause)(struct _LinphonePlayer* player); + int (*seek)(struct _LinphonePlayer* player, int time_ms); + MSPlayerState (*get_state)(struct _LinphonePlayer* player); + void (*close)(struct _LinphonePlayer* player); + LinphonePlayerEofCallback cb; + void *user_data; + void *impl; +}; + + /***************************************************************************** * XML UTILITY FUNCTIONS * ****************************************************************************/ diff --git a/gtk/conference.c b/gtk/conference.c index 08262c771..e273470a2 100644 --- a/gtk/conference.c +++ b/gtk/conference.c @@ -140,7 +140,7 @@ void linphone_gtk_set_in_conference(LinphoneCall *call){ gtk_box_pack_start(GTK_BOX(conferencee_box),participant,FALSE,FALSE,PADDING_PIXELS); g_object_set_data_full(G_OBJECT(participant),"call",linphone_call_ref(call),(GDestroyNotify)linphone_call_unref); gtk_notebook_set_current_page(GTK_NOTEBOOK(viewswitch), - gtk_notebook_page_num(GTK_NOTEBOOK(viewswitch),conf_frame)); + gtk_notebook_page_num(GTK_NOTEBOOK(viewswitch),conf_frame)); } } diff --git a/mediastreamer2 b/mediastreamer2 index 3c16405c5..4d3ab5f25 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 3c16405c515e5f49ef39400f7b8593c52fb90e0d +Subproject commit 4d3ab5f253334282baad3177bf9d33e57e65c71d diff --git a/tester/setup_tester.c b/tester/setup_tester.c index cc72e2e95..aa1a6f73e 100644 --- a/tester/setup_tester.c +++ b/tester/setup_tester.c @@ -23,6 +23,12 @@ #include "lpconfig.h" #include "private.h" +static void linphone_version_test(void){ + const char *version=linphone_core_get_version(); + /*make sure the git version is always included in the version number*/ + CU_ASSERT_TRUE(strstr(version,"unknown")==NULL); +} + static void core_init_test(void) { LinphoneCoreVTable v_table; LinphoneCore* lc; @@ -177,6 +183,7 @@ static void chat_root_test(void) { } test_t setup_tests[] = { + { "Version check", linphone_version_test }, { "Linphone Address", linphone_address_test }, { "Linphone proxy config address equal (internal api)", linphone_proxy_config_address_equal_test}, { "Linphone proxy config server address change (internal api)", linphone_proxy_config_is_server_config_changed_test},