Changeset 88500 in vbox for trunk/src/VBox/Devices
- Timestamp:
- Apr 13, 2021 7:42:54 PM (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostAudioPulseAudio.cpp
r88492 r88500 52 52 /** Max number of errors reported by drvHostAudioPaError per instance. 53 53 * @todo Make this configurable thru driver config. */ 54 #define VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS 6454 #define VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS 99 55 55 56 56 … … 143 143 /** Pulse playback and buffer metrics. */ 144 144 pa_buffer_attr BufAttr; 145 int fOpSuccess;146 145 /** Pointer to Pulse sample peeking buffer. */ 147 146 const uint8_t *pu8PeekBuf; … … 151 150 /** Our offset (in bytes) in peeking buffer. */ 152 151 size_t offPeekBuf; 152 /** Asynchronous drain operation. This is used as an indicator of whether 153 * we're currently draining the stream (will be cleaned up before 154 * resume/re-enable). */ 153 155 pa_operation *pDrainOp; 154 /** Number of occurred audio data underflows. */ 155 uint32_t cUnderflows; 156 /** Asynchronous cork/uncork operation. 157 * (This solely for cancelling before destroying the stream, so the callback 158 * won't do any after-freed accesses.) */ 159 pa_operation *pCorkOp; 160 /** Asynchronous trigger operation. 161 * (This solely for cancelling before destroying the stream, so the callback 162 * won't do any after-freed accesses.) */ 163 pa_operation *pTriggerOp; 156 164 /** Current latency (in us). */ 157 165 uint64_t cUsLatency; … … 161 169 /** Time stamp (in us) when last read from / written to the stream. */ 162 170 pa_usec_t tsLastReadWrittenUs; 171 #endif 172 #ifdef DEBUG 173 /** Number of occurred audio data underflows. */ 174 uint32_t cUnderflows; 163 175 #endif 164 176 } PULSEAUDIOSTREAM; … … 223 235 224 236 225 226 /** @todo Implement va handling. */ 227 static int drvHostAudioPaError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg) 237 /** 238 * Converts a pulse audio error to a VBox status. 239 * 240 * @returns VBox status code. 241 * @param rcPa The error code to convert. 242 */ 243 static int drvHostAudioPaErrorToVBox(int rcPa) 244 { 245 /** @todo Implement some PulseAudio -> VBox mapping here. */ 246 RT_NOREF(rcPa); 247 return VERR_GENERAL_FAILURE; 248 } 249 250 251 /** 252 * Logs a pulse audio (from context) and converts it to VBox status. 253 * 254 * @returns VBox status code. 255 * @param pThis Our instance data. 256 * @param pszFormat The format string for the release log (no newline) . 257 * @param ... Format string arguments. 258 */ 259 static int drvHostAudioPaError(PDRVHOSTPULSEAUDIO pThis, const char *pszFormat, ...) 228 260 { 229 261 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 230 AssertPtrReturn(szMsg, VERR_INVALID_POINTER); 262 AssertPtr(pszFormat); 263 264 int const rcPa = pa_context_errno(pThis->pContext); 265 int const rcVBox = drvHostAudioPaErrorToVBox(rcPa); 231 266 232 267 if ( pThis->cLogErrors < VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS 233 268 && LogRelIs2Enabled()) 234 269 { 235 pThis->cLogErrors++; 236 int rc2 = pa_context_errno(pThis->pContext); 237 LogRel2(("PulseAudio: %s: %s\n", szMsg, pa_strerror(rc2))); 238 } 239 240 /** @todo Implement some PulseAudio -> IPRT mapping here. */ 241 return VERR_GENERAL_FAILURE; 270 va_list va; 271 va_start(va, pszFormat); 272 LogRel(("PulseAudio: %N: %s (%d, %Rrc)\n", pszFormat, &va, pa_strerror(rcPa), rcPa, rcVBox)); 273 va_end(va); 274 275 if (++pThis->cLogErrors == VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS) 276 LogRel(("PulseAudio: muting errors (max %u)\n", VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS)); 277 } 278 279 return rcVBox; 242 280 } 243 281 … … 290 328 break; 291 329 } 292 }293 294 295 /**296 * Callback used with pa_stream_cork() in a number of places.297 */298 static void drvHostAudioPaStreamSuccessCallback(pa_stream *pStream, int fSuccess, void *pvUser)299 {300 AssertPtrReturnVoid(pStream);301 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvUser;302 AssertPtrReturnVoid(pStrm);303 304 pStrm->fOpSuccess = fSuccess;305 306 if (fSuccess)307 drvHostAudioPaSignalWaiter(pStrm->pDrv);308 else309 drvHostAudioPaError(pStrm->pDrv, "Failed to finish stream operation");310 330 } 311 331 … … 1098 1118 } 1099 1119 1120 /** 1121 * Cancel and release any pending stream requests (drain and cork/uncork). 1122 * 1123 * @note Caller has locked the mainloop. 1124 */ 1125 static void drvHostAudioPaStreamCancelAndReleaseOperations(PPULSEAUDIOSTREAM pStreamPA) 1126 { 1127 if (pStreamPA->pDrainOp) 1128 { 1129 LogFlowFunc(("drain operation (%p) status: %d\n", pStreamPA->pDrainOp, pa_operation_get_state(pStreamPA->pDrainOp))); 1130 pa_operation_cancel(pStreamPA->pDrainOp); 1131 pa_operation_unref(pStreamPA->pDrainOp); 1132 pStreamPA->pDrainOp = NULL; 1133 } 1134 1135 if (pStreamPA->pCorkOp) 1136 { 1137 LogFlowFunc(("cork operation (%p) status: %d\n", pStreamPA->pCorkOp, pa_operation_get_state(pStreamPA->pCorkOp))); 1138 pa_operation_cancel(pStreamPA->pCorkOp); 1139 pa_operation_unref(pStreamPA->pCorkOp); 1140 pStreamPA->pCorkOp = NULL; 1141 } 1142 1143 if (pStreamPA->pTriggerOp) 1144 { 1145 LogFlowFunc(("trigger operation (%p) status: %d\n", pStreamPA->pTriggerOp, pa_operation_get_state(pStreamPA->pTriggerOp))); 1146 pa_operation_cancel(pStreamPA->pTriggerOp); 1147 pa_operation_unref(pStreamPA->pTriggerOp); 1148 pStreamPA->pTriggerOp = NULL; 1149 } 1150 } 1151 1100 1152 1101 1153 /** … … 1112 1164 pa_threaded_mainloop_lock(pThis->pMainLoop); 1113 1165 1114 /* Make sure to cancel a pending draining operation, if any. */ 1115 if (pStreamPA->pDrainOp) 1116 { 1117 pa_operation_cancel(pStreamPA->pDrainOp); 1118 pStreamPA->pDrainOp = NULL; 1119 } 1120 1166 drvHostAudioPaStreamCancelAndReleaseOperations(pStreamPA); 1121 1167 pa_stream_disconnect(pStreamPA->pStream); 1122 1168 … … 1132 1178 1133 1179 /** 1134 * Pulse audio pa_stream_drain() completion callback. 1135 */ 1136 static void drvHostAudioPaStreamDrainCompletionCallback(pa_stream *pStream, int fSuccess, void *pvUser) 1137 { 1138 AssertPtrReturnVoid(pStream); 1139 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pvUser; 1180 * Common worker for the cork/uncork completion callbacks. 1181 * @note This is fully async, so nobody is waiting for this. 1182 */ 1183 static void drvHostAudioPaStreamCorkUncorkCommon(PPULSEAUDIOSTREAM pStreamPA, int fSuccess, const char *pszOperation) 1184 { 1140 1185 AssertPtrReturnVoid(pStreamPA); 1141 LogFlowFunc(("fSuccess=%d\n", fSuccess)); 1142 1143 pStreamPA->fOpSuccess = fSuccess; 1144 if (fSuccess) 1145 pa_operation_unref(pa_stream_cork(pStream, 1, drvHostAudioPaStreamSuccessCallback, pvUser)); 1146 else 1147 drvHostAudioPaError(pStreamPA->pDrv, "Failed to drain stream"); 1148 1149 if (pStreamPA->pDrainOp) 1150 { 1151 pa_operation_unref(pStreamPA->pDrainOp); 1152 pStreamPA->pDrainOp = NULL; 1153 } 1186 LogFlowFunc(("%s '%s': fSuccess=%RTbool\n", pszOperation, pStreamPA->Cfg.szName, fSuccess)); 1187 1188 if (!fSuccess) 1189 drvHostAudioPaError(pStreamPA->pDrv, "%s stream '%s' failed", pszOperation, pStreamPA->Cfg.szName); 1190 1191 if (pStreamPA->pCorkOp) 1192 { 1193 pa_operation_unref(pStreamPA->pCorkOp); 1194 pStreamPA->pCorkOp = NULL; 1195 } 1196 } 1197 1198 1199 /** 1200 * Completion callback used with pa_stream_cork(,false,). 1201 */ 1202 static void drvHostAudioPaStreamUncorkCompletionCallback(pa_stream *pStream, int fSuccess, void *pvUser) 1203 { 1204 RT_NOREF(pStream); 1205 drvHostAudioPaStreamCorkUncorkCommon((PPULSEAUDIOSTREAM)pvUser, fSuccess, "Uncorking"); 1206 } 1207 1208 1209 /** 1210 * Completion callback used with pa_stream_cork(,true,). 1211 */ 1212 static void drvHostAudioPaStreamCorkCompletionCallback(pa_stream *pStream, int fSuccess, void *pvUser) 1213 { 1214 RT_NOREF(pStream); 1215 drvHostAudioPaStreamCorkUncorkCommon((PPULSEAUDIOSTREAM)pvUser, fSuccess, "Corking"); 1154 1216 } 1155 1217 … … 1164 1226 LogFlowFunc(("\n")); 1165 1227 1228 /* 1229 * Uncork (start or resume playback/capture) the stream. 1230 */ 1166 1231 pa_threaded_mainloop_lock(pThis->pMainLoop); 1167 1232 1168 /* Cancel and dispose of pending drain. We'll unconditionally uncork 1169 the stream even if we cancelled a drain, I hope that will work... */ 1170 if (pStreamPA->pDrainOp) 1171 { 1172 pa_operation_state_t const enmOpState = pa_operation_get_state(pStreamPA->pDrainOp); 1173 if (enmOpState != PA_OPERATION_RUNNING) 1233 drvHostAudioPaStreamCancelAndReleaseOperations(pStreamPA); 1234 pStreamPA->pCorkOp = pa_stream_cork(pStreamPA->pStream, 0 /*uncork it*/, 1235 drvHostAudioPaStreamUncorkCompletionCallback, pStreamPA); 1236 LogFlowFunc(("Uncorking '%s': %p (async)\n", pStreamPA->Cfg.szName, pStreamPA->pCorkOp)); 1237 int const rc = pStreamPA->pCorkOp ? VINF_SUCCESS 1238 : drvHostAudioPaError(pThis, "pa_stream_cork('%s', 0 /*uncork it*/,,) failed", pStreamPA->Cfg.szName); 1239 1240 1241 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1242 1243 LogFlowFunc(("returns %Rrc\n", rc)); 1244 return rc; 1245 } 1246 1247 1248 /** 1249 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable} 1250 */ 1251 static DECLCALLBACK(int) drvHostAudioPaHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1252 { 1253 PDRVHOSTPULSEAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPULSEAUDIO, IHostAudio); 1254 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1255 LogFlowFunc(("\n")); 1256 1257 pa_threaded_mainloop_lock(pThis->pMainLoop); 1258 1259 /* 1260 * For output streams, we will ignore the request if there is a pending drain 1261 * as it will cork the stream in the end. 1262 */ 1263 if (pStreamPA->Cfg.enmDir == PDMAUDIODIR_OUT) 1264 { 1265 /* Output - Ignore request if we've got a drain running, it will cork it 1266 when it completes. */ 1267 if (pStreamPA->pDrainOp) 1174 1268 { 1175 pa_operation_cancel(pStreamPA->pDrainOp); 1176 LogFlowFunc(("cancelled drain (%d)\n", pa_operation_get_state(pStreamPA->pDrainOp))); 1269 pa_operation_state_t const enmOpState = pa_operation_get_state(pStreamPA->pDrainOp); 1270 if (enmOpState == PA_OPERATION_RUNNING) 1271 { 1272 LogFlowFunc(("Drain (%p) already running on '%s', skipping.\n", pStreamPA->pDrainOp, pStreamPA->Cfg.szName)); 1273 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1274 return VINF_SUCCESS; 1275 } 1276 LogFlowFunc(("Drain (%p) not running: %d\n", pStreamPA->pDrainOp, enmOpState)); 1177 1277 } 1178 pa_operation_unref(pStreamPA->pDrainOp); 1179 pStreamPA->pDrainOp = NULL; 1180 } 1181 1182 /* 1183 * Uncork (start or resume play/capture) the stream. 1184 */ 1185 /** @todo do this asynchronously as the caller is usually an EMT which cannot 1186 * wait on a potentally missing-in-action audio daemon. */ 1187 int rc = drvHostAudioPaWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Uncork */, 1188 drvHostAudioPaStreamSuccessCallback, pStreamPA)); 1278 } 1279 /* 1280 * For input stream we always cork it, but we clean up the peek buffer first. 1281 */ 1282 else if (pStreamPA->pu8PeekBuf) /** @todo Do we need to drop the peek buffer?*/ 1283 { 1284 pa_stream_drop(pStreamPA->pStream); 1285 pStreamPA->pu8PeekBuf = NULL; 1286 } 1287 1288 /* 1289 * Cork (pause playback/capture) the stream. 1290 */ 1291 drvHostAudioPaStreamCancelAndReleaseOperations(pStreamPA); 1292 pStreamPA->pCorkOp = pa_stream_cork(pStreamPA->pStream, 1 /* cork it */, 1293 drvHostAudioPaStreamCorkCompletionCallback, pStreamPA); 1294 LogFlowFunc(("Corking '%s': %p (async)\n", pStreamPA->Cfg.szName, pStreamPA->pCorkOp)); 1295 int const rc = pStreamPA->pCorkOp ? VINF_SUCCESS 1296 : drvHostAudioPaError(pThis, "pa_stream_cork('%s', 1 /*cork*/,,) failed", pStreamPA->Cfg.szName); 1189 1297 1190 1298 pa_threaded_mainloop_unlock(pThis->pMainLoop); … … 1195 1303 1196 1304 /** 1197 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}1198 */1199 static DECLCALLBACK(int) drvHostAudioPaHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)1200 {1201 PDRVHOSTPULSEAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPULSEAUDIO, IHostAudio);1202 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream;1203 LogFlowFunc(("\n"));1204 1205 pa_threaded_mainloop_lock(pThis->pMainLoop);1206 1207 /*1208 * Do some direction specific cleanups before we cork the stream.1209 */1210 bool fCorkIt = true;1211 if (pStreamPA->Cfg.enmDir == PDMAUDIODIR_IN)1212 {1213 /* Input - drop the peek buffer. */1214 if (pStreamPA->pu8PeekBuf) /* Do we need to drop the peek buffer?*/1215 {1216 pa_stream_drop(pStreamPA->pStream);1217 pStreamPA->pu8PeekBuf = NULL;1218 }1219 }1220 else1221 {1222 /* Output - Ignore request if we've got a running drain. */1223 fCorkIt = !pStreamPA->pDrainOp1224 || pa_operation_get_state(pStreamPA->pDrainOp) != PA_OPERATION_RUNNING;1225 }1226 1227 /*1228 * Cork (pause) the stream.1229 */1230 int rc = VINF_SUCCESS;1231 if (fCorkIt)1232 {1233 LogFlowFunc(("Corking '%s'...\n", pStreamPA->Cfg.szName));1234 /** @todo do this asynchronously as the caller is usually an EMT which cannot1235 * wait on a potentally missing-in-action audio daemon. */1236 rc = drvHostAudioPaWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 1 /* cork it */,1237 drvHostAudioPaStreamSuccessCallback, pStreamPA));1238 }1239 else1240 LogFlowFunc(("Stream '%s' is already draining, skipping corking.\n", pStreamPA->Cfg.szName));1241 1242 pa_threaded_mainloop_unlock(pThis->pMainLoop);1243 LogFlowFunc(("returns %Rrc\n", rc));1244 return rc;1245 }1246 1247 1248 /**1249 1305 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause} 1250 1306 */ … … 1263 1319 /* Same as enable. */ 1264 1320 return drvHostAudioPaHA_StreamEnable(pInterface, pStream); 1321 } 1322 1323 1324 /** 1325 * Pulse audio pa_stream_drain() completion callback. 1326 * @note This is fully async, so nobody is waiting for this. 1327 */ 1328 static void drvHostAudioPaStreamDrainCompletionCallback(pa_stream *pStream, int fSuccess, void *pvUser) 1329 { 1330 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pvUser; 1331 AssertPtrReturnVoid(pStreamPA); 1332 Assert(pStreamPA->pStream == pStream); 1333 LogFlowFunc(("'%s': fSuccess=%RTbool\n", pStreamPA->Cfg.szName, fSuccess)); 1334 1335 if (!fSuccess) 1336 drvHostAudioPaError(pStreamPA->pDrv, "Draining stream '%s' failed", pStreamPA->Cfg.szName); 1337 1338 /* Now cork the stream (doing it unconditionally atm). */ 1339 if (pStreamPA->pCorkOp) 1340 { 1341 LogFlowFunc(("Cancelling & releasing cork/uncork operation %p (state: %d)\n", 1342 pStreamPA->pCorkOp, pa_operation_get_state(pStreamPA->pCorkOp))); 1343 pa_operation_cancel(pStreamPA->pCorkOp); 1344 pa_operation_unref(pStreamPA->pCorkOp); 1345 } 1346 1347 pStreamPA->pCorkOp = pa_stream_cork(pStream, 1 /* cork it*/, drvHostAudioPaStreamCorkCompletionCallback, pStreamPA); 1348 if (pStreamPA->pCorkOp) 1349 LogFlowFunc(("Started cork operation %p of %s (following drain)\n", pStreamPA->pCorkOp, pStreamPA->Cfg.szName)); 1350 else 1351 drvHostAudioPaError(pStreamPA->pDrv, "pa_stream_cork failed on '%s' (following drain)", pStreamPA->Cfg.szName); 1352 } 1353 1354 1355 /** 1356 * Callback used with pa_stream_tigger(), starts draining. 1357 */ 1358 static void drvHostAudioPaStreamTriggerCompletionCallback(pa_stream *pStream, int fSuccess, void *pvUser) 1359 { 1360 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pvUser; 1361 AssertPtrReturnVoid(pStreamPA); 1362 RT_NOREF(pStream); 1363 LogFlowFunc(("'%s': fSuccess=%RTbool\n", pStreamPA->Cfg.szName, fSuccess)); 1364 1365 if (!fSuccess) 1366 drvHostAudioPaError(pStreamPA->pDrv, "Forcing playback before drainig '%s' failed", pStreamPA->Cfg.szName); 1367 1368 if (pStreamPA->pTriggerOp) 1369 { 1370 pa_operation_unref(pStreamPA->pTriggerOp); 1371 pStreamPA->pTriggerOp = NULL; 1372 } 1265 1373 } 1266 1374 … … 1279 1387 1280 1388 /* 1281 * We must make sure any pre-buffered stuff is played before we drain 1282 * the stream. Also, there might already be a drain request around, 1283 * in case we're called multiple times. Re-issue the drain if the old 1284 * one has completed just to be sure. 1285 */ 1286 int rc = VINF_SUCCESS; 1287 if (!pStreamPA->pDrainOp) 1288 { 1289 LogFlowFunc(("pa_stream_trigger...\n")); 1290 rc = drvHostAudioPaWaitFor(pThis, pa_stream_trigger(pStreamPA->pStream, 1291 drvHostAudioPaStreamSuccessCallback, pStreamPA)); 1292 } 1293 else if ( pStreamPA->pDrainOp 1294 && pa_operation_get_state(pStreamPA->pDrainOp) != PA_OPERATION_RUNNING) 1295 { 1389 * If there is a drain running already, don't try issue another as pulse 1390 * doesn't support more than one concurrent drain per stream. 1391 */ 1392 if (pStreamPA->pDrainOp) 1393 { 1394 if (pa_operation_get_state(pStreamPA->pDrainOp) == PA_OPERATION_RUNNING) 1395 { 1396 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1397 LogFlowFunc(("returns VINF_SUCCESS (drain already running)\n")); 1398 return VINF_SUCCESS; 1399 } 1400 LogFlowFunc(("Releasing drain operation %p (state: %d)\n", pStreamPA->pDrainOp, pa_operation_get_state(pStreamPA->pDrainOp))); 1296 1401 pa_operation_unref(pStreamPA->pDrainOp); 1297 1402 pStreamPA->pDrainOp = NULL; 1298 1403 } 1299 1404 1300 if (!pStreamPA->pDrainOp && RT_SUCCESS(rc)) 1301 { 1302 pStreamPA->pDrainOp = pa_stream_drain(pStreamPA->pStream, drvHostAudioPaStreamDrainCompletionCallback, pStreamPA); 1303 if (pStreamPA->pDrainOp) 1304 LogFlowFunc(("Started drain operation %p of %s\n", pStreamPA->pDrainOp, pStreamPA->Cfg.szName)); 1405 /* 1406 * Make sure pre-buffered data is played before we drain it. 1407 * 1408 * ASSUMES that the async stream requests are executed in the order they're 1409 * issued here, so that we avoid waiting for the trigger request to complete. 1410 */ 1411 int rc = VINF_SUCCESS; 1412 if (true /** @todo skip this if we're already playing or haven't written any data to the stream since xxxx. */) 1413 { 1414 if (pStreamPA->pTriggerOp) 1415 { 1416 LogFlowFunc(("Cancelling & releasing trigger operation %p (state: %d)\n", 1417 pStreamPA->pTriggerOp, pa_operation_get_state(pStreamPA->pTriggerOp))); 1418 pa_operation_cancel(pStreamPA->pTriggerOp); 1419 pa_operation_unref(pStreamPA->pTriggerOp); 1420 } 1421 pStreamPA->pTriggerOp = pa_stream_trigger(pStreamPA->pStream, drvHostAudioPaStreamTriggerCompletionCallback, pStreamPA); 1422 if (pStreamPA->pTriggerOp) 1423 LogFlowFunc(("Started tigger operation %p on %s\n", pStreamPA->pTriggerOp, pStreamPA->Cfg.szName)); 1305 1424 else 1306 LogFunc(("pa_stream_drain failed on '%s': %s (%d)\n", pStreamPA->Cfg.szName, 1307 pa_strerror(pa_context_errno(pThis->pContext)), pa_context_errno(pThis->pContext) )); 1308 } 1309 else if (RT_SUCCESS(rc)) 1310 LogFlowFunc(("Already draining (%p) ...\n", pStreamPA->pDrainOp)); 1425 rc = drvHostAudioPaError(pStreamPA->pDrv, "pa_stream_trigger failed on '%s'", pStreamPA->Cfg.szName); 1426 } 1427 1428 /* 1429 * Initiate the draining (async), will cork the stream when it completes. 1430 */ 1431 pStreamPA->pDrainOp = pa_stream_drain(pStreamPA->pStream, drvHostAudioPaStreamDrainCompletionCallback, pStreamPA); 1432 if (pStreamPA->pDrainOp) 1433 LogFlowFunc(("Started drain operation %p of %s\n", pStreamPA->pDrainOp, pStreamPA->Cfg.szName)); 1311 1434 else 1312 LogFunc(("pa_stream_trigger + wait failed: %Rrc\n", rc));1435 rc = drvHostAudioPaError(pStreamPA->pDrv, "pa_stream_drain failed on '%s'", pStreamPA->Cfg.szName); 1313 1436 1314 1437 pa_threaded_mainloop_unlock(pThis->pMainLoop);
Note:
See TracChangeset
for help on using the changeset viewer.