Ticket #504: ticket504.4.patch
File ticket504.4.patch, 37.9 KB (added by nanang, 16 years ago) |
---|
-
pjsip/include/pjsua-lib/pjsua.h
C:\project\pjproject-trunk>svn diff pjsip\include\pjsua-lib\pjsua.h pjsip\src\pjsua-lib\pjsua_media.c pjsip\src\pjsua-lib\pjsua_core.c pjsip-apps\src\pjsua\pjsua_app.c pjmedia\src\pjmedia\conference.c pjmedia\include\pjmedia\stereo.h pjmedia\src\pjmedia\stereo_port.c pjmedia\build\pjmedia.dsp pjmedia\build\pjmedia.vcproj pjsip-apps\src\samples\stereotest.c pjmedia\include\pjmedia.h
3749 3749 unsigned snd_clock_rate; 3750 3750 3751 3751 /** 3752 * Channel count be applied when opening the sound device and 3753 * conference bridge. 3754 */ 3755 unsigned channel_count; 3756 3757 /** 3752 3758 * Specify audio frame ptime. The value here will affect the 3753 3759 * samples per frame of both the sound device and the conference 3754 3760 * bridge. Specifying lower ptime will normally reduce the -
pjsip/src/pjsua-lib/pjsua_media.c
169 169 /* Save additional conference bridge parameters for future 170 170 * reference. 171 171 */ 172 pjsua_var.mconf_cfg.channel_count = 1;172 pjsua_var.mconf_cfg.channel_count = pjsua_var.media_cfg.channel_count; 173 173 pjsua_var.mconf_cfg.bits_per_sample = 16; 174 174 pjsua_var.mconf_cfg.samples_per_frame = pjsua_var.media_cfg.clock_rate * 175 175 pjsua_var.mconf_cfg.channel_count * … … 1834 1834 fps = 1000 / pjsua_var.media_cfg.audio_frame_ptime; 1835 1835 status = pjmedia_snd_port_create(pjsua_var.pool, capture_dev, 1836 1836 playback_dev, 1837 clock_rates[i], 1, 1838 clock_rates[i]/fps, 1837 clock_rates[i], 1838 pjsua_var.media_cfg.channel_count, 1839 clock_rates[i]/fps * 1840 pjsua_var.media_cfg.channel_count, 1839 1841 16, 0, &pjsua_var.snd_port); 1840 1842 1841 1843 if (status == PJ_SUCCESS) { -
pjsip/src/pjsua-lib/pjsua_core.c
163 163 164 164 cfg->clock_rate = PJSUA_DEFAULT_CLOCK_RATE; 165 165 cfg->snd_clock_rate = 0; 166 cfg->channel_count = 1; 166 167 cfg->audio_frame_ptime = PJSUA_DEFAULT_AUDIO_FRAME_PTIME; 167 168 cfg->max_media_ports = PJSUA_MAX_CONF_PORTS; 168 169 cfg->has_ioqueue = PJ_TRUE; -
pjsip-apps/src/pjsua/pjsua_app.c
175 175 puts (" --dis-codec=name Disable codec (can be specified multiple times)"); 176 176 puts (" --clock-rate=N Override conference bridge clock rate"); 177 177 puts (" --snd-clock-rate=N Override sound device clock rate"); 178 puts (" --stereo Audio device and conference bridge opened in stereo mode"); 178 179 puts (" --null-audio Use NULL audio device"); 179 180 puts (" --play-file=file Register WAV file in conference bridge."); 180 181 puts (" This can be specified multiple times."); … … 389 390 OPT_NAMESERVER, OPT_STUN_DOMAIN, OPT_STUN_SRV, 390 391 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE, 391 392 OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP, 392 OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_ USE_ICE,393 OPT_USE_ SRTP, OPT_SRTP_SECURE,393 OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO, 394 OPT_USE_ICE, OPT_USE_SRTP, OPT_SRTP_SECURE, 394 395 OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC, 395 396 OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC, 396 397 OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD, … … 413 414 { "version", 0, 0, OPT_VERSION}, 414 415 { "clock-rate", 1, 0, OPT_CLOCK_RATE}, 415 416 { "snd-clock-rate", 1, 0, OPT_SND_CLOCK_RATE}, 417 { "stereo", 0, 0, OPT_STEREO}, 416 418 { "null-audio", 0, 0, OPT_NULL_AUDIO}, 417 419 { "local-port", 1, 0, OPT_LOCAL_PORT}, 418 420 { "ip-addr", 1, 0, OPT_IP_ADDR}, … … 582 584 cfg->media_cfg.snd_clock_rate = lval; 583 585 break; 584 586 587 case OPT_STEREO: 588 cfg->media_cfg.channel_count = 2; 589 break; 590 585 591 case OPT_LOCAL_PORT: /* local-port */ 586 592 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg)); 587 593 if (lval < 0 || lval > 65535) { … … 1412 1418 pj_strcat2(&cfg, line); 1413 1419 } 1414 1420 1421 /* Stereo mode. */ 1422 if (config->media_cfg.channel_count == 2) { 1423 pj_ansi_sprintf(line, "--stereo\n"); 1424 pj_strcat2(&cfg, line); 1425 } 1426 1415 1427 /* quality */ 1416 1428 if (config->media_cfg.quality != PJSUA_DEFAULT_CODEC_QUALITY) { 1417 1429 pj_ansi_sprintf(line, "--quality %d\n", -
pjmedia/src/pjmedia/conference.c
24 24 #include <pjmedia/resample.h> 25 25 #include <pjmedia/silencedet.h> 26 26 #include <pjmedia/sound_port.h> 27 #include <pjmedia/stereo.h> 27 28 #include <pjmedia/stream.h> 28 29 #include <pj/array.h> 29 30 #include <pj/assert.h> … … 117 118 /* Shortcut for port info. */ 118 119 unsigned clock_rate; /**< Port's clock rate. */ 119 120 unsigned samples_per_frame; /**< Port's samples per frame. */ 121 unsigned channel_count; /**< Port's channel count. */ 120 122 121 123 /* Calculated signal levels: */ 122 124 unsigned tx_level; /**< Last tx level to this port. */ … … 285 287 conf_port->port = port; 286 288 conf_port->clock_rate = port->info.clock_rate; 287 289 conf_port->samples_per_frame = port->info.samples_per_frame; 290 conf_port->channel_count = port->info.channel_count; 288 291 } else { 289 292 conf_port->port = NULL; 290 293 conf_port->clock_rate = conf->clock_rate; 291 294 conf_port->samples_per_frame = conf->samples_per_frame; 295 conf_port->channel_count = conf->channel_count; 292 296 } 293 297 294 298 /* Create and init vad. */ … … 349 353 * port's clock rate is different then the conference bridge settings. 350 354 */ 351 355 if (conf_port->clock_rate != conf->clock_rate || 356 conf_port->channel_count != conf->channel_count || 352 357 conf_port->samples_per_frame != conf->samples_per_frame) 353 358 { 359 unsigned port_ptime, conf_ptime, buff_ptime; 360 361 port_ptime = conf_port->samples_per_frame / conf_port->channel_count * 362 1000 / conf_port->clock_rate; 363 conf_ptime = conf->samples_per_frame / conf->channel_count * 364 1000 / conf->clock_rate; 365 366 /* Calculate ptime to be applied to the port buffer */ 367 if (port_ptime > conf_ptime) { 368 buff_ptime = conf_ptime * (port_ptime / conf_ptime); 369 if (port_ptime % conf_ptime) 370 buff_ptime += conf_ptime; 371 } else { 372 buff_ptime = port_ptime * (conf_ptime / port_ptime); 373 if (conf_ptime % port_ptime) 374 buff_ptime += port_ptime; 375 } 376 354 377 /* Create RX buffer. */ 355 conf_port->rx_buf_cap = (unsigned)(conf_port->samples_per_frame + 356 conf->samples_per_frame * 357 conf_port->clock_rate * 1.0 / 358 conf->clock_rate); 378 //conf_port->rx_buf_cap = (unsigned)(conf_port->samples_per_frame + 379 // conf->samples_per_frame * 380 // conf_port->clock_rate * 1.0 / 381 // conf->clock_rate); 382 conf_port->rx_buf_cap = conf_port->clock_rate * buff_ptime / 1000; 383 if (conf_port->channel_count > conf->channel_count) 384 conf_port->rx_buf_cap *= conf_port->channel_count; 385 else 386 conf_port->rx_buf_cap *= conf->channel_count; 387 359 388 conf_port->rx_buf_count = 0; 360 389 conf_port->rx_buf = (pj_int16_t*) 361 390 pj_pool_alloc(pool, conf_port->rx_buf_cap * … … 698 727 if (!port_name) 699 728 port_name = &strm_port->info.name; 700 729 701 /* For this version of PJMEDIA, port MUST have the same number of 702 * PCM channels. 730 /* For this version of PJMEDIA, channel(s) number MUST be: 731 * - same between port & conference bridge. 732 * - monochannel on port or conference bridge. 703 733 */ 704 if (strm_port->info.channel_count != conf->channel_count) { 734 if (strm_port->info.channel_count != conf->channel_count && 735 (strm_port->info.channel_count != 1 && conf->channel_count != 1)) 736 { 705 737 pj_assert(!"Number of channels mismatch"); 706 738 return PJMEDIA_ENCCHANNEL; 707 739 } … … 766 798 767 799 PJ_ASSERT_RETURN(conf && pool, PJ_EINVAL); 768 800 769 /* For this version of PJMEDIA, port MUST have the same number of 770 * PCM channels. 801 /* For this version of PJMEDIA, channel(s) number MUST be: 802 * - same between port & conference bridge. 803 * - monochannel on port or conference bridge. 771 804 */ 772 if (channel_count != conf->channel_count) { 805 if (channel_count != conf->channel_count && 806 (channel_count != 1 && conf->channel_count != 1)) 807 { 773 808 pj_assert(!"Number of channels mismatch"); 774 809 return PJMEDIA_ENCCHANNEL; 775 810 } … … 1274 1309 (int)cport->name.slen, cport->name.ptr, 1275 1310 count)); 1276 1311 1277 /* If port's samples per frame and sampling rate matches conference 1312 /* 1313 * If port's samples per frame and sampling rate matches conference 1278 1314 * bridge's settings, get the frame directly from the port. 1279 1315 */ 1280 1316 if (cport->rx_buf_cap == 0) { … … 1295 1331 return status; 1296 1332 1297 1333 } else { 1334 unsigned samples_req; 1298 1335 1299 1336 /* Initialize frame type */ 1300 1337 if (cport->rx_buf_count == 0) { … … 1306 1343 1307 1344 /* 1308 1345 * If we don't have enough samples in rx_buf, read from the port 1309 * first. Remember that rx_buf may be in different clock rate! 1346 * first. Remember that rx_buf may be in different clock rate and 1347 * channel count! 1310 1348 */ 1311 while (cport->rx_buf_count < count * 1.0 *1312 cport->clock_rate / conf->clock_rate) {1313 1349 1350 samples_req = (unsigned) (count * 1.0 * 1351 cport->clock_rate / conf->clock_rate); 1352 1353 while (cport->rx_buf_count < samples_req) { 1354 1314 1355 pjmedia_frame f; 1315 1356 pj_status_t status; 1316 1357 … … 1336 1377 *type = PJMEDIA_FRAME_TYPE_AUDIO; 1337 1378 } 1338 1379 1339 cport->rx_buf_count += cport->samples_per_frame; 1380 /* Adjust channels */ 1381 if (cport->channel_count != conf->channel_count) { 1382 if (cport->channel_count == 1) { 1383 pjmedia_stereo_convert_from_mono(f.buf, f.buf, 1384 conf->channel_count, 1385 cport->samples_per_frame, 1386 0); 1387 cport->rx_buf_count += (cport->samples_per_frame * 1388 conf->channel_count); 1389 } else { /* conf->channel_count == 1 */ 1390 pjmedia_stereo_convert_to_mono(f.buf, f.buf, 1391 cport->channel_count, 1392 cport->samples_per_frame/ 1393 cport->channel_count, 1394 0); 1395 cport->rx_buf_count += (cport->samples_per_frame / 1396 cport->channel_count); 1397 } 1398 } else { 1399 cport->rx_buf_count += cport->samples_per_frame; 1400 } 1340 1401 1341 1402 TRACE_((THIS_FILE, " rx buffer size is now %d", 1342 1403 cport->rx_buf_count)); … … 1361 1422 conf->clock_rate); 1362 1423 cport->rx_buf_count -= src_count; 1363 1424 if (cport->rx_buf_count) { 1364 pjmedia_ copy_samples(cport->rx_buf, cport->rx_buf+src_count,1425 pjmedia_move_samples(cport->rx_buf, cport->rx_buf+src_count, 1365 1426 cport->rx_buf_count); 1366 1427 } 1367 1428 … … 1373 1434 pjmedia_copy_samples(frame, cport->rx_buf, count); 1374 1435 cport->rx_buf_count -= count; 1375 1436 if (cport->rx_buf_count) { 1376 pjmedia_ copy_samples(cport->rx_buf, cport->rx_buf+count,1437 pjmedia_move_samples(cport->rx_buf, cport->rx_buf+count, 1377 1438 cport->rx_buf_count); 1378 1439 } 1379 1440 } … … 1395 1456 pj_status_t status; 1396 1457 pj_int32_t adj_level; 1397 1458 pj_int32_t tx_level; 1459 unsigned dst_count; 1398 1460 1399 1461 *frm_type = PJMEDIA_FRAME_TYPE_AUDIO; 1400 1462 … … 1412 1474 1413 1475 /* Add sample counts to heart-beat samples */ 1414 1476 cport->tx_heart_beat += conf->samples_per_frame * cport->clock_rate / 1415 conf->clock_rate; 1477 conf->clock_rate * 1478 cport->channel_count / conf->channel_count; 1416 1479 1417 1480 /* Set frame timestamp */ 1418 1481 frame.timestamp.u64 = timestamp->u64 * cport->clock_rate / … … 1508 1571 * the conference bridge, transmit the frame as is. 1509 1572 */ 1510 1573 if (cport->clock_rate == conf->clock_rate && 1511 cport->samples_per_frame == conf->samples_per_frame) 1574 cport->samples_per_frame == conf->samples_per_frame && 1575 cport->channel_count == conf->channel_count) 1512 1576 { 1513 1577 if (cport->port != NULL) { 1514 1578 pjmedia_frame frame; … … 1532 1596 1533 1597 /* If it has different clock_rate, must resample. */ 1534 1598 if (cport->clock_rate != conf->clock_rate) { 1535 1536 unsigned dst_count;1537 1538 1599 pjmedia_resample_run( cport->tx_resample, buf, 1539 1600 cport->tx_buf + cport->tx_buf_count ); 1540 1541 1601 dst_count = (unsigned)(conf->samples_per_frame * 1.0 * 1542 1602 cport->clock_rate / conf->clock_rate); 1543 cport->tx_buf_count += dst_count;1544 1545 1603 } else { 1546 1604 /* Same clock rate. 1547 1605 * Just copy the samples to tx_buffer. 1548 1606 */ 1549 1607 pjmedia_copy_samples( cport->tx_buf + cport->tx_buf_count, 1550 1608 buf, conf->samples_per_frame ); 1551 cport->tx_buf_count += conf->samples_per_frame;1609 dst_count = conf->samples_per_frame; 1552 1610 } 1553 1611 1612 /* Adjust channels */ 1613 if (cport->channel_count != conf->channel_count) { 1614 pj_int16_t *tx_buf = cport->tx_buf + cport->tx_buf_count; 1615 if (conf->channel_count == 1) { 1616 pjmedia_stereo_convert_from_mono(tx_buf, tx_buf, 1617 cport->channel_count, 1618 dst_count, 0); 1619 dst_count *= cport->channel_count; 1620 } else { /* cport->channel_count == 1 */ 1621 pjmedia_stereo_convert_to_mono(tx_buf, tx_buf, 1622 conf->channel_count, 1623 dst_count/ 1624 conf->channel_count, 1625 0); 1626 dst_count /= conf->channel_count; 1627 } 1628 } 1629 1630 cport->tx_buf_count += dst_count; 1631 1632 pj_assert(cport->tx_buf_count <= cport->tx_buf_cap); 1633 1554 1634 /* Transmit while we have enough frame in the tx_buf. */ 1555 1635 status = PJ_SUCCESS; 1556 1636 ts = 0; … … 1589 1669 1590 1670 cport->tx_buf_count -= cport->samples_per_frame; 1591 1671 if (cport->tx_buf_count) { 1592 pjmedia_ copy_samples(cport->tx_buf,1672 pjmedia_move_samples(cport->tx_buf, 1593 1673 cport->tx_buf + cport->samples_per_frame, 1594 1674 cport->tx_buf_count); 1595 1675 } -
pjmedia/include/pjmedia/stereo.h
1 /* $Id$ */ 2 /* 3 * Copyright (C) 2003-2007 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 #ifndef __PJMEDIA_STEREO_H__ 20 #define __PJMEDIA_STEREO_H__ 21 22 /** 23 * @file stereo.h 24 * @brief Monochannel and multichannel converter. 25 */ 26 27 #include <pjmedia/errno.h> 28 #include <pjmedia/port.h> 29 #include <pjmedia/types.h> 30 #include <pj/assert.h> 31 32 33 /** 34 * @defgroup PJMEDIA_STEREO Monochannel and multichannel audio frame converter 35 * @ingroup PJMEDIA_MISC 36 * @brief Interface for converting monochannel audio frame to multichannel 37 * audio frame and vice versa. 38 * @{ 39 * 40 */ 41 42 PJ_BEGIN_DECL 43 44 45 /** 46 * Options for channel converter. 47 */ 48 enum pjmedia_stereo_options 49 { 50 /** 51 * Multichannel to monochannel conversion just takes audio samples from 52 * the first channel. 53 */ 54 PJMEDIA_STEREO_SIMPLE = 0, 55 56 /** 57 * Multichannel to monochannel conversion mixes samples from all channels 58 * into the monochannel. 59 */ 60 PJMEDIA_STEREO_MIX = 1 61 }; 62 63 64 /** 65 * Multichannel to monochannel conversion, it has two operation mode specified 66 * by param options, @see pjmedia_stereo_options. This function can work safely 67 * using the same buffer (in place conversion). 68 * 69 * @param mono Monochannel audio frame. 70 * @param multi Multichannel audio frame. 71 * @param channel_count Number of channel. 72 * @param nsamples Number of samples of monochannel. 73 * @param options Options for conversion, @see pjmedia_stereo_options. 74 * 75 * @return PJ_SUCCESS on success; 76 */ 77 PJ_INLINE(pj_status_t) pjmedia_stereo_convert_to_mono(pj_int16_t mono[], 78 const pj_int16_t multi[], 79 unsigned channel_count, 80 unsigned nsamples, 81 unsigned options) 82 { 83 unsigned i; 84 85 PJ_ASSERT_RETURN(mono && multi && channel_count && nsamples, PJ_EINVAL); 86 87 if ((options & PJMEDIA_STEREO_MIX) == 0) { 88 for (i = 0; i < nsamples; ++i) 89 mono[i] = multi[i * channel_count]; 90 } else { 91 unsigned j; 92 int tmp; 93 for (i = 0; i < nsamples; ++i) { 94 tmp = 0; 95 for(j = 0; j < channel_count; ++j) 96 tmp += multi[i * channel_count + j]; 97 98 if (tmp > 32767) tmp = 32767; 99 else if (tmp < -32768) tmp = -32768; 100 mono[i] = (pj_int16_t) tmp; 101 } 102 } 103 104 return PJ_SUCCESS; 105 } 106 107 108 /** 109 * Monochannel to multichannel conversion, it will just duplicate the samples 110 * from monochannel frame to all channels in the multichannel frame. 111 * This function can work safely using the same buffer (in place conversion) 112 * as long as the buffer is big enough for the multichannel samples. 113 * 114 * @param multi Multichannel audio frame. 115 * @param mono Monochannel audio frame. 116 * @param channel_count Number of channel. 117 * @param nsamples Number of samples of monochannel. 118 * @param options Options for conversion, currently must be zero. 119 * 120 * @return PJ_SUCCESS on success; 121 */ 122 PJ_INLINE(pj_status_t) pjmedia_stereo_convert_from_mono(pj_int16_t multi[], 123 const pj_int16_t mono[], 124 unsigned channel_count, 125 unsigned nsamples, 126 unsigned options) 127 { 128 int i; 129 130 PJ_ASSERT_RETURN(mono && multi && channel_count && nsamples, PJ_EINVAL); 131 PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); 132 133 PJ_UNUSED_ARG(options); 134 135 while (nsamples--) { 136 for(i = channel_count-1; i >= 0; --i) 137 multi[nsamples * channel_count + i] = mono[nsamples]; 138 } 139 140 return PJ_SUCCESS; 141 } 142 143 144 /** 145 * Options for channel converter port. The @pjmedia_stereo_options is also 146 * valid for this port options. 147 */ 148 enum pjmedia_stereo_port_options 149 { 150 /** 151 * Specifies whether this port should not destroy downstream port when 152 * this port is destroyed. 153 */ 154 PJMEDIA_STEREO_DONT_DESTROY_DN = 4 155 }; 156 157 158 /** 159 * Create a mono-multi channel converter port. This creates a converter session, 160 * which will adjust the samples of audio frame to a different channel count 161 * when the port's get_frame() and put_frame() is called. 162 * 163 * When the port's get_frame() is called, this port will get a frame from 164 * the downstream port and convert the frame to the target channel count before 165 * returning it to the caller. 166 * 167 * When the port's put_frame() is called, this port will convert the frame 168 * to the downstream port's channel count before giving the frame to the 169 * downstream port. 170 * 171 * @param pool Pool to allocate the structure and buffers. 172 * @param dn_port The downstream port, which channel count is to 173 * be converted to the target channel count. 174 * @param channel_count This port channel count. 175 * @param options Flags from #pjmedia_stereo_port_options. 176 * When this flag is zero, the default behavior 177 * is to use simple N-to-1 channel converter and 178 * to destroy downstream port when this port is 179 * destroyed. 180 * @param p_port Pointer to receive the stereo port instance. 181 * 182 * @return PJ_SUCCESS on success. 183 */ 184 PJ_DECL(pj_status_t) pjmedia_stereo_port_create( pj_pool_t *pool, 185 pjmedia_port *dn_port, 186 unsigned channel_count, 187 unsigned options, 188 pjmedia_port **p_port ); 189 190 PJ_END_DECL 191 192 /** 193 * @} 194 */ 195 196 197 #endif /* __PJMEDIA_STEREO_H__ */ 198 -
pjmedia/src/pjmedia/stereo_port.c
1 /* $Id$ */ 2 /* 3 * Copyright (C) 2003-2007 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/stereo.h> 21 #include <pj/assert.h> 22 #include <pj/pool.h> 23 #include <pj/string.h> 24 25 26 #define SIGNATURE PJMEDIA_PORT_SIGNATURE('S','T','R','O') 27 28 29 struct stereo_port 30 { 31 pjmedia_port base; 32 pjmedia_port *dn_port; 33 unsigned options; 34 pj_int16_t *put_buf; 35 pj_int16_t *get_buf; 36 }; 37 38 39 40 static pj_status_t stereo_put_frame(pjmedia_port *this_port, 41 const pjmedia_frame *frame); 42 static pj_status_t stereo_get_frame(pjmedia_port *this_port, 43 pjmedia_frame *frame); 44 static pj_status_t stereo_destroy(pjmedia_port *this_port); 45 46 47 48 PJ_DEF(pj_status_t) pjmedia_stereo_port_create( pj_pool_t *pool, 49 pjmedia_port *dn_port, 50 unsigned channel_count, 51 unsigned options, 52 pjmedia_port **p_port ) 53 { 54 const pj_str_t name = pj_str("stereo"); 55 struct stereo_port *sport; 56 unsigned samples_per_frame; 57 58 /* Validate arguments. */ 59 PJ_ASSERT_RETURN(pool && dn_port && channel_count && p_port, PJ_EINVAL); 60 61 /* Only supports 16bit samples per frame */ 62 PJ_ASSERT_RETURN(dn_port->info.bits_per_sample == 16, PJMEDIA_ENCBITS); 63 64 /* Validate channel counts */ 65 PJ_ASSERT_RETURN(((dn_port->info.channel_count>1 && channel_count==1) || 66 (dn_port->info.channel_count==1 && channel_count>1)), 67 PJ_EINVAL); 68 69 /* Create and initialize port. */ 70 sport = PJ_POOL_ZALLOC_T(pool, struct stereo_port); 71 PJ_ASSERT_RETURN(sport != NULL, PJ_ENOMEM); 72 73 samples_per_frame = dn_port->info.samples_per_frame * channel_count / 74 dn_port->info.channel_count; 75 76 pjmedia_port_info_init(&sport->base.info, &name, SIGNATURE, 77 dn_port->info.clock_rate, 78 channel_count, 79 dn_port->info.bits_per_sample, 80 samples_per_frame); 81 82 sport->dn_port = dn_port; 83 sport->options = options; 84 85 /* We always need buffer for put_frame */ 86 sport->put_buf = (pj_int16_t*) 87 pj_pool_alloc(pool, dn_port->info.bytes_per_frame); 88 89 /* See if we need buffer for get_frame */ 90 if (dn_port->info.channel_count > channel_count) { 91 sport->get_buf = (pj_int16_t*) 92 pj_pool_alloc(pool, dn_port->info.bytes_per_frame); 93 } 94 95 /* Media port interface */ 96 sport->base.get_frame = &stereo_get_frame; 97 sport->base.put_frame = &stereo_put_frame; 98 sport->base.on_destroy = &stereo_destroy; 99 100 101 /* Done */ 102 *p_port = &sport->base; 103 104 return PJ_SUCCESS; 105 } 106 107 static pj_status_t stereo_put_frame(pjmedia_port *this_port, 108 const pjmedia_frame *frame) 109 { 110 struct stereo_port *sport = (struct stereo_port*) this_port; 111 pjmedia_frame tmp_frame; 112 113 /* Return if we don't have downstream port. */ 114 if (sport->dn_port == NULL) { 115 return PJ_SUCCESS; 116 } 117 118 if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) { 119 tmp_frame.buf = sport->put_buf; 120 if (sport->dn_port->info.channel_count == 1) { 121 pjmedia_stereo_convert_to_mono(tmp_frame.buf, frame->buf, 122 sport->base.info.channel_count, 123 sport->dn_port->info.samples_per_frame, 124 sport->options); 125 } else { 126 pjmedia_stereo_convert_from_mono(tmp_frame.buf, frame->buf, 127 sport->dn_port->info.channel_count, 128 sport->base.info.samples_per_frame, 129 sport->options); 130 } 131 tmp_frame.size = sport->dn_port->info.bytes_per_frame; 132 } else { 133 tmp_frame.buf = frame->buf; 134 tmp_frame.size = frame->size; 135 } 136 137 tmp_frame.type = frame->type; 138 tmp_frame.timestamp.u64 = frame->timestamp.u64; 139 140 return pjmedia_port_put_frame( sport->dn_port, &tmp_frame ); 141 } 142 143 144 145 static pj_status_t stereo_get_frame(pjmedia_port *this_port, 146 pjmedia_frame *frame) 147 { 148 struct stereo_port *sport = (struct stereo_port*) this_port; 149 pjmedia_frame tmp_frame; 150 pj_status_t status; 151 152 /* Return silence if we don't have downstream port */ 153 if (sport->dn_port == NULL) { 154 pj_bzero(frame->buf, frame->size); 155 return PJ_SUCCESS; 156 } 157 158 tmp_frame.buf = sport->get_buf? sport->get_buf : frame->buf; 159 tmp_frame.size = sport->dn_port->info.bytes_per_frame; 160 tmp_frame.timestamp.u64 = frame->timestamp.u64; 161 tmp_frame.type = PJMEDIA_FRAME_TYPE_AUDIO; 162 163 status = pjmedia_port_get_frame( sport->dn_port, &tmp_frame); 164 if (status != PJ_SUCCESS) 165 return status; 166 167 if (tmp_frame.type != PJMEDIA_FRAME_TYPE_AUDIO) { 168 frame->type = tmp_frame.type; 169 frame->timestamp = tmp_frame.timestamp; 170 frame->size = tmp_frame.size; 171 if (tmp_frame.size && tmp_frame.buf == sport->get_buf) 172 pj_memcpy(frame->buf, tmp_frame.buf, tmp_frame.size); 173 return PJ_SUCCESS; 174 } 175 176 if (sport->base.info.channel_count == 1) { 177 pjmedia_stereo_convert_to_mono(frame->buf, tmp_frame.buf, 178 sport->dn_port->info.channel_count, 179 sport->base.info.samples_per_frame, 180 sport->options); 181 } else { 182 pjmedia_stereo_convert_from_mono(frame->buf, tmp_frame.buf, 183 sport->base.info.channel_count, 184 sport->dn_port->info.samples_per_frame, 185 sport->options); 186 } 187 188 frame->size = sport->base.info.bytes_per_frame; 189 frame->type = PJMEDIA_FRAME_TYPE_AUDIO; 190 191 return PJ_SUCCESS; 192 } 193 194 195 static pj_status_t stereo_destroy(pjmedia_port *this_port) 196 { 197 struct stereo_port *sport = (struct stereo_port*) this_port; 198 199 if ((sport->options & PJMEDIA_STEREO_DONT_DESTROY_DN)==0) { 200 pjmedia_port_destroy(sport->dn_port); 201 sport->dn_port = NULL; 202 } 203 204 return PJ_SUCCESS; 205 } 206 -
pjmedia/build/pjmedia.dsp
237 237 # End Source File 238 238 # Begin Source File 239 239 240 SOURCE=..\src\pjmedia\stereo_port.c 241 # End Source File 242 # Begin Source File 243 240 244 SOURCE=..\src\pjmedia\stream.c 241 245 # End Source File 242 246 # Begin Source File … … 401 405 # End Source File 402 406 # Begin Source File 403 407 408 SOURCE=..\include\pjmedia\stereo.h 409 # End Source File 410 # Begin Source File 411 404 412 SOURCE=..\include\pjmedia\stream.h 405 413 # End Source File 406 414 # Begin Source File -
pjmedia/build/pjmedia.vcproj
918 918 </FileConfiguration> 919 919 </File> 920 920 <File 921 RelativePath="..\src\pjmedia\stereo_port.c" 922 > 923 </File> 924 <File 921 925 RelativePath="..\src\pjmedia\stream.c" 922 926 > 923 927 <FileConfiguration … … 1209 1213 > 1210 1214 </File> 1211 1215 <File 1216 RelativePath="..\include\pjmedia\stereo.h" 1217 > 1218 </File> 1219 <File 1212 1220 RelativePath="..\include\pjmedia\stream.h" 1213 1221 > 1214 1222 </File> -
pjsip-apps/src/samples/stereotest.c
1 /* $Id$ */ 2 /* 3 * Copyright (C) 2003-2008 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 /** 21 * \page page_pjmedia_samples_stereo_c Samples: Using Stereo Port 22 * 23 * This example demonstrates how to use @ref PJMEDIA_STEREO_PORT to 24 * change the channel count of the media streams. 25 * 26 * This file is pjsip-apps/src/samples/stereotest.c 27 * 28 * \includelineno stereotest.c 29 */ 30 31 #include <pjmedia.h> 32 #include <pjlib-util.h> 33 #include <pjlib.h> 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 38 #include "util.h" 39 40 #define REC_CLOCK_RATE 16000 41 #define PTIME 10 42 43 #define MODE_PLAY 1 44 #define MODE_RECORD 2 45 46 47 /* For logging purpose. */ 48 #define THIS_FILE "stereotest.c" 49 50 51 static const char *desc = 52 " FILE \n" 53 " \n" 54 " stereotest.c \n" 55 " \n" 56 " PURPOSE \n" 57 " \n" 58 " Demonstrate how use stereo port to play a WAV file to sound \n" 59 " device or record to a WAV file from sound device with different \n" 60 " channel count. \n" 61 " \n" 62 " USAGE \n" 63 " \n" 64 " stereotest [options] WAV \n" 65 " \n" 66 " Options: \n" 67 " -m, --mode=N Operation mode: 1 = playing, 2 = recording.\n" 68 " -C, --rec-ch-cnt=N Number of channel for recording file. \n" 69 " -c, --snd-ch-cnt=N Number of channel for opening sound device.\n" 70 " \n"; 71 72 int main(int argc, char *argv[]) 73 { 74 pj_caching_pool cp; 75 pjmedia_endpt *med_endpt; 76 pj_pool_t *pool; 77 78 pjmedia_port *file_port = NULL; 79 pjmedia_port *stereo_port = NULL; 80 pjmedia_snd_port *snd_port = NULL; 81 82 int dev_id = -1; 83 char tmp[10]; 84 pj_status_t status; 85 86 char *wav_file = NULL; 87 unsigned mode = 0; 88 unsigned rec_ch_cnt = 1; 89 unsigned snd_ch_cnt = 2; 90 91 enum { 92 OPT_MODE = 'm', 93 OPT_REC_CHANNEL = 'C', 94 OPT_SND_CHANNEL = 'c', 95 }; 96 97 struct pj_getopt_option long_options[] = { 98 { "mode", 1, 0, OPT_MODE }, 99 { "rec-ch-cnt", 1, 0, OPT_REC_CHANNEL }, 100 { "snd-ch-cnt", 1, 0, OPT_SND_CHANNEL }, 101 { NULL, 0, 0, 0 }, 102 }; 103 104 int c; 105 int option_index; 106 107 /* Must init PJLIB first: */ 108 status = pj_init(); 109 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); 110 111 /* Parse arguments */ 112 pj_optind = 0; 113 while((c=pj_getopt_long(argc,argv, "m:C:c:", long_options, &option_index))!=-1) { 114 115 switch (c) { 116 case OPT_MODE: 117 if (mode) { 118 app_perror(THIS_FILE, "Cannot record and play at once!", 119 PJ_EINVAL); 120 return 1; 121 } 122 mode = atoi(pj_optarg); 123 break; 124 125 case OPT_REC_CHANNEL: 126 rec_ch_cnt = atoi(pj_optarg); 127 break; 128 129 case OPT_SND_CHANNEL: 130 snd_ch_cnt = atoi(pj_optarg); 131 break; 132 133 default: 134 printf("Invalid options %s\n", argv[pj_optind]); 135 puts(desc); 136 return 1; 137 } 138 139 } 140 141 wav_file = argv[pj_optind]; 142 143 /* Verify arguments. */ 144 if (!wav_file) { 145 app_perror(THIS_FILE, "WAV file not specified!", PJ_EINVAL); 146 puts(desc); 147 return 1; 148 } 149 if (!snd_ch_cnt || !rec_ch_cnt || rec_ch_cnt > 6) { 150 app_perror(THIS_FILE, "Invalid or too many channel count!", PJ_EINVAL); 151 puts(desc); 152 return 1; 153 } 154 if (mode != MODE_RECORD && mode != MODE_PLAY) { 155 app_perror(THIS_FILE, "Invalid operation mode!", PJ_EINVAL); 156 puts(desc); 157 return 1; 158 } 159 160 /* Must create a pool factory before we can allocate any memory. */ 161 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); 162 163 /* 164 * Initialize media endpoint. 165 * This will implicitly initialize PJMEDIA too. 166 */ 167 status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); 168 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); 169 170 /* Create memory pool for our file player */ 171 pool = pj_pool_create( &cp.factory, /* pool factory */ 172 "app", /* pool name. */ 173 4000, /* init size */ 174 4000, /* increment size */ 175 NULL /* callback on error */ 176 ); 177 178 if (mode == MODE_PLAY) { 179 /* Create WAVE file player port. */ 180 status = pjmedia_wav_player_port_create( pool, wav_file, PTIME, 0, 181 0, &file_port); 182 if (status != PJ_SUCCESS) { 183 app_perror(THIS_FILE, "Unable to open file", status); 184 return 1; 185 } 186 187 /* Create sound player port. */ 188 status = pjmedia_snd_port_create_player( 189 pool, /* pool */ 190 dev_id, /* device id. */ 191 file_port->info.clock_rate, /* clock rate. */ 192 snd_ch_cnt, /* # of channels. */ 193 snd_ch_cnt * PTIME * /* samples per frame. */ 194 file_port->info.clock_rate / 1000, 195 file_port->info.bits_per_sample, /* bits per sample. */ 196 0, /* options */ 197 &snd_port /* returned port */ 198 ); 199 if (status != PJ_SUCCESS) { 200 app_perror(THIS_FILE, "Unable to open sound device", status); 201 return 1; 202 } 203 204 if (snd_ch_cnt != file_port->info.channel_count) { 205 status = pjmedia_stereo_port_create( pool, 206 file_port, 207 snd_ch_cnt, 208 0, 209 &stereo_port); 210 if (status != PJ_SUCCESS) { 211 app_perror(THIS_FILE, "Unable to create stereo port", status); 212 return 1; 213 } 214 215 status = pjmedia_snd_port_connect(snd_port, stereo_port); 216 } else { 217 status = pjmedia_snd_port_connect(snd_port, file_port); 218 } 219 220 if (status != PJ_SUCCESS) { 221 app_perror(THIS_FILE, "Unable to connect sound port", status); 222 return 1; 223 } 224 225 } else { 226 /* Create WAVE file writer port. */ 227 status = pjmedia_wav_writer_port_create(pool, wav_file, 228 REC_CLOCK_RATE, 229 rec_ch_cnt, 230 rec_ch_cnt * PTIME * 231 REC_CLOCK_RATE / 1000, 232 NBITS, 233 0, 0, 234 &file_port); 235 if (status != PJ_SUCCESS) { 236 app_perror(THIS_FILE, "Unable to open file", status); 237 return 1; 238 } 239 240 /* Create sound player port. */ 241 status = pjmedia_snd_port_create_rec( 242 pool, /* pool */ 243 dev_id, /* device id. */ 244 REC_CLOCK_RATE, /* clock rate. */ 245 snd_ch_cnt, /* # of channels. */ 246 snd_ch_cnt * PTIME * 247 REC_CLOCK_RATE / 1000, /* samples per frame. */ 248 NBITS, /* bits per sample. */ 249 0, /* options */ 250 &snd_port /* returned port */ 251 ); 252 if (status != PJ_SUCCESS) { 253 app_perror(THIS_FILE, "Unable to open sound device", status); 254 return 1; 255 } 256 257 if (rec_ch_cnt != snd_ch_cnt) { 258 status = pjmedia_stereo_port_create( pool, 259 file_port, 260 snd_ch_cnt, 261 0, 262 &stereo_port); 263 if (status != PJ_SUCCESS) { 264 app_perror(THIS_FILE, "Unable to create stereo port", status); 265 return 1; 266 } 267 268 status = pjmedia_snd_port_connect(snd_port, stereo_port); 269 } else { 270 status = pjmedia_snd_port_connect(snd_port, file_port); 271 } 272 273 if (status != PJ_SUCCESS) { 274 app_perror(THIS_FILE, "Unable to connect sound port", status); 275 return 1; 276 } 277 } 278 279 /* Dump memory usage */ 280 dump_pool_usage(THIS_FILE, &cp); 281 282 /* 283 * File should be playing and looping now, using sound device's thread. 284 */ 285 286 287 /* Sleep to allow log messages to flush */ 288 pj_thread_sleep(100); 289 290 printf("Mode = %s\n", (mode == MODE_PLAY? "playing" : "recording") ); 291 printf("File port channel count = %d\n", file_port->info.channel_count); 292 printf("Sound port channel count = %d\n", 293 pjmedia_snd_port_get_port(snd_port)->info.channel_count); 294 puts(""); 295 puts("Press <ENTER> to stop and quit"); 296 297 fgets(tmp, sizeof(tmp), stdin); 298 299 300 /* Start deinitialization: */ 301 302 303 /* Destroy sound device */ 304 status = pjmedia_snd_port_destroy( snd_port ); 305 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); 306 307 308 /* Destroy stereo port and file_port. 309 * Stereo port will destroy all downstream ports (e.g. the file port) 310 */ 311 status = pjmedia_port_destroy( stereo_port? stereo_port : file_port); 312 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); 313 314 315 /* Release application pool */ 316 pj_pool_release( pool ); 317 318 /* Destroy media endpoint. */ 319 pjmedia_endpt_destroy( med_endpt ); 320 321 /* Destroy pool factory */ 322 pj_caching_pool_destroy( &cp ); 323 324 /* Shutdown PJLIB */ 325 pj_shutdown(); 326 327 328 /* Done. */ 329 return 0; 330 331 } 332 333 334 -
pjmedia/include/pjmedia.h
51 51 #include <pjmedia/sound.h> 52 52 #include <pjmedia/sound_port.h> 53 53 #include <pjmedia/splitcomb.h> 54 #include <pjmedia/stereo.h> 54 55 #include <pjmedia/tonegen.h> 55 56 #include <pjmedia/transport.h> 56 57 #include <pjmedia/transport_ice.h>