Changeset 89526 in vbox for trunk/src/VBox/Devices
- Timestamp:
- Jun 6, 2021 10:39:46 PM (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostAudioPulseAudio.cpp
r89522 r89526 742 742 static void drvHstAudPaStreamUnderflowDebugCallback(pa_stream *pStream, void *pvContext) 743 743 { 744 PDRVHSTAUDPASTREAM pStrm = (PDRVHSTAUDPASTREAM)pvContext; 745 AssertPtrReturnVoid(pStrm); 746 747 pStrm->cUnderflows++; 748 749 LogRel2(("PulseAudio: Warning: Hit underflow #%RU32\n", pStrm->cUnderflows)); 750 751 # if 0 752 if ( pStrm->cUnderflows >= 6 /** @todo Make this check configurable. */ 753 && pStrm->cUsLatency < 2U*RT_US_1SEC) 754 { 755 pStrm->cUsLatency = pStrm->cUsLatency * 3 / 2; 756 LogRel2(("PulseAudio: Increasing output latency to %'RU64 us\n", pStrm->cUsLatency)); 757 758 pStrm->BufAttr.maxlength = pa_usec_to_bytes(pStrm->cUsLatency, &pStrm->SampleSpec); 759 pStrm->BufAttr.tlength = pa_usec_to_bytes(pStrm->cUsLatency, &pStrm->SampleSpec); 760 pa_operation *pOperation = pa_stream_set_buffer_attr(pStream, &pStrm->BufAttr, NULL, NULL); 744 PDRVHSTAUDPASTREAM pStreamPA = (PDRVHSTAUDPASTREAM)pvContext; 745 AssertPtrReturnVoid(pStreamPA); 746 747 pStreamPA->cUnderflows++; 748 749 LogRel2(("PulseAudio: Warning: Hit underflow #%RU32%s%s\n", pStreamPA->cUnderflows, 750 pStreamPA->pDrainOp && pa_operation_get_state(pStreamPA->pDrainOp) == PA_OPERATION_RUNNING ? " (draining)" : "", 751 pStreamPA->pCorkOp && pa_operation_get_state(pStreamPA->pCorkOp) == PA_OPERATION_RUNNING ? " (corking)" : "" )); 752 753 # if 0 /* bird: It's certifiably insane to make buffer changes here and make DEBUG build behave differently from RELEASE builds. */ 754 if ( pStreamPA->cUnderflows >= 6 /** @todo Make this check configurable. */ 755 && pStreamPA->cUsLatency < 2U*RT_US_1SEC) 756 { 757 pStreamPA->cUsLatency = pStreamPA->cUsLatency * 3 / 2; 758 LogRel2(("PulseAudio: Increasing output latency to %'RU64 us\n", pStreamPA->cUsLatency)); 759 760 pStreamPA->BufAttr.maxlength = pa_usec_to_bytes(pStreamPA->cUsLatency, &pStreamPA->SampleSpec); 761 pStreamPA->BufAttr.tlength = pa_usec_to_bytes(pStreamPA->cUsLatency, &pStreamPA->SampleSpec); 762 pa_operation *pOperation = pa_stream_set_buffer_attr(pStream, &pStreamPA->BufAttr, NULL, NULL); 761 763 if (pOperation) 762 764 pa_operation_unref(pOperation); … … 764 766 LogRel2(("pa_stream_set_buffer_attr failed!\n")); 765 767 766 pStr m->cUnderflows = 0;768 pStreamPA->cUnderflows = 0; 767 769 } 768 770 # endif … … 782 784 Log2Func(("writepos=%'RU64 us, readpost=%'RU64 us, age=%'RU64 us, latency=%'RU64 us (%RU32Hz %RU8ch)\n", 783 785 pa_bytes_to_usec(pTInfo->write_index, pSpec), pa_bytes_to_usec(pTInfo->read_index, pSpec), 784 pa_rtclock_now() - pStr m->tsStartUs, cUsLatency, pSpec->rate, pSpec->channels));786 pa_rtclock_now() - pStreamPA->tsStartUs, cUsLatency, pSpec->rate, pSpec->channels)); 785 787 } 786 788 # endif … … 884 886 return VINF_SUCCESS; 885 887 } 888 889 890 #if 0 /* experiment */ 891 /** 892 * Completion callback used with pa_stream_set_buffer_attr(). 893 */ 894 static void drvHstAudPaStreamSetBufferAttrCompletionCallback(pa_stream *pStream, int fSuccess, void *pvUser) 895 { 896 PDRVHSTAUDPA pThis = (PDRVHSTAUDPA)pvUser; 897 LogFlowFunc(("fSuccess=%d\n", fSuccess)); 898 pa_threaded_mainloop_signal(pThis->pMainLoop, 0 /*fWaitForAccept*/); 899 RT_NOREF(pStream); 900 } 901 #endif 886 902 887 903 … … 968 984 */ 969 985 const pa_buffer_attr *pBufAttribs = pa_stream_get_buffer_attr(pStream); 986 #if 0 /* Experiment for getting tlength closer to what we requested (ADJUST_LATENCY effect). 987 Will slow down stream creation, so not pursued any further at present. */ 988 if ( pCfgAcq->enmDir == PDMAUDIODIR_OUT 989 && pBufAttribs 990 && pBufAttribs->tlength < pStreamPA->BufAttr.tlength) 991 { 992 pStreamPA->BufAttr.maxlength += (pStreamPA->BufAttr.tlength - pBufAttribs->tlength) * 2; 993 pStreamPA->BufAttr.tlength += (pStreamPA->BufAttr.tlength - pBufAttribs->tlength) * 2; 994 LogRel(("Before pa_stream_set_buffer_attr: tlength=%#x (trying =%#x)\n", pBufAttribs->tlength, pStreamPA->BufAttr.tlength)); 995 drvHstAudPaWaitFor(pThis, pa_stream_set_buffer_attr(pStream, &pStreamPA->BufAttr, 996 drvHstAudPaStreamSetBufferAttrCompletionCallback, pThis)); 997 pBufAttribs = pa_stream_get_buffer_attr(pStream); 998 LogRel(("After pa_stream_set_buffer_attr: tlength=%#x\n", pBufAttribs->tlength)); 999 } 1000 #endif 970 1001 AssertPtr(pBufAttribs); 971 1002 if (pBufAttribs) … … 1151 1182 { 1152 1183 /* 1184 * Convert the requested buffer parameters to PA bytes. 1185 */ 1186 uint32_t const cbBuffer = pa_usec_to_bytes(PDMAudioPropsFramesToMicro(&pCfgAcq->Props, 1187 pCfgReq->Backend.cFramesBufferSize), 1188 &pStreamPA->SampleSpec); 1189 uint32_t const cbPreBuffer = pa_usec_to_bytes(PDMAudioPropsFramesToMicro(&pCfgAcq->Props, 1190 pCfgReq->Backend.cFramesPreBuffering), 1191 &pStreamPA->SampleSpec); 1192 uint32_t const cbSchedHint = pa_usec_to_bytes(pCfgReq->Device.cMsSchedulingHint * RT_US_1MS, &pStreamPA->SampleSpec); 1193 RT_NOREF(cbBuffer, cbSchedHint, cbPreBuffer); 1194 1195 /* 1153 1196 * Set up buffer attributes according to the stream type. 1154 *1155 * For output streams we configure pre-buffering as requested, since1156 * there is little point in using a different size than DrvAudio. This1157 * assumes that a 'drain' request will override the prebuf size.1158 1197 */ 1159 pStreamPA->BufAttr.maxlength = UINT32_MAX; /* Let the PulseAudio server choose the biggest size it can handle. */1160 1198 if (pCfgReq->enmDir == PDMAUDIODIR_IN) 1161 1199 { 1162 pStreamPA->BufAttr.fragsize = PDMAudioPropsFramesToBytes(&pCfgAcq->Props, pCfgReq->Backend.cFramesPeriod); 1163 LogFunc(("Requesting: BufAttr: fragsize=%RU32\n", pStreamPA->BufAttr.fragsize)); 1164 /* (rlength, minreq and prebuf are playback only) */ 1200 /* Set maxlength to the requested buffer size. */ 1201 pStreamPA->BufAttr.maxlength = cbBuffer; 1202 1203 /* Set the fragment size according to the scheduling hint (forget 1204 cFramesPeriod, it's generally rubbish on input). */ 1205 pStreamPA->BufAttr.fragsize = cbSchedHint; 1206 1207 /* (tlength, minreq and prebuf are playback only) */ 1208 LogFunc(("Requesting: BufAttr: fragsize=%#RX32 maxLength=%#RX32\n", 1209 pStreamPA->BufAttr.fragsize, pStreamPA->BufAttr.maxlength)); 1165 1210 } 1166 1211 else 1167 1212 { 1168 pStreamPA->cUsLatency = PDMAudioPropsFramesToMicro(&pCfgAcq->Props, pCfgReq->Backend.cFramesBufferSize); 1169 pStreamPA->BufAttr.tlength = pa_usec_to_bytes(pStreamPA->cUsLatency, &pStreamPA->SampleSpec); 1170 #if 0 /* bird: Bad bad idea. Messes up output via a "Intel Corporation 200 Series PCH HD Audio" 1171 device here on fedora-32. Just use the default instead. */ 1172 pStreamPA->BufAttr.minreq = PDMAudioPropsFramesToBytes(&pCfgAcq->Props, pCfgReq->Backend.cFramesPeriod); 1173 #else 1174 pStreamPA->BufAttr.minreq = -1; 1175 #endif 1176 pStreamPA->BufAttr.prebuf = pa_usec_to_bytes(PDMAudioPropsFramesToMicro(&pCfgAcq->Props, 1177 pCfgReq->Backend.cFramesPreBuffering), 1178 &pStreamPA->SampleSpec); 1213 /* Set tlength to the desired buffer size as PA doesn't have any way 1214 of telling us if anything beyond tlength is writable or not (see 1215 drvHstAudPaStreamGetWritableLocked for more). Because of the 1216 ADJUST_LATENCY flag, this value will be adjusted down, so we'll 1217 end up with less buffer than what we requested, however it should 1218 probably reflect the actual latency a bit closer. Probably not 1219 worth trying to adjust this via pa_stream_set_buffer_attr. */ 1220 pStreamPA->BufAttr.tlength = cbBuffer; 1221 1222 /* Set maxlength to the same as tlength as we won't ever write more 1223 than tlength. */ 1224 pStreamPA->BufAttr.maxlength = pStreamPA->BufAttr.tlength; 1225 1226 /* According to vlc, pulseaudio goes berserk if the minreq is not 1227 significantly smaller than half of tlength. They use a 1:3 ratio 1228 between minreq and tlength. Traditionally, we've used to just 1229 pass the period value here, however the quality of the incoming 1230 cFramesPeriod value is so variable that just ignore it. This 1231 minreq value is mainly about updating the pa_stream_writable_size 1232 return value, so it makes sense that it need to be well below 1233 half of the buffer length, otherwise we will think the buffer 1234 is full for too long when it isn't. 1235 1236 The DMA scheduling hint is often a much better indicator. Just 1237 to avoid generating too much IPC, limit this to 10 ms. */ 1238 uint32_t const cbMinUpdate = pa_usec_to_bytes(RT_US_10MS, &pStreamPA->SampleSpec); 1239 pStreamPA->BufAttr.minreq = RT_MIN(RT_MAX(cbSchedHint, cbMinUpdate), 1240 pStreamPA->BufAttr.tlength / 4); 1241 1242 /* Just pass along the requested pre-buffering size. This seems 1243 typically to be unaltered by pa_stream_connect_playback. Not 1244 sure if tlength is perhaps adjusted relative to it... Ratio 1245 seen here is prebuf=93.75% of tlength. This isn't entirely 1246 optimal as we use 50% by default (see DrvAudio) so that there 1247 is equal room for the guest to run too fast and too slow. Not 1248 much we can do about it w/o slowing down stream creation. */ 1249 pStreamPA->BufAttr.prebuf = cbPreBuffer; 1250 1179 1251 /* (fragsize is capture only) */ 1252 LogRel2(("PulseAudio: Requesting: BufAttr: tlength=%#RX32 minReq=%#RX32 prebuf=%#RX32 maxLength=%#RX32\n", 1253 pStreamPA->BufAttr.tlength, pStreamPA->BufAttr.minreq, pStreamPA->BufAttr.prebuf, pStreamPA->BufAttr.maxlength)); 1254 1255 /* This (cUsLatency) isn't used for anything. */ 1256 pStreamPA->cUsLatency = PDMAudioPropsFramesToMicro(&pCfgAcq->Props, pCfgReq->Backend.cFramesBufferSize); 1180 1257 LogRel2(("PulseAudio: Initial output latency is %RU64 us (%RU32 bytes)\n", 1181 1258 pStreamPA->cUsLatency, pStreamPA->BufAttr.tlength)); 1182 LogFunc(("Requesting: BufAttr: tlength=%RU32 maxLength=%RU32 minReq=%RU32 maxlength=-1\n",1183 pStreamPA->BufAttr.tlength, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq));1184 1259 } 1185 1260 … … 1195 1270 * Set the acquired stream config according to the actual buffer 1196 1271 * attributes we got and the stream type. 1272 * 1273 * Note! We use maxlength for input buffer and tlength for the 1274 * output buffer size. See above for why. 1197 1275 */ 1198 1276 if (pCfgReq->enmDir == PDMAUDIODIR_IN) 1199 1277 { 1278 LogRel2(("PulseAudio: Got: BufAttr: fragsize=%#RX32 maxLength=%#RX32\n", 1279 pStreamPA->BufAttr.fragsize, pStreamPA->BufAttr.maxlength)); 1200 1280 pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.fragsize); 1201 1281 pCfgAcq->Backend.cFramesBufferSize = pStreamPA->BufAttr.maxlength != UINT32_MAX /* paranoia */ 1202 1282 ? PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.maxlength) 1203 : pCfgAcq->Backend.cFramesPeriod * 2 /* whatever */; 1204 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod; 1283 : pCfgAcq->Backend.cFramesPeriod * 3 /* whatever */; 1284 pCfgAcq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesPreBuffering * pCfgAcq->Backend.cFramesBufferSize 1285 / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1); 1205 1286 } 1206 1287 else 1207 1288 { 1208 pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.minreq);1209 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.tlength);1210 pCfgAcq->Backend.cFramesP reBuffering = pCfgReq->Backend.cFramesPreBuffering1211 * pCfgAcq->Backend.cFramesBufferSize1212 / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1);1289 LogRel2(("PulseAudio: Got: BufAttr: tlength=%#RX32 minReq=%#RX32 prebuf=%#RX32 maxLength=%#RX32\n", 1290 pStreamPA->BufAttr.tlength, pStreamPA->BufAttr.minreq, pStreamPA->BufAttr.prebuf, pStreamPA->BufAttr.maxlength)); 1291 pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.minreq); 1292 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.tlength); 1293 pCfgAcq->Backend.cFramesPreBuffering = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, pStreamPA->BufAttr.prebuf); 1213 1294 } 1214 1295 … … 1609 1690 1610 1691 /** 1692 * Gets the number of bytes that can safely be written to a stream. 1693 * 1694 * @returns Number of writable bytes, ~(size_t)0 on error. 1695 * @param pStreamPA The stream. 1696 */ 1697 DECLINLINE(uint32_t) drvHstAudPaStreamGetWritableLocked(PDRVHSTAUDPASTREAM pStreamPA) 1698 { 1699 /* pa_stream_writable_size() returns the amount requested currently by the 1700 server, we could write more than this if we liked. The documentation says 1701 up to maxlength, whoever I'm not sure how that limitation is enforced or 1702 what would happen if we exceed it. There seems to be no (simple) way to 1703 figure out how much buffer we have left between what pa_stream_writable_size 1704 returns and what maxlength indicates. 1705 1706 An alternative would be to guess the difference using the read and write 1707 positions in the timing info, however the read position is only updated 1708 when starting and stopping. In the auto update mode it's updated at a 1709 sharply decreasing rate starting at 10ms and ending at 1500ms. So, not 1710 all that helpful. (As long as pa_stream_writable_size returns a non-zero 1711 value, though, we could just add the maxlength-tlength difference. But 1712 the problem is after that.) 1713 1714 So, for now we just use tlength = maxlength for output streams and 1715 problem solved. */ 1716 size_t const cbWritablePa = pa_stream_writable_size(pStreamPA->pStream); 1717 #if 1 1718 return cbWritablePa; 1719 #else 1720 if (cbWritablePa > 0 && cbWritablePa != (size_t)-1) 1721 return cbWritablePa + (pStreamPA->BufAttr.maxlength - pStreamPA->BufAttr.tlength); 1722 //const pa_timing_info * const pTimingInfo = pa_stream_get_timing_info(pStreamPA->pStream); 1723 return 0; 1724 #endif 1725 } 1726 1727 1728 /** 1611 1729 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable} 1612 1730 */ … … 1623 1741 if (PA_STREAM_IS_GOOD(enmState)) 1624 1742 { 1625 size_t cbWritablePa = pa_stream_writable_size(pStreamPA->pStream);1743 size_t cbWritablePa = drvHstAudPaStreamGetWritableLocked(pStreamPA); 1626 1744 if (cbWritablePa != (size_t)-1) 1627 1745 cbWritable = cbWritablePa <= UINT32_MAX ? (uint32_t)cbWritablePa : UINT32_MAX; … … 1669 1787 1670 1788 /* 1671 * Using a loop here so we can take maxlength into account when writing.1789 * Using a loop here so we can stuff the buffer as full as it gets. 1672 1790 */ 1673 1791 int rc = VINF_SUCCESS; … … 1676 1794 for (iLoop = 0; ; iLoop++) 1677 1795 { 1678 size_t const cbWriteable = pa_stream_writable_size(pStreamPA->pStream);1796 size_t const cbWriteable = drvHstAudPaStreamGetWritableLocked(pStreamPA); 1679 1797 if ( cbWriteable != (size_t)-1 1680 1798 && cbWriteable >= PDMAudioPropsFrameSize(&pStreamPA->Cfg.Props)) 1681 1799 { 1682 uint32_t cbToWrite = (uint32_t)RT_MIN( RT_MIN(cbWriteable, pStreamPA->BufAttr.maxlength), cbBuf);1800 uint32_t cbToWrite = (uint32_t)RT_MIN(cbWriteable, cbBuf); 1683 1801 cbToWrite = PDMAudioPropsFloorBytesToFrame(&pStreamPA->Cfg.Props, cbToWrite); 1684 1802 if (pa_stream_write(pStreamPA->pStream, pvBuf, cbToWrite, NULL /*pfnFree*/, 0 /*offset*/, PA_SEEK_RELATIVE) >= 0) … … 1884 2002 else 1885 2003 { 1886 if (cbAvail != (size_t)-1)2004 if (cbAvail == (size_t)-1) 1887 2005 rc = drvHstAudPaError(pStreamPA->pDrv, "pa_stream_readable_size failed on '%s'", pStreamPA->Cfg.szName); 1888 2006 break;
Note:
See TracChangeset
for help on using the changeset viewer.