2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include "mediastreamer-config.h"
25 #include "mediastreamer2/mediastream.h"
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 #include "mediastreamer2/msvolume.h"
33 #include "mediastreamer2/msequalizer.h"
34 #include "mediastreamer2/msspeexec.h"
37 #include <sys/types.h>
39 #include <sys/socket.h>
45 #define MAX_RTP_SIZE 1500
48 /* this code is not part of the library itself, it is part of the mediastream program */
49 void audio_stream_free(AudioStream *stream)
51 if (stream->session!=NULL) rtp_session_destroy(stream->session);
52 if (stream->rtpsend!=NULL) ms_filter_destroy(stream->rtpsend);
53 if (stream->rtprecv!=NULL) ms_filter_destroy(stream->rtprecv);
54 if (stream->soundread!=NULL) ms_filter_destroy(stream->soundread);
55 if (stream->soundwrite!=NULL) ms_filter_destroy(stream->soundwrite);
56 if (stream->encoder!=NULL) ms_filter_destroy(stream->encoder);
57 if (stream->decoder!=NULL) ms_filter_destroy(stream->decoder);
58 if (stream->dtmfgen!=NULL) ms_filter_destroy(stream->dtmfgen);
59 if (stream->ec!=NULL) ms_filter_destroy(stream->ec);
60 if (stream->volrecv!=NULL) ms_filter_destroy(stream->volrecv);
61 if (stream->volsend!=NULL) ms_filter_destroy(stream->volsend);
62 if (stream->equalizer!=NULL) ms_filter_destroy(stream->equalizer);
63 if (stream->ticker!=NULL) ms_ticker_destroy(stream->ticker);
64 if (stream->read_resampler!=NULL) ms_filter_destroy(stream->read_resampler);
65 if (stream->write_resampler!=NULL) ms_filter_destroy(stream->write_resampler);
69 static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};
71 static void on_dtmf_received(RtpSession *s, int dtmf, void * user_data)
73 AudioStream *stream=(AudioStream*)user_data;
75 ms_warning("Unsupported telephone-event type.");
78 ms_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
79 if (stream->dtmfgen!=NULL && stream->play_dtmfs){
80 ms_filter_call_method(stream->dtmfgen,MS_DTMF_GEN_PUT,&dtmf_tab[dtmf]);
86 static void on_timestamp_jump(RtpSession *s,uint32_t* ts, void * user_data)
88 ms_warning("The remote sip-phone has send data with a future timestamp: %u,"
89 "resynchronising session.",*ts);
96 bool_t ms_is_ipv6(const char *remote){
99 struct addrinfo hints, *res0;
102 memset(&hints, 0, sizeof(hints));
103 hints.ai_family = PF_UNSPEC;
104 hints.ai_socktype = SOCK_DGRAM;
105 err = getaddrinfo(remote,"8000", &hints, &res0);
107 ms_warning ("get_local_addr_for: %s", gai_strerror(err));
110 ret=(res0->ai_addr->sa_family==AF_INET6);
116 static void audio_stream_configure_resampler(MSFilter *resampler,MSFilter *from,MSFilter *to) {
117 int from_rate=0, to_rate=0;
118 ms_filter_call_method(from,MS_FILTER_GET_SAMPLE_RATE,&from_rate);
119 ms_filter_call_method(to,MS_FILTER_GET_SAMPLE_RATE,&to_rate);
120 ms_filter_call_method(resampler,MS_FILTER_SET_SAMPLE_RATE,&from_rate);
121 ms_filter_call_method(resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&to_rate);
122 ms_debug("configuring from rate[%i] to rate [%i]",from_rate,to_rate);
125 RtpSession * create_duplex_rtpsession( int locport, bool_t ipv6){
127 rtpr=rtp_session_new(RTP_SESSION_SENDRECV);
128 rtp_session_set_recv_buf_size(rtpr,MAX_RTP_SIZE);
129 rtp_session_set_scheduling_mode(rtpr,0);
130 rtp_session_set_blocking_mode(rtpr,0);
131 rtp_session_enable_adaptive_jitter_compensation(rtpr,TRUE);
132 rtp_session_set_symmetric_rtp(rtpr,TRUE);
133 rtp_session_set_local_addr(rtpr,ipv6 ? "::" : "0.0.0.0",locport);
134 rtp_session_signal_connect(rtpr,"timestamp_jump",(RtpCallback)rtp_session_resync,(long)NULL);
135 rtp_session_signal_connect(rtpr,"ssrc_changed",(RtpCallback)rtp_session_resync,(long)NULL);
139 #if defined(_WIN32_WCE)
143 DWORD timemillis = GetTickCount();
147 *t = timemillis/1000;
149 return timemillis/1000;
153 bool_t audio_stream_alive(AudioStream * stream, int timeout){
154 RtpSession *session=stream->session;
155 const rtp_stats_t *stats=rtp_session_get_stats(session);
157 if (stats->recv!=stream->last_packet_count){
158 stream->last_packet_count=stats->recv;
159 stream->last_packet_time=ms_time(NULL);
161 if (ms_time(NULL)-stream->last_packet_time>timeout){
162 /* more than timeout seconds of inactivity*/
170 /*this function must be called from the MSTicker thread:
171 it replaces one filter by another one.
172 This is a dirty hack that works anyway.
173 It would be interesting to have something that does the job
174 simplier within the MSTicker api
176 void audio_stream_change_decoder(AudioStream *stream, int payload){
177 RtpSession *session=stream->session;
178 RtpProfile *prof=rtp_session_get_profile(session);
179 PayloadType *pt=rtp_profile_get_payload(prof,payload);
181 MSFilter *dec=ms_filter_create_decoder(pt->mime_type);
183 ms_filter_unlink(stream->rtprecv, 0, stream->decoder, 0);
184 ms_filter_unlink(stream->decoder,0,stream->dtmfgen,0);
185 ms_filter_postprocess(stream->decoder);
186 ms_filter_destroy(stream->decoder);
188 if (pt->recv_fmtp!=NULL)
189 ms_filter_call_method(stream->decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);
190 ms_filter_link (stream->rtprecv, 0, stream->decoder, 0);
191 ms_filter_link (stream->decoder,0 , stream->dtmfgen, 0);
192 ms_filter_preprocess(stream->decoder,stream->ticker);
195 ms_warning("No decoder found for %s",pt->mime_type);
198 ms_warning("No payload defined with number %i",payload);
202 static void payload_type_changed(RtpSession *session, unsigned long data){
203 AudioStream *stream=(AudioStream*)data;
204 int pt=rtp_session_get_recv_payload_type(stream->session);
205 audio_stream_change_decoder(stream,pt);
209 int audio_stream_start_full(AudioStream *stream, RtpProfile *profile, const char *remip,int remport,
210 int rem_rtcp_port, int payload,int jitt_comp, const char *infile, const char *outfile,
211 MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec)
213 RtpSession *rtps=stream->session;
216 MSConnectionHelper h;
218 rtp_session_set_profile(rtps,profile);
219 if (remport>0) rtp_session_set_remote_addr_full(rtps,remip,remport,rem_rtcp_port);
220 rtp_session_set_payload_type(rtps,payload);
221 rtp_session_set_jitter_compensation(rtps,jitt_comp);
224 ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_SET_SESSION,rtps);
225 stream->rtprecv=ms_filter_new(MS_RTP_RECV_ID);
226 ms_filter_call_method(stream->rtprecv,MS_RTP_RECV_SET_SESSION,rtps);
227 stream->session=rtps;
229 stream->dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
230 rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,(unsigned long)stream);
231 rtp_session_signal_connect(rtps,"payload_type_changed",(RtpCallback)payload_type_changed,(unsigned long)stream);
233 /* creates the local part */
234 if (captcard!=NULL) stream->soundread=ms_snd_card_create_reader(captcard);
236 stream->soundread=ms_filter_new(MS_FILE_PLAYER_ID);
237 stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
238 if (infile!=NULL) audio_stream_play(stream,infile);
240 if (playcard!=NULL) stream->soundwrite=ms_snd_card_create_writer(playcard);
242 stream->soundwrite=ms_filter_new(MS_FILE_REC_ID);
243 if (outfile!=NULL) audio_stream_record(stream,outfile);
246 /* creates the couple of encoder/decoder */
247 pt=rtp_profile_get_payload(profile,payload);
249 ms_error("audiostream.c: undefined payload type.");
252 stream->encoder=ms_filter_create_encoder(pt->mime_type);
253 stream->decoder=ms_filter_create_decoder(pt->mime_type);
254 if ((stream->encoder==NULL) || (stream->decoder==NULL)){
255 /* big problem: we have not a registered codec for this payload...*/
256 ms_error("mediastream.c: No decoder available for payload %i.",payload);
261 stream->ec=ms_filter_new(MS_SPEEX_EC_ID);
262 ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
263 if (stream->ec_tail_len!=0)
264 ms_filter_call_method(stream->ec,MS_SPEEX_EC_SET_TAIL_LENGTH,&stream->ec_tail_len);
265 if (stream->ec_delay!=0)
266 ms_filter_call_method(stream->ec,MS_SPEEX_EC_SET_DELAY,&stream->ec_delay);
267 if (stream->ec_framesize!=0)
268 ms_filter_call_method(stream->ec,MS_SPEEX_EC_SET_FRAME_SIZE,&stream->ec_framesize);
271 if (stream->el_type!=ELInactive || stream->use_gc || stream->use_ng){
272 stream->volsend=ms_filter_new(MS_VOLUME_ID);
273 stream->volrecv=ms_filter_new(MS_VOLUME_ID);
274 if (stream->el_type!=ELInactive){
275 if (stream->el_type==ELControlSpeaker)
276 ms_filter_call_method(stream->volrecv,MS_VOLUME_SET_PEER,stream->volsend);
277 else ms_filter_call_method(stream->volsend,MS_VOLUME_SET_PEER,stream->volrecv);
281 ms_filter_call_method(stream->volsend,MS_VOLUME_ENABLE_NOISE_GATE,&tmp);
285 if (stream->use_agc){
287 if (stream->volsend==NULL)
288 stream->volsend=ms_filter_new(MS_VOLUME_ID);
289 ms_filter_call_method(stream->volsend,MS_VOLUME_ENABLE_AGC,&tmp);
292 /* give the sound filters some properties */
293 if (ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate) != 0) {
294 /* need to add resampler*/
295 if (stream->read_resampler == NULL) stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
298 if (ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate) != 0) {
299 /* need to add resampler*/
300 if (stream->write_resampler == NULL) stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
305 ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS, &tmp);
307 /* give the encoder/decoder some parameters*/
308 ms_filter_call_method(stream->encoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
309 ms_message("Payload's bitrate is %i",pt->normal_bitrate);
310 if (pt->normal_bitrate>0){
311 ms_message("Setting audio encoder network bitrate to %i",pt->normal_bitrate);
312 ms_filter_call_method(stream->encoder,MS_FILTER_SET_BITRATE,&pt->normal_bitrate);
314 ms_filter_call_method(stream->decoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
316 if (pt->send_fmtp!=NULL) ms_filter_call_method(stream->encoder,MS_FILTER_ADD_FMTP, (void*)pt->send_fmtp);
317 if (pt->recv_fmtp!=NULL) ms_filter_call_method(stream->decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);
319 /*create the equalizer*/
320 stream->equalizer=ms_filter_new(MS_EQUALIZER_ID);
321 tmp=stream->eq_active;
322 ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_ACTIVE,&tmp);
323 /*configure resampler if needed*/
324 if (stream->read_resampler){
325 audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->rtpsend);
328 if (stream->write_resampler){
329 audio_stream_configure_resampler(stream->write_resampler,stream->rtprecv,stream->soundwrite);
331 /* and then connect all */
332 /* tip: draw yourself the picture if you don't understand */
335 ms_connection_helper_start(&h);
336 ms_connection_helper_link(&h,stream->soundread,-1,0);
337 if (stream->read_resampler)
338 ms_connection_helper_link(&h,stream->read_resampler,0,0);
340 ms_connection_helper_link(&h,stream->ec,1,1);
342 ms_connection_helper_link(&h,stream->volsend,0,0);
343 ms_connection_helper_link(&h,stream->encoder,0,0);
344 ms_connection_helper_link(&h,stream->rtpsend,0,-1);
347 ms_connection_helper_start(&h);
348 ms_connection_helper_link(&h,stream->rtprecv,-1,0);
349 ms_connection_helper_link(&h,stream->decoder,0,0);
350 ms_connection_helper_link(&h,stream->dtmfgen,0,0);
351 if (stream->equalizer)
352 ms_connection_helper_link(&h,stream->equalizer,0,0);
354 ms_connection_helper_link(&h,stream->volrecv,0,0);
356 ms_connection_helper_link(&h,stream->ec,0,0);
357 if (stream->write_resampler)
358 ms_connection_helper_link(&h,stream->write_resampler,0,0);
359 ms_connection_helper_link(&h,stream->soundwrite,0,-1);
362 stream->ticker=ms_ticker_new();
363 ms_ticker_set_name(stream->ticker,"Audio MSTicker");
364 ms_ticker_attach(stream->ticker,stream->soundread);
365 ms_ticker_attach(stream->ticker,stream->rtprecv);
371 int audio_stream_start_with_files(AudioStream *stream, RtpProfile *prof,const char *remip, int remport,
372 int rem_rtcp_port, int pt,int jitt_comp, const char *infile, const char * outfile)
374 return audio_stream_start_full(stream,prof,remip,remport,rem_rtcp_port,pt,jitt_comp,infile,outfile,NULL,NULL,FALSE);
377 AudioStream * audio_stream_start(RtpProfile *prof,int locport,const char *remip,int remport,int profile,int jitt_comp,bool_t use_ec)
379 MSSndCard *sndcard_playback;
380 MSSndCard *sndcard_capture;
382 sndcard_capture=ms_snd_card_manager_get_default_capture_card(ms_snd_card_manager_get());
383 sndcard_playback=ms_snd_card_manager_get_default_playback_card(ms_snd_card_manager_get());
384 if (sndcard_capture==NULL || sndcard_playback==NULL)
386 stream=audio_stream_new(locport, ms_is_ipv6(remip));
387 if (audio_stream_start_full(stream,prof,remip,remport,remport+1,profile,jitt_comp,NULL,NULL,sndcard_playback,sndcard_capture,use_ec)==0) return stream;
388 audio_stream_free(stream);
392 AudioStream *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)
395 if (playcard==NULL) {
396 ms_error("No playback card.");
399 if (captcard==NULL) {
400 ms_error("No capture card.");
403 stream=audio_stream_new(locport, ms_is_ipv6(remip));
404 if (audio_stream_start_full(stream,prof,remip,remport,remport+1,profile,jitt_comp,NULL,NULL,playcard,captcard,use_ec)==0) return stream;
405 audio_stream_free(stream);
409 void audio_stream_set_rtcp_information(AudioStream *st, const char *cname, const char *tool){
410 if (st->session!=NULL){
411 rtp_session_set_source_description(st->session,cname,NULL,NULL,NULL,NULL,tool , "This is free software (GPL) !");
415 void audio_stream_play(AudioStream *st, const char *name){
416 if (ms_filter_get_id(st->soundread)==MS_FILE_PLAYER_ID){
417 ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_CLOSE);
418 ms_filter_call_method(st->soundread,MS_FILE_PLAYER_OPEN,(void*)name);
419 if (st->read_resampler){
420 audio_stream_configure_resampler(st,st->soundread,st->rtpsend);
422 ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_START);
424 ms_error("Cannot play file: the stream hasn't been started with"
425 " audio_stream_start_with_files");
429 void audio_stream_record(AudioStream *st, const char *name){
430 if (ms_filter_get_id(st->soundwrite)==MS_FILE_REC_ID){
431 ms_filter_call_method_noarg(st->soundwrite,MS_FILE_REC_CLOSE);
432 ms_filter_call_method(st->soundwrite,MS_FILE_REC_OPEN,(void*)name);
433 ms_filter_call_method_noarg(st->soundwrite,MS_FILE_REC_START);
435 ms_error("Cannot record file: the stream hasn't been started with"
436 " audio_stream_start_with_files");
441 AudioStream *audio_stream_new(int locport, bool_t ipv6){
442 AudioStream *stream=(AudioStream *)ms_new0(AudioStream,1);
443 stream->session=create_duplex_rtpsession(locport,ipv6);
444 stream->rtpsend=ms_filter_new(MS_RTP_SEND_ID);
445 stream->play_dtmfs=TRUE;
446 stream->use_gc=FALSE;
447 stream->use_agc=FALSE;
448 stream->use_ng=FALSE;
452 void audio_stream_play_received_dtmfs(AudioStream *st, bool_t yesno){
453 st->play_dtmfs=yesno;
456 int 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){
457 return audio_stream_start_full(stream,prof,remip,remport,rem_rtcp_port,
458 payload_type,jitt_comp,NULL,NULL,playcard,captcard,use_ec);
461 void audio_stream_set_relay_session_id(AudioStream *stream, const char *id){
462 ms_filter_call_method(stream->rtpsend, MS_RTP_SEND_SET_RELAY_SESSION_ID,(void*)id);
465 void audio_stream_set_echo_canceller_params(AudioStream *st, int tail_len_ms, int delay_ms, int framesize){
466 st->ec_tail_len=tail_len_ms;
467 st->ec_delay=delay_ms;
468 st->ec_framesize=framesize;
471 void audio_stream_enable_echo_limiter(AudioStream *stream, EchoLimiterType type){
472 stream->el_type=type;
475 void audio_stream_enable_gain_control(AudioStream *stream, bool_t val){
479 void audio_stream_enable_automatic_gain_control(AudioStream *stream, bool_t val){
483 void audio_stream_enable_noise_gate(AudioStream *stream, bool_t val){
487 void audio_stream_set_mic_gain(AudioStream *stream, float gain){
488 if (stream->volsend){
489 ms_filter_call_method(stream->volsend,MS_VOLUME_SET_GAIN,&gain);
490 }else ms_warning("Could not apply gain: gain control wasn't activated. "
491 "Use audio_stream_enable_gain_control() before starting the stream.");
494 void audio_stream_enable_equalizer(AudioStream *stream, bool_t enabled){
495 stream->eq_active=enabled;
496 if (stream->equalizer){
498 ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_ACTIVE,&tmp);
502 void audio_stream_equalizer_set_gain(AudioStream *stream, int frequency, float gain, int freq_width){
503 if (stream->equalizer){
505 d.frequency=frequency;
508 ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_GAIN,&d);
512 void audio_stream_stop(AudioStream * stream)
515 MSConnectionHelper h;
516 ms_ticker_detach(stream->ticker,stream->soundread);
517 ms_ticker_detach(stream->ticker,stream->rtprecv);
519 rtp_stats_display(rtp_session_get_stats(stream->session),"Audio session's RTP statistics");
521 /*dismantle the outgoing graph*/
522 ms_connection_helper_start(&h);
523 ms_connection_helper_unlink(&h,stream->soundread,-1,0);
524 if (stream->read_resampler!=NULL)
525 ms_connection_helper_unlink(&h,stream->read_resampler,0,0);
526 if (stream->ec!=NULL)
527 ms_connection_helper_unlink(&h,stream->ec,1,1);
528 if (stream->volsend!=NULL)
529 ms_connection_helper_unlink(&h,stream->volsend,0,0);
530 ms_connection_helper_unlink(&h,stream->encoder,0,0);
531 ms_connection_helper_unlink(&h,stream->rtpsend,0,-1);
533 /*dismantle the receiving graph*/
534 ms_connection_helper_start(&h);
535 ms_connection_helper_unlink(&h,stream->rtprecv,-1,0);
536 ms_connection_helper_unlink(&h,stream->decoder,0,0);
537 ms_connection_helper_unlink(&h,stream->dtmfgen,0,0);
538 if (stream->equalizer)
539 ms_connection_helper_unlink(&h,stream->equalizer,0,0);
540 if (stream->volrecv!=NULL)
541 ms_connection_helper_unlink(&h,stream->volrecv,0,0);
542 if (stream->ec!=NULL)
543 ms_connection_helper_unlink(&h,stream->ec,0,0);
544 if (stream->write_resampler!=NULL)
545 ms_connection_helper_unlink(&h,stream->write_resampler,0,0);
546 ms_connection_helper_unlink(&h,stream->soundwrite,0,-1);
549 audio_stream_free(stream);
552 RingStream * ring_start(const char *file, int interval, MSSndCard *sndcard){
553 return ring_start_with_cb(file,interval,sndcard,NULL,NULL);
556 RingStream * ring_start_with_cb(const char *file,int interval,MSSndCard *sndcard, MSFilterNotifyFunc func,void * user_data)
560 stream=(RingStream *)ms_new0(RingStream,1);
561 stream->source=ms_filter_new(MS_FILE_PLAYER_ID);
562 if (ms_filter_call_method(stream->source,MS_FILE_PLAYER_OPEN,(void*)file)<0){
563 ms_filter_destroy(stream->source);
567 ms_filter_call_method(stream->source,MS_FILE_PLAYER_LOOP,&interval);
568 ms_filter_call_method_noarg(stream->source,MS_FILE_PLAYER_START);
570 ms_filter_set_notify_callback(stream->source,func,user_data);
571 stream->sndwrite=ms_snd_card_create_writer(sndcard);
572 ms_filter_call_method(stream->source,MS_FILTER_GET_SAMPLE_RATE,&tmp);
573 ms_filter_call_method(stream->sndwrite,MS_FILTER_SET_SAMPLE_RATE,&tmp);
574 ms_filter_call_method(stream->source,MS_FILTER_GET_NCHANNELS,&tmp);
575 ms_filter_call_method(stream->sndwrite,MS_FILTER_SET_NCHANNELS,&tmp);
576 stream->ticker=ms_ticker_new();
577 ms_ticker_set_name(stream->ticker,"Audio (ring) MSTicker");
578 ms_filter_link(stream->source,0,stream->sndwrite,0);
579 ms_ticker_attach(stream->ticker,stream->source);
583 void ring_stop(RingStream *stream){
584 ms_ticker_detach(stream->ticker,stream->source);
585 ms_filter_unlink(stream->source,0,stream->sndwrite,0);
586 ms_ticker_destroy(stream->ticker);
587 ms_filter_destroy(stream->source);
588 ms_filter_destroy(stream->sndwrite);
591 ms_warning("Sleeping a bit after closing the audio device...");
597 int audio_stream_send_dtmf(AudioStream *stream, char dtmf)
600 ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_SEND_DTMF,&dtmf);
602 ms_filter_call_method(stream->dtmfgen,MS_DTMF_GEN_PUT,&dtmf);