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