blob: 2b85421eee8f6cf73312c1679cc5cf5c4b2b2ffa [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsua-lib/pjsua.h>
20#include <pjsua-lib/pjsua_internal.h>
21
22#if defined(PJSUA_MEDIA_HAS_PJMEDIA) && PJSUA_MEDIA_HAS_PJMEDIA != 0
23# error The PJSUA_MEDIA_HAS_PJMEDIA should be declared as zero
24#endif
25
26
27#define THIS_FILE "alt_pjsua_aud.c"
28#define UNIMPLEMENTED(func) PJ_LOG(2,(THIS_FILE, "*** Call to unimplemented function %s ***", #func));
29
30
31/*****************************************************************************
32 * Our dummy codecs. Since we won't use any PJMEDIA codecs, we need to declare
33 * our own codecs and register them to PJMEDIA's codec manager. We just need
34 * the info so that they can be listed in SDP. The encoding and decoding will
35 * happen in your third party media stream and will not use these codecs,
36 * hence the "dummy" name.
37 */
38static struct alt_codec
39{
40 pj_str_t encoding_name;
41 pj_uint8_t payload_type;
42 unsigned clock_rate;
43 unsigned channel_cnt;
44 unsigned frm_ptime;
45 unsigned avg_bps;
46 unsigned max_bps;
47} codec_list[] =
48{
49 /* G.729 */
50 { { "G729", 4 }, 18, 8000, 1, 10, 8000, 8000 },
51 /* PCMU */
52 { { "PCMU", 4 }, 0, 8000, 1, 10, 64000, 64000 },
53 /* Our proprietary high end low bit rate (5kbps) codec, if you wish */
54 { { "FOO", 3 }, PJMEDIA_RTP_PT_START+0, 16000, 1, 20, 5000, 5000 },
55};
56
57static struct alt_codec_factory
58{
59 pjmedia_codec_factory base;
60} alt_codec_factory;
61
62static pj_status_t alt_codec_test_alloc( pjmedia_codec_factory *factory,
63 const pjmedia_codec_info *id )
64{
65 unsigned i;
66 for (i=0; i<PJ_ARRAY_SIZE(codec_list); ++i) {
67 if (pj_stricmp(&id->encoding_name, &codec_list[i].encoding_name)==0)
68 return PJ_SUCCESS;
69 }
70 return PJ_ENOTSUP;
71}
72
73static pj_status_t alt_codec_default_attr( pjmedia_codec_factory *factory,
74 const pjmedia_codec_info *id,
75 pjmedia_codec_param *attr )
76{
77 struct alt_codec *ac;
78 unsigned i;
79
80 PJ_UNUSED_ARG(factory);
81
82 for (i=0; i<PJ_ARRAY_SIZE(codec_list); ++i) {
83 if (pj_stricmp(&id->encoding_name, &codec_list[i].encoding_name)==0)
84 break;
85 }
86 if (i == PJ_ARRAY_SIZE(codec_list))
87 return PJ_ENOTFOUND;
88
89 ac = &codec_list[i];
90
91 pj_bzero(attr, sizeof(pjmedia_codec_param));
92 attr->info.clock_rate = ac->clock_rate;
93 attr->info.channel_cnt = ac->channel_cnt;
94 attr->info.avg_bps = ac->avg_bps;
95 attr->info.max_bps = ac->max_bps;
96 attr->info.pcm_bits_per_sample = 16;
97 attr->info.frm_ptime = ac->frm_ptime;
98 attr->info.pt = ac->payload_type;
99
100 attr->setting.frm_per_pkt = 1;
101 attr->setting.vad = 1;
102 attr->setting.plc = 1;
103
104 return PJ_SUCCESS;
105}
106
107static pj_status_t alt_codec_enum_codecs(pjmedia_codec_factory *factory,
108 unsigned *count,
109 pjmedia_codec_info codecs[])
110{
111 unsigned i;
112
113 for (i=0; i<*count && i<PJ_ARRAY_SIZE(codec_list); ++i) {
114 struct alt_codec *ac = &codec_list[i];
115 pj_bzero(&codecs[i], sizeof(pjmedia_codec_info));
116 codecs[i].encoding_name = ac->encoding_name;
117 codecs[i].pt = ac->payload_type;
118 codecs[i].type = PJMEDIA_TYPE_AUDIO;
119 codecs[i].clock_rate = ac->clock_rate;
120 codecs[i].channel_cnt = ac->channel_cnt;
121 }
122
123 *count = i;
124
125 return PJ_SUCCESS;
126}
127
128static pj_status_t alt_codec_alloc_codec(pjmedia_codec_factory *factory,
129 const pjmedia_codec_info *id,
130 pjmedia_codec **p_codec)
131{
132 /* This will never get called since we won't be using this codec */
133 UNIMPLEMENTED(alt_codec_alloc_codec)
134 return PJ_ENOTSUP;
135}
136
137static pj_status_t alt_codec_dealloc_codec( pjmedia_codec_factory *factory,
138 pjmedia_codec *codec )
139{
140 /* This will never get called */
141 UNIMPLEMENTED(alt_codec_dealloc_codec)
142 return PJ_ENOTSUP;
143}
144
145static pj_status_t alt_codec_deinit(void)
146{
147 pjmedia_codec_mgr *codec_mgr;
148 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
149 return pjmedia_codec_mgr_unregister_factory(codec_mgr,
150 &alt_codec_factory.base);
151
152}
153
154static pjmedia_codec_factory_op alt_codec_factory_op =
155{
156 &alt_codec_test_alloc,
157 &alt_codec_default_attr,
158 &alt_codec_enum_codecs,
159 &alt_codec_alloc_codec,
160 &alt_codec_dealloc_codec,
161 &alt_codec_deinit
162};
163
164
165/*****************************************************************************
166 * API
167 */
168
169/* Initialize third party media library. */
170pj_status_t pjsua_aud_subsys_init()
171{
172 pjmedia_codec_mgr *codec_mgr;
173 pj_status_t status;
174
175 /* Register our "dummy" codecs */
176 alt_codec_factory.base.op = &alt_codec_factory_op;
177 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
178 status = pjmedia_codec_mgr_register_factory(codec_mgr,
179 &alt_codec_factory.base);
180 if (status != PJ_SUCCESS)
181 return status;
182
183 /* TODO: initialize your evil library here */
184 return PJ_SUCCESS;
185}
186
187/* Start (audio) media library. */
188pj_status_t pjsua_aud_subsys_start(void)
189{
190 /* TODO: */
191 return PJ_SUCCESS;
192}
193
194/* Cleanup and deinitialize third party media library. */
195pj_status_t pjsua_aud_subsys_destroy()
196{
197 /* TODO: */
198 return PJ_SUCCESS;
199}
200
201/* Our callback to receive incoming RTP packets */
202static void aud_rtp_cb(void *user_data, void *pkt, pj_ssize_t size)
203{
204 pjsua_call_media *call_med = (pjsua_call_media*) user_data;
205
206 /* TODO: Do something with the packet */
207 PJ_LOG(4,(THIS_FILE, "RX %d bytes audio RTP packet", (int)size));
208}
209
210/* Our callback to receive RTCP packets */
211static void aud_rtcp_cb(void *user_data, void *pkt, pj_ssize_t size)
212{
213 pjsua_call_media *call_med = (pjsua_call_media*) user_data;
214
215 /* TODO: Do something with the packet here */
216 PJ_LOG(4,(THIS_FILE, "RX %d bytes audio RTCP packet", (int)size));
217}
218
219/* A demo function to send dummy "RTP" packets periodically. You would not
220 * need to have this function in the real app!
221 */
222static void timer_to_send_aud_rtp(void *user_data)
223{
224 pjsua_call_media *call_med = (pjsua_call_media*) user_data;
225 const char *pkt = "Not RTP packet";
226
227 if (!call_med->call || !call_med->call->inv || !call_med->tp) {
228 /* Call has been disconnected. There is race condition here as
229 * this cb may be called sometime after call has been disconnected */
230 return;
231 }
232
233 pjmedia_transport_send_rtp(call_med->tp, pkt, strlen(pkt));
234
235 pjsua_schedule_timer2(&timer_to_send_aud_rtp, call_med, 2000);
236}
237
238static void timer_to_send_aud_rtcp(void *user_data)
239{
240 pjsua_call_media *call_med = (pjsua_call_media*) user_data;
241 const char *pkt = "Not RTCP packet";
242
243 if (!call_med->call || !call_med->call->inv || !call_med->tp) {
244 /* Call has been disconnected. There is race condition here as
245 * this cb may be called sometime after call has been disconnected */
246 return;
247 }
248
249 pjmedia_transport_send_rtcp(call_med->tp, pkt, strlen(pkt));
250
251 pjsua_schedule_timer2(&timer_to_send_aud_rtcp, call_med, 5000);
252}
253
254/* Stop the audio stream of a call. */
255void pjsua_aud_stop_stream(pjsua_call_media *call_med)
256{
257 /* Detach our RTP/RTCP callbacks from transport */
258 pjmedia_transport_detach(call_med->tp, call_med);
259
260 /* TODO: destroy your audio stream here */
261}
262
263/*
264 * This function is called whenever SDP negotiation has completed
265 * successfully. Here you'd want to start your audio stream
266 * based on the info in the SDPs.
267 */
268pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med,
269 pj_pool_t *tmp_pool,
270 pjmedia_stream_info *si,
271 const pjmedia_sdp_session *local_sdp,
272 const pjmedia_sdp_session *remote_sdp)
273{
274 pj_status_t status = PJ_SUCCESS;
275
276 PJ_LOG(4,(THIS_FILE,"Alt audio channel update.."));
277 pj_log_push_indent();
278
279 /* Check if no media is active */
280 if (si->dir != PJMEDIA_DIR_NONE) {
281 /* Attach our RTP and RTCP callbacks to the media transport */
282 status = pjmedia_transport_attach(call_med->tp, call_med,
283 &si->rem_addr, &si->rem_rtcp,
284 pj_sockaddr_get_len(&si->rem_addr),
285 &aud_rtp_cb, &aud_rtcp_cb);
286
287 /* For a demonstration, let's use a timer to send "RTP" packet
288 * periodically.
289 */
290 pjsua_schedule_timer2(&timer_to_send_aud_rtp, call_med, 0);
291 pjsua_schedule_timer2(&timer_to_send_aud_rtcp, call_med, 2500);
292
293 /* TODO:
294 * - Create and start your media stream based on the parameters
295 * in si
296 */
297 }
298
299on_return:
300 pj_log_pop_indent();
301 return status;
302}
303
304void pjsua_check_snd_dev_idle()
305{
306}
307
308/*****************************************************************************
309 *
310 * Call API which MAY need to be re-implemented if different backend is used.
311 */
312
313/* Check if call has an active media session. */
314PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
315{
316 UNIMPLEMENTED(pjsua_call_has_media)
317 return PJ_TRUE;
318}
319
320
321/* Get the conference port identification associated with the call. */
322PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
323{
324 UNIMPLEMENTED(pjsua_call_get_conf_port)
325 return PJSUA_INVALID_ID;
326}
327
328/* Get media stream info for the specified media index. */
329PJ_DEF(pj_status_t) pjsua_call_get_stream_info( pjsua_call_id call_id,
330 unsigned med_idx,
331 pjsua_stream_info *psi)
332{
333 pj_bzero(psi, sizeof(*psi));
334 UNIMPLEMENTED(pjsua_call_get_stream_info)
335 return PJ_ENOTSUP;
336}
337
338/* Get media stream statistic for the specified media index. */
339PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id,
340 unsigned med_idx,
341 pjsua_stream_stat *stat)
342{
343 pj_bzero(stat, sizeof(*stat));
344 UNIMPLEMENTED(pjsua_call_get_stream_stat)
345 return PJ_ENOTSUP;
346}
347
348/*
349 * Send DTMF digits to remote using RFC 2833 payload formats.
350 */
351PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
352 const pj_str_t *digits)
353{
354 UNIMPLEMENTED(pjsua_call_dial_dtmf)
355 return PJ_ENOTSUP;
356}
357
358/*****************************************************************************
359 * Below are auxiliary API that we don't support (feel free to implement them
360 * with the other media stack)
361 */
362
363/* Get maximum number of conference ports. */
364PJ_DEF(unsigned) pjsua_conf_get_max_ports(void)
365{
366 UNIMPLEMENTED(pjsua_conf_get_max_ports)
367 return 0xFF;
368}
369
370/* Get current number of active ports in the bridge. */
371PJ_DEF(unsigned) pjsua_conf_get_active_ports(void)
372{
373 UNIMPLEMENTED(pjsua_conf_get_active_ports)
374 return 0;
375}
376
377/* Enumerate all conference ports. */
378PJ_DEF(pj_status_t) pjsua_enum_conf_ports(pjsua_conf_port_id id[],
379 unsigned *count)
380{
381 *count = 0;
382 UNIMPLEMENTED(pjsua_enum_conf_ports)
383 return PJ_ENOTSUP;
384}
385
386/* Get information about the specified conference port */
387PJ_DEF(pj_status_t) pjsua_conf_get_port_info( pjsua_conf_port_id id,
388 pjsua_conf_port_info *info)
389{
390 UNIMPLEMENTED(pjsua_conf_get_port_info)
391 return PJ_ENOTSUP;
392}
393
394/* Add arbitrary media port to PJSUA's conference bridge. */
395PJ_DEF(pj_status_t) pjsua_conf_add_port( pj_pool_t *pool,
396 pjmedia_port *port,
397 pjsua_conf_port_id *p_id)
398{
399 *p_id = PJSUA_INVALID_ID;
400 UNIMPLEMENTED(pjsua_conf_add_port)
401 /* We should return PJ_ENOTSUP here, but this API is needed by pjsua
402 * application or otherwise it will refuse to start.
403 */
404 return PJ_SUCCESS;
405}
406
407/* Remove arbitrary slot from the conference bridge. */
408PJ_DEF(pj_status_t) pjsua_conf_remove_port(pjsua_conf_port_id id)
409{
410 UNIMPLEMENTED(pjsua_conf_remove_port)
411 return PJ_ENOTSUP;
412}
413
414/* Establish unidirectional media flow from souce to sink. */
415PJ_DEF(pj_status_t) pjsua_conf_connect( pjsua_conf_port_id source,
416 pjsua_conf_port_id sink)
417{
418 UNIMPLEMENTED(pjsua_conf_connect)
419 return PJ_ENOTSUP;
420}
421
422/* Disconnect media flow from the source to destination port. */
423PJ_DEF(pj_status_t) pjsua_conf_disconnect( pjsua_conf_port_id source,
424 pjsua_conf_port_id sink)
425{
426 UNIMPLEMENTED(pjsua_conf_disconnect)
427 return PJ_ENOTSUP;
428}
429
430/* Adjust the signal level to be transmitted from the bridge to the
431 * specified port by making it louder or quieter.
432 */
433PJ_DEF(pj_status_t) pjsua_conf_adjust_tx_level(pjsua_conf_port_id slot,
434 float level)
435{
436 UNIMPLEMENTED(pjsua_conf_adjust_tx_level)
437 return PJ_ENOTSUP;
438}
439
440/* Adjust the signal level to be received from the specified port (to
441 * the bridge) by making it louder or quieter.
442 */
443PJ_DEF(pj_status_t) pjsua_conf_adjust_rx_level(pjsua_conf_port_id slot,
444 float level)
445{
446 UNIMPLEMENTED(pjsua_conf_adjust_rx_level)
447 return PJ_ENOTSUP;
448}
449
450
451/* Get last signal level transmitted to or received from the specified port. */
452PJ_DEF(pj_status_t) pjsua_conf_get_signal_level(pjsua_conf_port_id slot,
453 unsigned *tx_level,
454 unsigned *rx_level)
455{
456 UNIMPLEMENTED(pjsua_conf_get_signal_level)
457 return PJ_ENOTSUP;
458}
459
460/* Create a file player, and automatically connect this player to
461 * the conference bridge.
462 */
463PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename,
464 unsigned options,
465 pjsua_player_id *p_id)
466{
467 UNIMPLEMENTED(pjsua_player_create)
468 return PJ_ENOTSUP;
469}
470
471/* Create a file playlist media port, and automatically add the port
472 * to the conference bridge.
473 */
474PJ_DEF(pj_status_t) pjsua_playlist_create( const pj_str_t file_names[],
475 unsigned file_count,
476 const pj_str_t *label,
477 unsigned options,
478 pjsua_player_id *p_id)
479{
480 UNIMPLEMENTED(pjsua_playlist_create)
481 return PJ_ENOTSUP;
482}
483
484/* Get conference port ID associated with player. */
485PJ_DEF(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id)
486{
487 UNIMPLEMENTED(pjsua_player_get_conf_port)
488 return -1;
489}
490
491/* Get the media port for the player. */
492PJ_DEF(pj_status_t) pjsua_player_get_port( pjsua_player_id id,
493 pjmedia_port **p_port)
494{
495 UNIMPLEMENTED(pjsua_player_get_port)
496 return PJ_ENOTSUP;
497}
498
499/* Set playback position. */
500PJ_DEF(pj_status_t) pjsua_player_set_pos( pjsua_player_id id,
501 pj_uint32_t samples)
502{
503 UNIMPLEMENTED(pjsua_player_set_pos)
504 return PJ_ENOTSUP;
505}
506
507/* Close the file, remove the player from the bridge, and free
508 * resources associated with the file player.
509 */
510PJ_DEF(pj_status_t) pjsua_player_destroy(pjsua_player_id id)
511{
512 UNIMPLEMENTED(pjsua_player_destroy)
513 return PJ_ENOTSUP;
514}
515
516/* Create a file recorder, and automatically connect this recorder to
517 * the conference bridge.
518 */
519PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename,
520 unsigned enc_type,
521 void *enc_param,
522 pj_ssize_t max_size,
523 unsigned options,
524 pjsua_recorder_id *p_id)
525{
526 UNIMPLEMENTED(pjsua_recorder_create)
527 return PJ_ENOTSUP;
528}
529
530
531/* Get conference port associated with recorder. */
532PJ_DEF(pjsua_conf_port_id) pjsua_recorder_get_conf_port(pjsua_recorder_id id)
533{
534 UNIMPLEMENTED(pjsua_recorder_get_conf_port)
535 return -1;
536}
537
538/* Get the media port for the recorder. */
539PJ_DEF(pj_status_t) pjsua_recorder_get_port( pjsua_recorder_id id,
540 pjmedia_port **p_port)
541{
542 UNIMPLEMENTED(pjsua_recorder_get_port)
543 return PJ_ENOTSUP;
544}
545
546/* Destroy recorder (this will complete recording). */
547PJ_DEF(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id)
548{
549 UNIMPLEMENTED(pjsua_recorder_destroy)
550 return PJ_ENOTSUP;
551}
552
553/* Enum sound devices. */
554PJ_DEF(pj_status_t) pjsua_enum_aud_devs( pjmedia_aud_dev_info info[],
555 unsigned *count)
556{
557 UNIMPLEMENTED(pjsua_enum_aud_devs)
558 return PJ_ENOTSUP;
559}
560
561PJ_DEF(pj_status_t) pjsua_enum_snd_devs( pjmedia_snd_dev_info info[],
562 unsigned *count)
563{
564 UNIMPLEMENTED(pjsua_enum_snd_devs)
565 return PJ_ENOTSUP;
566}
567
568/* Select or change sound device. */
569PJ_DEF(pj_status_t) pjsua_set_snd_dev( int capture_dev, int playback_dev)
570{
571 UNIMPLEMENTED(pjsua_set_snd_dev)
572 return PJ_SUCCESS;
573}
574
575/* Get currently active sound devices. */
576PJ_DEF(pj_status_t) pjsua_get_snd_dev(int *capture_dev, int *playback_dev)
577{
578 *capture_dev = *playback_dev = PJSUA_INVALID_ID;
579 UNIMPLEMENTED(pjsua_get_snd_dev)
580 return PJ_ENOTSUP;
581}
582
583/* Use null sound device. */
584PJ_DEF(pj_status_t) pjsua_set_null_snd_dev(void)
585{
586 UNIMPLEMENTED(pjsua_set_null_snd_dev)
587 return PJ_ENOTSUP;
588}
589
590/* Use no device! */
591PJ_DEF(pjmedia_port*) pjsua_set_no_snd_dev(void)
592{
593 UNIMPLEMENTED(pjsua_set_no_snd_dev)
594 return NULL;
595}
596
597/* Configure the AEC settings of the sound port. */
598PJ_DEF(pj_status_t) pjsua_set_ec(unsigned tail_ms, unsigned options)
599{
600 UNIMPLEMENTED(pjsua_set_ec)
601 return PJ_ENOTSUP;
602}
603
604/* Get current AEC tail length. */
605PJ_DEF(pj_status_t) pjsua_get_ec_tail(unsigned *p_tail_ms)
606{
607 UNIMPLEMENTED(pjsua_get_ec_tail)
608 return PJ_ENOTSUP;
609}
610
611/* Check whether the sound device is currently active. */
612PJ_DEF(pj_bool_t) pjsua_snd_is_active(void)
613{
614 UNIMPLEMENTED(pjsua_snd_is_active)
615 return PJ_FALSE;
616}
617
618/* Configure sound device setting to the sound device being used. */
619PJ_DEF(pj_status_t) pjsua_snd_set_setting( pjmedia_aud_dev_cap cap,
620 const void *pval, pj_bool_t keep)
621{
622 UNIMPLEMENTED(pjsua_snd_set_setting)
623 return PJ_ENOTSUP;
624}
625
626/* Retrieve a sound device setting. */
627PJ_DEF(pj_status_t) pjsua_snd_get_setting(pjmedia_aud_dev_cap cap, void *pval)
628{
629 UNIMPLEMENTED(pjsua_snd_get_setting)
630 return PJ_ENOTSUP;
631}