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
static void setup_av_recorder(AudioStream *stream, int sample_rate, int nchannels){
Simon Morlat's avatar
Simon Morlat committed
294
	stream->av_recorder.recorder=ms_filter_new(MS_MKV_RECORDER_ID);
Simon Morlat's avatar
Simon Morlat committed
295
	if (stream->av_recorder.recorder){
Simon Morlat's avatar
Simon Morlat committed
296
		MSPinFormat pinfmt={0};
Simon Morlat's avatar
Simon Morlat committed
297 298
		stream->av_recorder.video_input=ms_filter_new(MS_ITC_SOURCE_ID);
		stream->av_recorder.resampler=ms_filter_new(MS_RESAMPLE_ID);
Simon Morlat's avatar
Simon Morlat committed
299
#ifdef REMOVE_ME_WHEN_RECORDER_SUPPORTS_OPUS
Simon Morlat's avatar
Simon Morlat committed
300
		stream->av_recorder.encoder=ms_filter_new(MS_OPUS_ENC_ID);
Simon Morlat's avatar
Simon Morlat committed
301
#endif
Simon Morlat's avatar
Simon Morlat committed
302 303 304 305 306 307 308 309
		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
310
			pinfmt.fmt=ms_factory_get_audio_format(ms_factory_get_fallback(),"pcmu",g711_rate,g711_nchannels,NULL);
Simon Morlat's avatar
Simon Morlat committed
311 312 313 314 315 316 317 318 319 320
			
		}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
321
			pinfmt.fmt=ms_factory_get_audio_format(ms_factory_get_fallback(),"opus",48000,nchannels,NULL);
Simon Morlat's avatar
Simon Morlat committed
322
		}
Simon Morlat's avatar
Simon Morlat committed
323 324 325
		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
326 327 328 329 330 331 332 333 334
	}
}

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
335
	ms_connection_helper_link(&ch, stream->av_recorder.recorder,1,-1);
Simon Morlat's avatar
Simon Morlat committed
336
	
Simon Morlat's avatar
Simon Morlat committed
337
	ms_filter_link(stream->av_recorder.video_input,0,stream->av_recorder.recorder,0);
Simon Morlat's avatar
Simon Morlat committed
338 339 340 341 342 343 344 345
}

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);
Simon Morlat's avatar
Simon Morlat committed
346
	ms_connection_helper_unlink(&ch, stream->av_recorder.recorder,1,-1);
Simon Morlat's avatar
Simon Morlat committed
347
	
Simon Morlat's avatar
Simon Morlat committed
348
	ms_filter_unlink(stream->av_recorder.video_input,0,stream->av_recorder.recorder,0);
Simon Morlat's avatar
Simon Morlat committed
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
}

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);
}

370 371
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,
372 373
	MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec){
	RtpSession *rtps=stream->ms.sessions.rtp_session;
Simon Morlat's avatar
Simon Morlat committed
374
	PayloadType *pt,*tel_ev;
375 376
	int tmp;
	MSConnectionHelper h;
377
	int sample_rate;
jehan's avatar
jehan committed
378
	MSRtpPayloadPickerContext picker_context;
379
	int nchannels;
380
	bool_t has_builtin_ec=FALSE;
381 382 383 384 385 386 387 388
	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
389 390

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

401
	if (rem_rtp_port>0)
Ghislain MARY's avatar
Ghislain MARY committed
402 403 404
		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);
405
	stream->ms.sessions.rtp_session=rtps;
Jehan Monnier's avatar
Jehan Monnier committed
406

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

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

Yann Diorcet's avatar
Yann Diorcet committed
438
	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)))
439
		&& ( strcasecmp(pt->mime_type,"pcmu")==0 || strcasecmp(pt->mime_type,"pcma")==0)){
440 441 442
		/*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
443 444
	} else {
		stream->dtmfgen_rtp=NULL;
445
	}
446

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

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

	/* 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
468 469 470 471 472
	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;
	}
473

474 475
	/* check echo canceller max frequency and adjust sampling rate if needed when codec used is opus */
	if (stream->ec!=NULL) {
476 477 478 479 480 481 482 483 484 485 486
		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;
487 488 489
			}
		}
	}
490 491 492
	/*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;
493

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

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

518 519 520 521 522 523 524
	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);
	}

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

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

546
	if (stream->ec){
547 548 549 550 551
		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);
			}
552
			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
Simon Morlat's avatar
Simon Morlat committed
553 554
		}else {
			ms_message("Setting echo canceller delay with value configured by application.");
555
		}
556
		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
557
	}
558

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

aymeric's avatar
aymeric committed
561
	/* give the encoder/decoder some parameters*/
562
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
jehan's avatar
jehan committed
563 564 565 566 567 568
	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
569
	}
570
	rtp_session_set_target_upload_bandwidth(rtps, stream->ms.target_bitrate);
571
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_NCHANNELS,&nchannels);
572
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
573
	ms_filter_call_method(stream->ms.decoder,MS_FILTER_SET_NCHANNELS,&nchannels);
Jehan Monnier's avatar
Jehan Monnier committed
574

575 576 577 578 579 580 581 582 583
	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);
			}
		}
584
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_ADD_FMTP, (void*)pt->send_fmtp);
585
	}
Ghislain MARY's avatar
Ghislain MARY committed
586
	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
587

smorlat's avatar
smorlat committed
588
	/*create the equalizer*/
589
	if ((stream->features & AUDIO_STREAM_FEATURE_EQUALIZER) != 0){
Yann Diorcet's avatar
Yann Diorcet committed
590
		stream->equalizer=ms_filter_new(MS_EQUALIZER_ID);
591 592 593 594 595
		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
596
		stream->equalizer=NULL;
597

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

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

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

611
	/* Create generic PLC if not handled by the decoder directly*/
Yann Diorcet's avatar
Yann Diorcet committed
612 613
	if ((stream->features & AUDIO_STREAM_FEATURE_PLC) != 0) {
		int decoder_have_plc = 0;
614 615 616
		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");
617 618
			}
		} else {
Yann Diorcet's avatar
Yann Diorcet committed
619 620
			ms_warning("MS_DECODER_HAVE_PLC function not implemented by the decoder: enable default plc");
		}
621
		if (decoder_have_plc == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
622
			stream->plc = ms_filter_new(MS_GENERIC_PLC_ID);
623
		}
Yann Diorcet's avatar
Yann Diorcet committed
624

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

633 634 635
	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
636

Simon Morlat's avatar
Simon Morlat committed
637
	/* create ticker */
638 639
	if (stream->ms.sessions.ticker==NULL) media_stream_start_ticker(&stream->ms);
	if (stream->ms.state==MSStreamPreparing){
Simon Morlat's avatar
Simon Morlat committed
640 641
		/*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
642
	}
643

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

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

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

687
	/*call recording part, attached to both outgoing and incoming graphs*/
Simon Morlat's avatar
Simon Morlat committed
688 689
	if (stream->av_recorder.recorder)
		plumb_av_recorder(stream);
690 691 692 693 694
	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);
	}
695

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

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

aymeric's avatar
aymeric committed
705 706 707 708 709 710
	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)
{
711
	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
712 713 714 715
}

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
716 717
	MSSndCard *sndcard_playback;
	MSSndCard *sndcard_capture;
aymeric's avatar
aymeric committed
718
	AudioStream *stream;
aymeric's avatar
aymeric committed
719 720 721
	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
722
		return NULL;
723
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
724
	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
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
	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;
	}
740
	stream=audio_stream_new(locport, locport+1, ms_is_ipv6(remip));
741
	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
742 743 744 745
	audio_stream_free(stream);
	return NULL;
}

746
// Pass NULL to stop playing
aymeric's avatar
aymeric committed
747
void audio_stream_play(AudioStream *st, const char *name){
748 749 750 751
	if (st->soundread == NULL) {
		ms_warning("Cannot play file: the stream hasn't been started");
		return;
	}
aymeric's avatar
aymeric committed
752 753
	if (ms_filter_get_id(st->soundread)==MS_FILE_PLAYER_ID){
		ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_CLOSE);
754 755 756
		if (name != NULL) {
			ms_filter_call_method(st->soundread,MS_FILE_PLAYER_OPEN,(void*)name);
			if (st->read_resampler){
757 758 759
				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);
760 761
			}
			ms_filter_call_method_noarg(st->soundread,MS_FILE_PLAYER_START);
762
		}
aymeric's avatar
aymeric committed
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
	}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");
	}
}

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

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
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
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;
}

812 813 814 815
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
816 817 818 819
		MSFilter *recorder=get_recorder(st);
		
		if (recorder==NULL) return -1;
		ms_filter_call_method(recorder,MS_RECORDER_GET_STATE,&state);
820
		if (state==MSRecorderClosed){
Simon Morlat's avatar
Simon Morlat committed
821
			if (ms_filter_call_method(recorder,MS_RECORDER_OPEN,st->recorder_file)==-1)
822 823
				return -1;
		}
Simon Morlat's avatar
Simon Morlat committed
824
		ms_filter_call_method_noarg(recorder,MS_RECORDER_START);
825 826 827 828 829 830 831 832 833 834
		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
835 836 837
		MSFilter *recorder=get_recorder(st);
		
		if (recorder==NULL) return -1;
838 839
		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
840 841
		ms_filter_call_method_noarg(recorder,MS_RECORDER_PAUSE);
		ms_filter_call_method_noarg(recorder,MS_RECORDER_CLOSE);
842 843 844 845
	}
	return 0;
}

Yann Diorcet's avatar
Yann Diorcet committed
846 847