Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 1 | /* $Id$ */ |
| 2 | /* |
| 3 | * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> |
| 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 | |
| 20 | #include <pjmedia.h> |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 21 | #include <pjlib-util.h> /* pj_getopt */ |
| 22 | #include <pjlib.h> |
| 23 | |
| 24 | #include <stdlib.h> /* atoi() */ |
| 25 | #include <stdio.h> |
| 26 | |
| 27 | #include "util.h" |
| 28 | |
| 29 | /* For logging purpose. */ |
| 30 | #define THIS_FILE "confsample.c" |
| 31 | |
| 32 | |
| 33 | /* Shall we put recorder in the conference */ |
| 34 | #define RECORDER 1 |
| 35 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 36 | |
Benny Prijono | fa137ca | 2006-03-20 17:42:37 +0000 | [diff] [blame] | 37 | static const char *desc = |
| 38 | " FILE: \n" |
| 39 | " \n" |
| 40 | " confsample.c \n" |
| 41 | " \n" |
| 42 | " PURPOSE: \n" |
| 43 | " \n" |
| 44 | " Demonstrate how to use conference bridge. \n" |
| 45 | " \n" |
| 46 | " USAGE: \n" |
| 47 | " \n" |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 48 | " confsample [options] [file1.wav] [file2.wav] ... \n" |
Benny Prijono | fa137ca | 2006-03-20 17:42:37 +0000 | [diff] [blame] | 49 | " \n" |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 50 | " options: \n" |
| 51 | SND_USAGE |
| 52 | " \n" |
Benny Prijono | fa137ca | 2006-03-20 17:42:37 +0000 | [diff] [blame] | 53 | " fileN.wav are optional WAV files to be connected to the conference \n" |
| 54 | " bridge. The WAV files MUST have single channel (mono) and 16 bit PCM \n" |
| 55 | " samples. It can have arbitrary sampling rate. \n" |
| 56 | " \n" |
| 57 | " DESCRIPTION: \n" |
| 58 | " \n" |
| 59 | " Here we create a conference bridge, with at least one port (port zero \n" |
| 60 | " is always created for the sound device). \n" |
| 61 | " \n" |
| 62 | " If WAV files are specified, the WAV file player ports will be connected \n" |
| 63 | " to slot starting from number one in the bridge. The WAV files can have \n" |
| 64 | " arbitrary sampling rate; the bridge will convert it to its clock rate. \n" |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 65 | " However, the files MUST have a single audio channel only (i.e. mono). \n"; |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 66 | |
Benny Prijono | fa137ca | 2006-03-20 17:42:37 +0000 | [diff] [blame] | 67 | |
| 68 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 69 | /* |
| 70 | * Prototypes: |
| 71 | */ |
| 72 | |
| 73 | /* List the ports in the conference bridge */ |
| 74 | static void conf_list(pjmedia_conf *conf, pj_bool_t detail); |
| 75 | |
| 76 | /* Display VU meter */ |
| 77 | static void monitor_level(pjmedia_conf *conf, int slot, int dir, int dur); |
| 78 | |
| 79 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 80 | /* Show usage */ |
| 81 | static void usage(void) |
| 82 | { |
| 83 | puts(""); |
Benny Prijono | 8482783 | 2006-03-23 13:15:59 +0000 | [diff] [blame] | 84 | puts(desc); |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 85 | } |
| 86 | |
| 87 | |
| 88 | |
| 89 | /* Input simple string */ |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 90 | static pj_bool_t input(const char *title, char *buf, pj_size_t len) |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 91 | { |
| 92 | char *p; |
| 93 | |
| 94 | printf("%s (empty to cancel): ", title); fflush(stdout); |
| 95 | fgets(buf, len, stdin); |
| 96 | |
| 97 | /* Remove trailing newlines. */ |
| 98 | for (p=buf; ; ++p) { |
| 99 | if (*p=='\r' || *p=='\n') *p='\0'; |
| 100 | else if (!*p) break; |
| 101 | } |
| 102 | |
| 103 | if (!*buf) |
| 104 | return PJ_FALSE; |
| 105 | |
| 106 | return PJ_TRUE; |
| 107 | } |
| 108 | |
| 109 | |
| 110 | /***************************************************************************** |
| 111 | * main() |
| 112 | */ |
| 113 | int main(int argc, char *argv[]) |
| 114 | { |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 115 | int dev_id = -1; |
| 116 | int clock_rate = CLOCK_RATE; |
| 117 | int channel_count = NCHANNELS; |
| 118 | int samples_per_frame = NSAMPLES; |
| 119 | int bits_per_sample = NBITS; |
| 120 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 121 | pj_caching_pool cp; |
| 122 | pjmedia_endpt *med_endpt; |
| 123 | pj_pool_t *pool; |
| 124 | pjmedia_conf *conf; |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 125 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 126 | int i, port_count, file_count; |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 127 | pjmedia_port **file_port; /* Array of file ports */ |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 128 | pjmedia_port *rec_port = NULL; /* Wav writer port */ |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 129 | |
| 130 | char tmp[10]; |
| 131 | pj_status_t status; |
| 132 | |
| 133 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 134 | /* Must init PJLIB first: */ |
| 135 | status = pj_init(); |
| 136 | PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); |
| 137 | |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 138 | /* Get command line options. */ |
| 139 | if (get_snd_options(THIS_FILE, argc, argv, &dev_id, &clock_rate, |
| 140 | &channel_count, &samples_per_frame, &bits_per_sample)) |
| 141 | { |
| 142 | usage(); |
| 143 | return 1; |
| 144 | } |
| 145 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 146 | /* Must create a pool factory before we can allocate any memory. */ |
| 147 | pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); |
| 148 | |
| 149 | /* |
| 150 | * Initialize media endpoint. |
| 151 | * This will implicitly initialize PJMEDIA too. |
| 152 | */ |
Benny Prijono | 275fd68 | 2006-03-22 11:59:11 +0000 | [diff] [blame] | 153 | status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 154 | PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); |
| 155 | |
| 156 | /* Create memory pool to allocate memory */ |
| 157 | pool = pj_pool_create( &cp.factory, /* pool factory */ |
| 158 | "wav", /* pool name. */ |
| 159 | 4000, /* init size */ |
| 160 | 4000, /* increment size */ |
| 161 | NULL /* callback on error */ |
| 162 | ); |
| 163 | |
| 164 | |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 165 | file_count = argc - pj_optind; |
| 166 | port_count = file_count + 1 + RECORDER; |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 167 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 168 | /* Create the conference bridge. |
| 169 | * With default options (zero), the bridge will create an instance of |
| 170 | * sound capture and playback device and connect them to slot zero. |
| 171 | */ |
| 172 | status = pjmedia_conf_create( pool, /* pool to use */ |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 173 | port_count,/* number of ports */ |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 174 | clock_rate, |
| 175 | channel_count, |
| 176 | samples_per_frame, |
| 177 | bits_per_sample, |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 178 | 0, /* options */ |
| 179 | &conf /* result */ |
| 180 | ); |
| 181 | if (status != PJ_SUCCESS) { |
| 182 | app_perror(THIS_FILE, "Unable to create conference bridge", status); |
| 183 | return 1; |
| 184 | } |
| 185 | |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 186 | #if RECORDER |
Benny Prijono | 1595301 | 2006-04-27 22:37:08 +0000 | [diff] [blame] | 187 | status = pjmedia_wav_writer_port_create( pool, "confrecord.wav", |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 188 | clock_rate, channel_count, |
| 189 | samples_per_frame, |
| 190 | bits_per_sample, 0, 0, NULL, |
| 191 | &rec_port); |
| 192 | if (status != PJ_SUCCESS) { |
| 193 | app_perror(THIS_FILE, "Unable to create WAV writer", status); |
| 194 | return 1; |
| 195 | } |
| 196 | |
| 197 | pjmedia_conf_add_port(conf, pool, rec_port, NULL, NULL); |
| 198 | #endif |
| 199 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 200 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 201 | /* Create file ports. */ |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 202 | file_port = pj_pool_alloc(pool, file_count * sizeof(pjmedia_port*)); |
| 203 | |
| 204 | for (i=0; i<file_count; ++i) { |
| 205 | |
| 206 | /* Load the WAV file to file port. */ |
Benny Prijono | 1595301 | 2006-04-27 22:37:08 +0000 | [diff] [blame] | 207 | status = pjmedia_wav_player_port_create( |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 208 | pool, /* pool. */ |
| 209 | argv[i+pj_optind], /* filename */ |
Benny Prijono | 1595301 | 2006-04-27 22:37:08 +0000 | [diff] [blame] | 210 | 0, /* use default ptime */ |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 211 | 0, /* flags */ |
| 212 | 0, /* buf size */ |
| 213 | NULL, /* user data */ |
| 214 | &file_port[i] /* result */ |
| 215 | ); |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 216 | if (status != PJ_SUCCESS) { |
| 217 | char title[80]; |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 218 | pj_ansi_sprintf(title, "Unable to use %s", argv[i+pj_optind]); |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 219 | app_perror(THIS_FILE, title, status); |
| 220 | usage(); |
| 221 | return 1; |
| 222 | } |
| 223 | |
| 224 | /* Add the file port to conference bridge */ |
| 225 | status = pjmedia_conf_add_port( conf, /* The bridge */ |
| 226 | pool, /* pool */ |
| 227 | file_port[i], /* port to connect */ |
| 228 | NULL, /* Use port's name */ |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 229 | NULL /* ptr for slot # */ |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 230 | ); |
| 231 | if (status != PJ_SUCCESS) { |
| 232 | app_perror(THIS_FILE, "Unable to add conference port", status); |
| 233 | return 1; |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | |
| 238 | /* |
| 239 | * All ports are set up in the conference bridge. |
| 240 | * But at this point, no media will be flowing since no ports are |
| 241 | * "connected". User must connect the port manually. |
| 242 | */ |
| 243 | |
| 244 | |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 245 | /* Dump memory usage */ |
| 246 | dump_pool_usage(THIS_FILE, &cp); |
| 247 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 248 | /* Sleep to allow log messages to flush */ |
| 249 | pj_thread_sleep(100); |
| 250 | |
| 251 | |
| 252 | /* |
| 253 | * UI Menu: |
| 254 | */ |
| 255 | for (;;) { |
| 256 | char tmp1[10]; |
| 257 | char tmp2[10]; |
| 258 | char *err; |
| 259 | int src, dst, level; |
| 260 | |
| 261 | puts(""); |
| 262 | conf_list(conf, 0); |
| 263 | puts(""); |
| 264 | puts("Menu:"); |
| 265 | puts(" s Show ports details"); |
| 266 | puts(" c Connect one port to another"); |
| 267 | puts(" d Disconnect port connection"); |
| 268 | puts(" t Adjust signal level transmitted (tx) to a port"); |
| 269 | puts(" r Adjust signal level received (rx) from a port"); |
| 270 | puts(" v Display VU meter for a particular port"); |
| 271 | puts(" q Quit"); |
| 272 | puts(""); |
| 273 | |
| 274 | printf("Enter selection: "); fflush(stdout); |
| 275 | |
| 276 | fgets(tmp, sizeof(tmp), stdin); |
| 277 | |
| 278 | switch (tmp[0]) { |
| 279 | case 's': |
| 280 | puts(""); |
| 281 | conf_list(conf, 1); |
| 282 | break; |
| 283 | |
| 284 | case 'c': |
| 285 | puts(""); |
| 286 | puts("Connect source port to destination port"); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 287 | if (!input("Enter source port number", tmp1, sizeof(tmp1)) ) |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 288 | continue; |
| 289 | src = strtol(tmp1, &err, 10); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 290 | if (*err || src < 0 || src >= port_count) { |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 291 | puts("Invalid slot number"); |
| 292 | continue; |
| 293 | } |
| 294 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 295 | if (!input("Enter destination port number", tmp2, sizeof(tmp2)) ) |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 296 | continue; |
| 297 | dst = strtol(tmp2, &err, 10); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 298 | if (*err || dst < 0 || dst >= port_count) { |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 299 | puts("Invalid slot number"); |
| 300 | continue; |
| 301 | } |
| 302 | |
| 303 | status = pjmedia_conf_connect_port(conf, src, dst, 0); |
| 304 | if (status != PJ_SUCCESS) |
| 305 | app_perror(THIS_FILE, "Error connecting port", status); |
| 306 | |
| 307 | break; |
| 308 | |
| 309 | case 'd': |
| 310 | puts(""); |
| 311 | puts("Disconnect port connection"); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 312 | if (!input("Enter source port number", tmp1, sizeof(tmp1)) ) |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 313 | continue; |
| 314 | src = strtol(tmp1, &err, 10); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 315 | if (*err || src < 0 || src >= port_count) { |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 316 | puts("Invalid slot number"); |
| 317 | continue; |
| 318 | } |
| 319 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 320 | if (!input("Enter destination port number", tmp2, sizeof(tmp2)) ) |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 321 | continue; |
| 322 | dst = strtol(tmp2, &err, 10); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 323 | if (*err || dst < 0 || dst >= port_count) { |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 324 | puts("Invalid slot number"); |
| 325 | continue; |
| 326 | } |
| 327 | |
| 328 | status = pjmedia_conf_disconnect_port(conf, src, dst); |
| 329 | if (status != PJ_SUCCESS) |
| 330 | app_perror(THIS_FILE, "Error connecting port", status); |
| 331 | |
| 332 | |
| 333 | break; |
| 334 | |
| 335 | case 't': |
| 336 | puts(""); |
| 337 | puts("Adjust transmit level of a port"); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 338 | if (!input("Enter port number", tmp1, sizeof(tmp1)) ) |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 339 | continue; |
| 340 | src = strtol(tmp1, &err, 10); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 341 | if (*err || src < 0 || src >= port_count) { |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 342 | puts("Invalid slot number"); |
| 343 | continue; |
| 344 | } |
| 345 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 346 | if (!input("Enter level (-128 to +127, 0 for normal)", |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 347 | tmp2, sizeof(tmp2)) ) |
| 348 | continue; |
| 349 | level = strtol(tmp2, &err, 10); |
| 350 | if (*err || level < -128 || level > 127) { |
| 351 | puts("Invalid level"); |
| 352 | continue; |
| 353 | } |
| 354 | |
| 355 | status = pjmedia_conf_adjust_tx_level( conf, src, level); |
| 356 | if (status != PJ_SUCCESS) |
| 357 | app_perror(THIS_FILE, "Error adjusting level", status); |
| 358 | break; |
| 359 | |
| 360 | |
| 361 | case 'r': |
| 362 | puts(""); |
| 363 | puts("Adjust receive level of a port"); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 364 | if (!input("Enter port number", tmp1, sizeof(tmp1)) ) |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 365 | continue; |
| 366 | src = strtol(tmp1, &err, 10); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 367 | if (*err || src < 0 || src >= port_count) { |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 368 | puts("Invalid slot number"); |
| 369 | continue; |
| 370 | } |
| 371 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 372 | if (!input("Enter level (-128 to +127, 0 for normal)", |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 373 | tmp2, sizeof(tmp2)) ) |
| 374 | continue; |
| 375 | level = strtol(tmp2, &err, 10); |
| 376 | if (*err || level < -128 || level > 127) { |
| 377 | puts("Invalid level"); |
| 378 | continue; |
| 379 | } |
| 380 | |
| 381 | status = pjmedia_conf_adjust_rx_level( conf, src, level); |
| 382 | if (status != PJ_SUCCESS) |
| 383 | app_perror(THIS_FILE, "Error adjusting level", status); |
| 384 | break; |
| 385 | |
| 386 | case 'v': |
| 387 | puts(""); |
| 388 | puts("Display VU meter"); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 389 | if (!input("Enter port number to monitor", tmp1, sizeof(tmp1)) ) |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 390 | continue; |
| 391 | src = strtol(tmp1, &err, 10); |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 392 | if (*err || src < 0 || src >= port_count) { |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 393 | puts("Invalid slot number"); |
| 394 | continue; |
| 395 | } |
| 396 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 397 | if (!input("Enter r for rx level or t for tx level", tmp2, sizeof(tmp2))) |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 398 | continue; |
| 399 | if (tmp2[0] != 'r' && tmp2[0] != 't') { |
| 400 | puts("Invalid option"); |
| 401 | continue; |
| 402 | } |
| 403 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 404 | if (!input("Duration to monitor (in seconds)", tmp1, sizeof(tmp1)) ) |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 405 | continue; |
| 406 | strtol(tmp1, &err, 10); |
| 407 | if (*err) { |
| 408 | puts("Invalid duration number"); |
| 409 | continue; |
| 410 | } |
| 411 | |
| 412 | monitor_level(conf, src, tmp2[0], strtol(tmp1, &err, 10)); |
| 413 | break; |
| 414 | |
| 415 | case 'q': |
| 416 | goto on_quit; |
| 417 | |
| 418 | default: |
| 419 | printf("Invalid input character '%c'\n", tmp[0]); |
| 420 | break; |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | on_quit: |
| 425 | |
| 426 | /* Start deinitialization: */ |
| 427 | |
| 428 | /* Destroy conference bridge */ |
| 429 | status = pjmedia_conf_destroy( conf ); |
| 430 | PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); |
| 431 | |
| 432 | |
| 433 | /* Destroy file ports */ |
| 434 | for (i=0; i<file_count; ++i) { |
| 435 | status = pjmedia_port_destroy( file_port[i]); |
| 436 | PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); |
| 437 | } |
| 438 | |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 439 | /* Destroy recorder port */ |
| 440 | if (rec_port) |
| 441 | pjmedia_port_destroy(rec_port); |
| 442 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 443 | /* Release application pool */ |
| 444 | pj_pool_release( pool ); |
| 445 | |
| 446 | /* Destroy media endpoint. */ |
| 447 | pjmedia_endpt_destroy( med_endpt ); |
| 448 | |
| 449 | /* Destroy pool factory */ |
| 450 | pj_caching_pool_destroy( &cp ); |
| 451 | |
| 452 | |
| 453 | /* Done. */ |
| 454 | return 0; |
| 455 | } |
| 456 | |
| 457 | |
| 458 | /* |
| 459 | * List the ports in conference bridge |
| 460 | */ |
| 461 | static void conf_list(pjmedia_conf *conf, int detail) |
| 462 | { |
| 463 | enum { MAX_PORTS = 32 }; |
| 464 | unsigned i, count; |
| 465 | pjmedia_conf_port_info info[MAX_PORTS]; |
| 466 | |
| 467 | printf("Conference ports:\n"); |
| 468 | |
| 469 | count = PJ_ARRAY_SIZE(info); |
| 470 | pjmedia_conf_get_ports_info(conf, &count, info); |
| 471 | |
| 472 | for (i=0; i<count; ++i) { |
| 473 | char txlist[4*MAX_PORTS]; |
| 474 | unsigned j; |
| 475 | pjmedia_conf_port_info *port_info = &info[i]; |
| 476 | |
| 477 | txlist[0] = '\0'; |
Benny Prijono | c78c3a3 | 2006-06-16 15:54:43 +0000 | [diff] [blame^] | 478 | for (j=0; j<port_info->listener_cnt; ++j) { |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 479 | char s[10]; |
Benny Prijono | c78c3a3 | 2006-06-16 15:54:43 +0000 | [diff] [blame^] | 480 | pj_ansi_sprintf(s, "#%d ", port_info->listener_slots[j]); |
| 481 | pj_ansi_strcat(txlist, s); |
| 482 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 483 | } |
| 484 | |
| 485 | if (txlist[0] == '\0') { |
| 486 | txlist[0] = '-'; |
| 487 | txlist[1] = '\0'; |
| 488 | } |
| 489 | |
| 490 | if (!detail) { |
| 491 | printf("Port #%02d %-25.*s transmitting to: %s\n", |
| 492 | port_info->slot, |
| 493 | (int)port_info->name.slen, |
| 494 | port_info->name.ptr, |
| 495 | txlist); |
| 496 | } else { |
| 497 | unsigned tx_level, rx_level; |
| 498 | |
| 499 | pjmedia_conf_get_signal_level(conf, port_info->slot, |
| 500 | &tx_level, &rx_level); |
| 501 | |
| 502 | printf("Port #%02d:\n" |
| 503 | " Name : %.*s\n" |
| 504 | " Sampling rate : %d Hz\n" |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 505 | " Samples per frame : %d\n" |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 506 | " Frame time : %d ms\n" |
| 507 | " Signal level adjustment : tx=%d, rx=%d\n" |
| 508 | " Current signal level : tx=%u, rx=%u\n" |
| 509 | " Transmitting to ports : %s\n\n", |
| 510 | port_info->slot, |
| 511 | (int)port_info->name.slen, |
| 512 | port_info->name.ptr, |
| 513 | port_info->clock_rate, |
Benny Prijono | bc79731 | 2006-03-24 20:44:27 +0000 | [diff] [blame] | 514 | port_info->samples_per_frame, |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 515 | port_info->samples_per_frame*1000/port_info->clock_rate, |
| 516 | port_info->tx_adj_level, |
| 517 | port_info->rx_adj_level, |
| 518 | tx_level, |
| 519 | rx_level, |
| 520 | txlist); |
| 521 | } |
| 522 | |
| 523 | } |
| 524 | puts(""); |
| 525 | } |
| 526 | |
| 527 | |
| 528 | /* |
| 529 | * Display VU meter |
| 530 | */ |
| 531 | static void monitor_level(pjmedia_conf *conf, int slot, int dir, int dur) |
| 532 | { |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 533 | enum { SLEEP = 20, SAMP_CNT = 2}; |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 534 | pj_status_t status; |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 535 | int i, total_count; |
| 536 | unsigned level, samp_cnt; |
| 537 | |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 538 | |
| 539 | puts(""); |
| 540 | printf("Displaying VU meter for port %d for about %d seconds\n", |
| 541 | slot, dur); |
| 542 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 543 | total_count = dur * 1000 / SLEEP; |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 544 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 545 | level = 0; |
| 546 | samp_cnt = 0; |
| 547 | |
| 548 | for (i=0; i<total_count; ++i) { |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 549 | unsigned tx_level, rx_level; |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 550 | int j, length; |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 551 | char meter[21]; |
| 552 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 553 | /* Poll the volume every 20 msec */ |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 554 | status = pjmedia_conf_get_signal_level(conf, slot, |
| 555 | &tx_level, &rx_level); |
| 556 | if (status != PJ_SUCCESS) { |
| 557 | app_perror(THIS_FILE, "Unable to read level", status); |
| 558 | return; |
| 559 | } |
| 560 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 561 | level += (dir=='r' ? rx_level : tx_level); |
| 562 | ++samp_cnt; |
| 563 | |
| 564 | /* Accumulate until we have enough samples */ |
| 565 | if (samp_cnt < SAMP_CNT) { |
| 566 | pj_thread_sleep(SLEEP); |
| 567 | continue; |
| 568 | } |
| 569 | |
| 570 | /* Get average */ |
| 571 | level = level / samp_cnt; |
| 572 | |
| 573 | /* Draw bar */ |
| 574 | length = 20 * level / 255; |
| 575 | for (j=0; j<length; ++j) |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 576 | meter[j] = '#'; |
| 577 | for (; j<20; ++j) |
| 578 | meter[j] = ' '; |
| 579 | meter[20] = '\0'; |
| 580 | |
Benny Prijono | adb3465 | 2006-03-19 12:09:53 +0000 | [diff] [blame] | 581 | printf("Port #%02d %cx level: [%s] %d \r", |
| 582 | slot, dir, meter, level); |
| 583 | |
| 584 | /* Next.. */ |
| 585 | samp_cnt = 0; |
| 586 | level = 0; |
Benny Prijono | cbc1c47 | 2006-03-19 00:50:23 +0000 | [diff] [blame] | 587 | |
| 588 | pj_thread_sleep(SLEEP); |
| 589 | } |
| 590 | |
| 591 | puts(""); |
| 592 | } |
| 593 | |