audiostream.c 43.4 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 21 22 23 24 25 26 27
/*
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.
*/


#include "mediastreamer2/mediastream.h"

#include "mediastreamer2/dtmfgen.h"
#include "mediastreamer2/mssndcard.h"
#include "mediastreamer2/msrtp.h"
#include "mediastreamer2/msfileplayer.h"
#include "mediastreamer2/msfilerec.h"
28
#include "mediastreamer2/msvolume.h"
smorlat's avatar
smorlat committed
29
#include "mediastreamer2/msequalizer.h"
30 31
#include "mediastreamer2/mstee.h"
#include "mediastreamer2/msaudiomixer.h"
32
#include "mediastreamer2/mscodecutils.h"
Simon Morlat's avatar
Simon Morlat committed
33
#include "mediastreamer2/msitc.h"
Ghislain MARY's avatar
Ghislain MARY committed
34
#include "private.h"
aymeric's avatar
aymeric committed
35

36 37 38 39 40
#ifdef HAVE_CONFIG_H
#include "mediastreamer-config.h"
#endif


41 42
#include <sys/types.h>

aymeric's avatar
aymeric committed
43 44 45 46 47
#ifndef WIN32
	#include <sys/socket.h>
	#include <netdb.h>
#endif

48
static void audio_stream_free(AudioStream *stream) {
Ghislain MARY's avatar
Ghislain MARY committed
49
	media_stream_free(&stream->ms);
aymeric's avatar
aymeric committed
50 51 52
	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);
53
	if (stream->plc!=NULL)	ms_filter_destroy(stream->plc);
aymeric's avatar
aymeric committed
54
	if (stream->ec!=NULL)	ms_filter_destroy(stream->ec);
55 56
	if (stream->volrecv!=NULL) ms_filter_destroy(stream->volrecv);
	if (stream->volsend!=NULL) ms_filter_destroy(stream->volsend);
57
	if (stream->equalizer!=NULL) ms_filter_destroy(stream->equalizer);
jehan's avatar
jehan committed
58 59
	if (stream->read_resampler!=NULL) ms_filter_destroy(stream->read_resampler);
	if (stream->write_resampler!=NULL) ms_filter_destroy(stream->write_resampler);
60
	if (stream->dtmfgen_rtp!=NULL) ms_filter_destroy(stream->dtmfgen_rtp);
Simon Morlat's avatar
Simon Morlat committed
61
	if (stream->dummy) ms_filter_destroy(stream->dummy);
62 63 64 65
	if (stream->recv_tee) ms_filter_destroy(stream->recv_tee);
	if (stream->send_tee) ms_filter_destroy(stream->send_tee);
	if (stream->recorder) ms_filter_destroy(stream->recorder);
	if (stream->recorder_mixer) ms_filter_destroy(stream->recorder_mixer);
66 67 68
	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
69 70 71 72
	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);
73
	if (stream->recorder_file) ms_free(stream->recorder_file);
Simon Morlat's avatar
Simon Morlat committed
74
	
aymeric's avatar
aymeric committed
75 76 77 78 79 80 81
	ms_free(stream);
}

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

static void on_dtmf_received(RtpSession *s, int dtmf, void * user_data)
{
smorlat's avatar
smorlat committed
82
	AudioStream *stream=(AudioStream*)user_data;
aymeric's avatar
aymeric committed
83 84 85 86 87
	if (dtmf>15){
		ms_warning("Unsupported telephone-event type.");
		return;
	}
	ms_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
smorlat's avatar
smorlat committed
88 89
	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
90 91 92
	}
}

93 94 95 96 97
/*
 * note: since not all filters implement MS_FILTER_GET_SAMPLE_RATE, fallback_from_rate and fallback_to_rate are expected to provide sample rates
 * obtained by another context, such as the RTP clock rate for example.
 */
static void audio_stream_configure_resampler(MSFilter *resampler,MSFilter *from, MSFilter *to, int fallback_from_rate, int fallback_to_rate) {
98
	int from_rate=0, to_rate=0;
99
	int from_channels = 0, to_channels = 0;
100 101
	ms_filter_call_method(from,MS_FILTER_GET_SAMPLE_RATE,&from_rate);
	ms_filter_call_method(to,MS_FILTER_GET_SAMPLE_RATE,&to_rate);
102 103
	ms_filter_call_method(from, MS_FILTER_GET_NCHANNELS, &from_channels);
	ms_filter_call_method(to, MS_FILTER_GET_NCHANNELS, &to_channels);
104 105 106 107 108 109 110 111
	if (from_channels == 0) {
		from_channels = 1;
		ms_error("Filter %s does not implement the MS_FILTER_GET_NCHANNELS method", from->desc->name);
	}
	if (to_channels == 0) {
		to_channels = 1;
		ms_error("Filter %s does not implement the MS_FILTER_GET_NCHANNELS method", to->desc->name);
	}
112
	if (from_rate == 0){
113 114
		ms_error("Filter %s does not implement the MS_FILTER_GET_SAMPLE_RATE method", from->desc->name);
		from_rate=fallback_from_rate;
115 116
	}
	if (to_rate == 0){
117 118
		ms_error("Filter %s does not implement the MS_FILTER_GET_SAMPLE_RATE method", to->desc->name);
		to_rate=fallback_to_rate;
119
	}
120 121
	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);
122 123 124
	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]",
125
			   from->desc->name, to->desc->name, from_rate, to_rate, from_channels, to_channels);
126 127
}

jehan's avatar
jehan committed
128
static void audio_stream_process_rtcp(MediaStream *media_stream, mblk_t *m){
129 130
}

131
void audio_stream_iterate(AudioStream *stream){
132
	media_stream_iterate(&stream->ms);
133 134 135
}

bool_t audio_stream_alive(AudioStream * stream, int timeout){
136
	return media_stream_alive((MediaStream*)stream,timeout);
aymeric's avatar
aymeric committed
137 138
}

jehan's avatar
jehan committed
139 140
/*invoked from FEC capable filters*/
static  mblk_t* audio_stream_payload_picker(MSRtpPayloadPickerContext* context,unsigned int sequence_number) {
141
	return rtp_session_pick_with_cseq(((AudioStream*)(context->filter_graph_manager))->ms.sessions.rtp_session, sequence_number);
Simon Morlat's avatar
Simon Morlat committed
142 143 144
}

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

Ghislain MARY's avatar
Ghislain MARY committed
147 148 149 150
	if (stream->ms.voidsink) {
		ms_filter_unlink(stream->dummy,0,stream->ms.voidsink,0);
		ms_filter_destroy(stream->ms.voidsink);
		stream->ms.voidsink=NULL;
151 152
	}else if (stream->soundwrite) {
		ms_filter_unlink(stream->dummy,0,stream->soundwrite,0);
153
	}
Simon Morlat's avatar
Simon Morlat committed
154 155 156 157 158
	ms_filter_destroy(stream->dummy);
	stream->dummy=NULL;
}

bool_t audio_stream_started(AudioStream *stream){
159
	return stream->ms.start_time!=0;
Simon Morlat's avatar
Simon Morlat committed
160 161
}

162
/* 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
163
void audio_stream_prepare_sound(AudioStream *stream, MSSndCard *playcard, MSSndCard *captcard){
164 165
	audio_stream_unprepare_sound(stream);
	stream->dummy=ms_filter_new(MS_RTP_RECV_ID);
166
	rtp_session_set_payload_type(stream->ms.sessions.rtp_session,0);
167
	rtp_session_enable_rtcp(stream->ms.sessions.rtp_session, FALSE);
168
	ms_filter_call_method(stream->dummy,MS_RTP_RECV_SET_SESSION,stream->ms.sessions.rtp_session);
169

Simon Morlat's avatar
Simon Morlat committed
170
	if (captcard && playcard){
171
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
172 173 174
		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);
175
#else
Ghislain MARY's avatar
Ghislain MARY committed
176 177
		stream->ms.voidsink=ms_filter_new(MS_VOID_SINK_ID);
		ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
Simon Morlat's avatar
Simon Morlat committed
178
#endif
179
	} else {
Ghislain MARY's avatar
Ghislain MARY committed
180 181
		stream->ms.voidsink=ms_filter_new(MS_VOID_SINK_ID);
		ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
182
	}
183 184 185
	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
186 187
}

Simon Morlat's avatar
Simon Morlat committed
188
static void _audio_stream_unprepare_sound(AudioStream *stream, bool_t keep_sound_resources){
189
	if (stream->ms.state==MSStreamPreparing){
Simon Morlat's avatar
Simon Morlat committed
190
		stop_preload_graph(stream);
191
#ifdef __ios
Simon Morlat's avatar
Simon Morlat committed
192 193 194 195 196 197
		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
198
#endif
199
	}
200
	stream->ms.state=MSStreamInitialized;
Simon Morlat's avatar
Simon Morlat committed
201 202
}

Simon Morlat's avatar
Simon Morlat committed
203 204 205 206
void audio_stream_unprepare_sound(AudioStream *stream){
	_audio_stream_unprepare_sound(stream,FALSE);
}

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
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){
		case MS_PLAYER_FORMAT_CHANGED:
			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
227
	int master=0;
228

229 230
	stream->local_player=ms_filter_new(MS_FILE_PLAYER_ID);
	stream->local_player_resampler=ms_filter_new(MS_RESAMPLE_ID);
231

232 233 234 235
	ms_connection_helper_start(&cnx);
	ms_connection_helper_link(&cnx,stream->local_player,-1,0);
	ms_connection_helper_link(&cnx,stream->local_player_resampler,0,0);
	ms_connection_helper_link(&cnx,stream->local_mixer,1,-1);
236

237 238 239 240
	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);
	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
241
	ms_filter_call_method(stream->local_mixer,MS_AUDIO_MIXER_SET_MASTER_CHANNEL,&master);
242 243 244
	ms_filter_add_notify_callback(stream->local_player,player_callback,stream,TRUE);
}

245 246 247 248
static OrtpRtcpXrPlcStatus audio_stream_get_rtcp_xr_plc_status(unsigned long userdata) {
	AudioStream *stream = (AudioStream *)userdata;
	if ((stream->features & AUDIO_STREAM_FEATURE_PLC) != 0) {
		int decoder_have_plc = 0;
249
		if (stream->ms.decoder && ms_filter_has_method(stream->ms.decoder, MS_AUDIO_DECODER_HAVE_PLC)) {
250 251 252 253 254 255 256 257 258 259 260
			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;
}

261
static int audio_stream_get_rtcp_xr_signal_level(unsigned long userdata) {
262 263
	AudioStream *stream = (AudioStream *)userdata;
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0) {
264 265 266
		float volume = 0.f;
		if (stream->volrecv)
			ms_filter_call_method(stream->volrecv, MS_VOLUME_GET_MAX, &volume);
267
		return (int)volume;
268 269 270 271
	}
	return ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;
}

272
static int audio_stream_get_rtcp_xr_noise_level(unsigned long userdata) {
273 274
	AudioStream *stream = (AudioStream *)userdata;
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0) {
275 276 277
		float volume = 0.f;
		if (stream->volrecv)
			ms_filter_call_method(stream->volrecv, MS_VOLUME_GET_MIN, &volume);
278
		return (int)volume;
279 280 281 282 283 284 285 286 287 288 289 290 291 292
	}
	return ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;
}

static float audio_stream_get_rtcp_xr_average_quality_rating(unsigned long userdata) {
	AudioStream *stream = (AudioStream *)userdata;
	return audio_stream_get_average_quality_rating(stream);
}

static float audio_stream_get_rtcp_xr_average_lq_quality_rating(unsigned long userdata) {
	AudioStream *stream = (AudioStream *)userdata;
	return audio_stream_get_average_lq_quality_rating(stream);
}

Simon Morlat's avatar
Simon Morlat committed
293 294 295 296
static void setup_av_recorder(AudioStream *stream, int sample_rate, int nchannels){
	
	stream->av_recorder.recorder=ms_filter_new(MS_MKV_WRITER_ID);
	if (stream->av_recorder.recorder){
Simon Morlat's avatar
Simon Morlat committed
297
		MSPinFormat pinfmt={0};
Simon Morlat's avatar
Simon Morlat committed
298 299 300 301 302 303 304 305 306 307 308
		stream->av_recorder.video_input=ms_filter_new(MS_ITC_SOURCE_ID);
		stream->av_recorder.resampler=ms_filter_new(MS_RESAMPLE_ID);
		stream->av_recorder.encoder=ms_filter_new(MS_OPUS_ENC_ID);
		if (stream->av_recorder.encoder==NULL){
			int g711_rate=8000;
			int g711_nchannels=1;
			stream->av_recorder.encoder=ms_filter_new(MS_ULAW_ENC_ID);
			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);
Simon Morlat's avatar
Simon Morlat committed
309
			pinfmt.fmt=ms_factory_get_audio_format(ms_factory_get_fallback(),"pcmu",g711_rate,g711_nchannels,NULL);
Simon Morlat's avatar
Simon Morlat committed
310 311 312 313 314 315 316 317 318 319
			
		}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);
Simon Morlat's avatar
Simon Morlat committed
320
			pinfmt.fmt=ms_factory_get_audio_format(ms_factory_get_fallback(),"opus",48000,nchannels,NULL);
Simon Morlat's avatar
Simon Morlat committed
321
		}
Simon Morlat's avatar
Simon Morlat committed
322 323 324
		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
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
	}
}

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);
	ms_connection_helper_link(&ch, stream->av_recorder.recorder,0,0);
	
	ms_filter_link(stream->av_recorder.video_input,0,stream->av_recorder.recorder,1);
}

static void unplumb_av_recorder(AudioStream *stream){
	MSConnectionHelper ch;
	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);
	ms_connection_helper_unlink(&ch, stream->av_recorder.recorder,0,0);
	
	ms_filter_unlink(stream->av_recorder.video_input,0,stream->av_recorder.recorder,1);
}

static void setup_recorder(AudioStream *stream, int sample_rate, int nchannels){
	int val=0;
	int pin=1;
	
	stream->recorder=ms_filter_new(MS_FILE_REC_ID);
	stream->recorder_mixer=ms_filter_new(MS_AUDIO_MIXER_ID);
	stream->recv_tee=ms_filter_new(MS_TEE_ID);
	stream->send_tee=ms_filter_new(MS_TEE_ID);
	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);
	ms_filter_call_method(stream->send_tee,MS_TEE_MUTE,&pin);
	ms_filter_call_method(stream->recorder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
	ms_filter_call_method(stream->recorder,MS_FILTER_SET_NCHANNELS,&nchannels);
	
	setup_av_recorder(stream,sample_rate,nchannels);
}

369 370
int audio_stream_start_full(AudioStream *stream, RtpProfile *profile, const char *rem_rtp_ip,int rem_rtp_port,
	const char *rem_rtcp_ip, int rem_rtcp_port, int payload,int jitt_comp, const char *infile, const char *outfile,
371 372
	MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec){
	RtpSession *rtps=stream->ms.sessions.rtp_session;
Simon Morlat's avatar
Simon Morlat committed
373
	PayloadType *pt,*tel_ev;
374 375
	int tmp;
	MSConnectionHelper h;
376
	int sample_rate;
jehan's avatar
jehan committed
377
	MSRtpPayloadPickerContext picker_context;
378
	int nchannels;
379
	bool_t has_builtin_ec=FALSE;
380 381 382 383 384 385 386 387
	const OrtpRtcpXrMediaCallbacks rtcp_xr_media_cbs = {
		audio_stream_get_rtcp_xr_plc_status,
		audio_stream_get_rtcp_xr_signal_level,
		audio_stream_get_rtcp_xr_noise_level,
		audio_stream_get_rtcp_xr_average_quality_rating,
		audio_stream_get_rtcp_xr_average_lq_quality_rating,
		(unsigned long)stream
	};
aymeric's avatar
aymeric committed
388 389

	rtp_session_set_profile(rtps,profile);
390
	if (rem_rtp_port>0) rtp_session_set_remote_addr_full(rtps,rem_rtp_ip,rem_rtp_port,rem_rtcp_ip,rem_rtcp_port);
391 392 393 394
	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
395
	}
aymeric's avatar
aymeric committed
396 397
	rtp_session_set_payload_type(rtps,payload);
	rtp_session_set_jitter_compensation(rtps,jitt_comp);
398
	rtp_session_set_rtcp_xr_media_callbacks(rtps, &rtcp_xr_media_cbs);
Jehan Monnier's avatar
Jehan Monnier committed
399

400
	if (rem_rtp_port>0)
Ghislain MARY's avatar
Ghislain MARY committed
401 402 403
		ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_SET_SESSION,rtps);
	stream->ms.rtprecv=ms_filter_new(MS_RTP_RECV_ID);
	ms_filter_call_method(stream->ms.rtprecv,MS_RTP_RECV_SET_SESSION,rtps);
404
	stream->ms.sessions.rtp_session=rtps;
Jehan Monnier's avatar
Jehan Monnier committed
405

Yann Diorcet's avatar
Yann Diorcet committed
406 407
	if((stream->features & AUDIO_STREAM_FEATURE_DTMF) != 0)
		stream->dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
Yann Diorcet's avatar
Yann Diorcet committed
408 409
	else
		stream->dtmfgen=NULL;
smorlat's avatar
smorlat committed
410
	rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,(unsigned long)stream);
411
	rtp_session_signal_connect(rtps,"payload_type_changed",(RtpCallback)mediastream_payload_type_changed,(unsigned long)&stream->ms);
aymeric's avatar
aymeric committed
412
	/* creates the local part */
Simon Morlat's avatar
Simon Morlat committed
413 414 415
	if (captcard!=NULL){
		if (stream->soundread==NULL)
			stream->soundread=ms_snd_card_create_reader(captcard);
416
		has_builtin_ec=!!(ms_snd_card_get_capabilities(captcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER);
Simon Morlat's avatar
Simon Morlat committed
417
	}else {
aymeric's avatar
aymeric committed
418
		stream->soundread=ms_filter_new(MS_FILE_PLAYER_ID);
419
		stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
aymeric's avatar
aymeric committed
420
	}
Simon Morlat's avatar
Simon Morlat committed
421 422 423 424
	if (playcard!=NULL) {
		if (stream->soundwrite==NULL)
			stream->soundwrite=ms_snd_card_create_writer(playcard);
	}else {
aymeric's avatar
aymeric committed
425 426
		stream->soundwrite=ms_filter_new(MS_FILE_REC_ID);
	}
Jehan Monnier's avatar
Jehan Monnier committed
427

aymeric's avatar
aymeric committed
428 429 430 431 432 433
	/* 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;
	}
434
	nchannels=pt->channels;
Simon Morlat's avatar
Simon Morlat committed
435 436
	tel_ev=rtp_profile_get_payload_from_mime (profile,"telephone-event");

Yann Diorcet's avatar
Yann Diorcet committed
437
	if ((stream->features & AUDIO_STREAM_FEATURE_DTMF_ECHO) != 0 && (tel_ev==NULL || ( (tel_ev->flags & PAYLOAD_TYPE_FLAG_CAN_RECV) && !(tel_ev->flags & PAYLOAD_TYPE_FLAG_CAN_SEND)))
438
		&& ( strcasecmp(pt->mime_type,"pcmu")==0 || strcasecmp(pt->mime_type,"pcma")==0)){
439 440 441
		/*if no telephone-event payload is usable and pcma or pcmu is used, we will generate
		  inband dtmf*/
		stream->dtmfgen_rtp=ms_filter_new (MS_DTMF_GEN_ID);
Yann Diorcet's avatar
Yann Diorcet committed
442 443
	} else {
		stream->dtmfgen_rtp=NULL;
444
	}
445

Ghislain MARY's avatar
Ghislain MARY committed
446
	if (ms_filter_call_method(stream->ms.rtpsend,MS_FILTER_GET_SAMPLE_RATE,&sample_rate)!=0){
447 448 449
		ms_error("Sample rate is unknown for RTP side !");
		return -1;
	}
450

Ghislain MARY's avatar
Ghislain MARY committed
451 452
	stream->ms.encoder=ms_filter_create_encoder(pt->mime_type);
	stream->ms.decoder=ms_filter_create_decoder(pt->mime_type);
453 454 455 456 457 458 459 460 461 462 463 464 465 466

	/* sample rate is already set for rtpsend and rtprcv, check if we have to adjust it to */
	/* be able to use the echo canceller wich may be limited (webrtc aecm max frequency is 16000 Hz) */
	// First check if we need to use the echo canceller
	// Overide feature if not requested or done at sound card level
	if ( ((stream->features & AUDIO_STREAM_FEATURE_EC) && !use_ec) || has_builtin_ec )
		stream->features &=~AUDIO_STREAM_FEATURE_EC;

	/*configure the echo canceller if required */
	if ((stream->features & AUDIO_STREAM_FEATURE_EC) == 0 && stream->ec != NULL) {
		ms_filter_destroy(stream->ec);
		stream->ec=NULL;
	}

Simon Morlat's avatar
Simon Morlat committed
467 468 469 470 471
	if ((stream->ms.encoder==NULL) || (stream->ms.decoder==NULL)){
		/* big problem: we have not a registered codec for this payload...*/
		ms_error("audio_stream_start_full: No decoder or encoder available for payload %s.",pt->mime_type);
		return -1;
	}
472

473 474
	/* check echo canceller max frequency and adjust sampling rate if needed when codec used is opus */
	if (stream->ec!=NULL) {
475 476 477 478 479 480 481 482 483 484 485
		int ec_sample_rate = sample_rate;
		ms_filter_call_method(stream->ec, MS_FILTER_SET_SAMPLE_RATE, &sample_rate);
		ms_filter_call_method(stream->ec, MS_FILTER_GET_SAMPLE_RATE, &ec_sample_rate);
		if (sample_rate != ec_sample_rate) {
			if (ms_filter_get_id(stream->ms.encoder) == MS_OPUS_ENC_ID) {
				sample_rate = ec_sample_rate;
				ms_message("Sampling rate forced to %iHz to allow the use of echo canceller", sample_rate);
			} else {
				ms_warning("Echo canceller does not support sampling rate %iHz, so it has been disabled", sample_rate);
				ms_filter_destroy(stream->ec);
				stream->ec = NULL;
486 487 488
			}
		}
	}
489 490 491
	/*hack for opus, that claims stereo all the time, but we can't support stereo yet*/
	if (strcasecmp(pt->mime_type,"opus")==0)
		nchannels=1;
492

Simon Morlat's avatar
Simon Morlat committed
493
	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)) {
494
		ms_message("Decoder has FEC capabilities");
jehan's avatar
jehan committed
495 496
		picker_context.filter_graph_manager=stream;
		picker_context.picker=&audio_stream_payload_picker;
Simon Morlat's avatar
Simon Morlat committed
497
		ms_filter_call_method(stream->ms.decoder,MS_AUDIO_DECODER_SET_RTP_PAYLOAD_PICKER, &picker_context);
jehan's avatar
jehan committed
498
	}
499
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_SND) != 0)
Yann Diorcet's avatar
Yann Diorcet committed
500
		stream->volsend=ms_filter_new(MS_VOLUME_ID);
Yann Diorcet's avatar
Yann Diorcet committed
501 502
	else
		stream->volsend=NULL;
503
	if ((stream->features & AUDIO_STREAM_FEATURE_VOL_RCV) != 0)
Yann Diorcet's avatar
Yann Diorcet committed
504 505 506
		stream->volrecv=ms_filter_new(MS_VOLUME_ID);
	else
		stream->volrecv=NULL;
jehan's avatar
jehan committed
507 508
	audio_stream_enable_echo_limiter(stream,stream->el_type);
	audio_stream_enable_noise_gate(stream,stream->use_ng);
509

510 511 512
	if (ms_filter_implements_interface(stream->soundread,MSFilterPlayerInterface) && infile){
		audio_stream_play(stream,infile);
	}
Simon Morlat's avatar
Simon Morlat committed
513
	if (ms_filter_implements_interface(stream->soundwrite,MSFilterRecorderInterface) && outfile){
514 515
		audio_stream_record(stream,outfile);
	}
aymeric's avatar
aymeric committed
516

517 518 519 520 521 522 523
	if (stream->use_agc){
		int tmp=1;
		if (stream->volsend==NULL)
			stream->volsend=ms_filter_new(MS_VOLUME_ID);
		ms_filter_call_method(stream->volsend,MS_VOLUME_ENABLE_AGC,&tmp);
	}

524
	if (stream->dtmfgen) {
525
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
526
		ms_filter_call_method(stream->dtmfgen,MS_FILTER_SET_NCHANNELS,&nchannels);
527 528
	}
	if (stream->dtmfgen_rtp) {
529
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
530
		ms_filter_call_method(stream->dtmfgen_rtp,MS_FILTER_SET_NCHANNELS,&nchannels);
531
	}
aymeric's avatar
aymeric committed
532
	/* give the sound filters some properties */
533
	if (ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
534 535 536
		/* need to add resampler*/
		if (stream->read_resampler == NULL) stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
537
	ms_filter_call_method(stream->soundread,MS_FILTER_SET_NCHANNELS,&nchannels);
538

539
	if (ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
540 541 542
		/* need to add resampler*/
		if (stream->write_resampler == NULL) stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
	}
543
	ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS,&nchannels);
Jehan Monnier's avatar
Jehan Monnier committed
544

545
	if (stream->ec){
546 547 548 549 550
		if (!stream->is_ec_delay_set){
			int delay_ms=ms_snd_card_get_minimal_latency(captcard);
			if (delay_ms!=0){
				ms_message("Setting echo canceller delay with value provided by soundcard: %i ms",delay_ms);
			}
551
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
Simon Morlat's avatar
Simon Morlat committed
552 553
		}else {
			ms_message("Setting echo canceller delay with value configured by application.");
554
		}
555
		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
556
	}
557

Simon Morlat's avatar
Simon Morlat committed
558
	if (stream->features & AUDIO_STREAM_FEATURE_MIXED_RECORDING) setup_recorder(stream,sample_rate,nchannels);
559

aymeric's avatar
aymeric committed
560
	/* give the encoder/decoder some parameters*/
561
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
jehan's avatar
jehan committed
562 563 564 565 566 567
	if (stream->ms.target_bitrate<=0) {
		ms_message("target bitrate not set for stream [%p] using payload's bitrate is %i",stream,stream->ms.target_bitrate=pt->normal_bitrate);
	}
	if (stream->ms.target_bitrate>0){
		ms_message("Setting audio encoder network bitrate to [%i] on stream [%p]",stream->ms.target_bitrate,stream);
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_BITRATE,&stream->ms.target_bitrate);
aymeric's avatar
aymeric committed
568
	}
569
	rtp_session_set_target_upload_bandwidth(rtps, stream->ms.target_bitrate);
570
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_NCHANNELS,&nchannels);
571
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
572
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_NCHANNELS,&nchannels);
Jehan Monnier's avatar
Jehan Monnier committed
573

574 575 576 577 578 579 580 581 582
	if (pt->send_fmtp!=NULL) {
		char value[16]={0};
		int ptime;
		if (ms_filter_has_method(stream->ms.encoder,MS_AUDIO_ENCODER_SET_PTIME)){
			if (fmtp_get_value(pt->send_fmtp,"ptime",value,sizeof(value)-1)){
				ptime=atoi(value);
				ms_filter_call_method(stream->ms.encoder,MS_AUDIO_ENCODER_SET_PTIME,&ptime);
			}
		}
583
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_ADD_FMTP, (void*)pt->send_fmtp);
584
	}
Ghislain MARY's avatar
Ghislain MARY committed
585
	if (pt->recv_fmtp!=NULL) ms_filter_call_method(stream->ms.decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);
Jehan Monnier's avatar
Jehan Monnier committed
586

smorlat's avatar
smorlat committed
587
	/*create the equalizer*/
588
	if ((stream->features & AUDIO_STREAM_FEATURE_EQUALIZER) != 0){
Yann Diorcet's avatar
Yann Diorcet committed
589
		stream->equalizer=ms_filter_new(MS_EQUALIZER_ID);
590 591 592 593 594
		if(stream->equalizer) {
			tmp=stream->eq_active;
			ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_ACTIVE,&tmp);
		}
	}else
Yann Diorcet's avatar
Yann Diorcet committed
595
		stream->equalizer=NULL;
596

597 598
	/*configure resamplers if needed*/
	if (stream->read_resampler){
599
		audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->ms.encoder,8000,pt->clock_rate);
600
	}
Yann Diorcet's avatar
Yann Diorcet committed
601

602
	if (stream->write_resampler){
603
		audio_stream_configure_resampler(stream->write_resampler,stream->ms.decoder,stream->soundwrite,pt->clock_rate,8000);
604
	}
605

Ghislain MARY's avatar
Ghislain MARY committed
606
	if (stream->ms.use_rc){
607
		stream->ms.rc=ms_audio_bitrate_controller_new(stream->ms.sessions.rtp_session,stream->ms.encoder,0);
608
	}
609

610
	/* Create generic PLC if not handled by the decoder directly*/
Yann Diorcet's avatar
Yann Diorcet committed
611 612
	if ((stream->features & AUDIO_STREAM_FEATURE_PLC) != 0) {
		int decoder_have_plc = 0;
613 614 615
		if (ms_filter_has_method(stream->ms.decoder, MS_AUDIO_DECODER_HAVE_PLC)) {
			if (ms_filter_call_method(stream->ms.decoder, MS_AUDIO_DECODER_HAVE_PLC, &decoder_have_plc) != 0) {
				ms_warning("MS_AUDIO_DECODER_HAVE_PLC function error: enable default plc");
616 617
			}
		} else {
Yann Diorcet's avatar
Yann Diorcet committed
618 619
			ms_warning("MS_DECODER_HAVE_PLC function not implemented by the decoder: enable default plc");
		}
620
		if (decoder_have_plc == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
621
			stream->plc = ms_filter_new(MS_GENERIC_PLC_ID);
622
		}
Yann Diorcet's avatar
Yann Diorcet committed
623

Sylvain Berfini's avatar
Sylvain Berfini committed
624
		if (stream->plc) {
625
			ms_filter_call_method(stream->plc, MS_FILTER_SET_NCHANNELS, &nchannels);
Yann Diorcet's avatar
Yann Diorcet committed
626
			ms_filter_call_method(stream->plc, MS_FILTER_SET_SAMPLE_RATE, &sample_rate);
Sylvain Berfini's avatar
Sylvain Berfini committed
627
		}
Yann Diorcet's avatar
Yann Diorcet committed
628 629
	} else {
		stream->plc = NULL;
Yann Diorcet's avatar
Yann Diorcet committed
630
	}
631

632 633 634
	if (stream->features & AUDIO_STREAM_FEATURE_LOCAL_PLAYING){
		stream->local_mixer=ms_filter_new(MS_AUDIO_MIXER_ID);
	}
Yann Diorcet's avatar
Yann Diorcet committed
635

Simon Morlat's avatar
Simon Morlat committed
636
	/* create ticker */
637 638
	if (stream->ms.sessions.ticker==NULL) media_stream_start_ticker(&stream->ms);
	if (stream->ms.state==MSStreamPreparing){
Simon Morlat's avatar
Simon Morlat committed
639 640
		/*we were using the dummy preload graph, destroy it but keep sound filters*/
		_audio_stream_unprepare_sound(stream,TRUE);
Simon Morlat's avatar
Simon Morlat committed
641
	}
642

aymeric's avatar
aymeric committed
643 644
	/* and then connect all */
	/* tip: draw yourself the picture if you don't understand */
Jehan Monnier's avatar
Jehan Monnier committed
645

646 647 648
	/*sending graph*/
	ms_connection_helper_start(&h);
	ms_connection_helper_link(&h,stream->soundread,-1,0);
649 650
	if (stream->read_resampler)
		ms_connection_helper_link(&h,stream->read_resampler,0,0);
651 652 653 654
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,1,1);
	if (stream->volsend)
		ms_connection_helper_link(&h,stream->volsend,0,0);
655 656
	if (stream->dtmfgen_rtp)
		ms_connection_helper_link(&h,stream->dtmfgen_rtp,0,0);
657 658
	if (stream->send_tee)
		ms_connection_helper_link(&h,stream->send_tee,0,0);
Ghislain MARY's avatar
Ghislain MARY committed
659 660
	ms_connection_helper_link(&h,stream->ms.encoder,0,0);
	ms_connection_helper_link(&h,stream->ms.rtpsend,0,-1);
661 662 663

	/*receiving graph*/
	ms_connection_helper_start(&h);
Ghislain MARY's avatar
Ghislain MARY committed
664 665
	ms_connection_helper_link(&h,stream->ms.rtprecv,-1,0);
	ms_connection_helper_link(&h,stream->ms.decoder,0,0);
666
	if (stream->plc)
Yann Diorcet's avatar
Yann Diorcet committed
667 668 669
		ms_connection_helper_link(&h,stream->plc,0,0);
	if (stream->dtmfgen)
		ms_connection_helper_link(&h,stream->dtmfgen,0,0);
670 671
	if (stream->volrecv)
		ms_connection_helper_link(&h,stream->volrecv,0,0);
672 673
	if (stream->recv_tee)
		ms_connection_helper_link(&h,stream->recv_tee,0,0);
674 675
	if (stream->equalizer)
		ms_connection_helper_link(&h,stream->equalizer,0,0);
676 677
	if (stream->local_mixer){
		ms_connection_helper_link(&h,stream->local_mixer,0,0);
678
		setup_local_player(stream,sample_rate, nchannels);
679
	}
680 681
	if (stream->ec)
		ms_connection_helper_link(&h,stream->ec,0,0);
682 683
	if (stream->write_resampler)
		ms_connection_helper_link(&h,stream->write_resampler,0,0);
684
	ms_connection_helper_link(&h,stream->soundwrite,0,-1);
Jehan Monnier's avatar
Jehan Monnier committed
685

686
	/*call recording part, attached to both outgoing and incoming graphs*/
Simon Morlat's avatar
Simon Morlat committed
687 688
	if (stream->av_recorder.recorder)
		plumb_av_recorder(stream);
689 690 691 692 693
	if (stream->recorder){
		ms_filter_link(stream->send_tee,1,stream->recorder_mixer,0);
		ms_filter_link(stream->recv_tee,1,stream->recorder_mixer,1);
		ms_filter_link(stream->recorder_mixer,0,stream->recorder,0);
	}
694

jehan's avatar
jehan committed
695
	/*to make sure all preprocess are done before befre processing audio*/
696
	ms_ticker_attach_multiple(stream->ms.sessions.ticker
697 698 699
				,stream->soundread
				,stream->ms.rtprecv
				,NULL);
Jehan Monnier's avatar
Jehan Monnier committed
700

701
	stream->ms.start_time=stream->ms.last_packet_time=ms_time(NULL);
702
	stream->ms.state=MSStreamStarted;
703

aymeric's avatar
aymeric committed
704 705 706 707 708 709
	return 0;
}

int audio_stream_start_with_files(AudioStream *stream, RtpProfile *prof,const char *remip, int remport,
	int rem_rtcp_port, int pt,int jitt_comp, const char *infile, const char * outfile)
{
710
	return audio_stream_start_full(stream,prof,remip,remport,remip,rem_rtcp_port,pt,jitt_comp,infile,outfile,NULL,NULL,FALSE);
aymeric's avatar
aymeric committed
711 712 713 714
}

AudioStream * audio_stream_start(RtpProfile *prof,int locport,const char *remip,int remport,int profile,int jitt_comp,bool_t use_ec)
{
aymeric's avatar
aymeric committed
715 716
	MSSndCard *sndcard_playback;
	MSSndCard *sndcard_capture;
aymeric's avatar
aymeric committed
717
	AudioStream *stream;
aymeric's avatar
aymeric committed
718 719 720
	sndcard_capture=ms_snd_card_manager_get_default_capture_card(ms_snd_card_manager_get());
	sndcard_playback=ms_snd_card_manager_get_default_playback_card(ms_snd_card_manager_get());
	if (sndcard_capture==NULL || sndcard_playback==NULL)
aymeric's avatar
aymeric committed
721
		return NULL;
722
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
723
	if (audio_stream_start_full(stream,prof,remip,remport,remip,remport+1,profile,jitt_comp,NULL,NULL,sndcard_playback,sndcard_capture,use_ec)==0) return stream;
aymeric's avatar
aymeric committed
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
	audio_stream_free(stream);
	return NULL;
}

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)
{
	AudioStream *stream;
	if (playcard==NULL) {
		ms_error("No playback card.");
		return NULL;
	}
	if (captcard==NULL) {
		ms_error("No capture card.");
		return NULL;
	}
739
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
740
	if (audio_stream_start_full(stream,prof,remip,remport,remip,remport+1,profile,jitt_comp,NULL,NULL,playcard,captcard,use_ec)==0) return stream;
aymeric's avatar
aymeric committed
741 742 743 744
	audio_stream_free(stream);
	return NULL;
}

745
// Pass NULL to stop playing
aymeric's avatar
aymeric committed
746
void audio_stream_play(AudioStream *st, const char *name){
747 748 749 750
	if (st->soundread == NULL) {
		ms_warning("Cannot play file: the stream hasn't been started");
		return;
	}
aymeric's avatar
aymeric committed
751 752
	if (ms_filter_get_id(st->soundread)==MS_FILE_PLAYER_ID){
		ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_CLOSE);
753 754 755
		if (name != NULL) {
			ms_filter_call_method(st->soundread,MS_FILE_PLAYER_OPEN,(void*)name);
			if (st->read_resampler){
756 757 758
				int fallback_to_rate=8000;
				ms_filter_call_method(st->ms.rtpsend,MS_FILTER_GET_SAMPLE_RATE,&fallback_to_rate);
				audio_stream_configure_resampler(st->read_resampler,st->soundread,st->ms.encoder, 8000, fallback_to_rate);
759 760
			}
			ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_START);
761
		}
aymeric's avatar
aymeric committed
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
	}else{
		ms_error("Cannot play file: the stream hasn't been started with"
		" audio_stream_start_with_files");
	}
}

void audio_stream_record(AudioStream *st, const char *name){
	if (ms_filter_get_id(st->soundwrite)==MS_FILE_REC_ID){
		ms_filter_call_method_noarg(st->soundwrite,MS_FILE_REC_CLOSE);
		ms_filter_call_method(st->soundwrite,MS_FILE_REC_OPEN,(void*)name);
		ms_filter_call_method_noarg(st->soundwrite,MS_FILE_REC_START);
	}else{
		ms_error("Cannot record file: the stream hasn't been started with"
		" audio_stream_start_with_files");
	}
}

779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795

int audio_stream_mixed_record_open(AudioStream *st, const char* filename){
	if (!(st->features & AUDIO_STREAM_FEATURE_MIXED_RECORDING)){
		if (audio_stream_started(st)){
			ms_error("Too late - you cannot request a mixed recording when the stream is running because it did not have AUDIO_STREAM_FEATURE_MIXED_RECORDING feature.");
			return -1;
		}else{
			st->features|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
		}
	}
	if (st->recorder_file){
		audio_stream_mixed_record_stop(st);
	}
	st->recorder_file=filename ? ms_strdup(filename) : NULL;
	return 0;
}

Simon Morlat's avatar
Simon Morlat committed
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
static MSFilter *get_recorder(AudioStream *stream){
	const char *fname=stream->recorder_file;
	int len=strlen(fname);
	
	if (strstr(fname,".mkv")==fname+len-4){
		if (stream->av_recorder.recorder){
			return stream->av_recorder.recorder;
		}else{
			ms_error("Cannot record in mkv format, not supported in this build.");
			return NULL;
		}
	}
	return stream->recorder;
}

811 812 813 814
int audio_stream_mixed_record_start(AudioStream *st){
	if (st->recorder && st->recorder_file){
		int pin=1;
		MSRecorderState state;
Simon Morlat's avatar
Simon Morlat committed
815 816 817 818
		MSFilter *recorder=get_recorder(st);
		
		if (recorder==NULL) return -1;
		ms_filter_call_method(recorder,MS_RECORDER_GET_STATE,&state);
819
		if (state==MSRecorderClosed){
Simon Morlat's avatar
Simon Morlat committed
820
			if (ms_filter_call_method(recorder,MS_RECORDER_OPEN,st->recorder_file)==-1)
821 822
				return -1;
		}
Simon Morlat's avatar
Simon Morlat committed
823
		ms_filter_call_method_noarg(recorder,MS_RECORDER_START);
824 825 826 827 828 829 830 831 832 833
		ms_filter_call_method(st->recv_tee,MS_TEE_UNMUTE,&pin);
		ms_filter_call_method(st->send_tee,MS_TEE_UNMUTE,&pin);
		return 0;
	}
	return -1;
}

int audio_stream_mixed_record_stop(AudioStream *st){
	if (st->recorder && st->recorder_file){
		int pin=1;
Simon Morlat's avatar
Simon Morlat committed
834 835 836
		MSFilter *recorder=get_recorder(st);
		
		if (recorder==NULL) return -1;
837 838
		ms_filter_call_method(st->recv_tee,MS_TEE_MUTE,&pin);
		ms_filter_call_method(st->send_tee,MS_TEE_MUTE,&pin);
Simon Morlat's avatar
Simon Morlat committed
839 840
		ms_filter_call_method_noarg(recorder,MS_RECORDER_PAUSE);
		ms_filter_call_method_noarg(recorder,MS_RECORDER_CLOSE);
841 842 843 844
	}
	return 0;
}

Yann Diorcet's avatar
Yann Diorcet committed
845 846 847 848 849 850 851
uint32_t audio_stream_get_features(AudioStream *st){
	return st->features;
}

void audio_stream_set_features(AudioStream *st, uint32_t features){
	st->features = features;
}
aymeric's avatar
aymeric committed
852

853
AudioStream *audio_stream_new_with_sessions(const MSMediaStreamSessions *sessions){
aymeric's avatar
aymeric committed
854
	AudioStream *stream=(AudioStream *)ms_new0(AudioStream,1);
855
	MSFilterDesc *ec_desc=ms_filter_lookup_by_name("MSWebRTCAEC");
856

Simon Morlat's avatar
Simon Morlat committed
857 858
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();
Ghislain MARY's avatar
Ghislain MARY committed
859

Simon Morlat's avatar
Simon Morlat committed
860
	stream->ms.type = MSAudio;
861
	stream->ms.sessions=*sessions;