Sample to mix multiple files in the conference bridge and play the result to sound device.
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <pjmedia.h>
00022 #include <pjlib-util.h>
00023 #include <pjlib.h>
00024
00025 #include <stdlib.h>
00026 #include <stdio.h>
00027
00028 #include "util.h"
00029
00042
00043 #define THIS_FILE "confsample.c"
00044
00045
00046
00047 #define RECORDER 1
00048
00049
00050 static const char *desc =
00051 " FILE: \n"
00052 " \n"
00053 " confsample.c \n"
00054 " \n"
00055 " PURPOSE: \n"
00056 " \n"
00057 " Demonstrate how to use conference bridge. \n"
00058 " \n"
00059 " USAGE: \n"
00060 " \n"
00061 " confsample [options] [file1.wav] [file2.wav] ... \n"
00062 " \n"
00063 " options: \n"
00064 SND_USAGE
00065 " \n"
00066 " fileN.wav are optional WAV files to be connected to the conference \n"
00067 " bridge. The WAV files MUST have single channel (mono) and 16 bit PCM \n"
00068 " samples. It can have arbitrary sampling rate. \n"
00069 " \n"
00070 " DESCRIPTION: \n"
00071 " \n"
00072 " Here we create a conference bridge, with at least one port (port zero \n"
00073 " is always created for the sound device). \n"
00074 " \n"
00075 " If WAV files are specified, the WAV file player ports will be connected \n"
00076 " to slot starting from number one in the bridge. The WAV files can have \n"
00077 " arbitrary sampling rate; the bridge will convert it to its clock rate. \n"
00078 " However, the files MUST have a single audio channel only (i.e. mono). \n";
00079
00080
00081
00082
00083
00084
00085
00086
00087 static void conf_list(pjmedia_conf *conf, pj_bool_t detail);
00088
00089
00090 static void monitor_level(pjmedia_conf *conf, int slot, int dir, int dur);
00091
00092
00093
00094 static void usage(void)
00095 {
00096 puts("");
00097 puts(desc);
00098 }
00099
00100
00101
00102
00103 static pj_bool_t input(const char *title, char *buf, pj_size_t len)
00104 {
00105 char *p;
00106
00107 printf("%s (empty to cancel): ", title); fflush(stdout);
00108 if (fgets(buf, len, stdin) == NULL)
00109 return PJ_FALSE;
00110
00111
00112 for (p=buf; ; ++p) {
00113 if (*p=='\r' || *p=='\n') *p='\0';
00114 else if (!*p) break;
00115 }
00116
00117 if (!*buf)
00118 return PJ_FALSE;
00119
00120 return PJ_TRUE;
00121 }
00122
00123
00124
00125
00126
00127 int main(int argc, char *argv[])
00128 {
00129 int dev_id = -1;
00130 int clock_rate = CLOCK_RATE;
00131 int channel_count = NCHANNELS;
00132 int samples_per_frame = NSAMPLES;
00133 int bits_per_sample = NBITS;
00134
00135 pj_caching_pool cp;
00136 pjmedia_endpt *med_endpt;
00137 pj_pool_t *pool;
00138 pjmedia_conf *conf;
00139
00140 int i, port_count, file_count;
00141 pjmedia_port **file_port;
00142 pjmedia_port *rec_port = NULL;
00143
00144 char tmp[10];
00145 pj_status_t status;
00146
00147
00148
00149 status = pj_init();
00150 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00151
00152
00153 if (get_snd_options(THIS_FILE, argc, argv, &dev_id, &clock_rate,
00154 &channel_count, &samples_per_frame, &bits_per_sample))
00155 {
00156 usage();
00157 return 1;
00158 }
00159
00160
00161 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
00162
00163
00164
00165
00166
00167 status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
00168 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00169
00170
00171 pool = pj_pool_create( &cp.factory,
00172 "wav",
00173 4000,
00174 4000,
00175 NULL
00176 );
00177
00178
00179 file_count = argc - pj_optind;
00180 port_count = file_count + 1 + RECORDER;
00181
00182
00183
00184
00185
00186 status = pjmedia_conf_create( pool,
00187 port_count,
00188 clock_rate,
00189 channel_count,
00190 samples_per_frame,
00191 bits_per_sample,
00192 0,
00193 &conf
00194 );
00195 if (status != PJ_SUCCESS) {
00196 app_perror(THIS_FILE, "Unable to create conference bridge", status);
00197 return 1;
00198 }
00199
00200 #if RECORDER
00201 status = pjmedia_wav_writer_port_create( pool, "confrecord.wav",
00202 clock_rate, channel_count,
00203 samples_per_frame,
00204 bits_per_sample, 0, 0,
00205 &rec_port);
00206 if (status != PJ_SUCCESS) {
00207 app_perror(THIS_FILE, "Unable to create WAV writer", status);
00208 return 1;
00209 }
00210
00211 pjmedia_conf_add_port(conf, pool, rec_port, NULL, NULL);
00212 #endif
00213
00214
00215
00216 file_port = pj_pool_alloc(pool, file_count * sizeof(pjmedia_port*));
00217
00218 for (i=0; i<file_count; ++i) {
00219
00220
00221 status = pjmedia_wav_player_port_create(
00222 pool,
00223 argv[i+pj_optind],
00224 0,
00225 0,
00226 0,
00227 &file_port[i]
00228 );
00229 if (status != PJ_SUCCESS) {
00230 char title[80];
00231 pj_ansi_sprintf(title, "Unable to use %s", argv[i+pj_optind]);
00232 app_perror(THIS_FILE, title, status);
00233 usage();
00234 return 1;
00235 }
00236
00237
00238 status = pjmedia_conf_add_port( conf,
00239 pool,
00240 file_port[i],
00241 NULL,
00242 NULL
00243 );
00244 if (status != PJ_SUCCESS) {
00245 app_perror(THIS_FILE, "Unable to add conference port", status);
00246 return 1;
00247 }
00248 }
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259 dump_pool_usage(THIS_FILE, &cp);
00260
00261
00262 pj_thread_sleep(100);
00263
00264
00265
00266
00267
00268 for (;;) {
00269 char tmp1[10];
00270 char tmp2[10];
00271 char *err;
00272 int src, dst, level, dur;
00273
00274 puts("");
00275 conf_list(conf, 0);
00276 puts("");
00277 puts("Menu:");
00278 puts(" s Show ports details");
00279 puts(" c Connect one port to another");
00280 puts(" d Disconnect port connection");
00281 puts(" t Adjust signal level transmitted (tx) to a port");
00282 puts(" r Adjust signal level received (rx) from a port");
00283 puts(" v Display VU meter for a particular port");
00284 puts(" q Quit");
00285 puts("");
00286
00287 printf("Enter selection: "); fflush(stdout);
00288
00289 if (fgets(tmp, sizeof(tmp), stdin) == NULL)
00290 break;
00291
00292 switch (tmp[0]) {
00293 case 's':
00294 puts("");
00295 conf_list(conf, 1);
00296 break;
00297
00298 case 'c':
00299 puts("");
00300 puts("Connect source port to destination port");
00301 if (!input("Enter source port number", tmp1, sizeof(tmp1)) )
00302 continue;
00303 src = strtol(tmp1, &err, 10);
00304 if (*err || src < 0 || src >= port_count) {
00305 puts("Invalid slot number");
00306 continue;
00307 }
00308
00309 if (!input("Enter destination port number", tmp2, sizeof(tmp2)) )
00310 continue;
00311 dst = strtol(tmp2, &err, 10);
00312 if (*err || dst < 0 || dst >= port_count) {
00313 puts("Invalid slot number");
00314 continue;
00315 }
00316
00317 status = pjmedia_conf_connect_port(conf, src, dst, 0);
00318 if (status != PJ_SUCCESS)
00319 app_perror(THIS_FILE, "Error connecting port", status);
00320
00321 break;
00322
00323 case 'd':
00324 puts("");
00325 puts("Disconnect port connection");
00326 if (!input("Enter source port number", tmp1, sizeof(tmp1)) )
00327 continue;
00328 src = strtol(tmp1, &err, 10);
00329 if (*err || src < 0 || src >= port_count) {
00330 puts("Invalid slot number");
00331 continue;
00332 }
00333
00334 if (!input("Enter destination port number", tmp2, sizeof(tmp2)) )
00335 continue;
00336 dst = strtol(tmp2, &err, 10);
00337 if (*err || dst < 0 || dst >= port_count) {
00338 puts("Invalid slot number");
00339 continue;
00340 }
00341
00342 status = pjmedia_conf_disconnect_port(conf, src, dst);
00343 if (status != PJ_SUCCESS)
00344 app_perror(THIS_FILE, "Error connecting port", status);
00345
00346
00347 break;
00348
00349 case 't':
00350 puts("");
00351 puts("Adjust transmit level of a port");
00352 if (!input("Enter port number", tmp1, sizeof(tmp1)) )
00353 continue;
00354 src = strtol(tmp1, &err, 10);
00355 if (*err || src < 0 || src >= port_count) {
00356 puts("Invalid slot number");
00357 continue;
00358 }
00359
00360 if (!input("Enter level (-128 to >127, 0 for normal)",
00361 tmp2, sizeof(tmp2)) )
00362 continue;
00363 level = strtol(tmp2, &err, 10);
00364 if (*err || level < -128) {
00365 puts("Invalid level");
00366 continue;
00367 }
00368
00369 status = pjmedia_conf_adjust_tx_level( conf, src, level);
00370 if (status != PJ_SUCCESS)
00371 app_perror(THIS_FILE, "Error adjusting level", status);
00372 break;
00373
00374
00375 case 'r':
00376 puts("");
00377 puts("Adjust receive level of a port");
00378 if (!input("Enter port number", tmp1, sizeof(tmp1)) )
00379 continue;
00380 src = strtol(tmp1, &err, 10);
00381 if (*err || src < 0 || src >= port_count) {
00382 puts("Invalid slot number");
00383 continue;
00384 }
00385
00386 if (!input("Enter level (-128 to >127, 0 for normal)",
00387 tmp2, sizeof(tmp2)) )
00388 continue;
00389 level = strtol(tmp2, &err, 10);
00390 if (*err || level < -128) {
00391 puts("Invalid level");
00392 continue;
00393 }
00394
00395 status = pjmedia_conf_adjust_rx_level( conf, src, level);
00396 if (status != PJ_SUCCESS)
00397 app_perror(THIS_FILE, "Error adjusting level", status);
00398 break;
00399
00400 case 'v':
00401 puts("");
00402 puts("Display VU meter");
00403 if (!input("Enter port number to monitor", tmp1, sizeof(tmp1)) )
00404 continue;
00405 src = strtol(tmp1, &err, 10);
00406 if (*err || src < 0 || src >= port_count) {
00407 puts("Invalid slot number");
00408 continue;
00409 }
00410
00411 if (!input("Enter r for rx level or t for tx level", tmp2, sizeof(tmp2)))
00412 continue;
00413 if (tmp2[0] != 'r' && tmp2[0] != 't') {
00414 puts("Invalid option");
00415 continue;
00416 }
00417
00418 if (!input("Duration to monitor (in seconds)", tmp1, sizeof(tmp1)) )
00419 continue;
00420 dur = strtol(tmp1, &err, 10);
00421 if (*err) {
00422 puts("Invalid duration number");
00423 continue;
00424 }
00425
00426 monitor_level(conf, src, tmp2[0], dur);
00427 break;
00428
00429 case 'q':
00430 goto on_quit;
00431
00432 default:
00433 printf("Invalid input character '%c'\n", tmp[0]);
00434 break;
00435 }
00436 }
00437
00438 on_quit:
00439
00440
00441
00442
00443 status = pjmedia_conf_destroy( conf );
00444 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00445
00446
00447
00448 for (i=0; i<file_count; ++i) {
00449 status = pjmedia_port_destroy( file_port[i]);
00450 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00451 }
00452
00453
00454 if (rec_port)
00455 pjmedia_port_destroy(rec_port);
00456
00457
00458 pj_pool_release( pool );
00459
00460
00461 pjmedia_endpt_destroy( med_endpt );
00462
00463
00464 pj_caching_pool_destroy( &cp );
00465
00466
00467 pj_shutdown();
00468
00469
00470 return 0;
00471 }
00472
00473
00474
00475
00476
00477 static void conf_list(pjmedia_conf *conf, int detail)
00478 {
00479 enum { MAX_PORTS = 32 };
00480 unsigned i, count;
00481 pjmedia_conf_port_info info[MAX_PORTS];
00482
00483 printf("Conference ports:\n");
00484
00485 count = PJ_ARRAY_SIZE(info);
00486 pjmedia_conf_get_ports_info(conf, &count, info);
00487
00488 for (i=0; i<count; ++i) {
00489 char txlist[4*MAX_PORTS];
00490 unsigned j;
00491 pjmedia_conf_port_info *port_info = &info[i];
00492
00493 txlist[0] = '\0';
00494 for (j=0; j<port_info->listener_cnt; ++j) {
00495 char s[10];
00496 pj_ansi_sprintf(s, "#%d ", port_info->listener_slots[j]);
00497 pj_ansi_strcat(txlist, s);
00498
00499 }
00500
00501 if (txlist[0] == '\0') {
00502 txlist[0] = '-';
00503 txlist[1] = '\0';
00504 }
00505
00506 if (!detail) {
00507 printf("Port #%02d %-25.*s transmitting to: %s\n",
00508 port_info->slot,
00509 (int)port_info->name.slen,
00510 port_info->name.ptr,
00511 txlist);
00512 } else {
00513 unsigned tx_level, rx_level;
00514
00515 pjmedia_conf_get_signal_level(conf, port_info->slot,
00516 &tx_level, &rx_level);
00517
00518 printf("Port #%02d:\n"
00519 " Name : %.*s\n"
00520 " Sampling rate : %d Hz\n"
00521 " Samples per frame : %d\n"
00522 " Frame time : %d ms\n"
00523 " Signal level adjustment : tx=%d, rx=%d\n"
00524 " Current signal level : tx=%u, rx=%u\n"
00525 " Transmitting to ports : %s\n\n",
00526 port_info->slot,
00527 (int)port_info->name.slen,
00528 port_info->name.ptr,
00529 port_info->clock_rate,
00530 port_info->samples_per_frame,
00531 port_info->samples_per_frame*1000/port_info->clock_rate,
00532 port_info->tx_adj_level,
00533 port_info->rx_adj_level,
00534 tx_level,
00535 rx_level,
00536 txlist);
00537 }
00538
00539 }
00540 puts("");
00541 }
00542
00543
00544
00545
00546
00547 static void monitor_level(pjmedia_conf *conf, int slot, int dir, int dur)
00548 {
00549 enum { SLEEP = 20, SAMP_CNT = 2};
00550 pj_status_t status;
00551 int i, total_count;
00552 unsigned level, samp_cnt;
00553
00554
00555 puts("");
00556 printf("Displaying VU meter for port %d for about %d seconds\n",
00557 slot, dur);
00558
00559 total_count = dur * 1000 / SLEEP;
00560
00561 level = 0;
00562 samp_cnt = 0;
00563
00564 for (i=0; i<total_count; ++i) {
00565 unsigned tx_level, rx_level;
00566 int j, length;
00567 char meter[21];
00568
00569
00570 status = pjmedia_conf_get_signal_level(conf, slot,
00571 &tx_level, &rx_level);
00572 if (status != PJ_SUCCESS) {
00573 app_perror(THIS_FILE, "Unable to read level", status);
00574 return;
00575 }
00576
00577 level += (dir=='r' ? rx_level : tx_level);
00578 ++samp_cnt;
00579
00580
00581 if (samp_cnt < SAMP_CNT) {
00582 pj_thread_sleep(SLEEP);
00583 continue;
00584 }
00585
00586
00587 level = level / samp_cnt;
00588
00589
00590 length = 20 * level / 255;
00591 for (j=0; j<length; ++j)
00592 meter[j] = '#';
00593 for (; j<20; ++j)
00594 meter[j] = ' ';
00595 meter[20] = '\0';
00596
00597 printf("Port #%02d %cx level: [%s] %d \r",
00598 slot, dir, meter, level);
00599
00600
00601 samp_cnt = 0;
00602 level = 0;
00603
00604 pj_thread_sleep(SLEEP);
00605 }
00606
00607 puts("");
00608 }
00609