audiostream.c 68.5 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)

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.
*/


jehan's avatar
jehan committed
21 22 23
#ifdef HAVE_CONFIG_H
#include "mediastreamer-config.h"
#endif
aymeric's avatar
aymeric committed
24

jehan's avatar
jehan committed
25
#include "mediastreamer2/mediastream.h"
aymeric's avatar
aymeric committed
26 27 28 29 30
#include "mediastreamer2/dtmfgen.h"
#include "mediastreamer2/mssndcard.h"
#include "mediastreamer2/msrtp.h"
#include "mediastreamer2/msfileplayer.h"
#include "mediastreamer2/msfilerec.h"
31
#include "mediastreamer2/msvolume.h"
smorlat's avatar
smorlat committed
32
#include "mediastreamer2/msequalizer.h"
33 34
#include "mediastreamer2/mstee.h"
#include "mediastreamer2/msaudiomixer.h"
35
#include "mediastreamer2/mscodecutils.h"
Simon Morlat's avatar
Simon Morlat committed
36
#include "mediastreamer2/msitc.h"
37 38
#include "mediastreamer2/msvaddtx.h"
#include "mediastreamer2/msgenericplc.h"
39
#include "mediastreamer2/mseventqueue.h"
Ghislain MARY's avatar
Ghislain MARY committed
40
#include "private.h"
aymeric's avatar
aymeric committed
41

42 43 44
#ifdef ANDROID
#include "audiofilters/devices.h"
#endif
45

46 47
#include <sys/types.h>

48
#ifndef _WIN32
aymeric's avatar
aymeric committed
49 50 51 52
	#include <sys/socket.h>
	#include <netdb.h>
#endif

Simon Morlat's avatar
Simon Morlat committed
53
static void configure_av_recorder(AudioStream *stream);
54
static void configure_decoder(AudioStream *stream, PayloadType *pt, int sample_rate, int nchannels);
Simon Morlat's avatar
Simon Morlat committed
55
static void audio_stream_configure_resampler(AudioStream *st, MSFilter *resampler,MSFilter *from, MSFilter *to);
Simon Morlat's avatar
Simon Morlat committed
56
static void audio_stream_set_rtp_output_gain_db(AudioStream *stream, float gain_db);
Simon Morlat's avatar
Simon Morlat committed
57

58
static void audio_stream_free(AudioStream *stream) {
Ghislain MARY's avatar
Ghislain MARY committed
59
	media_stream_free(&stream->ms);
aymeric's avatar
aymeric committed
60 61 62
	if (stream->soundread!=NULL) ms_filter_destroy(stream->soundread);
	if (stream->soundwrite!=NULL) ms_filter_destroy(stream->soundwrite);
	if (stream->dtmfgen!=NULL) ms_filter_destroy(stream->dtmfgen);
63
	if (stream->plc!=NULL)	ms_filter_destroy(stream->plc);
aymeric's avatar
aymeric committed
64
	if (stream->ec!=NULL)	ms_filter_destroy(stream->ec);
65 66
	if (stream->volrecv!=NULL) ms_filter_destroy(stream->volrecv);
	if (stream->volsend!=NULL) ms_filter_destroy(stream->volsend);
67
	if (stream->equalizer!=NULL) ms_filter_destroy(stream->equalizer);
68 69
	if (stream->read_decoder != NULL) ms_filter_destroy(stream->read_decoder);
	if (stream->write_encoder != NULL) ms_filter_destroy(stream->write_encoder);
jehan's avatar
jehan committed
70 71
	if (stream->read_resampler!=NULL) ms_filter_destroy(stream->read_resampler);
	if (stream->write_resampler!=NULL) ms_filter_destroy(stream->write_resampler);
72
	if (stream->dtmfgen_rtp!=NULL) ms_filter_destroy(stream->dtmfgen_rtp);
Simon Morlat's avatar
Simon Morlat committed
73
	if (stream->dummy) ms_filter_destroy(stream->dummy);
74 75 76
	if (stream->recv_tee) ms_filter_destroy(stream->recv_tee);
	if (stream->recorder) ms_filter_destroy(stream->recorder);
	if (stream->recorder_mixer) ms_filter_destroy(stream->recorder_mixer);
77 78 79
	if (stream->local_mixer) ms_filter_destroy(stream->local_mixer);
	if (stream->local_player) ms_filter_destroy(stream->local_player);
	if (stream->local_player_resampler) ms_filter_destroy(stream->local_player_resampler);
Simon Morlat's avatar
Simon Morlat committed
80 81 82 83
	if (stream->av_recorder.encoder) ms_filter_destroy(stream->av_recorder.encoder);
	if (stream->av_recorder.recorder) ms_filter_destroy(stream->av_recorder.recorder);
	if (stream->av_recorder.resampler) ms_filter_destroy(stream->av_recorder.resampler);
	if (stream->av_recorder.video_input) ms_filter_destroy(stream->av_recorder.video_input);
84
	if (stream->vaddtx) ms_filter_destroy(stream->vaddtx);
85
	if (stream->outbound_mixer) ms_filter_destroy(stream->outbound_mixer);
86
	if (stream->recorder_file) ms_free(stream->recorder_file);
87
	if (stream->rtp_io_session) rtp_session_destroy(stream->rtp_io_session);
88

aymeric's avatar
aymeric committed
89 90 91 92 93
	ms_free(stream);
}

static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};

94
static void on_dtmf_received(RtpSession *s, uint32_t dtmf, void *user_data)
aymeric's avatar
aymeric committed
95
{
smorlat's avatar
smorlat committed
96
	AudioStream *stream=(AudioStream*)user_data;
aymeric's avatar
aymeric committed
97 98 99 100 101
	if (dtmf>15){
		ms_warning("Unsupported telephone-event type.");
		return;
	}
	ms_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
smorlat's avatar
smorlat committed
102 103
	if (stream->dtmfgen!=NULL && stream->play_dtmfs){
		ms_filter_call_method(stream->dtmfgen,MS_DTMF_GEN_PUT,&dtmf_tab[dtmf]);
aymeric's avatar
aymeric committed
104 105 106
	}
}

107 108 109 110 111 112 113 114
/**
 * This function must be called from the MSTicker thread:
 * it replaces one filter by another one.
 * This is a dirty hack that works anyway.
 * It would be interesting to have something that does the job
 * more easily within the MSTicker API.
 * return TRUE if the decoder was changed, FALSE otherwise.
 */
115
static bool_t audio_stream_payload_type_changed(RtpSession *session, void *data) {
116
	AudioStream *stream = (AudioStream *)data;
117
	RtpProfile *prof = rtp_session_get_profile(session);
118
	int payload = rtp_session_get_recv_payload_type(stream->ms.sessions.rtp_session);
119 120 121 122 123 124 125 126
	PayloadType *pt = rtp_profile_get_payload(prof, payload);

	if (stream->ms.decoder == NULL){
		ms_message("audio_stream_payload_type_changed(): no decoder!");
		return FALSE;
	}

	if (pt != NULL){
127 128 129 130 131 132
		MSFilter *dec;
		/* if new payload type is Comfort Noise (CN), just do nothing */
		if (strcasecmp(pt->mime_type, "CN")==0) {
			ms_message("Ignore payload type change to CN");
			return FALSE;
		}
133

134 135 136 137
		if (stream->ms.current_pt && strcasecmp(pt->mime_type, stream->ms.current_pt->mime_type)==0 && pt->clock_rate==stream->ms.current_pt->clock_rate){
			ms_message("Ignoring payload type number change because it points to the same payload type as the current one");
			return FALSE;
		}
138

139 140
		//dec = ms_filter_create_decoder(pt->mime_type);
		dec = ms_factory_create_decoder(stream->ms.factory, pt->mime_type);
141 142
		if (dec != NULL) {
			MSFilter *nextFilter = stream->ms.decoder->outputs[0]->next.filter;
143

144
			ms_message("Replacing decoder on the fly");
145 146 147 148 149
			ms_filter_unlink(stream->ms.rtprecv, 0, stream->ms.decoder, 0);
			ms_filter_unlink(stream->ms.decoder, 0, nextFilter, 0);
			ms_filter_postprocess(stream->ms.decoder);
			ms_filter_destroy(stream->ms.decoder);
			stream->ms.decoder = dec;
150 151
			configure_decoder(stream, pt, stream->sample_rate, stream->nchannels);
			if (stream->write_resampler){
Simon Morlat's avatar
Simon Morlat committed
152
				audio_stream_configure_resampler(stream, stream->write_resampler,stream->ms.decoder,stream->soundwrite);
153
			}
154 155 156
			ms_filter_link(stream->ms.rtprecv, 0, stream->ms.decoder, 0);
			ms_filter_link(stream->ms.decoder, 0, nextFilter, 0);
			ms_filter_preprocess(stream->ms.decoder, stream->ms.sessions.ticker);
157
			stream->ms.current_pt=pt;
158 159
			return TRUE;
		} else {
160
			ms_error("No decoder found for %s", pt->mime_type);
161 162
		}
	} else {
163
		ms_warning("No payload type defined with number %i", payload);
164 165 166 167
	}
	return FALSE;
}

168
/*
169
 * note: since not all filters implement MS_FILTER_GET_SAMPLE_RATE and MS_FILTER_GET_NCHANNELS, the PayloadType passed here is used to guess this information.
170
 */
Simon Morlat's avatar
Simon Morlat committed
171
static void audio_stream_configure_resampler(AudioStream *st, MSFilter *resampler,MSFilter *from, MSFilter *to) {
172
	int from_rate=0, to_rate=0;
173
	int from_channels = 0, to_channels = 0;
174 175
	ms_filter_call_method(from,MS_FILTER_GET_SAMPLE_RATE,&from_rate);
	ms_filter_call_method(to,MS_FILTER_GET_SAMPLE_RATE,&to_rate);
176 177
	ms_filter_call_method(from, MS_FILTER_GET_NCHANNELS, &from_channels);
	ms_filter_call_method(to, MS_FILTER_GET_NCHANNELS, &to_channels);
178
	if (from_channels == 0) {
Simon Morlat's avatar
Simon Morlat committed
179
		from_channels = st->nchannels;
180 181 182
		ms_error("Filter %s does not implement the MS_FILTER_GET_NCHANNELS method", from->desc->name);
	}
	if (to_channels == 0) {
Simon Morlat's avatar
Simon Morlat committed
183
		to_channels = st->nchannels;
184 185
		ms_error("Filter %s does not implement the MS_FILTER_GET_NCHANNELS method", to->desc->name);
	}
186
	if (from_rate == 0){
187
		ms_error("Filter %s does not implement the MS_FILTER_GET_SAMPLE_RATE method", from->desc->name);
Simon Morlat's avatar
Simon Morlat committed
188
		from_rate = st->sample_rate;
189 190
	}
	if (to_rate == 0){
191
		ms_error("Filter %s does not implement the MS_FILTER_GET_SAMPLE_RATE method", to->desc->name);
Simon Morlat's avatar
Simon Morlat committed
192
		to_rate = st->sample_rate;
193
	}
194 195
	ms_filter_call_method(resampler,MS_FILTER_SET_SAMPLE_RATE,&from_rate);
	ms_filter_call_method(resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&to_rate);
196 197 198
	ms_filter_call_method(resampler, MS_FILTER_SET_NCHANNELS, &from_channels);
	ms_filter_call_method(resampler, MS_FILTER_SET_OUTPUT_NCHANNELS, &to_channels);
	ms_message("configuring %s-->%s from rate [%i] to rate [%i] and from channel [%i] to channel [%i]",
199
			   from->desc->name, to->desc->name, from_rate, to_rate, from_channels, to_channels);
200 201
}

jehan's avatar
jehan committed
202
static void audio_stream_process_rtcp(MediaStream *media_stream, mblk_t *m){
203 204
}

205
void audio_stream_iterate(AudioStream *stream){
206
	media_stream_iterate(&stream->ms);
207 208 209
}

bool_t audio_stream_alive(AudioStream * stream, int timeout){
210
	return media_stream_alive((MediaStream*)stream,timeout);
aymeric's avatar
aymeric committed
211 212
}

jehan's avatar
jehan committed
213 214
/*invoked from FEC capable filters*/
static  mblk_t* audio_stream_payload_picker(MSRtpPayloadPickerContext* context,unsigned int sequence_number) {
215
	return rtp_session_pick_with_cseq(((AudioStream*)(context->filter_graph_manager))->ms.sessions.rtp_session, sequence_number);
Simon Morlat's avatar
Simon Morlat committed
216 217 218
}

static void stop_preload_graph(AudioStream *stream){
219
	ms_ticker_detach(stream->ms.sessions.ticker,stream->dummy);
220

Ghislain MARY's avatar
Ghislain MARY committed
221 222 223 224
	if (stream->ms.voidsink) {
		ms_filter_unlink(stream->dummy,0,stream->ms.voidsink,0);
		ms_filter_destroy(stream->ms.voidsink);
		stream->ms.voidsink=NULL;
225 226
	}else if (stream->soundwrite) {
		ms_filter_unlink(stream->dummy,0,stream->soundwrite,0);
227
	}
Simon Morlat's avatar
Simon Morlat committed
228 229 230 231 232
	ms_filter_destroy(stream->dummy);
	stream->dummy=NULL;
}

bool_t audio_stream_started(AudioStream *stream){
233
	return media_stream_started(&stream->ms);
Simon Morlat's avatar
Simon Morlat committed
234 235
}

236
/* This function is used either on IOS to workaround the long time to initialize the Audio Unit or for ICE candidates gathering. */
Simon Morlat's avatar
Simon Morlat committed
237
void audio_stream_prepare_sound(AudioStream *stream, MSSndCard *playcard, MSSndCard *captcard){
238
	audio_stream_unprepare_sound(stream);
239
	stream->dummy=ms_factory_create_filter(stream->ms.factory, MS_RTP_RECV_ID);
240
	rtp_session_set_payload_type(stream->ms.sessions.rtp_session,0);
241
	rtp_session_enable_rtcp(stream->ms.sessions.rtp_session, FALSE);
242
	ms_filter_call_method(stream->dummy,MS_RTP_RECV_SET_SESSION,stream->ms.sessions.rtp_session);
243

Simon Morlat's avatar
Simon Morlat committed
244
	if (captcard && playcard){
245
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
246 247 248
		stream->soundread=ms_snd_card_create_reader(captcard);
		stream->soundwrite=ms_snd_card_create_writer(playcard);
		ms_filter_link(stream->dummy,0,stream->soundwrite,0);
249
#else
250
		stream->ms.voidsink=ms_factory_create_filter(stream->ms.factory,  MS_VOID_SINK_ID);
Ghislain MARY's avatar
Ghislain MARY committed
251
		ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
Simon Morlat's avatar
Simon Morlat committed
252
#endif
253
	} else {
254
		stream->ms.voidsink=ms_factory_create_filter(stream->ms.factory,  MS_VOID_SINK_ID);
Ghislain MARY's avatar
Ghislain MARY committed
255
		ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
256
		
257
	}
258 259 260
	if (stream->ms.sessions.ticker == NULL) media_stream_start_ticker(&stream->ms);
	ms_ticker_attach(stream->ms.sessions.ticker,stream->dummy);
	stream->ms.state=MSStreamPreparing;
Simon Morlat's avatar
Simon Morlat committed
261 262
}

Simon Morlat's avatar
Simon Morlat committed
263
static void _audio_stream_unprepare_sound(AudioStream *stream, bool_t keep_sound_resources){
264
	if (stream->ms.state==MSStreamPreparing){
Simon Morlat's avatar
Simon Morlat committed
265
		stop_preload_graph(stream);
266
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
267 268 269 270 271 272
		if (!keep_sound_resources){
			if (stream->soundread) ms_filter_destroy(stream->soundread);
			stream->soundread=NULL;
			if (stream->soundwrite) ms_filter_destroy(stream->soundwrite);
			stream->soundwrite=NULL;
		}
Simon Morlat's avatar
Simon Morlat committed
273
#endif
274
	}
275
	stream->ms.state=MSStreamInitialized;
Simon Morlat's avatar
Simon Morlat committed
276 277
}

Simon Morlat's avatar
Simon Morlat committed
278 279 280 281
void audio_stream_unprepare_sound(AudioStream *stream){
	_audio_stream_unprepare_sound(stream,FALSE);
}

282 283 284 285 286
static void player_callback(void *ud, MSFilter *f, unsigned int id, void *arg){
	AudioStream *stream=(AudioStream *)ud;
	int sr=0;
	int channels=0;
	switch(id){
287
		case MS_FILTER_OUTPUT_FMT_CHANGED:
288 289 290 291 292 293 294 295 296 297 298 299 300 301
			ms_filter_call_method(f,MS_FILTER_GET_SAMPLE_RATE,&sr);
			ms_filter_call_method(f,MS_FILTER_GET_NCHANNELS,&channels);
			if (f==stream->local_player){
				ms_filter_call_method(stream->local_player_resampler,MS_FILTER_SET_SAMPLE_RATE,&sr);
				ms_filter_call_method(stream->local_player_resampler,MS_FILTER_SET_NCHANNELS,&channels);
			}
		break;
		default:
		break;
	}
}

static void setup_local_player(AudioStream *stream, int samplerate, int channels){
	MSConnectionHelper cnx;
Simon Morlat's avatar
Simon Morlat committed
302
	int master=0;
303

Sandrine Avakian's avatar
Sandrine Avakian committed
304

305 306
	stream->local_player=ms_factory_create_filter(stream->ms.factory, MS_FILE_PLAYER_ID);
	stream->local_player_resampler=ms_factory_create_filter(stream->ms.factory, MS_RESAMPLE_ID);
307

308 309
	ms_connection_helper_start(&cnx);
	ms_connection_helper_link(&cnx,stream->local_player,-1,0);
310 311 312
	if (stream->local_player_resampler){
		ms_connection_helper_link(&cnx,stream->local_player_resampler,0,0);
	}
313
	ms_connection_helper_link(&cnx,stream->local_mixer,1,-1);
314

315 316 317 318
	if (stream->local_player_resampler){
		ms_filter_call_method(stream->local_player_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&samplerate);
		ms_filter_call_method(stream->local_player_resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&channels);
	}
319 320
	ms_filter_call_method(stream->local_mixer,MS_FILTER_SET_SAMPLE_RATE,&samplerate);
	ms_filter_call_method(stream->local_mixer,MS_FILTER_SET_NCHANNELS,&channels);
Simon Morlat's avatar
Simon Morlat committed
321
	ms_filter_call_method(stream->local_mixer,MS_AUDIO_MIXER_SET_MASTER_CHANNEL,&master);
322 323 324
	ms_filter_add_notify_callback(stream->local_player,player_callback,stream,TRUE);
}

325
static OrtpRtcpXrPlcStatus audio_stream_get_rtcp_xr_plc_status(void *userdata) {
326 327 328
	AudioStream *stream = (AudioStream *)userdata;
	if ((stream->features & AUDIO_STREAM_FEATURE_PLC) != 0) {
		int decoder_have_plc = 0;
329
		if (stream->ms.decoder && ms_filter_has_method(stream->ms.decoder, MS_AUDIO_DECODER_HAVE_PLC)) {
330 331 332 333 334 335 336 337 338 339 340
			ms_filter_call_method(stream->ms.decoder, MS_AUDIO_DECODER_HAVE_PLC, &decoder_have_plc);
		}
		if (decoder_have_plc == 0) {
			return OrtpRtcpXrSilencePlc;
		} else {
			return OrtpRtcpXrEnhancedPlc;
		}
	}
	return OrtpRtcpXrNoPlc;
}

341
static int audio_stream_get_rtcp_xr_signal_level(void *userdata) {
342 343
	AudioStream *stream = (AudioStream *)userdata;
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0) {
344 345 346
		float volume = 0.f;
		if (stream->volrecv)
			ms_filter_call_method(stream->volrecv, MS_VOLUME_GET_MAX, &volume);
347
		return (int)volume;
348 349 350 351
	}
	return ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;
}

352
static int audio_stream_get_rtcp_xr_noise_level(void *userdata) {
353 354
	AudioStream *stream = (AudioStream *)userdata;
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0) {
355 356 357
		float volume = 0.f;
		if (stream->volrecv)
			ms_filter_call_method(stream->volrecv, MS_VOLUME_GET_MIN, &volume);
358
		return (int)volume;
359 360 361 362
	}
	return ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;
}

363
static float audio_stream_get_rtcp_xr_average_quality_rating(void *userdata) {
364 365 366 367
	AudioStream *stream = (AudioStream *)userdata;
	return audio_stream_get_average_quality_rating(stream);
}

368
static float audio_stream_get_rtcp_xr_average_lq_quality_rating(void *userdata) {
369 370 371 372
	AudioStream *stream = (AudioStream *)userdata;
	return audio_stream_get_average_lq_quality_rating(stream);
}

373
static bool_t ci_ends_with(const char *filename, const char*suffix){
374 375
	size_t filename_len=strlen(filename);
	size_t suffix_len=strlen(suffix);
376 377 378 379
	if (filename_len<suffix_len) return FALSE;
	return strcasecmp(filename+filename_len-suffix_len,suffix)==0;
}

380
MSFilter *_ms_create_av_player(const char *filename, MSFactory* factory){
381
	if (ci_ends_with(filename,".mkv"))
382
		return ms_factory_create_filter(factory, MS_MKV_PLAYER_ID);
383
	else if (ci_ends_with(filename,".wav"))
384
		return ms_factory_create_filter(factory, MS_FILE_PLAYER_ID);
385 386
	else
		ms_error("Cannot open %s, unsupported file extension", filename);
387 388 389 390 391 392 393
	return NULL;
}

static void unplumb_av_player(AudioStream *stream){
	struct _AVPlayer *player=&stream->av_player;
	MSConnectionHelper ch;
	bool_t reattach=stream->ms.state==MSStreamStarted;
394

395
	if (!player->plumbed) return;
396

François Grisez's avatar
François Grisez committed
397 398
	/*detach the outbound graph before modifying the graph*/
	ms_ticker_detach(stream->ms.sessions.ticker,stream->soundread);
399 400 401 402 403 404 405
	if (player->videopin!=-1){
		ms_connection_helper_start(&ch);
		ms_connection_helper_unlink(&ch,player->player,-1,player->videopin);
		ms_connection_helper_unlink(&ch,player->video_output,0,0);
	}
	ms_connection_helper_start(&ch);
	ms_connection_helper_unlink(&ch,player->player,-1,player->audiopin);
Simon Morlat's avatar
Simon Morlat committed
406 407
	if (player->decoder)
		ms_connection_helper_unlink(&ch,player->decoder,0,0);
408 409 410 411
	ms_connection_helper_unlink(&ch,player->resampler,0,0);
	ms_connection_helper_unlink(&ch,stream->outbound_mixer,1,-1);
	/*and attach back*/
	if (reattach) ms_ticker_attach(stream->ms.sessions.ticker,stream->soundread);
412
	player->plumbed = FALSE;
413 414 415 416
}

static void close_av_player(AudioStream *stream){
	struct _AVPlayer *player=&stream->av_player;
417

418 419
	if (player->player){
		MSPlayerState st=MSPlayerClosed;
Ghislain MARY's avatar
Ghislain MARY committed
420
		unplumb_av_player(stream);
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
		if (ms_filter_call_method(player->player,MS_PLAYER_GET_STATE,&st)==0){
			if (st!=MSPlayerClosed)
				ms_filter_call_method_noarg(player->player,MS_PLAYER_CLOSE);
		}
		ms_filter_destroy(player->player);
		player->player=NULL;
	}
	if (player->resampler){
		ms_filter_destroy(player->resampler);
		player->resampler=NULL;
	}
	if (player->decoder){
		ms_filter_destroy(player->decoder);
		player->decoder=NULL;
	}
}

static void configure_av_player(AudioStream *stream, const MSFmtDescriptor *audiofmt, const MSFmtDescriptor *videofmt){
	struct _AVPlayer *player=&stream->av_player;
	int stream_rate=0;
	int stream_channels=0;
442

443
	ms_message("AudioStream [%p] Configure av_player, audiofmt=%s videofmt=%s",stream,ms_fmt_descriptor_to_string(audiofmt),ms_fmt_descriptor_to_string(videofmt));
444

445 446 447 448 449 450 451 452
	if (audiofmt){
		if (player->decoder){
			if (audiofmt->nchannels>0){
				ms_filter_call_method(player->decoder,MS_FILTER_SET_NCHANNELS,(void*)&audiofmt->nchannels);
			}
			if (audiofmt->rate>0){
				ms_filter_call_method(player->decoder,MS_FILTER_SET_SAMPLE_RATE,(void*)&audiofmt->rate);
			}
Simon Morlat's avatar
Simon Morlat committed
453
		}
454 455
		ms_filter_call_method(player->resampler,MS_FILTER_SET_NCHANNELS,(void*)&audiofmt->nchannels);
		ms_filter_call_method(player->resampler,MS_FILTER_SET_SAMPLE_RATE,(void*)&audiofmt->rate);
456
	}
457

458 459 460 461 462 463 464 465
	ms_filter_call_method(stream->outbound_mixer,MS_FILTER_GET_SAMPLE_RATE,&stream_rate);
	ms_filter_call_method(stream->outbound_mixer,MS_FILTER_GET_NCHANNELS,&stream_channels);
	ms_filter_call_method(player->resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&stream_channels);
	ms_filter_call_method(player->resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&stream_rate);
	if (videofmt){
		MSPinFormat pf;
		pf.pin=0;
		pf.fmt=videofmt;
466
		ms_filter_call_method(player->video_output,MS_FILTER_SET_INPUT_FMT,&pf);
467 468 469 470 471 472 473
	}
}

static void plumb_av_player(AudioStream *stream){
	struct _AVPlayer *player=&stream->av_player;
	MSConnectionHelper ch;
	bool_t reattach=stream->ms.state==MSStreamStarted;
474

475 476 477 478 479 480 481
	if (player->videopin!=-1){
		ms_connection_helper_start(&ch);
		ms_connection_helper_link(&ch,player->player,-1,player->videopin);
		ms_connection_helper_link(&ch,player->video_output,0,0);
	}
	ms_connection_helper_start(&ch);
	ms_connection_helper_link(&ch,player->player,-1,player->audiopin);
Simon Morlat's avatar
Simon Morlat committed
482 483
	if (player->decoder)
		ms_connection_helper_link(&ch,player->decoder,0,0);
484 485 486 487 488 489 490 491 492 493 494 495 496 497
	ms_connection_helper_link(&ch,player->resampler,0,0);
	/*detach the outbound graph before attaching to the outbound mixer*/
	if (reattach) ms_ticker_detach(stream->ms.sessions.ticker,stream->soundread);
	ms_connection_helper_link(&ch,stream->outbound_mixer,1,-1);
	/*and attach back*/
	if (reattach) ms_ticker_attach(stream->ms.sessions.ticker,stream->soundread);
	player->plumbed=TRUE;
}

static int open_av_player(AudioStream *stream, const char *filename){
	struct _AVPlayer *player=&stream->av_player;
	MSPinFormat fmt1={0},fmt2={0};
	MSPinFormat *audiofmt=NULL;
	MSPinFormat *videofmt=NULL;
498

499
	if (player->player) close_av_player(stream);
500
	//player->player=_ms_create_av_player(filename);
501
	player->player=_ms_create_av_player(filename, stream->ms.factory);
502 503 504 505 506 507 508 509 510 511 512 513
	if (player->player==NULL){
		ms_warning("AudioStream[%p]: no way to open [%s].",stream,filename);
		return -1;
	}
	if (ms_filter_call_method(player->player,MS_PLAYER_OPEN,(void*)filename)==-1){
		close_av_player(stream);
		return -1;
	}
	fmt1.pin=0;
	ms_filter_call_method(player->player,MS_FILTER_GET_OUTPUT_FMT,&fmt1);
	fmt2.pin=1;
	ms_filter_call_method(player->player,MS_FILTER_GET_OUTPUT_FMT,&fmt2);
514
	if (fmt1.fmt==NULL && fmt2.fmt==NULL){
515 516 517 518 519
		/*assume PCM*/
		int sr=8000;
		int channels=1;
		ms_filter_call_method(player->player,MS_FILTER_GET_SAMPLE_RATE,&sr);
		ms_filter_call_method(player->player,MS_FILTER_GET_NCHANNELS,&channels);
520
		fmt1.fmt=ms_factory_get_audio_format(stream->ms.factory, "pcm", sr, channels, NULL);
521 522
		audiofmt=&fmt1;
	}else{
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
		if (fmt1.fmt) {
			if (fmt1.fmt->type==MSAudio){
				audiofmt=&fmt1;
				player->audiopin=0;
			}else{
				videofmt=&fmt1;
				player->videopin=0;
			}
		}
		if (fmt2.fmt){
			if (fmt2.fmt->type == MSAudio){
				audiofmt=&fmt2;
				player->audiopin=1;
			}else{
				videofmt=&fmt2;
				player->videopin=1;
			}
540

541
		}
Simon Morlat's avatar
Simon Morlat committed
542
	}
543
	if (audiofmt && audiofmt->fmt && strcasecmp(audiofmt->fmt->encoding,"pcm")!=0){
544 545
		player->decoder=ms_factory_create_decoder(stream->ms.factory, audiofmt->fmt->encoding);

546 547 548 549 550 551
		if (player->decoder==NULL){
			ms_warning("AudioStream[%p]: no way to decode [%s]",stream,filename);
			close_av_player(stream);
			return -1;
		}
	}
552 553 554
	player->resampler=ms_factory_create_filter(stream->ms.factory, MS_RESAMPLE_ID);
	if (videofmt && videofmt->fmt) player->video_output=ms_factory_create_filter(stream->videostream->ms.factory,MS_ITC_SINK_ID);

555
	else player->videopin=-1;
556
	configure_av_player(stream,audiofmt ? audiofmt->fmt : NULL ,videofmt ? videofmt->fmt : NULL);
557
	if (stream->videostream) video_stream_open_player(stream->videostream,player->video_output);
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
	plumb_av_player(stream);
	return 0;
}

MSFilter * audio_stream_open_remote_play(AudioStream *stream, const char *filename){
	if (stream->ms.state!=MSStreamStarted){
		ms_warning("AudioStream[%p]: audio_stream_play_to_remote() works only when the stream is started.",stream);
		return NULL;
	}
	if (stream->outbound_mixer==NULL){
		ms_warning("AudioStream[%p]: audio_stream_play_to_remote() works only when the stream has AUDIO_STREAM_FEATURE_REMOTE_PLAYING capability.",stream);
		return NULL;
	}
	if (open_av_player(stream,filename)==-1){
		return NULL;
	}
	return stream->av_player.player;
}

577 578 579 580 581 582 583 584 585
void audio_stream_close_remote_play(AudioStream *stream){
	MSPlayerState state;
	if (stream->av_player.player){
		ms_filter_call_method(stream->av_player.player,MS_PLAYER_GET_STATE,&state);
		if (state!=MSPlayerClosed)
			ms_filter_call_method_noarg(stream->av_player.player,MS_PLAYER_CLOSE);
	}
	if (stream->videostream) video_stream_close_player(stream->videostream);
}
586

Simon Morlat's avatar
Simon Morlat committed
587
static void video_input_updated(void *stream, MSFilter *f, unsigned int event_id, void *arg){
588
	if (event_id==MS_FILTER_OUTPUT_FMT_CHANGED){
Simon Morlat's avatar
Simon Morlat committed
589 590 591 592 593
		ms_message("Video ITC source updated.");
		configure_av_recorder((AudioStream*)stream);
	}
}

594
static void av_recorder_handle_event(void *userdata, MSFilter *recorder, unsigned int event, void *event_arg){
Simon Morlat's avatar
Simon Morlat committed
595
#ifdef VIDEO_ENABLED
596 597 598 599
	AudioStream *audiostream = (AudioStream *)userdata;
	if (audiostream->videostream != NULL) {
		video_recorder_handle_event(audiostream->videostream, recorder, event, event_arg);
	}
Simon Morlat's avatar
Simon Morlat committed
600
#endif
601 602
}

Simon Morlat's avatar
Simon Morlat committed
603
static void setup_av_recorder(AudioStream *stream, int sample_rate, int nchannels){
Sandrine Avakian's avatar
Sandrine Avakian committed
604

605
	stream->av_recorder.recorder=ms_factory_create_filter(stream->ms.factory, MS_MKV_RECORDER_ID);
Simon Morlat's avatar
Simon Morlat committed
606
	if (stream->av_recorder.recorder){
Simon Morlat's avatar
Simon Morlat committed
607
		MSPinFormat pinfmt={0};
608 609 610
		stream->av_recorder.video_input=ms_factory_create_filter(stream->ms.factory, MS_ITC_SOURCE_ID);
		stream->av_recorder.resampler=ms_factory_create_filter(stream->ms.factory,MS_RESAMPLE_ID);
		stream->av_recorder.encoder=ms_factory_create_filter(stream->ms.factory,MS_OPUS_ENC_ID);
611

Simon Morlat's avatar
Simon Morlat committed
612 613 614
		if (stream->av_recorder.encoder==NULL){
			int g711_rate=8000;
			int g711_nchannels=1;
615
			stream->av_recorder.encoder=ms_factory_create_filter(stream->ms.factory, MS_ULAW_ENC_ID);
Simon Morlat's avatar
Simon Morlat committed
616 617 618 619
			ms_filter_call_method(stream->av_recorder.resampler,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
			ms_filter_call_method(stream->av_recorder.resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&g711_rate);
			ms_filter_call_method(stream->av_recorder.resampler,MS_FILTER_SET_NCHANNELS,&nchannels);
			ms_filter_call_method(stream->av_recorder.resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&g711_nchannels);
620
			pinfmt.fmt=ms_factory_get_audio_format(stream->ms.factory, "pcmu",g711_rate,g711_nchannels,NULL);
621

Simon Morlat's avatar
Simon Morlat committed
622 623 624 625 626 627 628 629 630
		}else{
			int got_sr=0;
			ms_filter_call_method(stream->av_recorder.encoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
			ms_filter_call_method(stream->av_recorder.encoder,MS_FILTER_GET_SAMPLE_RATE,&got_sr);
			ms_filter_call_method(stream->av_recorder.encoder,MS_FILTER_SET_NCHANNELS,&nchannels);
			ms_filter_call_method(stream->av_recorder.resampler,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
			ms_filter_call_method(stream->av_recorder.resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&got_sr);
			ms_filter_call_method(stream->av_recorder.resampler,MS_FILTER_SET_NCHANNELS,&nchannels);
			ms_filter_call_method(stream->av_recorder.resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&nchannels);
631
			pinfmt.fmt=ms_factory_get_audio_format(stream->ms.factory,"opus",48000,nchannels,NULL);
Simon Morlat's avatar
Simon Morlat committed
632
		}
Simon Morlat's avatar
Simon Morlat committed
633 634 635
		pinfmt.pin=1;
		ms_message("Configuring av recorder with audio format %s",ms_fmt_descriptor_to_string(pinfmt.fmt));
		ms_filter_call_method(stream->av_recorder.recorder,MS_FILTER_SET_INPUT_FMT,&pinfmt);
Simon Morlat's avatar
Simon Morlat committed
636
		ms_filter_add_notify_callback(stream->av_recorder.video_input,video_input_updated,stream,TRUE);
637
		ms_filter_add_notify_callback(stream->av_recorder.recorder, av_recorder_handle_event, stream, TRUE);
Simon Morlat's avatar
Simon Morlat committed
638 639 640 641 642 643 644 645 646
	}
}

static void plumb_av_recorder(AudioStream *stream){
	MSConnectionHelper ch;
	ms_connection_helper_start(&ch);
	ms_connection_helper_link(&ch, stream->recorder_mixer,-1, 1);
	ms_connection_helper_link(&ch, stream->av_recorder.resampler,0,0);
	ms_connection_helper_link(&ch, stream->av_recorder.encoder,0,0);
Simon Morlat's avatar
Simon Morlat committed
647
	ms_connection_helper_link(&ch, stream->av_recorder.recorder,1,-1);
648

Simon Morlat's avatar
Simon Morlat committed
649
	ms_filter_link(stream->av_recorder.video_input,0,stream->av_recorder.recorder,0);
Simon Morlat's avatar
Simon Morlat committed
650 651 652 653
}

static void unplumb_av_recorder(AudioStream *stream){
	MSConnectionHelper ch;
654
	MSRecorderState rstate;
Simon Morlat's avatar
Simon Morlat committed
655 656 657 658
	ms_connection_helper_start(&ch);
	ms_connection_helper_unlink(&ch, stream->recorder_mixer,-1, 1);
	ms_connection_helper_unlink(&ch, stream->av_recorder.resampler,0,0);
	ms_connection_helper_unlink(&ch, stream->av_recorder.encoder,0,0);
Simon Morlat's avatar
Simon Morlat committed
659
	ms_connection_helper_unlink(&ch, stream->av_recorder.recorder,1,-1);
660

Simon Morlat's avatar
Simon Morlat committed
661
	ms_filter_unlink(stream->av_recorder.video_input,0,stream->av_recorder.recorder,0);
662

663 664 665 666 667
	if (ms_filter_call_method(stream->av_recorder.recorder,MS_RECORDER_GET_STATE,&rstate)==0){
		if (rstate!=MSRecorderClosed){
			ms_filter_call_method_noarg(stream->av_recorder.recorder, MS_RECORDER_CLOSE);
		}
	}
Simon Morlat's avatar
Simon Morlat committed
668 669 670 671 672
}

static void setup_recorder(AudioStream *stream, int sample_rate, int nchannels){
	int val=0;
	int pin=1;
673
	MSAudioMixerCtl mctl={0};
674

675 676 677 678
	stream->recorder=ms_factory_create_filter(stream->ms.factory, MS_FILE_REC_ID);
	stream->recorder_mixer=ms_factory_create_filter(stream->ms.factory, MS_AUDIO_MIXER_ID);
	stream->recv_tee=ms_factory_create_filter(stream->ms.factory, MS_TEE_ID);

Simon Morlat's avatar
Simon Morlat committed
679 680 681 682
	ms_filter_call_method(stream->recorder_mixer,MS_AUDIO_MIXER_ENABLE_CONFERENCE_MODE,&val);
	ms_filter_call_method(stream->recorder_mixer,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
	ms_filter_call_method(stream->recorder_mixer,MS_FILTER_SET_NCHANNELS,&nchannels);
	ms_filter_call_method(stream->recv_tee,MS_TEE_MUTE,&pin);
683 684 685
	mctl.pin=pin;
	mctl.param.enabled=FALSE;
	ms_filter_call_method(stream->outbound_mixer,MS_AUDIO_MIXER_ENABLE_OUTPUT,&mctl);
Simon Morlat's avatar
Simon Morlat committed
686 687
	ms_filter_call_method(stream->recorder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
	ms_filter_call_method(stream->recorder,MS_FILTER_SET_NCHANNELS,&nchannels);
688

Simon Morlat's avatar
Simon Morlat committed
689 690 691
	setup_av_recorder(stream,sample_rate,nchannels);
}

692
static void on_silence_detected(void *data, MSFilter *f, unsigned int event_id, void *event_arg){
693 694 695 696
	AudioStream *as=(AudioStream*)data;
	if (as->ms.rtpsend){
		switch(event_id){
			case MS_VAD_DTX_NO_VOICE:
Simon Morlat's avatar
Simon Morlat committed
697
				/*ms_message("on_silence_detected(): CN packet to be sent !");*/
698 699 700 701
				ms_filter_call_method(as->ms.rtpsend, MS_RTP_SEND_SEND_GENERIC_CN, event_arg);
				ms_filter_call_method(as->ms.rtpsend, MS_RTP_SEND_MUTE, event_arg);
			break;
			case MS_VAD_DTX_VOICE:
Simon Morlat's avatar
Simon Morlat committed
702
				/*ms_message("on_silence_detected(): resuming audio");*/
703 704 705 706 707 708 709 710 711 712 713 714
				ms_filter_call_method(as->ms.rtpsend, MS_RTP_SEND_UNMUTE, event_arg);
			break;
		}
	}
}

static void on_cn_received(void *data, MSFilter *f, unsigned int event_id, void *event_arg){
	AudioStream *as=(AudioStream*)data;
	if (as->plc){
		ms_message("CN packet received, given to MSGenericPlc filter.");
		ms_filter_call_method(as->plc, MS_GENERIC_PLC_SET_CN, event_arg);
	}
715 716 717 718 719 720
}

static void setup_generic_confort_noise(AudioStream *stream){
	RtpProfile *prof=rtp_session_get_profile(stream->ms.sessions.rtp_session);
	PayloadType *pt=rtp_profile_get_payload(prof, rtp_session_get_send_payload_type(stream->ms.sessions.rtp_session));
	PayloadType *cn=rtp_profile_find_payload(prof, "CN", 8000, 1);
721

722 723 724 725 726
	if (cn && pt && pt->channels==1){
		int samplerate = pt->clock_rate;
		ms_filter_call_method(stream->ms.decoder, MS_FILTER_GET_SAMPLE_RATE, &samplerate);
		if (samplerate == 8000){
			/* RFC3389 CN can be used only for 8khz codecs*/
727
			stream->vaddtx=ms_factory_create_filter(stream->ms.factory, MS_VAD_DTX_ID);
728 729 730 731 732 733
			if (stream->vaddtx) {
				ms_filter_add_notify_callback(stream->vaddtx, on_silence_detected, stream, TRUE);
				ms_filter_add_notify_callback(stream->ms.rtprecv, on_cn_received, stream, TRUE);
			} else {
				ms_warning("Cannot instantiate vaddtx filter!");
			}
734

735
		}
736 737 738
	}
}

739 740 741 742 743 744 745 746 747 748 749 750 751
static void configure_decoder(AudioStream *stream, PayloadType *pt, int sample_rate, int nchannels){
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_NCHANNELS,&nchannels);
	if (pt->recv_fmtp!=NULL) ms_filter_call_method(stream->ms.decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);
	if (ms_filter_has_method(stream->ms.decoder, MS_AUDIO_DECODER_SET_RTP_PAYLOAD_PICKER) || ms_filter_has_method(stream->ms.decoder, MS_FILTER_SET_RTP_PAYLOAD_PICKER)) {
		MSRtpPayloadPickerContext picker_context;
		ms_message("Decoder has FEC capabilities");
		picker_context.filter_graph_manager=stream;
		picker_context.picker=&audio_stream_payload_picker;
		ms_filter_call_method(stream->ms.decoder,MS_AUDIO_DECODER_SET_RTP_PAYLOAD_PICKER, &picker_context);
	}
}

752 753
static int get_usable_telephone_event(RtpProfile *profile, int clock_rate){
	int i;
754
	int fallback_pt=-1;
755 756
	for(i=0; i<128; ++i){
		PayloadType *pt = profile->payload[i];
757 758 759 760 761
		if (pt && strcasecmp(pt->mime_type, "telephone-event")==0 && (pt->flags & PAYLOAD_TYPE_FLAG_CAN_SEND)){
			if (pt->clock_rate == clock_rate)
				return i;
			if (pt->clock_rate == 8000)
				fallback_pt = i;
762 763
		}
	}
764 765 766 767 768 769
	/*
	 * the fallback payload type is used if the remote equipment doesn't conform to RFC4733 2.1,
	 * that requires to use a clock rate which is the same as the audio codec.
	 */
	if (fallback_pt !=-1) ms_warning("The remote equipment doesn't conform to RFC4733 2.1 - it wants to use telephone-event/8000 despite the clock rate of the audio codec is %i", clock_rate);
	return fallback_pt;
770 771
}

772
int audio_stream_start_from_io(AudioStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
773
	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, const MSMediaStreamIO *io) {
774
	RtpSession *rtps=stream->ms.sessions.rtp_session;
775 776
	PayloadType *pt;
	int tmp, tev_pt;
777
	MSConnectionHelper h;
778
	int sample_rate;
779
	int nchannels;
780
	int err1,err2;
781
	bool_t has_builtin_ec=FALSE;
782

783
	if (!ms_media_stream_io_is_consistent(io)) return -1;
aymeric's avatar
aymeric committed
784 785

	rtp_session_set_profile(rtps,profile);
786
	if (rem_rtp_port>0) rtp_session_set_remote_addr_full(rtps,rem_rtp_ip,rem_rtp_port,rem_rtcp_ip,rem_rtcp_port);
787 788 789 790
	if (rem_rtcp_port > 0) {
		rtp_session_enable_rtcp(rtps, TRUE);
	} else {
		rtp_session_enable_rtcp(rtps, FALSE);
Simon Morlat's avatar
Simon Morlat committed
791
	}
aymeric's avatar
aymeric committed
792
	rtp_session_set_payload_type(rtps,payload);
Jehan Monnier's avatar
Jehan Monnier committed
793

794
	if (rem_rtp_port>0)
Ghislain MARY's avatar
Ghislain MARY committed
795
		ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_SET_SESSION,rtps);
796
	stream->ms.rtprecv=ms_factory_create_filter(stream->ms.factory,MS_RTP_RECV_ID);
Ghislain MARY's avatar
Ghislain MARY committed
797
	ms_filter_call_method(stream->ms.rtprecv,MS_RTP_RECV_SET_SESSION,rtps);
798
	stream->ms.sessions.rtp_session=rtps;
Jehan Monnier's avatar
Jehan Monnier committed
799

800
	if((stream->features & AUDIO_STREAM_FEATURE_DTMF_ECHO) != 0)
801
		stream->dtmfgen=ms_factory_create_filter(stream->ms.factory, MS_DTMF_GEN_ID);
Yann Diorcet's avatar
Yann Diorcet committed
802 803
	else
		stream->dtmfgen=NULL;
Ghislain MARY's avatar
Ghislain MARY committed
804
	rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,stream);
805
	rtp_session_signal_connect(rtps,"payload_type_changed",(RtpCallback)audio_stream_payload_type_changed,stream);
806

807 808
	if (stream->ms.state==MSStreamPreparing){
		/*we were using the dummy preload graph, destroy it but keep sound filters unless no soundcard is given*/
809
		_audio_stream_unprepare_sound(stream, io->input.type == MSResourceSoundcard);
810
	}
811

aymeric's avatar
aymeric committed
812
	/* creates the local part */
813
	if (io->input.type == MSResourceSoundcard){
Simon Morlat's avatar
Simon Morlat committed
814
		if (stream->soundread==NULL)
815 816 817 818
			stream->soundread = ms_snd_card_create_reader(io->input.soundcard);
		has_builtin_ec=!!(ms_snd_card_get_capabilities(io->input.soundcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER);
	} else if (io->input.type == MSResourceRtp) {
		stream->rtp_io_session = io->input.session;
819 820
		pt = rtp_profile_get_payload(rtp_session_get_profile(stream->rtp_io_session),
			rtp_session_get_recv_payload_type(stream->rtp_io_session));
821
		stream->soundread = ms_factory_create_filter(stream->ms.factory, MS_RTP_RECV_ID);
822
		ms_filter_call_method(stream->soundread, MS_RTP_RECV_SET_SESSION, stream->rtp_io_session);
823
		stream->read_decoder = ms_factory_create_decoder(stream->ms.factory, pt->mime_type);
824
	} else {
825 826 827
		stream->soundread=ms_factory_create_filter(stream->ms.factory, MS_FILE_PLAYER_ID);
		stream->read_resampler=ms_factory_create_filter(stream->ms.factory, MS_RESAMPLE_ID);

aymeric's avatar
aymeric committed
828
	}
829
	if (io->output.type == MSResourceSoundcard) {
Simon Morlat's avatar
Simon Morlat committed
830
		if (stream->soundwrite==NULL)
831 832 833
			stream->soundwrite=ms_snd_card_create_writer(io->output.soundcard);
	} else if (io->output.type == MSResourceRtp) {
		stream->rtp_io_session = io->output.session;
834 835
		pt = rtp_profile_get_payload(rtp_session_get_profile(stream->rtp_io_session),
			rtp_session_get_send_payload_type(stream->rtp_io_session));
836 837
		stream->soundwrite = ms_factory_create_filter(stream->ms.factory, MS_RTP_SEND_ID);

838
		ms_filter_call_method(stream->soundwrite, MS_RTP_SEND_SET_SESSION, stream->rtp_io_session);
Sandrine Avakian's avatar
Sandrine Avakian committed
839

840 841
		stream->write_encoder = ms_factory_create_decoder(stream->ms.factory,pt->mime_type);

842
	} else {
843 844
		stream->soundwrite=ms_factory_create_filter(stream->ms.factory, MS_FILE_REC_ID);

aymeric's avatar
aymeric committed
845
	}
Jehan Monnier's avatar
Jehan Monnier committed
846

aymeric's avatar
aymeric committed
847 848 849 850 851 852
	/* creates the couple of encoder/decoder */
	pt=rtp_profile_get_payload(profile,payload);
	if (pt==NULL){
		ms_error("audiostream.c: undefined payload type.");
		return -1;
	}
<