VirtualBox

Changeset 73809 in vbox for trunk/src/VBox/Devices/Audio


Ignore:
Timestamp:
Aug 22, 2018 8:27:04 AM (6 years ago)
Author:
vboxsync
Message:

Audio/Mixer: Restructured internal sink writing code into multiple functions.

File:
1 edited

Legend:

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

    r73794 r73809  
    7272static int audioMixerSinkSetRecSourceInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
    7373static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink);
     74static int audioMixerSinkMultiplexSync(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWrittenMin);
     75static int audioMixerSinkWriteToStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream);
     76static int audioMixerSinkWriteToStreamEx(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream, uint32_t cbToWrite, uint32_t *pcbWritten);
    7477
    7578int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl);
     
    15651568    /* Number of disabled streams of this sink. */
    15661569    uint8_t cStreamsDisabled = pSink->cStreams;
     1570
     1571    /* Next, try to write (multiplex) as much audio data as possible to all connected mixer streams. */
     1572    uint32_t cbToWriteToStreams = AudioMixBufUsedBytes(&pSink->MixBuf);
     1573
     1574    uint8_t arrChunkBuf[_1K]; /** @todo Hm ... some zero copy / shared buffers would be nice! */
     1575    while (cbToWriteToStreams)
     1576    {
     1577        uint32_t cfChunk;
     1578        rc  = AudioMixBufAcquireReadBlock(&pSink->MixBuf, arrChunkBuf, RT_MIN(cbToWriteToStreams, sizeof(arrChunkBuf)), &cfChunk);
     1579        if (RT_FAILURE(rc))
     1580            break;
     1581
     1582        const uint32_t cbChunk = DrvAudioHlpFramesToBytes(cfChunk, &pSink->PCMProps);
     1583        Assert(cbChunk <= sizeof(arrChunkBuf));
     1584
     1585        /* Multiplex the current chunk in a synchronized fashion to all connected streams. */
     1586        uint32_t cbChunkWrittenMin = 0;
     1587        rc = audioMixerSinkMultiplexSync(pSink, AUDMIXOP_COPY, arrChunkBuf, cbChunk, &cbChunkWrittenMin);
     1588        if (RT_SUCCESS(rc))
     1589        {
     1590            PAUDMIXSTREAM pMixStream;
     1591            RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
     1592            {
     1593                int rc2 = audioMixerSinkWriteToStream(pSink, pMixStream);
     1594                AssertRC(rc2);
     1595            }
     1596        }
     1597
     1598        Log3Func(("[%s] cbChunk=%RU32, cbChunkWrittenMin=%RU32\n", pSink->pszName, cbChunk, cbChunkWrittenMin));
     1599
     1600        AudioMixBufReleaseReadBlock(&pSink->MixBuf, AUDIOMIXBUF_B2F(&pSink->MixBuf, cbChunkWrittenMin));
     1601
     1602        if (   RT_FAILURE(rc)
     1603            || cbChunkWrittenMin == 0)
     1604            break;
     1605
     1606        Assert(cbToWriteToStreams >= cbChunkWrittenMin);
     1607        cbToWriteToStreams -= cbChunkWrittenMin;
     1608    }
     1609
     1610    if (   !(pSink->fStatus & AUDMIXSINK_STS_DIRTY)
     1611        && AudioMixBufUsed(&pSink->MixBuf)) /* Still audio output data left? Consider the sink as being "dirty" then. */
     1612    {
     1613        /* Set dirty bit. */
     1614        pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
     1615    }
    15671616
    15681617    PAUDMIXSTREAM pMixStream, pMixStreamNext;
     
    17091758
    17101759/**
    1711  * Writes audio output data to all connected mixer streams (multiplex).
     1760 * Writes (buffered) output data of a sink's stream to the bound audio connector stream.
     1761 *
     1762 * @returns IPRT status code.
     1763 * @param   pSink               Sink of stream that contains the mixer stream.
     1764 * @param   pMixStream          Mixer stream to write output data for.
     1765 */
     1766static int audioMixerSinkWriteToStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream)
     1767{
     1768    if (!pMixStream->pCircBuf)
     1769        return VINF_SUCCESS;
     1770
     1771    return audioMixerSinkWriteToStreamEx(pSink, pMixStream, RTCircBufUsed(pMixStream->pCircBuf), NULL /* pcbWritten */);
     1772}
     1773
     1774/**
     1775 * Writes (buffered) output data of a sink's stream to the bound audio connector stream, extended version.
     1776 *
     1777 * @returns IPRT status code.
     1778 * @param   pSink               Sink of stream that contains the mixer stream.
     1779 * @param   pMixStream          Mixer stream to write output data for.
     1780 * @param   cbToWrite           Size (in bytes) to write.
     1781 * @param   pcbWritten          Size (in bytes) written on success. Optional.
     1782 */
     1783static int audioMixerSinkWriteToStreamEx(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream, uint32_t cbToWrite, uint32_t *pcbWritten)
     1784{
     1785    /* pcbWritten is optional. */
     1786
     1787    if (   !cbToWrite
     1788        || !DrvAudioHlpStreamStatusCanWrite(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream)))
     1789    {
     1790        if (pcbWritten)
     1791            *pcbWritten = 0;
     1792
     1793        return VINF_SUCCESS;
     1794    }
     1795
     1796    PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
     1797
     1798    const uint32_t cbWritableStream = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
     1799                   cbToWrite        = RT_MIN(cbToWrite, RT_MIN((uint32_t)RTCircBufUsed(pCircBuf), cbWritableStream));
     1800
     1801    Log3Func(("[%s] cbWritableStream=%RU32, cbToWrite=%RU32\n",
     1802              pMixStream->pszName, cbWritableStream, cbToWrite));
     1803
     1804    uint32_t cbWritten = 0;
     1805
     1806    int rc = VINF_SUCCESS;
     1807
     1808    while (cbToWrite)
     1809    {
     1810        void *pvChunk;
     1811        size_t cbChunk;
     1812        RTCircBufAcquireReadBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
     1813
     1814        Log3Func(("[%s] cbChunk=%RU32\n", pMixStream->pszName, cbChunk));
     1815
     1816        uint32_t cbChunkWritten = 0;
     1817        if (cbChunk)
     1818        {
     1819            rc = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvChunk, (uint32_t)cbChunk,
     1820                                                   &cbChunkWritten);
     1821            if (RT_FAILURE(rc))
     1822            {
     1823                if (rc == VERR_BUFFER_OVERFLOW)
     1824                {
     1825                    LogRel2(("Mixer: Buffer overrun for mixer stream '%s' (sink '%s')\n", pMixStream->pszName, pSink->pszName));
     1826                    break;
     1827                }
     1828                else if (rc != VERR_AUDIO_STREAM_NOT_READY)
     1829                    LogRel2(("Mixer: Writing to mixer stream '%s' (sink '%s') failed, rc=%Rrc\n",
     1830                             pMixStream->pszName, pSink->pszName, rc));
     1831
     1832                LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc));
     1833            }
     1834        }
     1835
     1836        RTCircBufReleaseReadBlock(pCircBuf, cbChunkWritten);
     1837
     1838        if (   RT_FAILURE(rc)
     1839            || !cbChunkWritten)
     1840            break;
     1841
     1842        Assert(cbToWrite >= cbChunkWritten);
     1843        cbToWrite -= (uint32_t)cbChunkWritten;
     1844
     1845        cbWritten += (uint32_t)cbChunkWritten;
     1846    }
     1847
     1848    Log3Func(("[%s] cbWritten=%RU32\n", pMixStream->pszName, cbWritten));
     1849
     1850    if (pcbWritten)
     1851        *pcbWritten = cbWritten;
     1852
     1853#ifdef DEBUG_andy
     1854    AssertRC(rc);
     1855#endif
     1856
     1857    return rc;
     1858}
     1859
     1860/**
     1861 * Multiplexes audio output data to all connected mixer streams in a synchronized fashion, e.g.
     1862 * only multiplex as much data as all streams can handle at this time.
    17121863 *
    17131864 * @returns IPRT status code.
     
    17161867 * @param   pvBuf               Pointer to audio data to write.
    17171868 * @param   cbBuf               Size (in bytes) of audio data to write.
    1718  * @param   pcbWrittenMin       Returns minimum size (in bytes) successfully written by all mixer streams. Optional.
    1719  */
    1720 int audioMixerSinkWriteToStreams(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWrittenMin)
    1721 {
     1869 * @param   pcbWrittenMin       Returns minimum size (in bytes) successfully written to all mixer streams. Optional.
     1870 */
     1871static int audioMixerSinkMultiplexSync(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf,
     1872                                       uint32_t *pcbWrittenMin)
     1873{
     1874    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    17221875    RT_NOREF(enmOp);
    17231876
     1877    AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
     1878              ("%s: Can't multiplex to a sink which is not an output sink\n", pSink->pszName));
     1879
    17241880    int rc = VINF_SUCCESS;
    17251881
    1726     uint32_t cbWrittenMin = UINT32_MAX;
     1882    uint32_t cbToWriteMin = UINT32_MAX;
     1883
     1884    Log3Func(("[%s] cbBuf=%RU32\n", pSink->pszName, cbBuf));
    17271885
    17281886    PAUDMIXSTREAM pMixStream;
     
    17321890            continue;
    17331891
    1734         PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
    1735         void *pvChunk;
    1736         size_t cbChunk;
    1737 
    1738         uint32_t cbWrittenBuf = 0;
    1739         uint32_t cbToWriteBuf = RT_MIN(cbBuf, (uint32_t)RTCircBufFree(pCircBuf));
    1740         while (cbToWriteBuf)
    1741         {
    1742             RTCircBufAcquireWriteBlock(pCircBuf, cbToWriteBuf, &pvChunk, &cbChunk);
    1743 
    1744             if (cbChunk)
    1745                 memcpy(pvChunk, (uint8_t *)pvBuf + cbWrittenBuf, cbChunk);
    1746 
    1747             RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
    1748 
    1749             cbWrittenBuf += (uint32_t)cbChunk;
    1750             Assert(cbWrittenBuf <= cbBuf);
    1751 
    1752             Assert(cbToWriteBuf >= cbChunk);
    1753             cbToWriteBuf -= (uint32_t)cbChunk;
    1754         }
    1755 
    1756         cbWrittenMin = RT_MIN(cbWrittenMin, cbWrittenBuf);
    1757 
    1758         if (cbWrittenBuf) /* Update the mixer stream's last written time stamp. */
    1759             pMixStream->tsLastReadWrittenNs = RTTimeNanoTS();
    1760 
    1761         uint32_t cbToWriteStream = DrvAudioHlpBytesAlign(cbWrittenBuf, &pSink->PCMProps);
    1762         uint32_t cbWrittenStream = 0;
    1763 
    1764         int rc2 = VINF_SUCCESS;
    1765 
    1766         while (cbToWriteStream)
    1767         {
    1768             RTCircBufAcquireReadBlock(pCircBuf, cbToWriteStream, &pvChunk, &cbChunk);
    1769 
    1770             uint32_t cbChunkWritten = 0;
    1771             if (cbChunk)
     1892        cbToWriteMin = RT_MIN(cbBuf, RT_MIN(cbToWriteMin, RTCircBufFree(pMixStream->pCircBuf)));
     1893    }
     1894
     1895    if (cbToWriteMin == UINT32_MAX) /* No space at all? */
     1896        cbToWriteMin = 0;
     1897
     1898    if (cbToWriteMin)
     1899    {
     1900        RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
     1901        {
     1902            PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
     1903            void *pvChunk;
     1904            size_t cbChunk;
     1905
     1906            uint32_t cbWrittenBuf = 0;
     1907            uint32_t cbToWriteBuf = cbToWriteMin;
     1908
     1909            while (cbToWriteBuf)
    17721910            {
    1773                 rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvChunk, (uint32_t)cbChunk,
    1774                                                         &cbChunkWritten);
    1775                 if (RT_FAILURE(rc2))
    1776                 {
    1777                     if (rc2 == VERR_BUFFER_OVERFLOW)
    1778                     {
    1779                         LogRel2(("Mixer: Buffer overrun for mixer stream '%s' (sink '%s')\n", pMixStream->pszName, pSink->pszName));
    1780 #ifdef DEBUG_andy
    1781                         AssertRC(rc2);
    1782 #endif
    1783                     }
    1784                     else if (rc2 != VERR_AUDIO_STREAM_NOT_READY)
    1785                         LogRel2(("Mixer: Writing to mixer stream '%s' (sink '%s') failed, rc=%Rrc\n", pMixStream->pszName, pSink->pszName, rc2));
    1786 
    1787                     LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc2));
    1788                 }
     1911                RTCircBufAcquireWriteBlock(pCircBuf, cbToWriteBuf, &pvChunk, &cbChunk);
     1912
     1913                if (cbChunk)
     1914                    memcpy(pvChunk, (uint8_t *)pvBuf + cbWrittenBuf, cbChunk);
     1915
     1916                RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
     1917
     1918                cbWrittenBuf += (uint32_t)cbChunk;
     1919                Assert(cbWrittenBuf <= cbBuf);
     1920
     1921                Assert(cbToWriteBuf >= cbChunk);
     1922                cbToWriteBuf -= (uint32_t)cbChunk;
    17891923            }
    17901924
    1791             RTCircBufReleaseReadBlock(pCircBuf, cbChunkWritten);
    1792 
    1793             if (RT_FAILURE(rc2))
    1794                 break;
    1795 
    1796             Assert(cbToWriteStream >= cbChunk);
    1797             cbToWriteStream -= (uint32_t)cbChunk;
    1798 
    1799             cbWrittenStream += (uint32_t)cbChunk;
    1800             Assert(cbWrittenStream <= cbBuf);
    1801         }
    1802 
    1803         Log3Func(("[%s] cbBuf=%RU32, cbWrittenBuf=%RU32, cbWrittenStream=%RU32\n",
    1804                   pMixStream->pszName, cbBuf, cbWrittenBuf, cbWrittenStream));
    1805     }
    1806 
    1807     if (cbWrittenMin == UINT32_MAX) /* Nothing written? */
    1808         cbWrittenMin = 0;
     1925            if (cbWrittenBuf) /* Update the mixer stream's last written time stamp. */
     1926                pMixStream->tsLastReadWrittenNs = RTTimeNanoTS();
     1927        }
     1928    }
     1929
     1930    Log3Func(("[%s] cbBuf=%RU32, cbToWriteMin=%RU32\n", pSink->pszName, cbBuf, cbToWriteMin));
    18091931
    18101932    if (pcbWrittenMin)
    1811         *pcbWrittenMin = cbWrittenMin;
     1933        *pcbWrittenMin = cbToWriteMin;
    18121934
    18131935    return rc;
     
    18411963              ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
    18421964
    1843 #ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
     1965    Assert(cbBuf <= AudioMixBufFreeBytes(&pSink->MixBuf));
     1966
    18441967    uint32_t cbWritten = 0;
    18451968    uint32_t cbToWrite = cbBuf;
     
    18601983    }
    18611984
    1862     Assert(cbWritten <= cbBuf);
    1863 
    1864     if (   RT_FAILURE(rc)
    1865         || cbWritten < cbBuf)
    1866     {
    1867         LogRel2(("Mixer: Buffer overrun for mixer sink '%s' (only %RU32/%RU32 bytes written)\n",
    1868                  pSink->pszName, cbWritten, cbBuf));
    1869 # ifdef DEBUG_andy
    1870         AssertFailed();
    1871 # endif
    1872     }
    1873 
    1874     /* Next, try to write (multiplex) as much audio data as possible to all connected mixer streams. */
    1875     uint8_t arrChunkBuf[_1K]; /** @todo Hm ... some zero copy / shared buffers would be nice! */
    1876     while (cbWritten)
    1877     {
    1878         uint32_t cfChunk;
    1879         rc  = AudioMixBufAcquireReadBlock(&pSink->MixBuf, arrChunkBuf, sizeof(arrChunkBuf), &cfChunk);
    1880         if (RT_FAILURE(rc))
    1881             break;
    1882 
    1883         const uint32_t cbChunk = DrvAudioHlpFramesToBytes(cfChunk, &pSink->PCMProps);
    1884         Assert(cbChunk <= sizeof(arrChunkBuf));
    1885         rc = audioMixerSinkWriteToStreams(pSink, enmOp, arrChunkBuf, cbChunk, NULL /* pcbWrittenMin */);
    1886         AudioMixBufReleaseReadBlock(&pSink->MixBuf, cfChunk);
    1887 
    1888         Assert(cbWritten >= cbChunk);
    1889         cbWritten -= cbChunk;
    1890     }
    1891 
    1892     if (   !(pSink->fStatus & AUDMIXSINK_STS_DIRTY)
    1893         && AudioMixBufUsed(&pSink->MixBuf)) /* Still audio output data left? Consider the sink as being "dirty" then. */
    1894     {
    1895         /* Set dirty bit. */
    1896         pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
    1897     }
    1898 #else
    1899     rc = audioMixerSinkWriteToStreams(pSink, enmOp, pvBuf, cbBuf, NULL /* pcbWrittenMin */);
    1900 #endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
     1985    Assert(cbWritten == cbBuf);
    19011986
    19021987    /* Update the sink's last written time stamp. */
     
    19041989
    19051990    if (pcbWritten)
    1906         *pcbWritten = cbBuf; /* Always report everything written, as the backends need to keep up themselves. */
     1991        *pcbWritten = cbWritten;
    19071992
    19081993    int rc2 = RTCritSectLeave(&pSink->CritSect);
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