source: mediastreamer2/linphone/mediastreamer2/src/audiostream.c @ 198:0c83883672a0

Last change on this file since 198:0c83883672a0 was 198:0c83883672a0, checked in by smorlat <smorlat@…>, 5 years ago

fix bug in msx264
fix segfault when sending a dtmf when call is not yet established.

git-svn-id: svn+ssh://svn.savannah.nongnu.org/linphone/trunk@201 3f6dc0c8-ddfe-455d-9043-3cd528dc4637

File size: 15.5 KB
Line 
1/*
2mediastreamer2 library - modular sound and video processing and streaming
3Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)
4
5This program is free software; you can redistribute it and/or
6modify it under the terms of the GNU General Public License
7as published by the Free Software Foundation; either version 2
8of the License, or (at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18*/
19
20
21#ifdef HAVE_CONFIG_H
22#include "mediastreamer-config.h"
23#endif
24
25#include "mediastreamer2/mediastream.h"
26
27#include "mediastreamer2/dtmfgen.h"
28#include "mediastreamer2/mssndcard.h"
29#include "mediastreamer2/msrtp.h"
30#include "mediastreamer2/msfileplayer.h"
31#include "mediastreamer2/msfilerec.h"
32
33#ifdef INET6
34        #include <sys/types.h>
35#ifndef WIN32
36        #include <sys/socket.h>
37        #include <netdb.h>
38#endif
39#endif
40
41
42#define MAX_RTP_SIZE    1500
43
44
45/* this code is not part of the library itself, it is part of the mediastream program */
46void audio_stream_free(AudioStream *stream)
47{
48        if (stream->session!=NULL) rtp_session_destroy(stream->session);
49        if (stream->rtpsend!=NULL) ms_filter_destroy(stream->rtpsend);
50        if (stream->rtprecv!=NULL) ms_filter_destroy(stream->rtprecv);
51        if (stream->soundread!=NULL) ms_filter_destroy(stream->soundread);
52        if (stream->soundwrite!=NULL) ms_filter_destroy(stream->soundwrite);
53        if (stream->encoder!=NULL) ms_filter_destroy(stream->encoder);
54        if (stream->decoder!=NULL) ms_filter_destroy(stream->decoder);
55        if (stream->dtmfgen!=NULL) ms_filter_destroy(stream->dtmfgen);
56        if (stream->ec!=NULL)   ms_filter_destroy(stream->ec);
57        if (stream->ticker!=NULL) ms_ticker_destroy(stream->ticker);
58        ms_free(stream);
59}
60
61static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};
62
63static void on_dtmf_received(RtpSession *s, int dtmf, void * user_data)
64{
65        AudioStream *stream=(AudioStream*)user_data;
66        if (dtmf>15){
67                ms_warning("Unsupported telephone-event type.");
68                return;
69        }
70        ms_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
71        if (stream->dtmfgen!=NULL && stream->play_dtmfs){
72                ms_filter_call_method(stream->dtmfgen,MS_DTMF_GEN_PUT,&dtmf_tab[dtmf]);
73        }
74}
75
76#if 0
77
78static void on_timestamp_jump(RtpSession *s,uint32_t* ts, void * user_data)
79{
80        ms_warning("The remote sip-phone has send data with a future timestamp: %u,"
81                        "resynchronising session.",*ts);
82        rtp_session_reset(s);
83}
84
85#endif
86
87
88bool_t ms_is_ipv6(const char *remote){
89        bool_t ret=FALSE;
90#ifdef INET6
91        struct addrinfo hints, *res0;
92       
93        int err;
94        memset(&hints, 0, sizeof(hints));
95        hints.ai_family = PF_UNSPEC;
96        hints.ai_socktype = SOCK_DGRAM;
97        err = getaddrinfo(remote,"8000", &hints, &res0);
98        if (err!=0) {
99                ms_warning ("get_local_addr_for: %s", gai_strerror(err));
100                return FALSE;
101        }
102        ret=(res0->ai_addr->sa_family==AF_INET6); 
103        freeaddrinfo(res0);
104#endif
105        return ret;
106}
107
108RtpSession * create_duplex_rtpsession( int locport, bool_t ipv6){
109        RtpSession *rtpr;
110        rtpr=rtp_session_new(RTP_SESSION_SENDRECV);
111        rtp_session_set_recv_buf_size(rtpr,MAX_RTP_SIZE);
112        rtp_session_set_scheduling_mode(rtpr,0);
113        rtp_session_set_blocking_mode(rtpr,0);
114        rtp_session_enable_adaptive_jitter_compensation(rtpr,TRUE);
115        rtp_session_set_symmetric_rtp(rtpr,TRUE);
116        rtp_session_set_local_addr(rtpr,ipv6 ? "::" : "0.0.0.0",locport);
117        rtp_session_signal_connect(rtpr,"timestamp_jump",(RtpCallback)rtp_session_resync,(long)NULL);
118        rtp_session_signal_connect(rtpr,"ssrc_changed",(RtpCallback)rtp_session_resync,(long)NULL);
119        return rtpr;
120}
121
122#if defined(_WIN32_WCE)
123time_t
124time (time_t *t)
125{
126    DWORD timemillis = GetTickCount();
127        if (timemillis>0)
128        {
129                if (t!=NULL)
130                        *t = timemillis/1000;
131        }
132        return timemillis/1000;
133}
134#endif
135
136bool_t audio_stream_alive(AudioStream * stream, int timeout){
137        RtpSession *session=stream->session;
138        const rtp_stats_t *stats=rtp_session_get_stats(session);
139        if (stats->recv!=0){
140                if (stats->recv!=stream->last_packet_count){
141                        stream->last_packet_count=stats->recv;
142                        stream->last_packet_time=time(NULL);
143                }else{
144                        if (time(NULL)-stream->last_packet_time>timeout){
145                                /* more than timeout seconds of inactivity*/
146                                return FALSE;
147                        }
148                }
149        }
150        return TRUE;
151}
152
153/*this function must be called from the MSTicker thread:
154it replaces one filter by another one.
155This is a dirty hack that works anyway.
156It would be interesting to have something that does the job
157simplier within the MSTicker api
158*/
159void audio_stream_change_decoder(AudioStream *stream, int payload){
160        RtpSession *session=stream->session;
161        RtpProfile *prof=rtp_session_get_profile(session);
162        PayloadType *pt=rtp_profile_get_payload(prof,payload);
163        if (pt!=NULL){
164                MSFilter *dec=ms_filter_create_decoder(pt->mime_type);
165                if (dec!=NULL){
166                        ms_filter_unlink(stream->rtprecv, 0, stream->decoder, 0);
167                        ms_filter_unlink(stream->decoder,0,stream->dtmfgen,0);
168                        ms_filter_postprocess(stream->decoder);
169                        ms_filter_destroy(stream->decoder);
170                        stream->decoder=dec;
171                        if (pt->recv_fmtp!=NULL)
172                                ms_filter_call_method(stream->decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);
173                        ms_filter_link (stream->rtprecv, 0, stream->decoder, 0);
174                        ms_filter_link (stream->decoder,0 , stream->dtmfgen, 0);
175                        ms_filter_preprocess(stream->decoder,stream->ticker);
176                       
177                }else{
178                        ms_warning("No decoder found for %s",pt->mime_type);
179                }
180        }else{
181                ms_warning("No payload defined with number %i",payload);
182        }
183}
184
185static void payload_type_changed(RtpSession *session, unsigned long data){
186        AudioStream *stream=(AudioStream*)data;
187        int pt=rtp_session_get_recv_payload_type(stream->session);
188        audio_stream_change_decoder(stream,pt);
189}
190
191
192int audio_stream_start_full(AudioStream *stream, RtpProfile *profile, const char *remip,int remport,
193        int rem_rtcp_port, int payload,int jitt_comp, const char *infile, const char *outfile,
194        MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec)
195{
196        RtpSession *rtps=stream->session;
197        PayloadType *pt;
198        int tmp;       
199
200        rtp_session_set_profile(rtps,profile);
201        if (remport>0) rtp_session_set_remote_addr_full(rtps,remip,remport,rem_rtcp_port);
202        rtp_session_set_payload_type(rtps,payload);
203        rtp_session_set_jitter_compensation(rtps,jitt_comp);
204       
205        if (remport>0)
206                ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_SET_SESSION,rtps);
207        stream->rtprecv=ms_filter_new(MS_RTP_RECV_ID);
208        ms_filter_call_method(stream->rtprecv,MS_RTP_RECV_SET_SESSION,rtps);
209        stream->session=rtps;
210       
211        stream->dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
212        rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,(unsigned long)stream);
213        rtp_session_signal_connect(rtps,"payload_type_changed",(RtpCallback)payload_type_changed,(unsigned long)stream);
214       
215        /* creates the local part */
216        if (captcard!=NULL) stream->soundread=ms_snd_card_create_reader(captcard);
217        else {
218                stream->soundread=ms_filter_new(MS_FILE_PLAYER_ID);
219                if (infile!=NULL) audio_stream_play(stream,infile);
220        }
221        if (playcard!=NULL) stream->soundwrite=ms_snd_card_create_writer(playcard);
222        else {
223                stream->soundwrite=ms_filter_new(MS_FILE_REC_ID);
224                if (outfile!=NULL) audio_stream_record(stream,outfile);
225        }
226       
227        /* creates the couple of encoder/decoder */
228        pt=rtp_profile_get_payload(profile,payload);
229        if (pt==NULL){
230                ms_error("audiostream.c: undefined payload type.");
231                return -1;
232        }
233        stream->encoder=ms_filter_create_encoder(pt->mime_type);
234        stream->decoder=ms_filter_create_decoder(pt->mime_type);
235        if ((stream->encoder==NULL) || (stream->decoder==NULL)){
236                /* big problem: we have not a registered codec for this payload...*/
237                ms_error("mediastream.c: No decoder available for payload %i.",payload);
238                return -1;
239        }
240       
241        if (use_ec) {
242                stream->ec=ms_filter_new(MS_SPEEX_EC_ID);
243                ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
244        }       
245
246        /* give the sound filters some properties */
247        ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
248        ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
249        tmp=1;
250        ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS, &tmp);
251       
252        /* give the encoder/decoder some parameters*/
253        ms_filter_call_method(stream->encoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
254        ms_message("Payload's bitrate is %i",pt->normal_bitrate);
255        if (pt->normal_bitrate>0){
256                ms_message("Setting audio encoder network bitrate to %i",pt->normal_bitrate);
257                ms_filter_call_method(stream->encoder,MS_FILTER_SET_BITRATE,&pt->normal_bitrate);
258        }
259        ms_filter_call_method(stream->decoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
260       
261        if (pt->send_fmtp!=NULL) ms_filter_call_method(stream->encoder,MS_FILTER_ADD_FMTP, (void*)pt->send_fmtp);
262        if (pt->recv_fmtp!=NULL) ms_filter_call_method(stream->decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);
263       
264        /* and then connect all */
265        /* tip: draw yourself the picture if you don't understand */
266        if (stream->ec){
267                ms_filter_link(stream->soundread,0,stream->ec,1);
268                ms_filter_link(stream->ec,1,stream->encoder,0);
269                ms_filter_link(stream->dtmfgen,0,stream->ec,0);
270                ms_filter_link(stream->ec,0,stream->soundwrite,0);
271        }else{
272                ms_filter_link(stream->soundread,0,stream->encoder,0);
273                ms_filter_link(stream->dtmfgen,0,stream->soundwrite,0);
274        }
275       
276        ms_filter_link(stream->encoder,0,stream->rtpsend,0);
277        ms_filter_link(stream->rtprecv,0,stream->decoder,0);
278        ms_filter_link(stream->decoder,0,stream->dtmfgen,0);
279       
280        /* create ticker */
281        stream->ticker=ms_ticker_new();
282
283        ms_ticker_attach(stream->ticker,stream->soundread);
284        ms_ticker_attach(stream->ticker,stream->rtprecv);
285       
286        return 0;
287}
288
289
290int audio_stream_start_with_files(AudioStream *stream, RtpProfile *prof,const char *remip, int remport,
291        int rem_rtcp_port, int pt,int jitt_comp, const char *infile, const char * outfile)
292{
293        return audio_stream_start_full(stream,prof,remip,remport,rem_rtcp_port,pt,jitt_comp,infile,outfile,NULL,NULL,FALSE);
294}
295
296AudioStream * audio_stream_start(RtpProfile *prof,int locport,const char *remip,int remport,int profile,int jitt_comp,bool_t use_ec)
297{
298        MSSndCard *sndcard;
299        AudioStream *stream;
300        sndcard=ms_snd_card_manager_get_default_card(ms_snd_card_manager_get());
301        if (sndcard==NULL)
302                return NULL;
303        stream=audio_stream_new(locport, ms_is_ipv6(remip));
304        if (audio_stream_start_full(stream,prof,remip,remport,remport+1,profile,jitt_comp,NULL,NULL,sndcard,sndcard,use_ec)==0) return stream;
305        audio_stream_free(stream);
306        return NULL;
307}
308
309AudioStream *audio_stream_start_with_sndcards(RtpProfile *prof,int locport,const char *remip,int remport,int profile,int jitt_comp,MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec)
310{
311        AudioStream *stream;
312        if (playcard==NULL) {
313                ms_error("No playback card.");
314                return NULL;
315        }
316        if (captcard==NULL) {
317                ms_error("No capture card.");
318                return NULL;
319        }
320        stream=audio_stream_new(locport, ms_is_ipv6(remip));
321        if (audio_stream_start_full(stream,prof,remip,remport,remport+1,profile,jitt_comp,NULL,NULL,playcard,captcard,use_ec)==0) return stream;
322        audio_stream_free(stream);
323        return NULL;
324}
325
326void audio_stream_set_rtcp_information(AudioStream *st, const char *cname, const char *tool){
327        if (st->session!=NULL){
328                rtp_session_set_source_description(st->session,cname,NULL,NULL,NULL,NULL,tool , "This is free software (GPL) !");
329        }
330}
331
332void audio_stream_play(AudioStream *st, const char *name){
333        if (ms_filter_get_id(st->soundread)==MS_FILE_PLAYER_ID){
334                ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_CLOSE);
335                ms_filter_call_method(st->soundread,MS_FILE_PLAYER_OPEN,(void*)name);
336                ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_START);
337        }else{
338                ms_error("Cannot play file: the stream hasn't been started with"
339                " audio_stream_start_with_files");
340        }
341}
342
343void audio_stream_record(AudioStream *st, const char *name){
344        if (ms_filter_get_id(st->soundwrite)==MS_FILE_REC_ID){
345                ms_filter_call_method_noarg(st->soundwrite,MS_FILE_REC_CLOSE);
346                ms_filter_call_method(st->soundwrite,MS_FILE_REC_OPEN,(void*)name);
347                ms_filter_call_method_noarg(st->soundwrite,MS_FILE_REC_START);
348        }else{
349                ms_error("Cannot record file: the stream hasn't been started with"
350                " audio_stream_start_with_files");
351        }
352}
353
354
355AudioStream *audio_stream_new(int locport, bool_t ipv6){
356        AudioStream *stream=(AudioStream *)ms_new0(AudioStream,1);
357        stream->session=create_duplex_rtpsession(locport,ipv6);
358        stream->rtpsend=ms_filter_new(MS_RTP_SEND_ID);
359        stream->play_dtmfs=TRUE;
360        return stream;
361}
362
363void audio_stream_play_received_dtmfs(AudioStream *st, bool_t yesno){
364        st->play_dtmfs=yesno;
365}
366
367int audio_stream_start_now(AudioStream *stream, RtpProfile * prof,  const char *remip, int remport, int rem_rtcp_port, int payload_type, int jitt_comp, MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec){
368        return audio_stream_start_full(stream,prof,remip,remport,rem_rtcp_port,
369                payload_type,jitt_comp,NULL,NULL,playcard,captcard,use_ec);
370}
371
372void audio_stream_set_relay_session_id(AudioStream *stream, const char *id){
373        ms_filter_call_method(stream->rtpsend, MS_RTP_SEND_SET_RELAY_SESSION_ID,(void*)id);
374}
375
376void audio_stream_stop(AudioStream * stream)
377{
378        if (stream->ticker){
379                ms_ticker_detach(stream->ticker,stream->soundread);
380                ms_ticker_detach(stream->ticker,stream->rtprecv);
381               
382                rtp_stats_display(rtp_session_get_stats(stream->session),"Audio session's RTP statistics");
383               
384                if (stream->ec!=NULL){
385                        ms_filter_unlink(stream->soundread,0,stream->ec,1);
386                        ms_filter_unlink(stream->ec,1,stream->encoder,0);
387                        ms_filter_unlink(stream->dtmfgen,0,stream->ec,0);
388                        ms_filter_unlink(stream->ec,0,stream->soundwrite,0);
389                }else{
390                        ms_filter_unlink(stream->soundread,0,stream->encoder,0);
391                        ms_filter_unlink(stream->dtmfgen,0,stream->soundwrite,0);
392                }
393               
394                ms_filter_unlink(stream->encoder,0,stream->rtpsend,0);
395                ms_filter_unlink(stream->rtprecv,0,stream->decoder,0);
396                ms_filter_unlink(stream->decoder,0,stream->dtmfgen,0);
397        }
398        audio_stream_free(stream);
399}
400
401RingStream * ring_start(const char *file, int interval, MSSndCard *sndcard){
402   return ring_start_with_cb(file,interval,sndcard,NULL,NULL);
403}
404
405RingStream * ring_start_with_cb(const char *file,int interval,MSSndCard *sndcard, MSFilterNotifyFunc func,void * user_data)
406{
407        RingStream *stream;
408        int tmp;
409        stream=(RingStream *)ms_new0(RingStream,1);
410        stream->source=ms_filter_new(MS_FILE_PLAYER_ID);
411        if (ms_filter_call_method(stream->source,MS_FILE_PLAYER_OPEN,(void*)file)<0){
412                ms_filter_destroy(stream->source);
413                ms_free(stream);
414                return NULL;
415        }
416        ms_filter_call_method(stream->source,MS_FILE_PLAYER_LOOP,&interval);
417        ms_filter_call_method_noarg(stream->source,MS_FILE_PLAYER_START);
418        if (func!=NULL)
419                ms_filter_set_notify_callback(stream->source,func,user_data);
420        stream->sndwrite=ms_snd_card_create_writer(sndcard);
421        ms_filter_call_method(stream->source,MS_FILTER_GET_SAMPLE_RATE,&tmp);
422        ms_filter_call_method(stream->sndwrite,MS_FILTER_SET_SAMPLE_RATE,&tmp);
423        ms_filter_call_method(stream->source,MS_FILTER_GET_NCHANNELS,&tmp);
424        ms_filter_call_method(stream->sndwrite,MS_FILTER_SET_NCHANNELS,&tmp);
425        stream->ticker=ms_ticker_new();
426        ms_filter_link(stream->source,0,stream->sndwrite,0);
427        ms_ticker_attach(stream->ticker,stream->source);
428        return stream;
429}
430
431void ring_stop(RingStream *stream){
432        ms_ticker_detach(stream->ticker,stream->source);
433        ms_filter_unlink(stream->source,0,stream->sndwrite,0);
434        ms_ticker_destroy(stream->ticker);
435        ms_filter_destroy(stream->source);
436        ms_filter_destroy(stream->sndwrite);
437        ms_free(stream);
438}
439
440
441int audio_stream_send_dtmf(AudioStream *stream, char dtmf)
442{
443        if (stream->rtpsend)
444                ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_SEND_DTMF,&dtmf);
445        if (stream->dtmfgen)
446                ms_filter_call_method(stream->dtmfgen,MS_DTMF_GEN_PUT,&dtmf);
447        return 0;
448}
Note: See TracBrowser for help on using the repository browser.