]> sjero.net Git - linphone/commitdiff
implement call recording.
authorSimon Morlat <simon.morlat@linphone.org>
Wed, 6 Feb 2013 13:35:36 +0000 (14:35 +0100)
committerSimon Morlat <simon.morlat@linphone.org>
Wed, 6 Feb 2013 14:06:40 +0000 (15:06 +0100)
coreapi/Makefile.am
coreapi/conference.c
coreapi/linphonecall.c
coreapi/linphonecore.c
coreapi/linphonecore.h
coreapi/misc.c
coreapi/private.h
gtk/incall_view.c
gtk/main.c
gtk/main.ui
mediastreamer2

index eab65b147577d072245bb4fb5c60a00908a815ec..90a1c32020d52209600ccd762edf5f3dbd88eb93 100644 (file)
@@ -16,7 +16,7 @@ CLEANFILES=$(GITVERSION_FILE)
 ## Process this file with automake to produce Makefile.in
 linphone_includedir=$(includedir)/linphone
 
-linphone_include_HEADERS=linphonecore.h linphonefriend.h linphonecore_utils.h ../config.h lpconfig.h sipsetup.h 
+linphone_include_HEADERS=linphonecore.h linphonefriend.h linphonecore_utils.h lpconfig.h sipsetup.h 
 
 if BUILD_TUNNEL
 linphone_include_HEADERS+=linphone_tunnel.h
index 9f1538f4225b2db301f1a6702cc787cd0e1dc549..a125673a29b0b91ef1861a748d76ffb8d5cc08db 100644 (file)
@@ -161,6 +161,7 @@ float linphone_core_get_conference_local_input_volume(LinphoneCore *lc){
  * 
  * If this is the first call that enters the conference, the virtual conference will be created automatically.
  * If the local user was actively part of the call (ie not in paused state), then the local user is automatically entered into the conference.
+ * If the call was in paused state, then it is automatically resumed when entering into the conference.
  * 
  * @returns 0 if successful, -1 otherwise.
 **/
@@ -256,10 +257,13 @@ static int convert_conference_to_call(LinphoneCore *lc){
  * @param call a call that has been previously merged into the conference.
  * 
  * After removing the remote participant belonging to the supplied call, the call becomes a normal call in paused state.
- * If one single remote participant is left alone in the conference after the removal, then it is
- * automatically removed from the conference and put into a simple call, like before entering the conference.
+ * If one single remote participant is left alone together with the local user in the conference after the removal, then the conference is
+ * automatically transformed into a simple call in StreamsRunning state.
  * The conference's resources are then automatically destroyed.
  * 
+ * In other words, unless linphone_core_leave_conference() is explicitely called, the last remote participant of a conference is automatically
+ * put in a simple call in running state.
+ * 
  * @returns 0 if successful, -1 otherwise.
  **/
 int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call){
index 3bfa7d9d450ebb117da451be6a0689bb53cd826e..36b8c69183f9a4b7a399ca502af302e096971eab 100644 (file)
@@ -471,7 +471,7 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr
        call->core=lc;
        linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip);
        linphone_call_init_common(call,from,to);
-       call->params=*params;
+       _linphone_call_params_copy(&call->params,params);
        if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) {
                call->ice_session = ice_session_new();
                ice_session_set_role(call->ice_session, IR_Controlling);
@@ -733,6 +733,8 @@ static void linphone_call_destroy(LinphoneCall *obj)
        if (obj->auth_token) {
                ms_free(obj->auth_token);
        }
+       if (obj->params.record_file)
+               ms_free(obj->params.record_file);
 
        ms_free(obj);
 }
@@ -768,7 +770,9 @@ void linphone_call_unref(LinphoneCall *obj){
 /**
  * Returns current parameters associated to the call.
 **/
-const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall *call){
+const LinphoneCallParams * linphone_call_get_current_params(LinphoneCall *call){
+       if (call->params.record_file)
+               call->current_params.record_file=call->params.record_file;
        return &call->current_params;
 }
 
@@ -987,10 +991,17 @@ void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){
        cp->has_video=enabled;
 }
 
+/**
+ * Returns the audio codec used in the call, described as a PayloadType structure.
+**/
 const PayloadType* linphone_call_params_get_used_audio_codec(const LinphoneCallParams *cp) {
        return cp->audio_codec;
 }
 
+
+/**
+ * Returns the video codec used in the call, described as a PayloadType structure.
+**/
 const PayloadType* linphone_call_params_get_used_video_codec(const LinphoneCallParams *cp) {
        return cp->video_codec;
 }
@@ -1073,12 +1084,18 @@ void linphone_call_send_vfu_request(LinphoneCall *call)
 }
 #endif
 
+
+void _linphone_call_params_copy(LinphoneCallParams *ncp, const LinphoneCallParams *cp){
+       memcpy(ncp,cp,sizeof(LinphoneCallParams));
+       if (cp->record_file) ncp->record_file=ms_strdup(cp->record_file);
+}
+
 /**
  *
 **/
 LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){
        LinphoneCallParams *ncp=ms_new0(LinphoneCallParams,1);
-       memcpy(ncp,cp,sizeof(LinphoneCallParams));
+       _linphone_call_params_copy(ncp,cp);
        return ncp;
 }
 
@@ -1086,6 +1103,7 @@ LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){
  *
 **/
 void linphone_call_params_destroy(LinphoneCallParams *p){
+       if (p->record_file) ms_free(p->record_file);
        ms_free(p);
 }
 
@@ -1329,10 +1347,10 @@ static void post_configure_audio_streams(LinphoneCall*call){
        LinphoneCore *lc=call->core;
        _post_configure_audio_stream(st,lc,call->audio_muted);
        if (lc->vtable.dtmf_received!=NULL){
-               /* replace by our default action*/
                audio_stream_play_received_dtmfs(call->audiostream,FALSE);
-               /*rtp_session_signal_connect(call->audiostream->session,"telephone-event",(RtpCallback)linphone_core_dtmf_received,(unsigned long)lc);*/
        }
+       if (call->record_active) 
+               linphone_call_start_recording(call);
 }
 
 static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){
@@ -1484,6 +1502,8 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna
                        if (captcard &&  stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(captcard, stream->max_rate);
                        audio_stream_enable_adaptive_bitrate_control(call->audiostream,use_arc);
                        audio_stream_enable_adaptive_jittcomp(call->audiostream, linphone_core_audio_adaptive_jittcomp_enabled(lc));
+                       if (!call->params.in_conference && call->params.record_file)
+                               audio_stream_mixed_record_open(call->audiostream,call->params.record_file);
                        audio_stream_start_full(
                                call->audiostream,
                                call->audio_profile,
@@ -1512,23 +1532,23 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna
                        }
                        audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool);
                        
-            /* valid local tags are > 0 */
+                       /* valid local tags are > 0 */
                        if (stream->proto == SalProtoRtpSavp) {
-                const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
-                                                                                            SalProtoRtpSavp,SalAudio);
-                int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag);
-                
-                if (crypto_idx >= 0) {
-                    audio_stream_enable_srtp(
-                                             call->audiostream, 
-                                             stream->crypto[0].algo,
-                                             local_st_desc->crypto[crypto_idx].master_key,
-                                             stream->crypto[0].master_key);
-                    call->audiostream_encrypted=TRUE;
-                } else {
-                    ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag);
-                    call->audiostream_encrypted=FALSE;
-                }
+                       const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
+                                                                                                       SalProtoRtpSavp,SalAudio);
+                       int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag);
+
+                       if (crypto_idx >= 0) {
+                               audio_stream_enable_srtp(
+                                                       call->audiostream, 
+                                                       stream->crypto[0].algo,
+                                                       local_st_desc->crypto[crypto_idx].master_key,
+                                                       stream->crypto[0].master_key);
+                               call->audiostream_encrypted=TRUE;
+                       } else {
+                               ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag);
+                               call->audiostream_encrypted=FALSE;
+                       }
                        }else call->audiostream_encrypted=FALSE;
                        if (call->params.in_conference){
                                /*transform the graph to connect it to the conference filter */
@@ -1981,6 +2001,53 @@ const LinphoneCallStats *linphone_call_get_video_stats(const LinphoneCall *call)
        return &call->stats[LINPHONE_CALL_STATS_VIDEO];
 }
 
+/**
+ * Enable recording of the call (voice-only).
+ * This function must be used before the call parameters are assigned to the call.
+ * The call recording can be started and paused after the call is established with
+ * linphone_call_start_recording() and linphone_call_pause_recording().
+ * @param cp the call parameters
+ * @param path path and filename of the file where audio is written.
+**/
+void linphone_call_params_set_record_file(LinphoneCallParams *cp, const char *path){
+       if (cp->record_file){
+               ms_free(cp->record_file);
+               cp->record_file=NULL;
+       }
+       if (path) cp->record_file=ms_strdup(path);
+}
+
+/**
+ * Retrieves the path for the audio recoding of the call.
+**/
+const char *linphone_call_params_get_record_file(const LinphoneCallParams *cp){
+       return cp->record_file;
+}
+
+/**
+ * Start call recording.
+ * The output file where audio is recorded must be previously specified with linphone_call_params_set_record_file().
+**/
+void linphone_call_start_recording(LinphoneCall *call){
+       if (!call->params.record_file){
+               ms_error("linphone_call_start_recording(): no output file specified. Use linphone_call_params_set_record_file().");
+               return;
+       }
+       if (call->audiostream && !call->params.in_conference){
+               audio_stream_mixed_record_start(call->audiostream);
+       }
+       call->record_active=TRUE;
+}
+
+/**
+ * Stop call recording.
+**/
+void linphone_call_stop_recording(LinphoneCall *call){
+       if (call->audiostream && !call->params.in_conference){
+               audio_stream_mixed_record_stop(call->audiostream);
+       }
+       call->record_active=FALSE;
+}
 
 /**
  * @}
index 673131218e6ad58ec8ece0db983a3237d4bfabd7..76ccc1f051ca6c074810e06ea756b75e43bd4c82 100644 (file)
@@ -486,11 +486,11 @@ static void sound_config_read(LinphoneCore *lc)
        check_sound_device(lc);
        lc->sound_conf.latency=0;
 #ifndef __ios 
-    tmp=TRUE;
+       tmp=TRUE;
 #else
-    tmp=FALSE; /* on iOS we have builtin echo cancellation.*/
+       tmp=FALSE; /* on iOS we have builtin echo cancellation.*/
 #endif
-    tmp=lp_config_get_int(lc->config,"sound","echocancellation",tmp);
+       tmp=lp_config_get_int(lc->config,"sound","echocancellation",tmp);
        linphone_core_enable_echo_cancellation(lc,tmp);
        linphone_core_enable_echo_limiter(lc,
                lp_config_get_int(lc->config,"sound","echolimiter",0));
@@ -3034,7 +3034,7 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call,
 
        if (params){
                const SalMediaDescription *md = sal_call_get_remote_media_description(call->op);
-               call->params=*params;
+               _linphone_call_params_copy(&call->params,params);
                // There might not be a md if the INVITE was lacking an SDP
                // In this case we use the parameters as is.
                if (md) call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
@@ -4685,7 +4685,8 @@ void linphone_core_set_play_file(LinphoneCore *lc, const char *file){
  * Sets a wav file where incoming stream is to be recorded,
  * when files are used instead of soundcards (see linphone_core_use_files()).
  *
- * The file must be a 16 bit linear wav file.
+ * This feature is different from call recording (linphone_call_params_set_record_file())
+ * The file will be a 16 bit linear wav file.
 **/
 void linphone_core_set_record_file(LinphoneCore *lc, const char *file){
        LinphoneCall *call=linphone_core_get_current_call(lc);
@@ -5536,8 +5537,6 @@ void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *para
        params->in_conference=FALSE;
 }
 
-
-
 void linphone_core_set_device_identifier(LinphoneCore *lc,const char* device_id) {
        if (lc->device_id) ms_free(lc->device_id);
        lc->device_id=ms_strdup(device_id);
index f780d2f658b76525af1ae4ed13fab42a6f17a313..6228861de57710dd8d3282fea39a6083c310ce66 100644 (file)
@@ -206,7 +206,8 @@ void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int
 void linphone_call_params_destroy(LinphoneCallParams *cp);
 bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp);
 void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t enabled);
-
+void linphone_call_params_set_record_file(LinphoneCallParams *cp, const char *path);
+const char *linphone_call_params_get_record_file(const LinphoneCallParams *cp);
 /**
  * Enum describing failure reasons.
  * @ingroup initializing
@@ -389,7 +390,7 @@ const char *linphone_call_get_refer_to(const LinphoneCall *call);
 bool_t linphone_call_has_transfer_pending(const LinphoneCall *call);
 LinphoneCall *linphone_call_get_replaced_call(LinphoneCall *call);
 int linphone_call_get_duration(const LinphoneCall *call);
-const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall *call);
+const LinphoneCallParams * linphone_call_get_current_params(LinphoneCall *call);
 const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call);
 void linphone_call_enable_camera(LinphoneCall *lc, bool_t enabled);
 bool_t linphone_call_camera_enabled(const LinphoneCall *lc);
@@ -410,6 +411,8 @@ void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer);
 void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, LinphoneCallCbFunc cb, void* user_data);
 LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call);
 void linphone_call_zoom_video(LinphoneCall* call, float zoom_factor, float* cx, float* cy);
+void linphone_call_start_recording(LinphoneCall *call);
+void linphone_call_stop_recording(LinphoneCall *call);
 /**
  * Return TRUE if this call is currently part of a conference
  *@param call #LinphoneCall
index 897300c61767f873d3781fc3a67e4ed381b7b0bf..927c3b55a2636b55eef5402285c54be1a59f22a5 100644 (file)
@@ -988,6 +988,7 @@ unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
                        else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV;
                        else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF;
                        else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO;
+                       else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
                        else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL;
                        else if (strcasecmp(name,"NONE")==0) ret=0;
                        else ms_error("Unsupported audio feature %s requested in config file.",name);
@@ -995,6 +996,12 @@ unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
                        p=n;
                }
        }else ret=AUDIO_STREAM_FEATURE_ALL;
+       
+       if (ret==AUDIO_STREAM_FEATURE_ALL){
+               /*since call recording is specified before creation of the stream in linphonecore,
+               * it will be requested on demand. It is not necessary to include it all the time*/
+               ret&=~AUDIO_STREAM_FEATURE_MIXED_RECORDING;
+       }
        return ret;
 }
 
index ca79a722f7acef67c7c68c92dd477e7d8f38d128..4048be98ace0d18aaf873174a0e7f3f872889ca9 100644 (file)
@@ -80,6 +80,7 @@ struct _LinphoneCallParams{
        int up_bw;
        int down_ptime;
        int up_ptime;
+       char *record_file;
        bool_t has_video;
        bool_t real_early_media; /*send real media even during early media (for outgoing calls)*/
        bool_t in_conference; /*in conference mode */
@@ -176,6 +177,7 @@ struct _LinphoneCall
        
        bool_t was_automatically_paused;
        bool_t ping_replied;
+       bool_t record_active;
 };
 
 
@@ -661,6 +663,7 @@ void call_logs_write_to_config_file(LinphoneCore *lc);
 
 int linphone_core_get_edge_bw(LinphoneCore *lc);
 int linphone_core_get_edge_ptime(LinphoneCore *lc);
+void _linphone_call_params_copy(LinphoneCallParams *params, const LinphoneCallParams *refparams);
 
 int linphone_upnp_init(LinphoneCore *lc);
 void linphone_upnp_destroy(LinphoneCore *lc);
index 65446d696ac956c7aafd31a79ae5d50711c6f79c..68c86d38aac93f5fc45fa026f7658e25fe1ed1f9 100644 (file)
@@ -667,8 +667,6 @@ void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call){
        GtkWidget *call_stats=(GtkWidget*)g_object_get_data(G_OBJECT(callview),"call_stats");
 
        display_peer_name_in_label(callee,linphone_call_get_remote_address (call));
-
-       gtk_widget_set_visible(linphone_gtk_get_widget(callview,"buttons_panel"),!in_conf);
        
        gtk_widget_hide(linphone_gtk_get_widget(callview,"answer_decline_panel"));
        gtk_label_set_markup(GTK_LABEL(status),in_conf ? _("In conference") : _("<b>In call</b>"));
@@ -693,6 +691,8 @@ void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call){
                gtk_widget_set_sensitive(linphone_gtk_get_widget(callview,"incall_mute"),FALSE);
        }
        gtk_widget_show_all(linphone_gtk_get_widget(callview,"buttons_panel"));
+       if (!in_conf) gtk_widget_show_all(linphone_gtk_get_widget(callview,"record_hbox"));
+       else gtk_widget_hide(linphone_gtk_get_widget(callview,"record_hbox"));
        if (call_stats) show_used_codecs(call_stats,call);
 }
 
@@ -740,11 +740,9 @@ void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_m
                   linphone_gtk_get_ui_config("stop_call_icon","stopcall-red.png"),FALSE);
 
        gtk_widget_hide(linphone_gtk_get_widget(callview,"answer_decline_panel"));
+       gtk_widget_hide(linphone_gtk_get_widget(callview,"record_hbox"));
+       gtk_widget_hide(linphone_gtk_get_widget(callview,"buttons_panel"));
        gtk_widget_hide(linphone_gtk_get_widget(callview,"incall_audioview"));
-       gtk_widget_hide(linphone_gtk_get_widget(callview,"terminate_call"));
-       gtk_widget_hide(linphone_gtk_get_widget(callview,"video_button"));
-       gtk_widget_hide(linphone_gtk_get_widget(callview,"transfer_button"));
-       gtk_widget_hide(linphone_gtk_get_widget(callview,"conference_button"));
        linphone_gtk_enable_mute_button(
                GTK_BUTTON(linphone_gtk_get_widget(callview,"incall_mute")),FALSE);
        linphone_gtk_enable_hold_button(call,FALSE,TRUE);
@@ -857,3 +855,20 @@ void linphone_gtk_call_statistics_closed(GtkWidget *call_stats){
        gtk_widget_destroy(call_stats);
 }
 
+void linphone_gtk_record_call_toggled(GtkWidget *button){
+       gboolean active=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
+       LinphoneCall *call=linphone_gtk_get_currently_displayed_call(NULL);
+       GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer (call);
+       const LinphoneCallParams *params=linphone_call_get_current_params(call);
+       const char *filepath=linphone_call_params_get_record_file(params);
+       gchar *message=g_strdup_printf(_("<small><i>Recording into %s %s</i></small>"),filepath,active ? "" : _("(Paused)"));
+       
+       if (active){
+               linphone_call_start_recording(call);
+       }else {
+               linphone_call_stop_recording(call);
+       }
+       gtk_label_set_markup(GTK_LABEL(linphone_gtk_get_widget(callview,"record_status")),message);
+       g_free(message);
+}
+
index cfbd8983fe36009aa635b948796ecda67d632c37..94d94f662e1696bfd37c8e725bf2fddd356e91d3 100644 (file)
@@ -750,10 +750,31 @@ static void linphone_gtk_update_call_buttons(LinphoneCall *call){
        }
 }
 
+gchar *linphone_gtk_get_call_record_path(LinphoneAddress *address){
+       const char *dir=g_get_user_special_dir(G_USER_DIRECTORY_MUSIC);
+       const char *id=linphone_address_get_username(address);
+       char filename[256]={0};
+       if (id==NULL) id=linphone_address_get_domain(address);
+       snprintf(filename,sizeof(filename)-1,"%s-%lu-%s-record.wav",
+                linphone_gtk_get_ui_config("title","Linphone"),
+                (unsigned long)time(NULL),id);
+       return g_build_filename(dir,filename,NULL);
+}
+
 static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){
        const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar));
-       if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) {
+       LinphoneCore *lc=linphone_gtk_get_core();
+       LinphoneAddress *addr=linphone_core_interpret_url(lc,entered);
+       
+       if (addr!=NULL){
+               LinphoneCallParams *params=linphone_core_create_default_call_parameters(lc);
+               gchar *record_file=linphone_gtk_get_call_record_path(addr);
+               linphone_call_params_set_record_file(params,record_file);
+               linphone_core_invite_address_with_params(lc,addr,params);
                completion_add_text(GTK_ENTRY(uri_bar),entered);
+               linphone_address_destroy(addr);
+               linphone_call_params_destroy(params);
+               g_free(record_file);
        }else{
                linphone_gtk_call_terminated(NULL,NULL);
        }
@@ -1792,6 +1813,7 @@ int main(int argc, char *argv[]){
        GtkSettings *settings;
        GdkPixbuf *pbuf;
        const char *app_name="Linphone";
+       LpConfig *factory;
 
 #if !GLIB_CHECK_VERSION(2, 31, 0)
        g_thread_init(NULL);
@@ -1867,6 +1889,11 @@ int main(int argc, char *argv[]){
                 since we want to have had time to change directory and to parse
                 the options, in case we needed to access the working directory */
        factory_config_file = linphone_gtk_get_factory_config_file();
+       if (factory_config_file){
+               factory=lp_config_new(NULL);
+               lp_config_read_file(factory,factory_config_file);
+               app_name=lp_config_get_string(factory,"GtkUi","title","Linphone");
+       }
 
        if (linphone_gtk_init_instance(app_name, addr_to_call) == FALSE){
                g_warning("Another running instance of linphone has been detected. It has been woken-up.");
index 6c15688c2169a30ab3ab5994144429ab8e4570aa..b42470e5ad612a59032fb420624d4a88996fcf36 100644 (file)
               <object class="GtkVBox" id="vbox3">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkLabel" id="in_call_uri">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">label</property>
+                    <property name="justify">center</property>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
                 <child>
                   <object class="GtkVBox" id="in_call_animation">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <child>
-                      <object class="GtkLabel" id="in_call_uri">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="label" translatable="yes">label</property>
-                        <property name="justify">center</property>
-                      </object>
-                      <packing>
-                        <property name="expand">True</property>
-                        <property name="fill">True</property>
-                        <property name="padding">2</property>
-                        <property name="position">0</property>
-                      </packing>
+                      <placeholder/>
                     </child>
                   </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">False</property>
-                    <property name="position">0</property>
+                    <property name="position">1</property>
                   </packing>
                 </child>
                 <child>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
-                    <property name="position">1</property>
+                    <property name="position">2</property>
                   </packing>
                 </child>
                 <child>
                     <property name="expand">False</property>
                     <property name="fill">False</property>
                     <property name="padding">2</property>
-                    <property name="position">2</property>
+                    <property name="position">3</property>
                   </packing>
                 </child>
                 <child>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">False</property>
-                    <property name="position">3</property>
+                    <property name="position">4</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="record_hbox">
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkToggleButton" id="record_button">
+                        <property name="label">gtk-media-record</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="tooltip_text" translatable="yes">Record this call to an audio file</property>
+                        <property name="use_action_appearance">False</property>
+                        <property name="use_stock">True</property>
+                        <signal name="toggled" handler="linphone_gtk_record_call_toggled" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="record_status">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="wrap">True</property>
+                        <property name="wrap_mode">char</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">5</property>
                   </packing>
                 </child>
                 <child>
                     <property name="expand">False</property>
                     <property name="fill">False</property>
                     <property name="padding">7</property>
-                    <property name="position">4</property>
+                    <property name="position">6</property>
                   </packing>
                 </child>
               </object>
index 1f0374f48290e0e852bcbb15c4d302c0c5b332f4..fd8f21d7087ed2d5af18e4cd3a7db9bdf0008ed3 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 1f0374f48290e0e852bcbb15c4d302c0c5b332f4
+Subproject commit fd8f21d7087ed2d5af18e4cd3a7db9bdf0008ed3