Changeset 6094
- Timestamp:
- Oct 17, 2019 7:02:50 AM (5 years ago)
- Location:
- pjproject/trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjmedia/include/pjmedia/audiodev.h
r6001 r6094 38 38 * @defgroup s2_audio_device_reference Audio Device API Reference 39 39 * @ingroup audio_device_api 40 * @brief API Reference40 * @brief Documentation and API Reference 41 41 * @{ 42 * 43 * @section ec_sec Hardware/Built-in Echo Cancellation 44 * 45 * On some platforms, audio device comes with built-in echo cancellation 46 * feature. This is usually done based on specific hardware configuration, 47 * such as the use of multiple microphones and/or a known fixed distance 48 * between the capture and playback device, in order to precalculate the 49 * echo time distance. Because of this, when using the hardware EC, 50 * users may not get the freedom to select their own audio devices. 51 * This is applicable for Mac (users must use default audio devices) and 52 * iOS (users must use the same built-in audio device). 53 * 54 * In PJMEDIA, applications wishing to use sofware echo instead can pass 55 * PJMEDIA_ECHO_USE_SW_ECHO when calling pjmedia_snd_port_create2(). 42 56 */ 43 57 -
pjproject/trunk/pjmedia/src/pjmedia-audiodev/coreaudio_dev.m
r5463 r6094 101 101 102 102 AudioComponent io_comp; 103 pj_bool_t has_vpio; 103 104 struct stream_list streams; 104 105 }; … … 271 272 if (cf->io_comp == NULL) 272 273 return PJMEDIA_EAUD_INIT; // cannot find IO unit; 274 275 desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO; 276 if (AudioComponentFindNext(NULL, &desc) != NULL) 277 cf->has_vpio = PJ_TRUE; 273 278 274 279 status = ca_factory_refresh(f); … … 632 637 } 633 638 } 639 if (cf->has_vpio) { 640 cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_EC; 641 } 634 642 635 643 cf->dev_count++; 636 644 637 645 PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d) %dHz", 638 i,646 cdi->dev_id, 639 647 cdi->info.name, 640 648 cdi->info.input_count, … … 1001 1009 } 1002 1010 1011 /* Copy 16-bit signed int samples to a destination buffer, which can be 1012 * either 16-bit signed int, or float. 1013 */ 1014 static void copy_samples(void *dst, unsigned *dst_pos, unsigned dst_elmt_size, 1015 pj_int16_t *src, unsigned nsamples) 1016 { 1017 if (dst_elmt_size == sizeof(pj_int16_t)) { 1018 /* Destination is also 16-bit signed int. */ 1019 pjmedia_copy_samples((pj_int16_t*)dst + *dst_pos, src, nsamples); 1020 } else { 1021 /* Convert it first to float. */ 1022 unsigned i; 1023 float *fdst = (float *)dst; 1024 1025 pj_assert(dst_elmt_size == sizeof(Float32)); 1026 1027 for (i = 0; i< nsamples; i++) { 1028 /* Value needs to be between -1.0 to 1.0 */ 1029 fdst[*dst_pos + i] = (float)src[i] / 32768.0f; 1030 } 1031 } 1032 *dst_pos += nsamples; 1033 } 1034 1003 1035 static OSStatus output_renderer(void *inRefCon, 1004 1036 AudioUnitRenderActionFlags *ioActionFlags, … … 1011 1043 pj_status_t status = 0; 1012 1044 unsigned nsamples_req = inNumberFrames * stream->param.channel_count; 1013 pj_int16_t *output = ioData->mBuffers[0].mData; 1045 void *output = ioData->mBuffers[0].mData; 1046 unsigned elmt_size = ioData->mBuffers[0].mDataByteSize / inNumberFrames; 1047 unsigned output_pos = 0; 1014 1048 1015 1049 pj_assert(!stream->quit_flag); … … 1037 1071 /* samples buffered >= requested by sound device */ 1038 1072 if (stream->play_buf_count >= nsamples_req) { 1039 pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,1040 1073 copy_samples(output, &output_pos, elmt_size, 1074 stream->play_buf, nsamples_req); 1041 1075 stream->play_buf_count -= nsamples_req; 1042 1076 pjmedia_move_samples(stream->play_buf, … … 1049 1083 1050 1084 /* samples buffered < requested by sound device */ 1051 pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,1052 1085 copy_samples(output, &output_pos, elmt_size, 1086 stream->play_buf, stream->play_buf_count); 1053 1087 nsamples_req -= stream->play_buf_count; 1054 output = (pj_int16_t*)output + stream->play_buf_count;1055 1088 stream->play_buf_count = 0; 1056 1089 } … … 1067 1100 1068 1101 if (nsamples_req >= stream->param.samples_per_frame) { 1069 frame.buf = output; 1102 /* If the output buffer is 16-bit signed int, we can 1103 * directly use the supplied buffer. 1104 */ 1105 if (elmt_size == sizeof(pj_int16_t)) { 1106 frame.buf = (pj_int16_t *)output + output_pos; 1107 } else { 1108 frame.buf = stream->play_buf; 1109 } 1070 1110 status = (*stream->play_cb)(stream->user_data, &frame); 1071 1111 if (status != PJ_SUCCESS) … … 1076 1116 1077 1117 nsamples_req -= stream->param.samples_per_frame; 1078 output = (pj_int16_t*)output + stream->param.samples_per_frame; 1118 if (elmt_size == sizeof(pj_int16_t)) { 1119 output_pos += stream->param.samples_per_frame; 1120 } else { 1121 copy_samples(output, &output_pos, elmt_size, stream->play_buf, 1122 stream->param.samples_per_frame); 1123 } 1079 1124 } else { 1080 1125 frame.buf = stream->play_buf; … … 1086 1131 pj_bzero(frame.buf, frame.size); 1087 1132 1088 pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,1089 1133 copy_samples(output, &output_pos, elmt_size, 1134 stream->play_buf, nsamples_req); 1090 1135 stream->play_buf_count = stream->param.samples_per_frame - 1091 1136 nsamples_req; … … 1293 1338 &enable, 1294 1339 sizeof(enable)); 1295 if (ostatus != noErr ) {1340 if (ostatus != noErr && !strm->param.ec_enabled) { 1296 1341 PJ_LOG(4, (THIS_FILE, 1297 1342 "Warning: cannot enable IO of capture device %d", … … 1308 1353 &enable, 1309 1354 sizeof(enable)); 1310 if (ostatus != noErr ) {1355 if (ostatus != noErr && !strm->param.ec_enabled) { 1311 1356 PJ_LOG(4, (THIS_FILE, 1312 1357 "Warning: cannot disable IO of capture device %d", … … 1327 1372 &enable, 1328 1373 sizeof(enable)); 1329 if (ostatus != noErr) { 1374 if (ostatus != noErr && !strm->param.ec_enabled) 1375 { 1330 1376 PJ_LOG(4, (THIS_FILE, 1331 1377 "Warning: cannot enable IO of playback device %d", … … 1336 1382 1337 1383 #if COREAUDIO_MAC 1338 PJ_LOG(5, (THIS_FILE, "Opening device %d", dev_id)); 1339 ostatus = AudioUnitSetProperty(*io_unit, 1340 kAudioOutputUnitProperty_CurrentDevice, 1341 kAudioUnitScope_Global, 1342 0, 1343 &dev_id, 1344 sizeof(dev_id)); 1345 if (ostatus != noErr) { 1346 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1384 if (!strm->param.ec_enabled) { 1385 /* When using VPIO on Mac, we shouldn't set the current device. 1386 * Doing so will cause getting the buffer size later to fail 1387 * with kAudioUnitErr_InvalidProperty error (-10879). 1388 */ 1389 PJ_LOG(4, (THIS_FILE, "Setting current device %d", dev_id)); 1390 ostatus = AudioUnitSetProperty(*io_unit, 1391 kAudioOutputUnitProperty_CurrentDevice, 1392 kAudioUnitScope_Global, 1393 0, 1394 &dev_id, 1395 sizeof(dev_id)); 1396 if (ostatus != noErr) { 1397 PJ_LOG(3, (THIS_FILE, "Failed setting current device %d, " 1398 "error: %d", dev_id, ostatus)); 1399 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1400 } 1347 1401 } 1348 1402 #endif … … 1353 1407 UInt32 size; 1354 1408 1355 /* 1356 * Keep the sample rate from the device, otherwise we will confuse 1357 * AUHAL 1358 */ 1359 size = sizeof(AudioStreamBasicDescription); 1360 ostatus = AudioUnitGetProperty(*io_unit, 1361 kAudioUnitProperty_StreamFormat, 1362 kAudioUnitScope_Input, 1363 1, 1364 &deviceFormat, 1365 &size); 1366 if (ostatus != noErr) { 1367 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1368 } 1369 strm->streamFormat.mSampleRate = deviceFormat.mSampleRate; 1409 if (!strm->param.ec_enabled) { 1410 /* Keep the sample rate from the device, otherwise we will confuse 1411 * AUHAL. 1412 */ 1413 size = sizeof(AudioStreamBasicDescription); 1414 ostatus = AudioUnitGetProperty(*io_unit, 1415 kAudioUnitProperty_StreamFormat, 1416 kAudioUnitScope_Input, 1417 1, 1418 &deviceFormat, 1419 &size); 1420 if (ostatus != noErr) { 1421 PJ_LOG(3, (THIS_FILE, "Failed getting stream format of device" 1422 " %d, error: %d", dev_id, ostatus)); 1423 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1424 } 1425 strm->streamFormat.mSampleRate = deviceFormat.mSampleRate; 1426 } 1370 1427 #endif 1371 1428 … … 1381 1438 sizeof(strm->streamFormat)); 1382 1439 if (ostatus != noErr) { 1440 PJ_LOG(3, (THIS_FILE, "Failed setting stream format of device %d" 1441 ", error: %d", dev_id, ostatus)); 1383 1442 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1384 1443 } … … 1395 1454 if (ostatus == noErr) { 1396 1455 if (strm->streamFormat.mSampleRate != deviceFormat.mSampleRate) { 1456 PJ_LOG(4, (THIS_FILE, "Creating audio resample from %d to %d", 1457 (int)deviceFormat.mSampleRate, 1458 (int)strm->streamFormat.mSampleRate)); 1397 1459 pj_status_t rc = create_audio_resample(strm, &deviceFormat); 1398 if (PJ_SUCCESS != rc) 1460 if (PJ_SUCCESS != rc) { 1461 PJ_LOG(3, (THIS_FILE, "Failed creating resample %d", 1462 rc)); 1399 1463 return rc; 1464 } 1400 1465 } 1401 1466 } else { 1467 PJ_LOG(3, (THIS_FILE, "Failed getting stream format of device %d" 1468 " (2), error: %d", dev_id, ostatus)); 1402 1469 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1403 1470 } … … 1407 1474 if (dir & PJMEDIA_DIR_PLAYBACK) { 1408 1475 AURenderCallbackStruct output_cb; 1476 AudioStreamBasicDescription streamFormat = strm->streamFormat; 1409 1477 1410 1478 /* Set the stream format */ 1479 #if COREAUDIO_MAC 1480 if (strm->param.ec_enabled) { 1481 /* When using VPIO on Mac, we need to use float data. Using 1482 * signed integer will generate no errors, but strangely, 1483 * no sound will be played. 1484 */ 1485 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat | 1486 kLinearPCMFormatFlagIsPacked; 1487 streamFormat.mBitsPerChannel = sizeof(float) * 8; 1488 streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame * 1489 streamFormat.mBitsPerChannel / 8; 1490 streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * 1491 streamFormat.mFramesPerPacket; 1492 } 1493 #endif 1411 1494 ostatus = AudioUnitSetProperty(*io_unit, 1412 1495 kAudioUnitProperty_StreamFormat, 1413 1496 kAudioUnitScope_Input, 1414 1497 0, 1415 &str m->streamFormat,1416 sizeof(str m->streamFormat));1498 &streamFormat, 1499 sizeof(streamFormat)); 1417 1500 if (ostatus != noErr) { 1418 1501 PJ_LOG(4, (THIS_FILE, … … 1431 1514 sizeof(output_cb)); 1432 1515 if (ostatus != noErr) { 1516 PJ_LOG(3, (THIS_FILE, "Failed setting render callback %d", 1517 ostatus)); 1433 1518 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1434 1519 } … … 1437 1522 strm->play_buf = (pj_int16_t*)pj_pool_alloc(strm->pool, 1438 1523 strm->param.samples_per_frame * 1524 (strm->param.ec_enabled? 2: 1) * 1439 1525 strm->param.bits_per_sample >> 3); 1440 1526 if (!strm->play_buf) … … 1458 1544 kAudioOutputUnitProperty_SetInputCallback, 1459 1545 kAudioUnitScope_Global, 1460 0,1546 1, 1461 1547 &input_cb, 1462 1548 sizeof(input_cb)); 1463 1549 if (ostatus != noErr) { 1550 PJ_LOG(3, (THIS_FILE, "Failed setting input callback %d", 1551 ostatus)); 1464 1552 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1465 1553 } 1466 1554 1467 1555 #if COREAUDIO_MAC 1556 /* Set device's buffer frame size */ 1557 size = sizeof(UInt32); 1558 buf_size = (20 * strm->streamFormat.mSampleRate) / 1000; 1559 ostatus = AudioUnitSetProperty (*io_unit, 1560 kAudioDevicePropertyBufferFrameSize, 1561 kAudioUnitScope_Global, 1562 0, 1563 &buf_size, 1564 size); 1565 if (ostatus != noErr) { 1566 PJ_LOG(3, (THIS_FILE, "Failed setting buffer size %d", 1567 ostatus)); 1568 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1569 } 1570 1468 1571 /* Get device's buffer frame size */ 1469 size = sizeof(UInt32);1470 1572 ostatus = AudioUnitGetProperty(*io_unit, 1471 1573 kAudioDevicePropertyBufferFrameSize, … … 1474 1576 &buf_size, 1475 1577 &size); 1476 if (ostatus != noErr) 1477 { 1578 if (ostatus != noErr) { 1579 PJ_LOG(3, (THIS_FILE, "Failed getting buffer size %d", 1580 ostatus)); 1478 1581 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1479 1582 } … … 1489 1592 ab->mNumberChannels = strm->streamFormat.mChannelsPerFrame; 1490 1593 ab->mDataByteSize = buf_size * ab->mNumberChannels * 1491 strm-> param.bits_per_sample>> 3;1594 strm->streamFormat.mBitsPerChannel >> 3; 1492 1595 ab->mData = pj_pool_alloc(strm->pool, 1493 1596 ab->mDataByteSize); … … 1522 1625 ostatus = AudioUnitInitialize(*io_unit); 1523 1626 if (ostatus != noErr) { 1627 PJ_LOG(3, (THIS_FILE, "Failed initializing audio unit %d", ostatus)); 1524 1628 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1525 1629 } … … 1563 1667 strm->streamFormat.mChannelsPerFrame = param->channel_count; 1564 1668 strm->streamFormat.mBytesPerFrame = strm->streamFormat.mChannelsPerFrame 1565 * strm->param.bits_per_sample >> 3; 1669 * strm->streamFormat.mBitsPerChannel 1670 / 8; 1566 1671 strm->streamFormat.mFramesPerPacket = 1; 1567 1672 strm->streamFormat.mBytesPerPacket = strm->streamFormat.mBytesPerFrame * … … 1580 1685 } 1581 1686 if (param->flags & PJMEDIA_AUD_DEV_CAP_EC) { 1582 ca_stream_set_cap(&strm->base, 1583 PJMEDIA_AUD_DEV_CAP_EC, 1584 ¶m->ec_enabled); 1687 status = ca_stream_set_cap(&strm->base, 1688 PJMEDIA_AUD_DEV_CAP_EC, 1689 ¶m->ec_enabled); 1690 if (status != PJ_SUCCESS) 1691 strm->param.ec_enabled = PJ_FALSE; 1585 1692 } else { 1586 1693 pj_bool_t ec = PJ_FALSE; … … 1589 1696 } 1590 1697 1698 #if !TARGET_OS_IPHONE 1699 if (strm->param.ec_enabled && 1700 param->rec_id != PJMEDIA_AUD_DEFAULT_CAPTURE_DEV && 1701 param->play_id != PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV) 1702 { 1703 PJ_LOG(4, (THIS_FILE, "Warning: audio device id settings are " 1704 "ignored when using VPIO")); 1705 } 1706 #endif 1707 1591 1708 strm->io_units[0] = strm->io_units[1] = NULL; 1592 if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK && 1593 param->rec_id == param->play_id) 1709 if ((param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK && 1710 param->rec_id == param->play_id) || 1711 (param->flags & PJMEDIA_AUD_DEV_CAP_EC && param->ec_enabled)) 1594 1712 { 1595 /* If both input and output are on the same device , only create1596 * on e audio unit to interface with the device.1713 /* If both input and output are on the same device or if EC is enabled, 1714 * only create one audio unit to interface with the device(s). 1597 1715 */ 1598 1716 status = create_audio_unit(cf->io_comp, … … 1891 2009 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); 1892 2010 1893 #if COREAUDIO_MAC1894 if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&1895 (strm->param.dir & PJMEDIA_DIR_PLAYBACK))1896 {1897 OSStatus ostatus;1898 Float32 volume = *(unsigned*)pval;1899 1900 /* Output volume setting */1901 volume /= 100.0;1902 ostatus = AudioUnitSetProperty (strm->io_units[1] ? strm->io_units[1] :1903 strm->io_units[0],1904 kAudioDevicePropertyVolumeScalar,1905 kAudioUnitScope_Output,1906 0,1907 &volume,1908 sizeof(Float32));1909 if (ostatus != noErr) {1910 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);1911 }1912 strm->param.output_vol = *(unsigned*)pval;1913 return PJ_SUCCESS;1914 }1915 1916 #else1917 2011 if (cap==PJMEDIA_AUD_DEV_CAP_EC) { 1918 2012 AudioComponentDescription desc; … … 1921 2015 desc.componentType = kAudioUnitType_Output; 1922 2016 desc.componentSubType = (*(pj_bool_t*)pval)? 1923 kAudioUnitSubType_VoiceProcessingIO : 1924 kAudioUnitSubType_RemoteIO; 2017 kAudioUnitSubType_VoiceProcessingIO : 2018 #if COREAUDIO_MAC 2019 kAudioUnitSubType_HALOutput; 2020 #else 2021 kAudioUnitSubType_RemoteIO; 2022 #endif 1925 2023 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 1926 2024 desc.componentFlags = 0; … … 1935 2033 PJ_LOG(4, (THIS_FILE, "Using %s audio unit", 1936 2034 (desc.componentSubType == 1937 kAudioUnitSubType_ RemoteIO? "RemoteIO":1938 "VoiceProcessingIO" )));2035 kAudioUnitSubType_VoiceProcessingIO? 2036 "VoiceProcessingIO": "default"))); 1939 2037 1940 2038 return PJ_SUCCESS; 1941 } else if ((cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && 2039 } 2040 #if COREAUDIO_MAC 2041 else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING && 2042 (strm->param.dir & PJMEDIA_DIR_PLAYBACK)) 2043 { 2044 OSStatus ostatus; 2045 Float32 volume = *(unsigned*)pval; 2046 2047 /* Output volume setting */ 2048 volume /= 100.0; 2049 ostatus = AudioUnitSetProperty (strm->io_units[1] ? strm->io_units[1] : 2050 strm->io_units[0], 2051 kAudioDevicePropertyVolumeScalar, 2052 kAudioUnitScope_Output, 2053 0, 2054 &volume, 2055 sizeof(Float32)); 2056 if (ostatus != noErr) { 2057 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 2058 } 2059 strm->param.output_vol = *(unsigned*)pval; 2060 return PJ_SUCCESS; 2061 } 2062 2063 #else 2064 else if ((cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && 1942 2065 (strm->param.dir & PJMEDIA_DIR_CAPTURE)) || 1943 2066 (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && -
pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
r6035 r6094 6546 6546 6547 6547 /** 6548 * Echo canceller options (see #pjmedia_echo_create()) 6548 * Echo canceller options (see #pjmedia_echo_create()). 6549 * Specify PJMEDIA_ECHO_USE_SW_ECHO here if application wishes 6550 * to use software echo canceller instead of device EC. 6549 6551 * 6550 6552 * Default: 0. -
pjproject/trunk/pjsip/include/pjsua2/endpoint.hpp
r6081 r6094 808 808 809 809 /** 810 * Echo canceller options (see pjmedia_echo_create()) 810 * Echo canceller options (see pjmedia_echo_create()). 811 * Specify PJMEDIA_ECHO_USE_SW_ECHO here if application wishes 812 * to use software echo canceller instead of device EC. 811 813 * 812 814 * Default: 0.
Note: See TracChangeset
for help on using the changeset viewer.