Changeset 88492 in vbox
- Timestamp:
- Apr 13, 2021 11:12:25 AM (3 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostAudioPulseAudio.cpp
r88487 r88492 1120 1120 1121 1121 pa_stream_disconnect(pStreamPA->pStream); 1122 1122 1123 pa_stream_unref(pStreamPA->pStream); 1123 1124 1124 pStreamPA->pStream = NULL; 1125 1125 … … 1139 1139 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pvUser; 1140 1140 AssertPtrReturnVoid(pStreamPA); 1141 LogFlowFunc(("fSuccess=%d\n", fSuccess)); 1141 1142 1142 1143 pStreamPA->fOpSuccess = fSuccess; … … 1154 1155 1155 1156 1156 static int drvHostAudioPaStreamControlOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd) 1157 { 1157 /** 1158 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable} 1159 */ 1160 static DECLCALLBACK(int) drvHostAudioPaHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1161 { 1162 PDRVHOSTPULSEAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPULSEAUDIO, IHostAudio); 1163 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1164 LogFlowFunc(("\n")); 1165 1166 pa_threaded_mainloop_lock(pThis->pMainLoop); 1167 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) 1174 { 1175 pa_operation_cancel(pStreamPA->pDrainOp); 1176 LogFlowFunc(("cancelled drain (%d)\n", pa_operation_get_state(pStreamPA->pDrainOp))); 1177 } 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)); 1189 1190 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1191 LogFlowFunc(("returns %Rrc\n", rc)); 1192 return rc; 1193 } 1194 1195 1196 /** 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 else 1221 { 1222 /* Output - Ignore request if we've got a running drain. */ 1223 fCorkIt = !pStreamPA->pDrainOp 1224 || pa_operation_get_state(pStreamPA->pDrainOp) != PA_OPERATION_RUNNING; 1225 } 1226 1227 /* 1228 * Cork (pause) the stream. 1229 */ 1158 1230 int rc = VINF_SUCCESS; 1159 1160 switch (enmStreamCmd) 1161 { 1162 case PDMAUDIOSTREAMCMD_ENABLE: 1163 case PDMAUDIOSTREAMCMD_RESUME: 1164 { 1165 pa_threaded_mainloop_lock(pThis->pMainLoop); 1166 1167 if ( pStreamPA->pDrainOp 1168 && pa_operation_get_state(pStreamPA->pDrainOp) != PA_OPERATION_DONE) 1169 { 1170 pa_operation_cancel(pStreamPA->pDrainOp); 1171 pa_operation_unref(pStreamPA->pDrainOp); 1172 1173 pStreamPA->pDrainOp = NULL; 1174 } 1175 else 1176 { 1177 /* Uncork (resume) stream. */ 1178 rc = drvHostAudioPaWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Uncork */, drvHostAudioPaStreamSuccessCallback, pStreamPA)); 1179 } 1180 1181 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1182 break; 1183 } 1184 1185 case PDMAUDIOSTREAMCMD_DISABLE: 1186 case PDMAUDIOSTREAMCMD_PAUSE: 1187 { 1188 /* Pause audio output (the Pause bit of the AC97 x_CR register is set). 1189 * Note that we must return immediately from here! */ 1190 pa_threaded_mainloop_lock(pThis->pMainLoop); 1191 if (!pStreamPA->pDrainOp) 1192 { 1193 rc = drvHostAudioPaWaitFor(pThis, pa_stream_trigger(pStreamPA->pStream, drvHostAudioPaStreamSuccessCallback, pStreamPA)); 1194 if (RT_SUCCESS(rc)) 1195 pStreamPA->pDrainOp = pa_stream_drain(pStreamPA->pStream, drvHostAudioPaStreamDrainCompletionCallback, pStreamPA); 1196 } 1197 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1198 break; 1199 } 1200 1201 default: 1202 rc = VERR_NOT_SUPPORTED; 1203 break; 1204 } 1205 1206 LogFlowFuncLeaveRC(rc); 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 cannot 1235 * 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 else 1240 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)); 1207 1244 return rc; 1208 1245 } 1209 1246 1210 1247 1211 static int drvHostAudioPaStreamControlIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd) 1212 { 1248 /** 1249 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause} 1250 */ 1251 static DECLCALLBACK(int) drvHostAudioPaHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1252 { 1253 /* Same as disable. */ 1254 return drvHostAudioPaHA_StreamDisable(pInterface, pStream); 1255 } 1256 1257 1258 /** 1259 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume} 1260 */ 1261 static DECLCALLBACK(int) drvHostAudioPaHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1262 { 1263 /* Same as enable. */ 1264 return drvHostAudioPaHA_StreamEnable(pInterface, pStream); 1265 } 1266 1267 1268 /** 1269 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain} 1270 */ 1271 static DECLCALLBACK(int) drvHostAudioPaHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1272 { 1273 PDRVHOSTPULSEAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPULSEAUDIO, IHostAudio); 1274 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1275 AssertReturn(pStreamPA->Cfg.enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER); 1276 LogFlowFunc(("\n")); 1277 1278 pa_threaded_mainloop_lock(pThis->pMainLoop); 1279 1280 /* 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 */ 1213 1286 int rc = VINF_SUCCESS; 1214 1215 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd)); 1216 1217 switch (enmStreamCmd) 1218 { 1219 case PDMAUDIOSTREAMCMD_ENABLE: 1220 case PDMAUDIOSTREAMCMD_RESUME: 1221 { 1222 pa_threaded_mainloop_lock(pThis->pMainLoop); 1223 rc = drvHostAudioPaWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Play / resume */, drvHostAudioPaStreamSuccessCallback, pStreamPA)); 1224 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1225 break; 1226 } 1227 1228 case PDMAUDIOSTREAMCMD_DISABLE: 1229 case PDMAUDIOSTREAMCMD_PAUSE: 1230 { 1231 pa_threaded_mainloop_lock(pThis->pMainLoop); 1232 if (pStreamPA->pu8PeekBuf) /* Do we need to drop the peek buffer?*/ 1233 { 1234 pa_stream_drop(pStreamPA->pStream); 1235 pStreamPA->pu8PeekBuf = NULL; 1236 } 1237 1238 rc = drvHostAudioPaWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 1 /* Stop / pause */, drvHostAudioPaStreamSuccessCallback, pStreamPA)); 1239 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1240 break; 1241 } 1242 1243 default: 1244 rc = VERR_NOT_SUPPORTED; 1245 break; 1246 } 1247 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 { 1296 pa_operation_unref(pStreamPA->pDrainOp); 1297 pStreamPA->pDrainOp = NULL; 1298 } 1299 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)); 1305 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)); 1311 else 1312 LogFunc(("pa_stream_trigger + wait failed: %Rrc\n", rc)); 1313 1314 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1315 LogFlowFunc(("returns %Rrc\n", rc)); 1248 1316 return rc; 1249 1317 } … … 1256 1324 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd) 1257 1325 { 1258 PDRVHOSTPULSEAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTPULSEAUDIO, IHostAudio); 1259 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1260 AssertPtrReturn(pStreamPA, VERR_INVALID_POINTER); 1261 1262 int rc; 1263 if (pStreamPA->Cfg.enmDir == PDMAUDIODIR_IN) 1264 rc = drvHostAudioPaStreamControlIn (pThis, pStreamPA, enmStreamCmd); 1265 else if (pStreamPA->Cfg.enmDir == PDMAUDIODIR_OUT) 1266 rc = drvHostAudioPaStreamControlOut(pThis, pStreamPA, enmStreamCmd); 1267 else 1268 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED); 1269 1270 return rc; 1326 /** @todo r=bird: I'd like to get rid of this pfnStreamControl method, 1327 * replacing it with individual StreamXxxx methods. That would save us 1328 * potentally huge switches and more easily see which drivers implement 1329 * which operations (grep for pfnStreamXxxx). */ 1330 switch (enmStreamCmd) 1331 { 1332 case PDMAUDIOSTREAMCMD_ENABLE: 1333 return drvHostAudioPaHA_StreamEnable(pInterface, pStream); 1334 case PDMAUDIOSTREAMCMD_DISABLE: 1335 return drvHostAudioPaHA_StreamDisable(pInterface, pStream); 1336 case PDMAUDIOSTREAMCMD_PAUSE: 1337 return drvHostAudioPaHA_StreamPause(pInterface, pStream); 1338 case PDMAUDIOSTREAMCMD_RESUME: 1339 return drvHostAudioPaHA_StreamResume(pInterface, pStream); 1340 case PDMAUDIOSTREAMCMD_DRAIN: 1341 return drvHostAudioPaHA_StreamDrain(pInterface, pStream); 1342 1343 case PDMAUDIOSTREAMCMD_END: 1344 case PDMAUDIOSTREAMCMD_32BIT_HACK: 1345 case PDMAUDIOSTREAMCMD_INVALID: 1346 /* no default*/ 1347 break; 1348 } 1349 return VERR_NOT_SUPPORTED; 1271 1350 } 1272 1351
Note:
See TracChangeset
for help on using the changeset viewer.

