/* $Id$ */ /* * Copyright (C) 2003-2006 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include static const char *desc = " FILE: \n" " \n" " confsample.c \n" " \n" " PURPOSE: \n" " \n" " Demonstrate how to use conference bridge. \n" " \n" " USAGE: \n" " \n" " confsample [file1.wav] [file2.wav] ... \n" " \n" " where: \n" " fileN.wav are optional WAV files to be connected to the conference \n" " bridge. The WAV files MUST have single channel (mono) and 16 bit PCM \n" " samples. It can have arbitrary sampling rate. \n" " \n" " DESCRIPTION: \n" " \n" " Here we create a conference bridge, with at least one port (port zero \n" " is always created for the sound device). \n" " \n" " If WAV files are specified, the WAV file player ports will be connected \n" " to slot starting from number one in the bridge. The WAV files can have \n" " arbitrary sampling rate; the bridge will convert it to its clock rate. \n" " However, the files MUST have a single audio channel only (i.e. mono). \n"; #include #include #include /* atoi() */ #include /* For logging purpose. */ #define THIS_FILE "confsample.c" /* Constants */ #define CLOCK_RATE 44100 #define NSAMPLES (CLOCK_RATE * 20 / 1000) #define NCHANNELS 1 #define NBITS 16 /* * Prototypes: */ /* List the ports in the conference bridge */ static void conf_list(pjmedia_conf *conf, pj_bool_t detail); /* Display VU meter */ static void monitor_level(pjmedia_conf *conf, int slot, int dir, int dur); /* Util to display the error message for the specified error code */ static int app_perror( const char *sender, const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); printf("%s: %s [code=%d]\n", title, errmsg, status); return 1; } /* Show usage */ static void usage(void) { puts(""); puts("Usage: confsample [file1.wav] [file2.wav] ..."); puts(""); puts("where:"); puts(" fileN.WAV are optional WAV files to be connected to the conference"); puts(" bridge. The WAV files MUST have single channel (mono) and 16 bit PCM"); puts(" samples. They can have arbitrary/different sampling rate."); } /* Input simple string */ static pj_bool_t input(const char *title, char *buf, pj_size_t len) { char *p; printf("%s (empty to cancel): ", title); fflush(stdout); fgets(buf, len, stdin); /* Remove trailing newlines. */ for (p=buf; ; ++p) { if (*p=='\r' || *p=='\n') *p='\0'; else if (!*p) break; } if (!*buf) return PJ_FALSE; return PJ_TRUE; } /***************************************************************************** * main() */ int main(int argc, char *argv[]) { pj_caching_pool cp; pjmedia_endpt *med_endpt; pj_pool_t *pool; pjmedia_conf *conf; int i, port_count, file_count; pjmedia_port **file_port; /* Array of file ports */ char tmp[10]; pj_status_t status; /* Just in case user needs help */ if (argc > 1 && (*argv[1]=='-' || *argv[1]=='/' || *argv[1]=='?')) { usage(); return 1; } /* Must init PJLIB first: */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); /* * Initialize media endpoint. * This will implicitly initialize PJMEDIA too. */ status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Create memory pool to allocate memory */ pool = pj_pool_create( &cp.factory, /* pool factory */ "wav", /* pool name. */ 4000, /* init size */ 4000, /* increment size */ NULL /* callback on error */ ); file_count = argc-1; port_count = file_count + 1; /* Create the conference bridge. * With default options (zero), the bridge will create an instance of * sound capture and playback device and connect them to slot zero. */ status = pjmedia_conf_create( pool, /* pool to use */ port_count,/* number of ports */ CLOCK_RATE,/* sampling rate */ NCHANNELS,/* # of channels. */ NSAMPLES, /* samples per frame */ NBITS, /* bits per sample */ 0, /* options */ &conf /* result */ ); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create conference bridge", status); return 1; } /* Create file ports. */ file_port = pj_pool_alloc(pool, file_count * sizeof(pjmedia_port*)); for (i=0; i= port_count) { puts("Invalid slot number"); continue; } if (!input("Enter destination port number", tmp2, sizeof(tmp2)) ) continue; dst = strtol(tmp2, &err, 10); if (*err || dst < 0 || dst >= port_count) { puts("Invalid slot number"); continue; } status = pjmedia_conf_connect_port(conf, src, dst, 0); if (status != PJ_SUCCESS) app_perror(THIS_FILE, "Error connecting port", status); break; case 'd': puts(""); puts("Disconnect port connection"); if (!input("Enter source port number", tmp1, sizeof(tmp1)) ) continue; src = strtol(tmp1, &err, 10); if (*err || src < 0 || src >= port_count) { puts("Invalid slot number"); continue; } if (!input("Enter destination port number", tmp2, sizeof(tmp2)) ) continue; dst = strtol(tmp2, &err, 10); if (*err || dst < 0 || dst >= port_count) { puts("Invalid slot number"); continue; } status = pjmedia_conf_disconnect_port(conf, src, dst); if (status != PJ_SUCCESS) app_perror(THIS_FILE, "Error connecting port", status); break; case 't': puts(""); puts("Adjust transmit level of a port"); if (!input("Enter port number", tmp1, sizeof(tmp1)) ) continue; src = strtol(tmp1, &err, 10); if (*err || src < 0 || src >= port_count) { puts("Invalid slot number"); continue; } if (!input("Enter level (-128 to +127, 0 for normal)", tmp2, sizeof(tmp2)) ) continue; level = strtol(tmp2, &err, 10); if (*err || level < -128 || level > 127) { puts("Invalid level"); continue; } status = pjmedia_conf_adjust_tx_level( conf, src, level); if (status != PJ_SUCCESS) app_perror(THIS_FILE, "Error adjusting level", status); break; case 'r': puts(""); puts("Adjust receive level of a port"); if (!input("Enter port number", tmp1, sizeof(tmp1)) ) continue; src = strtol(tmp1, &err, 10); if (*err || src < 0 || src >= port_count) { puts("Invalid slot number"); continue; } if (!input("Enter level (-128 to +127, 0 for normal)", tmp2, sizeof(tmp2)) ) continue; level = strtol(tmp2, &err, 10); if (*err || level < -128 || level > 127) { puts("Invalid level"); continue; } status = pjmedia_conf_adjust_rx_level( conf, src, level); if (status != PJ_SUCCESS) app_perror(THIS_FILE, "Error adjusting level", status); break; case 'v': puts(""); puts("Display VU meter"); if (!input("Enter port number to monitor", tmp1, sizeof(tmp1)) ) continue; src = strtol(tmp1, &err, 10); if (*err || src < 0 || src >= port_count) { puts("Invalid slot number"); continue; } if (!input("Enter r for rx level or t for tx level", tmp2, sizeof(tmp2))) continue; if (tmp2[0] != 'r' && tmp2[0] != 't') { puts("Invalid option"); continue; } if (!input("Duration to monitor (in seconds)", tmp1, sizeof(tmp1)) ) continue; strtol(tmp1, &err, 10); if (*err) { puts("Invalid duration number"); continue; } monitor_level(conf, src, tmp2[0], strtol(tmp1, &err, 10)); break; case 'q': goto on_quit; default: printf("Invalid input character '%c'\n", tmp[0]); break; } } on_quit: /* Start deinitialization: */ /* Destroy conference bridge */ status = pjmedia_conf_destroy( conf ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Destroy file ports */ for (i=0; ilistener[j]) { pj_ansi_sprintf(s, "#%d ", j); pj_ansi_strcat(txlist, s); } } if (txlist[0] == '\0') { txlist[0] = '-'; txlist[1] = '\0'; } if (!detail) { printf("Port #%02d %-25.*s transmitting to: %s\n", port_info->slot, (int)port_info->name.slen, port_info->name.ptr, txlist); } else { unsigned tx_level, rx_level; pjmedia_conf_get_signal_level(conf, port_info->slot, &tx_level, &rx_level); printf("Port #%02d:\n" " Name : %.*s\n" " Sampling rate : %d Hz\n" " Frame time : %d ms\n" " Signal level adjustment : tx=%d, rx=%d\n" " Current signal level : tx=%u, rx=%u\n" " Transmitting to ports : %s\n\n", port_info->slot, (int)port_info->name.slen, port_info->name.ptr, port_info->clock_rate, port_info->samples_per_frame*1000/port_info->clock_rate, port_info->tx_adj_level, port_info->rx_adj_level, tx_level, rx_level, txlist); } } puts(""); } /* * Display VU meter */ static void monitor_level(pjmedia_conf *conf, int slot, int dir, int dur) { enum { SLEEP = 20, SAMP_CNT = 2}; pj_status_t status; int i, total_count; unsigned level, samp_cnt; puts(""); printf("Displaying VU meter for port %d for about %d seconds\n", slot, dur); total_count = dur * 1000 / SLEEP; level = 0; samp_cnt = 0; for (i=0; i