VirtualBox

Changeset 67951 in vbox


Ignore:
Timestamp:
Jul 13, 2017 10:23:33 AM (8 years ago)
Author:
vboxsync
Message:

Audio/DrvHostPulseAudio: Added underflow / write callbacks from 5.1, more work on bringing latency down.

Location:
trunk/src/VBox/Devices/Audio
Files:
4 edited

Legend:

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

    r67909 r67951  
    4545*********************************************************************************************************************************/
    4646#define VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS 32 /** @todo Make this configurable thru driver options. */
     47
     48/* Whether to use PulseAudio's asynchronous handling or not. */
     49//#define PULSEAUDIO_ASYNC /** @todo Make this configurable thru driver options. */
    4750
    4851#ifndef PA_STREAM_NOFLAGS
     
    117120    size_t                 offPeekBuf;
    118121    pa_operation          *pDrainOp;
     122    /** Number of occurred audio data underflows. */
     123    uint32_t               cUnderflows;
     124    /** Current latency (in us). */
     125    uint64_t               curLatencyUs;
     126    /** Start time stamp (in us) of stream playback / recording. */
     127    pa_usec_t              tsStartUs;
    119128} PULSEAUDIOSTREAM, *PPULSEAUDIOSTREAM;
    120129
     
    177186static int  paEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum);
    178187static int  paError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg);
     188static void paStreamCbUnderflow(pa_stream *pStream, void *pvContext);
     189#ifdef PULSEAUDIO_ASYNC
     190 static void paStreamCbReqWrite(pa_stream *pStream, size_t cbLen, void *pvContext);
     191#endif
    179192static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvContext);
    180 
    181193
    182194
     
    390402
    391403
     404#ifdef PULSEAUDIO_ASYNC
     405static void paStreamCbReqWrite(pa_stream *pStream, size_t cbLen, void *pvContext)
     406{
     407    RT_NOREF(cbLen, pvContext);
     408
     409    PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
     410    AssertPtrReturnVoid(pStrm);
     411
     412    pa_usec_t usec = 0;
     413    int neg = 0;
     414    pa_stream_get_latency(pStream, &usec, &neg);
     415
     416    Log2Func(("Requested %zu bytes -- Current latency is %RU64ms\n", cbLen, usec / 1000));
     417}
     418#endif /* PULSEAUDIO_ASYNC */
     419
     420
     421static void paStreamCbUnderflow(pa_stream *pStream, void *pvContext)
     422{
     423    PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
     424    AssertPtrReturnVoid(pStrm);
     425
     426    pStrm->cUnderflows++;
     427
     428    Log2Func(("Warning: Hit underflow #%RU32\n", pStrm->cUnderflows));
     429
     430    if (   pStrm->cUnderflows  >= 6                /** @todo Make this check configurable. */
     431        && pStrm->curLatencyUs < 2000000 /* 2s */)
     432    {
     433        pStrm->curLatencyUs = (pStrm->curLatencyUs * 3) / 2;
     434        LogFunc(("Latency increased to %RU64ms\n", pStrm->curLatencyUs / 1000));
     435
     436        pStrm->BufAttr.maxlength = pa_usec_to_bytes(pStrm->curLatencyUs, &pStrm->SampleSpec);
     437        pStrm->BufAttr.tlength   = pa_usec_to_bytes(pStrm->curLatencyUs, &pStrm->SampleSpec);
     438
     439        pa_stream_set_buffer_attr(pStream, &pStrm->BufAttr, NULL, NULL);
     440
     441        pStrm->cUnderflows = 0;
     442    }
     443
     444#ifdef LOG_ENABLED
     445    pa_usec_t curLatencyUs = 0;
     446    pa_stream_get_latency(pStream, &curLatencyUs, NULL /* Neg */);
     447
     448    const pa_timing_info *pTInfo = pa_stream_get_timing_info(pStream);
     449    const pa_sample_spec *pSpec  = pa_stream_get_sample_spec(pStream);
     450
     451    pa_usec_t curPosWritesUs = pa_bytes_to_usec(pTInfo->write_index, pSpec);
     452    pa_usec_t curTsUs        = pa_rtclock_now() - pStrm->tsStartUs;
     453
     454    Log2Func(("curPosWrite=%RU64ms, curTs=%RU64ms, curDelta=%RI64ms, curLatency=%RU64ms\n",
     455              curPosWritesUs / 1000, curTsUs / 1000, (((int64_t)curPosWritesUs - (int64_t)curTsUs) / 1000), curLatencyUs / 1000));
     456#endif
     457}
     458
     459
    392460static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvUser)
    393461{
     
    406474
    407475
    408 static int paStreamOpen(PDRVHOSTPULSEAUDIO pThis, bool fIn, const char *pszName,
    409                         pa_sample_spec *pSampleSpec, pa_buffer_attr *pBufAttr,
    410                         pa_stream **ppStream)
    411 {
    412     AssertPtrReturn(pThis,       VERR_INVALID_POINTER);
    413     AssertPtrReturn(pszName,     VERR_INVALID_POINTER);
    414     AssertPtrReturn(pSampleSpec, VERR_INVALID_POINTER);
    415     AssertPtrReturn(pBufAttr,    VERR_INVALID_POINTER);
    416     AssertPtrReturn(ppStream,    VERR_INVALID_POINTER);
    417 
    418     if (!pa_sample_spec_valid(pSampleSpec))
    419     {
    420         LogRel(("PulseAudio: Unsupported sample specification for stream '%s'\n",
    421                 pszName));
    422         return VERR_NOT_SUPPORTED;
    423     }
     476static int paStreamOpen(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, bool fIn, const char *pszName)
     477{
     478    AssertPtrReturn(pThis,     VERR_INVALID_POINTER);
     479    AssertPtrReturn(pStreamPA, VERR_INVALID_POINTER);
     480    AssertPtrReturn(pszName,   VERR_INVALID_POINTER);
    424481
    425482    int rc = VINF_SUCCESS;
    426483
    427484    pa_stream *pStream = NULL;
    428     uint32_t   flags = PA_STREAM_NOFLAGS;
    429 
    430     LogFunc(("Opening '%s', rate=%dHz, channels=%d, format=%s\n",
    431              pszName, pSampleSpec->rate, pSampleSpec->channels,
    432              pa_sample_format_to_string(pSampleSpec->format)));
     485    uint32_t   flags   = PA_STREAM_NOFLAGS;
    433486
    434487    pa_threaded_mainloop_lock(pThis->pMainLoop);
     
    436489    do
    437490    {
     491        pa_sample_spec *pSampleSpec = &pStreamPA->SampleSpec;
     492
     493        LogFunc(("Opening '%s', rate=%dHz, channels=%d, format=%s\n",
     494                 pszName, pSampleSpec->rate, pSampleSpec->channels,
     495                 pa_sample_format_to_string(pSampleSpec->format)));
     496
     497        if (!pa_sample_spec_valid(pSampleSpec))
     498        {
     499            LogRel(("PulseAudio: Unsupported sample specification for stream '%s'\n", pszName));
     500            rc = VERR_NOT_SUPPORTED;
     501            break;
     502        }
     503
     504        pa_buffer_attr *pBufAttr = &pStreamPA->BufAttr;
     505
    438506        /** @todo r=andy Use pa_stream_new_with_proplist instead. */
    439         if (!(pStream = pa_stream_new(pThis->pContext, pszName, pSampleSpec,
    440                                       NULL /* pa_channel_map */)))
     507        if (!(pStream = pa_stream_new(pThis->pContext, pszName, pSampleSpec, NULL /* pa_channel_map */)))
    441508        {
    442509            LogRel(("PulseAudio: Could not create stream '%s'\n", pszName));
     
    445512        }
    446513
    447         pa_stream_set_state_callback(pStream, paStreamCbStateChanged, pThis);
     514#ifdef PULSEAUDIO_ASYNC
     515        pa_stream_set_write_callback(pStream,     paStreamCbReqWrite,     pStreamPA);
     516#endif
     517        pa_stream_set_underflow_callback(pStream, paStreamCbUnderflow,    pStreamPA);
     518        pa_stream_set_state_callback(pStream,     paStreamCbStateChanged, pThis);
    448519
    449520#if PA_API_VERSION >= 12
     
    451522        flags |= PA_STREAM_ADJUST_LATENCY;
    452523#endif
    453 
    454 #if 0
    455         /* Not applicable as we don't use pa_stream_get_latency() and pa_stream_get_time(). */
     524        /* For using pa_stream_get_latency() and pa_stream_get_time(). */
    456525        flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
    457 #endif
     526
    458527        /* No input/output right away after the stream was started. */
    459528        flags |= PA_STREAM_START_CORKED;
     
    506575        }
    507576
     577        pStreamPA->tsStartUs = pa_rtclock_now();
     578
    508579        if (RT_FAILURE(rc))
    509580            break;
     
    513584        memcpy(pBufAttr, pBufAttrObtained, sizeof(pa_buffer_attr));
    514585
    515         if (fIn)
    516             LogFunc(("Obtained record buffer attributes: maxlength=%RU32, fragsize=%RU32\n",
    517                      pBufAttr->maxlength, pBufAttr->fragsize));
    518         else
    519             LogFunc(("Obtained playback buffer attributes: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d\n",
    520                      pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
     586        LogFunc(("Obtained %s buffer attributes: tLength=%RU32, maxLength=%RU32, minReq=%RU32, fragSize=%RU32, preBuf=%RU32\n",
     587                 fIn ? "capture" : "playback",
     588                 pBufAttr->tlength, pBufAttr->maxlength, pBufAttr->minreq, pBufAttr->fragsize, pBufAttr->prebuf));
     589
     590        pStreamPA->pStream = pStream;
    521591
    522592    } while (0);
     
    533603            pa_stream_unref(pStream);
    534604    }
    535     else
    536         *ppStream = pStream;
    537605
    538606    LogFlowFuncLeaveRC(rc);
     
    662730    pStreamPA->SampleSpec.channels = pCfgReq->Props.cChannels;
    663731
    664     /* Note that setting maxlength to -1 does not work on PulseAudio servers
    665      * older than 0.9.10. So use the suggested value of 3/2 of tlength */
    666     pStreamPA->BufAttr.tlength     =   (pa_bytes_per_second(&pStreamPA->SampleSpec)
    667                                         * s_pulseCfg.buffer_msecs_out) / 1000;
    668     pStreamPA->BufAttr.maxlength   = (pStreamPA->BufAttr.tlength * 3) / 2;
    669     pStreamPA->BufAttr.prebuf      = -1; /* Same as tlength */
    670     pStreamPA->BufAttr.minreq      = -1;
     732    pStreamPA->curLatencyUs        = 100 * 1000; /** 100ms latency by default. @todo Make this configurable. */
     733
     734    const uint32_t mixsize = pa_usec_to_bytes(pStreamPA->curLatencyUs, &pStreamPA->SampleSpec);
     735
     736    pStreamPA->BufAttr.maxlength   = mixsize * 4;
     737    pStreamPA->BufAttr.tlength     = mixsize;
     738    pStreamPA->BufAttr.prebuf      = mixsize * 2;
     739    pStreamPA->BufAttr.minreq      = mixsize;
     740
     741    LogFunc(("BufAttr tlength=%RU32, maxLength=%RU32, minReq=%RU32\n",
     742             pStreamPA->BufAttr.tlength, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq));
    671743
    672744    /* Note that the struct BufAttr is updated to the obtained values after this call! */
    673     int rc = paStreamOpen(pThis, false /* fIn */, "PulseAudio (Out)",
    674                           &pStreamPA->SampleSpec, &pStreamPA->BufAttr, &pStreamPA->pStream);
     745    int rc = paStreamOpen(pThis, pStreamPA, false /* fIn */, "PulseAudio (Out)");
    675746    if (RT_FAILURE(rc))
    676747        return rc;
     
    714785
    715786    /* Note: Other members of BufAttr are ignored for record streams. */
    716     int rc = paStreamOpen(pThis, true /* fIn */, "PulseAudio (In)", &pStreamPA->SampleSpec, &pStreamPA->BufAttr,
    717                           &pStreamPA->pStream);
     787    int rc = paStreamOpen(pThis, pStreamPA, true /* fIn */, "PulseAudio (In)");
    718788    if (RT_FAILURE(rc))
    719789        return rc;
     
    14391509        {
    14401510            size_t cbWritable = pa_stream_writable_size(pStreamPA->pStream);
    1441             Log3Func(("cbWritable=%zu, cbMinReq=%RU32\n", cbWritable, pStreamPA->BufAttr.minreq));
    1442 
    1443             if (cbWritable >= pStreamPA->BufAttr.minreq)
    1444                 cbAvail = (uint32_t)cbWritable;
     1511
     1512            Log3Func(("cbWritable=%zu, maxLength=%RU32, minReq=%RU32\n",
     1513                      cbWritable, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq));
     1514
     1515            /* Don't report more writable than the PA server can handle. */
     1516            if (cbWritable > pStreamPA->BufAttr.maxlength)
     1517                cbWritable = pStreamPA->BufAttr.maxlength;
     1518
     1519            cbAvail = (uint32_t)cbWritable;
    14451520        }
    14461521        else
  • trunk/src/VBox/Devices/Audio/pulse_mangling.h

    r59987 r67951  
    66
    77/*
    8  * Copyright (C) 2013-2016 Oracle Corporation
     8 * Copyright (C) 2013-2017 Oracle Corporation
    99 *
    1010 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    3030#define pa_stream_unref                         PULSE_MANGLER(pa_stream_unref)
    3131#define pa_stream_get_state                     PULSE_MANGLER(pa_stream_get_state)
     32#define pa_stream_get_latency                   PULSE_MANGLER(pa_stream_get_latency)
     33#define pa_stream_get_timing_info               PULSE_MANGLER(pa_stream_get_timing_info)
     34#define pa_stream_set_buffer_attr               PULSE_MANGLER(pa_stream_set_buffer_attr)
    3235#define pa_stream_set_state_callback            PULSE_MANGLER(pa_stream_set_state_callback)
     36#define pa_stream_set_underflow_callback        PULSE_MANGLER(pa_stream_set_underflow_callback)
     37#define pa_stream_set_write_callback            PULSE_MANGLER(pa_stream_set_write_callback)
    3338#define pa_stream_flush                         PULSE_MANGLER(pa_stream_flush)
    3439#define pa_stream_drain                         PULSE_MANGLER(pa_stream_drain)
     
    6065#define pa_threaded_mainloop_lock               PULSE_MANGLER(pa_threaded_mainloop_lock)
    6166#define pa_bytes_per_second                     PULSE_MANGLER(pa_bytes_per_second)
     67#define pa_bytes_to_usec                        PULSE_MANGLER(pa_bytes_to_usec)
     68#define pa_usec_to_bytes                        PULSE_MANGLER(pa_usec_to_bytes)
     69#define pa_rtclock_now                          PULSE_MANGLER(pa_rtclock_now)
    6270#define pa_frame_size                           PULSE_MANGLER(pa_frame_size)
    6371#define pa_sample_format_to_string              PULSE_MANGLER(pa_sample_format_to_string)
  • trunk/src/VBox/Devices/Audio/pulse_stubs.c

    r64631 r67951  
    55
    66/*
    7  * Copyright (C) 2006-2016 Oracle Corporation
     7 * Copyright (C) 2006-2017 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    7979                (pa_stream *p),
    8080                (p))
     81PROXY_STUB     (pa_stream_get_latency, int,
     82                (pa_stream *s, pa_usec_t *r_usec, int *negative),
     83                (s, r_usec, negative))
     84PROXY_STUB     (pa_stream_get_timing_info, pa_timing_info*,
     85                (pa_stream *s),
     86                (s))
     87PROXY_STUB      (pa_stream_set_buffer_attr, pa_operation *,
     88                (pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata),
     89                (s, attr, cb, userdata))
    8190PROXY_STUB_VOID(pa_stream_set_state_callback,
    8291                (pa_stream *s, pa_stream_notify_cb_t cb, void *userdata),
     92                (s, cb, userdata))
     93PROXY_STUB_VOID(pa_stream_set_underflow_callback,
     94                (pa_stream *s, pa_stream_notify_cb_t cb, void *userdata),
     95                (s, cb, userdata))
     96PROXY_STUB_VOID(pa_stream_set_write_callback,
     97                (pa_stream *s, pa_stream_request_cb_t cb, void *userdata),
    8398                (s, cb, userdata))
    8499PROXY_STUB     (pa_stream_flush, pa_operation*,
     
    111126                (p))
    112127PROXY_STUB     (pa_context_connect, int,
    113                 (pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api),
     128                (pa_context *c, const char *server, pa_context_flags_t flags,
     129                 const pa_spawn_api *api),
    114130                (c, server, flags, api))
    115131PROXY_STUB_VOID(pa_context_disconnect,
     
    170186                (const pa_sample_spec *spec),
    171187                (spec))
     188PROXY_STUB     (pa_bytes_to_usec, pa_usec_t,
     189                (uint64_t l, const pa_sample_spec *spec),
     190                (l, spec))
     191PROXY_STUB     (pa_usec_to_bytes, size_t,
     192                (pa_usec_t t, const pa_sample_spec *spec),
     193                (t, spec))
     194PROXY_STUB     (pa_rtclock_now, pa_usec_t,
     195                (void),
     196                ())
    172197PROXY_STUB     (pa_frame_size, size_t,
    173198                (const pa_sample_spec *spec),
     
    216241    ELEMENT(pa_stream_unref),
    217242    ELEMENT(pa_stream_get_state),
     243    ELEMENT(pa_stream_get_latency),
     244    ELEMENT(pa_stream_get_timing_info),
     245    ELEMENT(pa_stream_set_buffer_attr),
    218246    ELEMENT(pa_stream_set_state_callback),
     247    ELEMENT(pa_stream_set_underflow_callback),
     248    ELEMENT(pa_stream_set_write_callback),
    219249    ELEMENT(pa_stream_flush),
    220250    ELEMENT(pa_stream_drain),
     
    246276    ELEMENT(pa_threaded_mainloop_lock),
    247277    ELEMENT(pa_bytes_per_second),
     278    ELEMENT(pa_bytes_to_usec),
     279    ELEMENT(pa_usec_to_bytes),
     280    ELEMENT(pa_rtclock_now),
    248281    ELEMENT(pa_frame_size),
    249282    ELEMENT(pa_sample_format_to_string),
     
    296329    return rc;
    297330}
     331
  • trunk/src/VBox/Devices/Audio/pulse_stubs.h

    r62517 r67951  
    55
    66/*
    7  * Copyright (C) 2006-2016 Oracle Corporation
     7 * Copyright (C) 2006-2017 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
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