pjsip logo pjsip.org
Open source SIP stack and media stack for presence, im/instant messaging, and multimedia communication

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

Samples: Using Conference Bridge

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 /* $Id: confsample.c 2039 2008-06-20 22:44:47Z bennylp $ */
00002 /* 
00003  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
00018  */
00019 
00020 #include <pjmedia.h>
00021 #include <pjlib-util.h> /* pj_getopt */
00022 #include <pjlib.h>
00023 
00024 #include <stdlib.h>     /* atoi() */
00025 #include <stdio.h>
00026 
00027 #include "util.h"
00028 
00041 /* For logging purpose. */
00042 #define THIS_FILE   "confsample.c"
00043 
00044 
00045 /* Shall we put recorder in the conference */
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  * Prototypes: 
00083  */
00084 
00085 /* List the ports in the conference bridge */
00086 static void conf_list(pjmedia_conf *conf, pj_bool_t detail);
00087 
00088 /* Display VU meter */
00089 static void monitor_level(pjmedia_conf *conf, int slot, int dir, int dur);
00090 
00091 
00092 /* Show usage */
00093 static void usage(void)
00094 {
00095     puts("");
00096     puts(desc);
00097 }
00098 
00099 
00100 
00101 /* Input simple string */
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     /* Remove trailing newlines. */
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  * main()
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;   /* Array of file ports */
00140     pjmedia_port *rec_port = NULL;  /* Wav writer port */
00141 
00142     char tmp[10];
00143     pj_status_t status;
00144 
00145 
00146     /* Must init PJLIB first: */
00147     status = pj_init();
00148     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00149 
00150     /* Get command line options. */
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     /* Must create a pool factory before we can allocate any memory. */
00159     pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
00160 
00161     /* 
00162      * Initialize media endpoint.
00163      * This will implicitly initialize PJMEDIA too.
00164      */
00165     status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
00166     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00167 
00168     /* Create memory pool to allocate memory */
00169     pool = pj_pool_create( &cp.factory,     /* pool factory         */
00170                            "wav",           /* pool name.           */
00171                            4000,            /* init size            */
00172                            4000,            /* increment size       */
00173                            NULL             /* callback on error    */
00174                            );
00175 
00176 
00177     file_count = argc - pj_optind;
00178     port_count = file_count + 1 + RECORDER;
00179 
00180     /* Create the conference bridge. 
00181      * With default options (zero), the bridge will create an instance of
00182      * sound capture and playback device and connect them to slot zero.
00183      */
00184     status = pjmedia_conf_create( pool,     /* pool to use          */
00185                                   port_count,/* number of ports     */
00186                                   clock_rate,
00187                                   channel_count,
00188                                   samples_per_frame,
00189                                   bits_per_sample,
00190                                   0,        /* options              */
00191                                   &conf     /* result               */
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     /* Create file ports. */
00214     file_port = pj_pool_alloc(pool, file_count * sizeof(pjmedia_port*));
00215 
00216     for (i=0; i<file_count; ++i) {
00217 
00218         /* Load the WAV file to file port. */
00219         status = pjmedia_wav_player_port_create( 
00220                         pool,               /* pool.        */
00221                         argv[i+pj_optind],  /* filename     */
00222                         0,                  /* use default ptime */
00223                         0,                  /* flags        */
00224                         0,                  /* buf size     */
00225                         &file_port[i]       /* result       */
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         /* Add the file port to conference bridge */
00236         status = pjmedia_conf_add_port( conf,           /* The bridge       */
00237                                         pool,           /* pool             */
00238                                         file_port[i],   /* port to connect  */
00239                                         NULL,           /* Use port's name  */
00240                                         NULL            /* ptr for slot #   */
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      * All ports are set up in the conference bridge.
00251      * But at this point, no media will be flowing since no ports are
00252      * "connected". User must connect the port manually.
00253      */
00254 
00255 
00256     /* Dump memory usage */
00257     dump_pool_usage(THIS_FILE, &cp);
00258 
00259     /* Sleep to allow log messages to flush */
00260     pj_thread_sleep(100);
00261 
00262 
00263     /*
00264      * UI Menu: 
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     /* Start deinitialization: */
00438 
00439     /* Destroy conference bridge */
00440     status = pjmedia_conf_destroy( conf );
00441     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00442 
00443 
00444     /* Destroy file ports */
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     /* Destroy recorder port */
00451     if (rec_port)
00452         pjmedia_port_destroy(rec_port);
00453 
00454     /* Release application pool */
00455     pj_pool_release( pool );
00456 
00457     /* Destroy media endpoint. */
00458     pjmedia_endpt_destroy( med_endpt );
00459 
00460     /* Destroy pool factory */
00461     pj_caching_pool_destroy( &cp );
00462 
00463     /* Shutdown PJLIB */
00464     pj_shutdown();
00465 
00466     /* Done. */
00467     return 0;
00468 }
00469 
00470 
00471 /*
00472  * List the ports in conference bridge
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  * Display VU meter
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         /* Poll the volume every 20 msec */
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         /* Accumulate until we have enough samples */
00578         if (samp_cnt < SAMP_CNT) {
00579             pj_thread_sleep(SLEEP);
00580             continue;
00581         }
00582 
00583         /* Get average */
00584         level = level / samp_cnt;
00585 
00586         /* Draw bar */
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         /* Next.. */
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