12024
|
1 /*
|
|
2 The mediastreamer library aims at providing modular media processing and I/O
|
|
3 for linphone, but also for any telephony application.
|
|
4 Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
|
|
5
|
|
6 This library is free software; you can redistribute it and/or
|
|
7 modify it under the terms of the GNU Lesser General Public
|
|
8 License as published by the Free Software Foundation; either
|
|
9 version 2.1 of the License, or (at your option) any later version.
|
|
10
|
|
11 This library is distributed in the hope that it will be useful,
|
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
14 Lesser General Public License for more details.
|
|
15
|
|
16 You should have received a copy of the GNU Lesser General Public
|
|
17 License along with this library; if not, write to the Free Software
|
|
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
19 */
|
|
20
|
|
21
|
|
22 #include "mediastream.h"
|
|
23 #ifdef INET6
|
|
24 #include <sys/types.h>
|
|
25 #include <sys/socket.h>
|
|
26 #include <netdb.h>
|
|
27 #endif
|
|
28
|
|
29
|
|
30 #define MAX_RTP_SIZE 1500
|
|
31
|
|
32 /* this code is not part of the library itself, it is part of the mediastream program */
|
|
33 void audio_stream_free(AudioStream *stream)
|
|
34 {
|
|
35 RtpSession *s;
|
|
36 RtpSession *destroyed=NULL;
|
|
37 if (stream->rtprecv!=NULL) {
|
|
38 s=ms_rtp_recv_get_session(MS_RTP_RECV(stream->rtprecv));
|
|
39 if (s!=NULL){
|
|
40 destroyed=s;
|
|
41 rtp_session_destroy(s);
|
|
42 }
|
|
43 ms_filter_destroy(stream->rtprecv);
|
|
44 }
|
|
45 if (stream->rtpsend!=NULL) {
|
|
46 s=ms_rtp_send_get_session(MS_RTP_SEND(stream->rtpsend));
|
|
47 if (s!=NULL){
|
|
48 if (s!=destroyed)
|
|
49 rtp_session_destroy(s);
|
|
50 }
|
|
51 ms_filter_destroy(stream->rtpsend);
|
|
52 }
|
|
53 if (stream->soundread!=NULL) ms_filter_destroy(stream->soundread);
|
|
54 if (stream->soundwrite!=NULL) ms_filter_destroy(stream->soundwrite);
|
|
55 if (stream->encoder!=NULL) ms_filter_destroy(stream->encoder);
|
|
56 if (stream->decoder!=NULL) ms_filter_destroy(stream->decoder);
|
|
57 if (stream->timer!=NULL) ms_sync_destroy(stream->timer);
|
|
58 g_free(stream);
|
|
59 }
|
|
60
|
|
61 static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};
|
|
62
|
|
63 static void on_dtmf_received(RtpSession *s,gint dtmf,gpointer user_data)
|
|
64 {
|
|
65 AudioStream *stream=(AudioStream*)user_data;
|
|
66 if (dtmf>15){
|
|
67 g_warning("Unsupported telephone-event type.");
|
|
68 return;
|
|
69 }
|
|
70 g_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
|
|
71 if (stream!=NULL){
|
|
72 if (strcmp(stream->soundwrite->klass->name,"OssWrite")==0)
|
|
73 ms_oss_write_play_dtmf(MS_OSS_WRITE(stream->soundwrite),dtmf_tab[dtmf]);
|
|
74 }
|
|
75 }
|
|
76
|
|
77 static void on_timestamp_jump(RtpSession *s,guint32* ts, gpointer user_data)
|
|
78 {
|
|
79 g_warning("The remote sip-phone has send data with a future timestamp: %u,"
|
|
80 "resynchronising session.",*ts);
|
|
81 rtp_session_reset(s);
|
|
82 }
|
|
83
|
|
84 static const char *ip4local="0.0.0.0";
|
|
85 static const char *ip6local="::";
|
|
86
|
|
87 const char *get_local_addr_for(const char *remote)
|
|
88 {
|
|
89 const char *ret;
|
|
90 #ifdef INET6
|
|
91 char num[8];
|
|
92 struct addrinfo hints, *res0;
|
|
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 g_warning ("get_local_addr_for: %s", gai_strerror(err));
|
|
100 return ip4local;
|
|
101 }
|
|
102 ret=(res0->ai_addr->sa_family==AF_INET6) ? ip6local : ip4local;
|
|
103 freeaddrinfo(res0);
|
|
104 #else
|
|
105 ret=ip4local;
|
|
106 #endif
|
|
107 return ret;
|
|
108 }
|
|
109
|
|
110 void create_duplex_rtpsession(RtpProfile *profile, int locport,char *remip,int remport,
|
|
111 int payload,int jitt_comp,
|
|
112 RtpSession **recvsend){
|
|
113 RtpSession *rtpr;
|
|
114 rtpr=rtp_session_new(RTP_SESSION_SENDRECV);
|
|
115 rtp_session_max_buf_size_set(rtpr,MAX_RTP_SIZE);
|
|
116 rtp_session_set_profile(rtpr,profile);
|
|
117 rtp_session_set_local_addr(rtpr,get_local_addr_for(remip),locport);
|
|
118 if (remport>0) rtp_session_set_remote_addr(rtpr,remip,remport);
|
|
119 rtp_session_set_scheduling_mode(rtpr,0);
|
|
120 rtp_session_set_blocking_mode(rtpr,0);
|
|
121 rtp_session_set_payload_type(rtpr,payload);
|
|
122 rtp_session_set_jitter_compensation(rtpr,jitt_comp);
|
|
123 rtp_session_enable_adaptive_jitter_compensation(rtpr,TRUE);
|
|
124 /*rtp_session_signal_connect(rtpr,"timestamp_jump",(RtpCallback)on_timestamp_jump,NULL);*/
|
|
125 *recvsend=rtpr;
|
|
126 }
|
|
127
|
|
128 void create_rtp_sessions(RtpProfile *profile, int locport,char *remip,int remport,
|
|
129 int payload,int jitt_comp,
|
|
130 RtpSession **recv, RtpSession **send){
|
|
131 RtpSession *rtps,*rtpr;
|
|
132 PayloadType *pt;
|
|
133 /* creates two rtp filters to recv send streams (remote part)*/
|
|
134
|
|
135 rtps=rtp_session_new(RTP_SESSION_SENDONLY);
|
|
136 rtp_session_max_buf_size_set(rtps,MAX_RTP_SIZE);
|
|
137 rtp_session_set_profile(rtps,profile);
|
|
138 #ifdef INET6
|
|
139 rtp_session_set_local_addr(rtps,"::",locport+2);
|
|
140 #else
|
|
141 rtp_session_set_local_addr(rtps,"0.0.0.0",locport+2);
|
|
142 #endif
|
|
143 rtp_session_set_remote_addr(rtps,remip,remport);
|
|
144 rtp_session_set_scheduling_mode(rtps,0);
|
|
145 rtp_session_set_blocking_mode(rtps,0);
|
|
146 rtp_session_set_payload_type(rtps,payload);
|
|
147 rtp_session_set_jitter_compensation(rtps,jitt_comp);
|
|
148
|
|
149 rtpr=rtp_session_new(RTP_SESSION_RECVONLY);
|
|
150 rtp_session_max_buf_size_set(rtpr,MAX_RTP_SIZE);
|
|
151 rtp_session_set_profile(rtpr,profile);
|
|
152 #ifdef INET6
|
|
153 rtp_session_set_local_addr(rtpr,"::",locport);
|
|
154 #else
|
|
155 rtp_session_set_local_addr(rtpr,"0.0.0.0",locport);
|
|
156 #endif
|
|
157 rtp_session_set_scheduling_mode(rtpr,0);
|
|
158 rtp_session_set_blocking_mode(rtpr,0);
|
|
159 rtp_session_set_payload_type(rtpr,payload);
|
|
160 rtp_session_set_jitter_compensation(rtpr,jitt_comp);
|
|
161 rtp_session_signal_connect(rtpr,"telephone-event",(RtpCallback)on_dtmf_received,NULL);
|
|
162 rtp_session_signal_connect(rtpr,"timestamp_jump",(RtpCallback)on_timestamp_jump,NULL);
|
|
163 *recv=rtpr;
|
|
164 *send=rtps;
|
|
165
|
|
166 }
|
|
167
|
|
168
|
|
169 AudioStream * audio_stream_start_full(RtpProfile *profile, int locport,char *remip,int remport,
|
|
170 int payload,int jitt_comp, gchar *infile, gchar *outfile, SndCard *playcard, SndCard *captcard)
|
|
171 {
|
|
172 AudioStream *stream=g_new0(AudioStream,1);
|
|
173 RtpSession *rtps,*rtpr;
|
|
174 PayloadType *pt;
|
|
175
|
|
176 //create_rtp_sessions(profile,locport,remip,remport,payload,jitt_comp,&rtpr,&rtps);
|
|
177
|
|
178 create_duplex_rtpsession(profile,locport,remip,remport,payload,jitt_comp,&rtpr);
|
|
179 rtp_session_signal_connect(rtpr,"telephone-event",(RtpCallback)on_dtmf_received,(gpointer)stream);
|
|
180 rtps=rtpr;
|
|
181
|
|
182 stream->rtpsend=ms_rtp_send_new();
|
|
183 ms_rtp_send_set_session(MS_RTP_SEND(stream->rtpsend),rtps);
|
|
184 stream->rtprecv=ms_rtp_recv_new();
|
|
185 ms_rtp_recv_set_session(MS_RTP_RECV(stream->rtprecv),rtpr);
|
|
186
|
|
187
|
|
188 /* creates the local part */
|
|
189 if (infile==NULL) stream->soundread=snd_card_create_read_filter(captcard);
|
|
190 else stream->soundread=ms_read_new(infile);
|
|
191 if (outfile==NULL) stream->soundwrite=snd_card_create_write_filter(playcard);
|
|
192 else stream->soundwrite=ms_write_new(outfile);
|
|
193
|
|
194 /* creates the couple of encoder/decoder */
|
|
195 pt=rtp_profile_get_payload(profile,payload);
|
|
196 if (pt==NULL){
|
|
197 g_error("audiostream.c: undefined payload type.");
|
|
198 return NULL;
|
|
199 }
|
|
200 stream->encoder=ms_encoder_new_with_string_id(pt->mime_type);
|
|
201 stream->decoder=ms_decoder_new_with_string_id(pt->mime_type);
|
|
202 if ((stream->encoder==NULL) || (stream->decoder==NULL)){
|
|
203 /* big problem: we have not a registered codec for this payload...*/
|
|
204 audio_stream_free(stream);
|
|
205 g_error("mediastream.c: No decoder available for payload %i.",payload);
|
|
206 return NULL;
|
|
207 }
|
|
208 /* give the sound filters some properties */
|
|
209 ms_filter_set_property(stream->soundread,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate);
|
|
210 ms_filter_set_property(stream->soundwrite,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate);
|
|
211
|
|
212 /* give the encoder/decoder some parameters*/
|
|
213 ms_filter_set_property(stream->encoder,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate);
|
|
214 ms_filter_set_property(stream->encoder,MS_FILTER_PROPERTY_BITRATE,&pt->normal_bitrate);
|
|
215 ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate);
|
|
216 ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_BITRATE,&pt->normal_bitrate);
|
|
217
|
|
218 ms_filter_set_property(stream->encoder,MS_FILTER_PROPERTY_FMTP, (void*)pt->fmtp);
|
|
219 ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_FMTP,(void*)pt->fmtp);
|
|
220 /* create the synchronisation source */
|
|
221 stream->timer=ms_timer_new();
|
|
222
|
|
223 /* and then connect all */
|
|
224 ms_filter_add_link(stream->soundread,stream->encoder);
|
|
225 ms_filter_add_link(stream->encoder,stream->rtpsend);
|
|
226 ms_filter_add_link(stream->rtprecv,stream->decoder);
|
|
227 ms_filter_add_link(stream->decoder,stream->soundwrite);
|
|
228
|
|
229 ms_sync_attach(stream->timer,stream->soundread);
|
|
230 ms_sync_attach(stream->timer,stream->rtprecv);
|
|
231
|
|
232 /* and start */
|
|
233 ms_start(stream->timer);
|
|
234
|
|
235 return stream;
|
|
236 }
|
|
237
|
|
238 static int defcard=0;
|
|
239
|
|
240 void audio_stream_set_default_card(int cardindex){
|
|
241 defcard=cardindex;
|
|
242 }
|
|
243
|
|
244 AudioStream * audio_stream_start_with_files(RtpProfile *prof,int locport,char *remip,
|
|
245 int remport,int profile,int jitt_comp,gchar *infile, gchar*outfile)
|
|
246 {
|
|
247 return audio_stream_start_full(prof,locport,remip,remport,profile,jitt_comp,infile,outfile,NULL,NULL);
|
|
248 }
|
|
249
|
|
250 AudioStream * audio_stream_start(RtpProfile *prof,int locport,char *remip,int remport,int profile,int jitt_comp)
|
|
251 {
|
|
252 SndCard *sndcard;
|
|
253 sndcard=snd_card_manager_get_card(snd_card_manager,defcard);
|
|
254 return audio_stream_start_full(prof,locport,remip,remport,profile,jitt_comp,NULL,NULL,sndcard,sndcard);
|
|
255 }
|
|
256
|
|
257 AudioStream *audio_stream_start_with_sndcards(RtpProfile *prof,int locport,char *remip,int remport,int profile,int jitt_comp,SndCard *playcard, SndCard *captcard)
|
|
258 {
|
|
259 g_return_val_if_fail(playcard!=NULL,NULL);
|
|
260 g_return_val_if_fail(captcard!=NULL,NULL);
|
|
261 return audio_stream_start_full(prof,locport,remip,remport,profile,jitt_comp,NULL,NULL,playcard,captcard);
|
|
262 }
|
|
263
|
|
264 void audio_stream_set_rtcp_information(AudioStream *st, const char *cname){
|
|
265 if (st->send_session!=NULL){
|
|
266 rtp_session_set_source_description(st->send_session,cname,NULL,NULL,NULL,NULL,"linphone-" "2.0.0", // SME
|
|
267 "This is free software (GPL) !");
|
|
268 }
|
|
269 }
|
|
270
|
|
271 void audio_stream_stop(AudioStream * stream)
|
|
272 {
|
|
273
|
|
274 ms_stop(stream->timer);
|
|
275 ortp_global_stats_display();
|
|
276 ms_sync_detach(stream->timer,stream->soundread);
|
|
277 ms_sync_detach(stream->timer,stream->rtprecv);
|
|
278
|
|
279 ms_filter_remove_links(stream->soundread,stream->encoder);
|
|
280 ms_filter_remove_links(stream->encoder,stream->rtpsend);
|
|
281 ms_filter_remove_links(stream->rtprecv,stream->decoder);
|
|
282 ms_filter_remove_links(stream->decoder,stream->soundwrite);
|
|
283
|
|
284 audio_stream_free(stream);
|
|
285 }
|
|
286
|
|
287 RingStream * ring_start(gchar *file,gint interval,SndCard *sndcard)
|
|
288 {
|
|
289 return ring_start_with_cb(file,interval,sndcard,NULL,NULL);
|
|
290 }
|
|
291
|
|
292 RingStream * ring_start_with_cb(gchar *file,gint interval,SndCard *sndcard, MSFilterNotifyFunc func,gpointer user_data)
|
|
293 {
|
|
294 RingStream *stream;
|
|
295 int tmp;
|
|
296 g_return_val_if_fail(sndcard!=NULL,NULL);
|
|
297 stream=g_new0(RingStream,1);
|
|
298 stream->source=ms_ring_player_new(file,interval);
|
|
299 if (stream->source==NULL) {
|
|
300 g_warning("Could not create ring player. Probably the ring file (%s) does not exist.",file);
|
|
301 return NULL;
|
|
302 }
|
|
303 if (func!=NULL) ms_filter_set_notify_func(MS_FILTER(stream->source),func,user_data);
|
|
304 stream->sndwrite=snd_card_create_write_filter(sndcard);
|
|
305 ms_filter_get_property(stream->source,MS_FILTER_PROPERTY_FREQ,&tmp);
|
|
306 ms_filter_set_property(stream->sndwrite,MS_FILTER_PROPERTY_FREQ,&tmp);
|
|
307 ms_filter_get_property(stream->source,MS_FILTER_PROPERTY_CHANNELS,&tmp);
|
|
308 ms_filter_set_property(stream->sndwrite,MS_FILTER_PROPERTY_CHANNELS,&tmp);
|
|
309 stream->timer=ms_timer_new();
|
|
310 ms_filter_add_link(stream->source,stream->sndwrite);
|
|
311 ms_sync_attach(stream->timer,stream->source);
|
|
312 ms_start(stream->timer);
|
|
313 return stream;
|
|
314 }
|
|
315
|
|
316 void ring_stop(RingStream *stream)
|
|
317 {
|
|
318 ms_stop(stream->timer);
|
|
319 ms_sync_detach(stream->timer,stream->source);
|
|
320 ms_sync_destroy(stream->timer);
|
|
321 ms_filter_remove_links(stream->source,stream->sndwrite);
|
|
322 ms_filter_destroy(stream->source);
|
|
323 ms_filter_destroy(stream->sndwrite);
|
|
324 g_free(stream);
|
|
325 }
|
|
326
|
|
327 /* returns the latency in samples if the audio device with id dev_id is openable in full duplex mode, else 0 */
|
|
328 gint test_audio_dev(int dev_id)
|
|
329 {
|
|
330 gint err;
|
|
331 SndCard *sndcard=snd_card_manager_get_card(snd_card_manager,dev_id);
|
|
332 if (sndcard==NULL) return -1;
|
|
333 err=snd_card_probe(sndcard,16,0,8000);
|
|
334 return err; /* return latency in number of sample */
|
|
335 }
|
|
336
|
|
337 gint audio_stream_send_dtmf(AudioStream *stream, gchar dtmf)
|
|
338 {
|
|
339 ms_rtp_send_dtmf(MS_RTP_SEND(stream->rtpsend), dtmf);
|
|
340 ms_oss_write_play_dtmf(MS_OSS_WRITE(stream->soundwrite),dtmf);
|
|
341 }
|