Review Board 1.7.16


Bridging API for Conference Bridge purposes

Review Request #93 - Created Dec. 15, 2008 and submitted

Joshua Colp
team/file/bridging
Reviewers
asterisk-dev
russell
Asterisk
This patch implements the new bridging API and brings with it a module for conference bridges. It does *not* replace existing internal bridging or features yet and will not cause any regressions when put in. It will essentially be introduced as a first test phase to work out any unforeseen critical issues. The bridging core itself is fully implemented besides the following: jitterbuffer support, native bridging, and interval hooks (hooks that are time based versus action based). If you would like an explanation of what the bridging API is made up of and how it works that can be found in the bridging.h header file.
Conference bridge testing using app_confbridge with features. Joining two channels with simple frame exchange and joining three channels to move it to a true conference bridge. IVR capability of app_confbridge was also tested.

Changes between revision 1 and 2

1 2 3 4 5
1 2 3 4 5

  1. /trunk/apps/app_confbridge.c: Loading...
  2. /trunk/bridges/bridge_multiplexed.c: Loading...
  3. /trunk/bridges/bridge_simple.c: Loading...
  4. /trunk/bridges/bridge_softmix.c: Loading...
  5. /trunk/channels/chan_bridge.c: Loading...
/trunk/apps/app_confbridge.c
Diff Revision 1 Diff Revision 2
[20] 148 lines
[+20] [+] AST_APP_OPTIONS(app_opts,{
149
/* Maximum number of buckets our conference bridges container can have */
149
/* Maximum number of buckets our conference bridges container can have */
150
#define MAX_CONFERENCE_BRIDGE_BUCKETS 53
150
#define MAX_CONFERENCE_BRIDGE_BUCKETS 53
151

    
   
151

   
152
/*! \brief The structure that represents a conference bridge */
152
/*! \brief The structure that represents a conference bridge */
153
struct conference_bridge {
153
struct conference_bridge {
154
	char name[MAX_CONF_NAME];                                  /*!< Name of the conference bridge */
154
	char name[MAX_CONF_NAME];                                         /*!< Name of the conference bridge */
155
	struct ast_bridge *bridge;                                 /*!< Bridge structure doing the mixing */
155
	struct ast_bridge *bridge;                                        /*!< Bridge structure doing the mixing */
156
	int users;                                                 /*!< Number of users present */
156
	int users;                                                        /*!< Number of users present */
157
	int markedusers;                                           /*!< Number of marked users present */
157
	int markedusers;                                                  /*!< Number of marked users present */
158
	unsigned int locked:1;                                     /*!< Is this conference bridge locked? */
158
	unsigned int locked:1;                                            /*!< Is this conference bridge locked? */
159
	AST_LIST_HEAD_NOLOCK(, conference_bridge_user) users_list; /*!< List of users participating in the conference bridge */
159
	AST_LIST_HEAD_NOLOCK(, conference_bridge_user) users_list;        /*!< List of users participating in the conference bridge */

    
   
160
	struct ast_channel *playback_chan;                                /*!< Channel used for playback into the conference bridge */

    
   
161
	ast_mutex_t playback_lock;                                        /*!< Lock used for playback channel */
160
};
162
};
161

    
   
163

   
162
/*! \brief The structure that represents a conference bridge user */
164
/*! \brief The structure that represents a conference bridge user */
163
struct conference_bridge_user {
165
struct conference_bridge_user {
164
	struct conference_bridge *conference_bridge; /*!< Conference bridge they are participating in */
166
	struct conference_bridge *conference_bridge; /*!< Conference bridge they are participating in */
[+20] [20] 12 lines
[+20] [+] static struct ao2_container *conference_bridges;
177

    
   
179

   
178
/*! \brief Hashing function used for conference bridges container */
180
/*! \brief Hashing function used for conference bridges container */
179
static int conference_bridge_hash_cb(const void *obj, const int flags)
181
static int conference_bridge_hash_cb(const void *obj, const int flags)
180
{
182
{
181
	const struct conference_bridge *conference_bridge = obj;
183
	const struct conference_bridge *conference_bridge = obj;
182
	return ast_str_hash(conference_bridge->name);
184
	return ast_str_case_hash(conference_bridge->name);
183
}
185
}
184

    
   
186

   
185
/*! \brief Comparison function used for conference bridges container */
187
/*! \brief Comparison function used for conference bridges container */
186
static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
188
static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
187
{
189
{
[+20] [20] 34 lines
[+20] [+] static void announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
222
	
224
	
223
	return;
225
	return;
224
}
226
}
225

    
   
227

   
226
/*!
228
/*!

    
   
229
 * \brief Play back an audio file to a channel

    
   
230
 *

    
   
231
 * \param conference_bridge Conference bridge they are in

    
   
232
 * \param chan Channel to play audio prompt to

    
   
233
 * \param file Prompt to play

    
   
234
 *

    
   
235
 * \return Returns nothing

    
   
236
 *

    
   
237
 * \note This function assumes that conference_bridge is locked

    
   
238
 */

    
   
239
static void play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)

    
   
240
{

    
   
241
	ao2_unlock(conference_bridge);

    
   
242
	ast_stream_and_wait(chan, file, "");

    
   
243
	ao2_lock(conference_bridge);

    
   
244
	return;

    
   
245
}

    
   
246

   

    
   
247
/*!
227
 * \brief Perform post-joining marked specific actions
248
 * \brief Perform post-joining marked specific actions
228
 *
249
 *
229
 * \param conference_bridge Conference bridge being joined
250
 * \param conference_bridge Conference bridge being joined
230
 * \param conference_bridge_user Conference bridge user joining
251
 * \param conference_bridge_user Conference bridge user joining
231
 *
252
 *
232
 * \return Nothing
253
 * \return Returns nothing
233
 */
254
 */
234
static void post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
255
static void post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
235
{
256
{
236
	if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
257
	if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
237
		struct conference_bridge_user *other_conference_bridge_user = NULL;
258
		struct conference_bridge_user *other_conference_bridge_user = NULL;
[+20] [20] 36 lines
[+20] static void post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
274
		}
295
		}
275
		/* Be sure we are muted so we can't talk to anybody else waiting */
296
		/* Be sure we are muted so we can't talk to anybody else waiting */
276
		conference_bridge_user->features.mute = 1;
297
		conference_bridge_user->features.mute = 1;
277
		/* If we have not been quieted play back that they are waiting for the leader */
298
		/* If we have not been quieted play back that they are waiting for the leader */
278
		if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
299
		if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
279
			ao2_unlock(conference_bridge);
300
			play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader");
280
			ast_stream_and_wait(conference_bridge_user->chan, "conf-waitforleader", "");

   
281
			ao2_lock(conference_bridge);

   
282
		}
301
		}
283
		/* Start music on hold if needed */
302
		/* Start music on hold if needed */
284
		if (ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
303
		if (ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
285
			ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
304
			ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
286
		}
305
		}
[+20] [20] 6 lines
[+20] static void post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
293
 * \brief Perform post-joining non-marked specific actions
312
 * \brief Perform post-joining non-marked specific actions
294
 *
313
 *
295
 * \param conference_bridge Conference bridge being joined
314
 * \param conference_bridge Conference bridge being joined
296
 * \param conference_bridge_user Conference bridge user joining
315
 * \param conference_bridge_user Conference bridge user joining
297
 *
316
 *
298
 * \return Nothing
317
 * \return Returns nothing
299
 */
318
 */
300
static void post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
319
static void post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
301
{
320
{
302
	/* Play back audio prompt and start MOH if need be if we are the first participant */
321
	/* Play back audio prompt and start MOH if need be if we are the first participant */
303
	if (conference_bridge->users == 1) {
322
	if (conference_bridge->users == 1) {
304
		/* If audio prompts have not been quieted or this prompt quieted play it on out */
323
		/* If audio prompts have not been quieted or this prompt quieted play it on out */
305
		if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET) && !ast_test_flag(&conference_bridge_user->flags, OPTION_NOONLYPERSON)) {
324
		if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET | OPTION_NOONLYPERSON)) {
306
			ao2_unlock(conference_bridge);
325
			play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson");
307
			ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyperson", "");

   
308
			ao2_lock(conference_bridge);

   
309
		}
326
		}
310
		/* If we need to start music on hold on the channel do so now */
327
		/* If we need to start music on hold on the channel do so now */
311
		if (ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
328
		if (ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
312
			ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
329
			ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
313
		}
330
		}
[+20] [20] 24 lines
[+20] static void post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
338
/*!
355
/*!
339
 * \brief Destroy a conference bridge
356
 * \brief Destroy a conference bridge
340
 *
357
 *
341
 * \param obj The conference bridge object
358
 * \param obj The conference bridge object
342
 *
359
 *
343
 * \return Nothing
360
 * \return Returns nothing
344
 */
361
 */
345
static void destroy_conference_bridge(void *obj)
362
static void destroy_conference_bridge(void *obj)
346
{
363
{
347
	struct conference_bridge *conference_bridge = obj;
364
	struct conference_bridge *conference_bridge = obj;
348

    
   
365

   
349
	ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
366
	ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
350

    
   
367

   

    
   
368
	ast_mutex_destroy(&conference_bridge->playback_lock);

    
   
369
	

    
   
370
	if (conference_bridge->playback_chan) {

    
   
371
		struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);

    
   
372
		ast_hangup(underlying_channel);

    
   
373
		ast_hangup(conference_bridge->playback_chan);

    
   
374
		conference_bridge->playback_chan = NULL;

    
   
375
	}

    
   
376
	
351
	/* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
377
	/* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
352
	ast_bridge_destroy(conference_bridge->bridge);
378
	ast_bridge_destroy(conference_bridge->bridge);

    
   
379
	conference_bridge->bridge = NULL;
353

    
   
380

   
354
	return;
381
	return;
355
}
382
}
356

    
   
383

   
357
/*!
384
/*!
[+20] [20] 9 lines
[+20] [+] static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
367
	struct conference_bridge *conference_bridge = NULL;
394
	struct conference_bridge *conference_bridge = NULL;
368
	struct conference_bridge tmp;
395
	struct conference_bridge tmp;
369

    
   
396

   
370
	ast_copy_string(tmp.name, name, sizeof(tmp.name));
397
	ast_copy_string(tmp.name, name, sizeof(tmp.name));
371

    
   
398

   

    
   
399
	/* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
372
	ao2_lock(conference_bridges);
400
	ao2_lock(conference_bridges);
373

    
   
401

   
374
	ast_debug(1, "Trying to find conference bridge '%s'\n", name);
402
	ast_debug(1, "Trying to find conference bridge '%s'\n", name);
375

    
   
403

   
376
	/* Attempt to find an existing conference bridge */
404
	/* Attempt to find an existing conference bridge */
[+20] [20] 21 lines
[+20] static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
398
		ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
426
		ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
399

    
   
427

   
400
		/* Create an actual bridge that will do the audio mixing */
428
		/* Create an actual bridge that will do the audio mixing */
401
		if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
429
		if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
402
			ao2_ref(conference_bridge, -1);
430
			ao2_ref(conference_bridge, -1);

    
   
431
			conference_bridge = NULL;
403
			ao2_unlock(conference_bridges);
432
			ao2_unlock(conference_bridges);
404
			ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
433
			ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
405
			return NULL;
434
			return NULL;
406
		}
435
		}
407

    
   
436

   

    
   
437
		/* Setup lock for playback channel */

    
   
438
		ast_mutex_init(&conference_bridge->playback_lock);

    
   
439
		
408
		/* Link it into the conference bridges container */
440
		/* Link it into the conference bridges container */
409
		ao2_link(conference_bridges, conference_bridge);
441
		ao2_link(conference_bridges, conference_bridge);
410
		ao2_ref(conference_bridge, -1);
442
		ao2_ref(conference_bridge, -1);
411

    
   
443

   
412
		ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
444
		ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
[+20] [20] 16 lines
[+20] static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
429
	if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
461
	if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
430
		conference_bridge->markedusers++;
462
		conference_bridge->markedusers++;
431
	}
463
	}
432

    
   
464

   
433
	/* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
465
	/* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
434
	if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) || ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED)) {
466
	if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
435
		post_join_marked(conference_bridge, conference_bridge_user);
467
		post_join_marked(conference_bridge, conference_bridge_user);
436
	} else {
468
	} else {
437
		post_join_unmarked(conference_bridge, conference_bridge_user);
469
		post_join_unmarked(conference_bridge, conference_bridge_user);
438
	}
470
	}
439

    
   
471

   
[+20] [20] 6 lines
[+20] static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
446
 * \brief Leave a conference bridge
478
 * \brief Leave a conference bridge
447
 *
479
 *
448
 * \param conference_bridge The conference bridge to leave
480
 * \param conference_bridge The conference bridge to leave
449
 * \param conference_bridge_user The conference bridge user structure
481
 * \param conference_bridge_user The conference bridge user structure
450
 *
482
 *
451
 * \return Returns 0 on success, -1 on failure
483
 * \retval 0 success

    
   
484
 * \retval -1 failure
452
 */
485
 */
453
static int leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
486
static int leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
454
{
487
{
455
	ao2_lock(conference_bridge);
488
	ao2_lock(conference_bridge);
456

    
   
489

   
[+20] [20] 55 lines
[+20] static int leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
512
 * \brief Play sound file into conference bridge
545
 * \brief Play sound file into conference bridge
513
 *
546
 *
514
 * \param conference_bridge The conference bridge to play sound file into
547
 * \param conference_bridge The conference bridge to play sound file into
515
 * \param filename Sound file to play
548
 * \param filename Sound file to play
516
 *
549
 *
517
 * \return Returns 0 on success, -1 on failure
550
 * \retval 0 success

    
   
551
 * \retval -1 failure
518
 */
552
 */
519
static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
553
static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
520
{
554
{

    
   
555
	struct ast_channel *underlying_channel;

    
   
556

   

    
   
557
	ast_mutex_lock(&conference_bridge->playback_lock);

    
   
558
	

    
   
559
	if (!(conference_bridge->playback_chan)) {
521
	int cause;
560
		int cause;
522
	struct ast_channel *chan = NULL;
561
		

    
   
562
		if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, "", &cause))) {

    
   
563
			ast_mutex_unlock(&conference_bridge->playback_lock);

    
   
564
			return -1;

    
   
565
		}
523

    
   
566

   
524
	/* Request a channel that we will use to interface with the bridge */
567
		conference_bridge->playback_chan->bridge = conference_bridge->bridge;
525
	if (!(chan = ast_request("Bridge", AST_FORMAT_SLINEAR, "", &cause))) {
568
		

    
   
569
		if (ast_call(conference_bridge->playback_chan, "", 0)) {

    
   
570
			ast_hangup(conference_bridge->playback_chan);

    
   
571
			conference_bridge->playback_chan = NULL;

    
   
572
			ast_mutex_unlock(&conference_bridge->playback_lock);
526
		return -1;
573
			return -1;
527
	}
574
		}
528

    
   
575

   
529
	/* Before we actually dial into the bridge we need to say what bridge */
576
		ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
530
	chan->bridge = conference_bridge->bridge;

   
531

    
   
577

   
532
	/* Now actually dial. Once this is done we have a clear conduit into the bridge. */
578
		underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
533
	if (ast_call(chan, "", 0)) {
579
	} else {
534
		ast_hangup(chan);
580
		/* Channel was already available so we just need to add it back into the bridge */
535
		return -1;
581
		underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);

    
   
582
		ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
536
	}
583
	}
537

    
   
584

   
538
	/* Stream the file into the conference while waiting for it to finish */
585
	/* The channel is all under our control, in goes the prompt */
539
	ast_stream_and_wait(chan, filename, "");
586
	ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
540

    
   
587

   
541
	/* Now that it is done drop the channel */
588
	ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
542
	ast_hangup(chan);
589
	ast_bridge_depart(conference_bridge->bridge, underlying_channel);
543

    
   
590

   

    
   
591
	ast_mutex_unlock(&conference_bridge->playback_lock);

    
   
592
	
544
	return 0;
593
	return 0;
545
}
594
}
546

    
   
595

   
547
/*!
596
/*!
548
 * \brief DTMF Menu Callback
597
 * \brief DTMF Menu Callback
549
 *
598
 *
550
 * \param bridge Bridge this is involving
599
 * \param bridge Bridge this is involving
551
 * \param bridge_channel Bridged channel this is involving
600
 * \param bridge_channel Bridged channel this is involving
552
 * \param hook_pvt User's conference bridge structure
601
 * \param hook_pvt User's conference bridge structure
553
 *
602
 *
554
 * \return Returns 0 on success, -1 on failure
603
 * \retval 0 success

    
   
604
 * \retval -1 failure
555
 */
605
 */
556
static int menu_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
606
static int menu_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
557
{
607
{
558
	struct conference_bridge_user *conference_bridge_user = hook_pvt;
608
	struct conference_bridge_user *conference_bridge_user = hook_pvt;
559
	struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
609
	struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
[+20] [20] 203 lines
/trunk/bridges/bridge_multiplexed.c
Diff Revision 1 Diff Revision 2
 
/trunk/bridges/bridge_simple.c
Diff Revision 1 Diff Revision 2
 
/trunk/bridges/bridge_softmix.c
Diff Revision 1 Diff Revision 2
 
/trunk/channels/chan_bridge.c
Diff Revision 1 Diff Revision 2
 
  1. /trunk/apps/app_confbridge.c: Loading...
  2. /trunk/bridges/bridge_multiplexed.c: Loading...
  3. /trunk/bridges/bridge_simple.c: Loading...
  4. /trunk/bridges/bridge_softmix.c: Loading...
  5. /trunk/channels/chan_bridge.c: Loading...

https://reviewboard.asterisk.org/ runs on a server provided by Digium, Inc. and uses bandwidth donated to the open source Asterisk community by API Digital Communications in Huntsville, AL USA.
Please report problems with this site to asteriskteam@digium.com.