VirtualBox

Changeset 88500 in vbox for trunk/src/VBox/Devices


Ignore:
Timestamp:
Apr 13, 2021 7:42:54 PM (4 years ago)
Author:
vboxsync
Message:

DrvHostAudioPulseAudio: Made all the control operation asynchronous (uncorking, corking, triggering, draining) so as to not delay EMT. bugref:9890

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/DrvHostAudioPulseAudio.cpp

    r88492 r88500  
    5252/** Max number of errors reported by drvHostAudioPaError per instance.
    5353 * @todo Make this configurable thru driver config. */
    54 #define VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS  64
     54#define VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS  99
    5555
    5656
     
    143143    /** Pulse playback and buffer metrics. */
    144144    pa_buffer_attr         BufAttr;
    145     int                    fOpSuccess;
    146145    /** Pointer to Pulse sample peeking buffer. */
    147146    const uint8_t         *pu8PeekBuf;
     
    151150    /** Our offset (in bytes) in peeking buffer. */
    152151    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). */
    153155    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;
    156164    /** Current latency (in us). */
    157165    uint64_t               cUsLatency;
     
    161169    /** Time stamp (in us) when last read from / written to the stream. */
    162170    pa_usec_t              tsLastReadWrittenUs;
     171#endif
     172#ifdef DEBUG
     173    /** Number of occurred audio data underflows. */
     174    uint32_t               cUnderflows;
    163175#endif
    164176} PULSEAUDIOSTREAM;
     
    223235
    224236
    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 */
     243static 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 */
     259static int drvHostAudioPaError(PDRVHOSTPULSEAUDIO pThis, const char *pszFormat, ...)
    228260{
    229261    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);
    231266
    232267    if (   pThis->cLogErrors < VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS
    233268        && LogRelIs2Enabled())
    234269    {
    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;
    242280}
    243281
     
    290328            break;
    291329    }
    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     else
    309         drvHostAudioPaError(pStrm->pDrv, "Failed to finish stream operation");
    310330}
    311331
     
    10981118}
    10991119
     1120/**
     1121 * Cancel and release any pending stream requests (drain and cork/uncork).
     1122 *
     1123 * @note Caller has locked the mainloop.
     1124 */
     1125static 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
    11001152
    11011153/**
     
    11121164        pa_threaded_mainloop_lock(pThis->pMainLoop);
    11131165
    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);
    11211167        pa_stream_disconnect(pStreamPA->pStream);
    11221168
     
    11321178
    11331179/**
    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 */
     1183static void drvHostAudioPaStreamCorkUncorkCommon(PPULSEAUDIOSTREAM pStreamPA, int fSuccess, const char *pszOperation)
     1184{
    11401185    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 */
     1202static 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 */
     1212static void drvHostAudioPaStreamCorkCompletionCallback(pa_stream *pStream, int fSuccess, void *pvUser)
     1213{
     1214    RT_NOREF(pStream);
     1215    drvHostAudioPaStreamCorkUncorkCommon((PPULSEAUDIOSTREAM)pvUser, fSuccess, "Corking");
    11541216}
    11551217
     
    11641226    LogFlowFunc(("\n"));
    11651227
     1228    /*
     1229     * Uncork (start or resume playback/capture) the stream.
     1230     */
    11661231    pa_threaded_mainloop_lock(pThis->pMainLoop);
    11671232
    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 */
     1251static 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)
    11741268        {
    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));
    11771277        }
    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);
    11891297
    11901298    pa_threaded_mainloop_unlock(pThis->pMainLoop);
     
    11951303
    11961304/**
    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      */
    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 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));
    1244     return rc;
    1245 }
    1246 
    1247 
    1248 /**
    12491305 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
    12501306 */
     
    12631319    /* Same as enable. */
    12641320    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 */
     1328static 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 */
     1358static 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    }
    12651373}
    12661374
     
    12791387
    12801388    /*
    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)));
    12961401        pa_operation_unref(pStreamPA->pDrainOp);
    12971402        pStreamPA->pDrainOp = NULL;
    12981403    }
    12991404
    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));
    13051424        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));
    13111434    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);
    13131436
    13141437    pa_threaded_mainloop_unlock(pThis->pMainLoop);
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette