Repository: xbmc/xbmc
File: xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp (C++)

Code before:
      // Per MS doc, Discontinuity() may not take effect until after the entire buffer queue is
      // consumed, which wouldn't invoke the callback or reset the voice stats.
      // Solution: submit a 1 sample buffer with end of stream flag and wait for StreamEnd callback
      // The StreamEnd event is manual reset so that it cannot be missed even if raised before this
      // code starts waiting for it

      AddEndOfStreamPacket();

      constexpr uint32_t waitSafety = 100; // extra ms wait in case of scheduler issue
      DWORD waitRc =
          WaitForSingleObject(m_voiceCallback.m_StreamEndEvent,
                              m_framesInBuffers * 1000 / m_format.m_sampleRate + waitSafety);

      if (waitRc != WAIT_OBJECT_0)
      {
        if (waitRc == WAIT_FAILED)
        {
          //! @todo use FormatMessage for a human readable error message
          CLog::LogF(LOGERROR,
                     "error WAIT_FAILED while waiting for queued buffers to drain. GetLastError:{}",
                     GetLastError());
        }
        else
        {
          CLog::LogF(LOGERROR, "error {} while waiting for queued buffers to drain.", waitRc);
        }
      }

      m_sourceVoice->Stop();
      ResetEvent(m_voiceCallback.m_StreamEndEvent);

      m_sinkFrames = 0;
      m_framesInBuffers = 0;
    }
    catch (...)
    {
      CLog::Log(LOGERROR, "{}: Invalidated source voice - Releasing", __FUNCTION__);
    }
  }
  m_running = false;
}

bool CAESinkXAudio::IsUSBDevice()
{
#if 0 // TODO
  IPropertyStore *pProperty = nullptr;
  PROPVARIANT varName;
  PropVariantInit(&varName);
  bool ret = false;

  HRESULT hr = m_pDevice->OpenPropertyStore(STGM_READ, &pProperty);

Review comment (suggestion):
If your custom eof signal comes at the end of.thr already queued buffer all fine. If it would be first, stop might cause a chop.

Code after:
  return true;
}

void CAESinkXAudio::Drain()
{
  if(!m_sourceVoice)
    return;

  if (m_running)
  {
    try
    {
      // Contrary to MS doc, the voice must play a buffer with end of stream flag for the voice
      // SamplesPlayed counter to be reset.
      // Per MS doc, Discontinuity() may not take effect until after the entire buffer queue is
      // consumed, which wouldn't invoke the callback or reset the voice stats.
      // Solution: submit a 1 sample buffer with end of stream flag and wait for StreamEnd callback
      // The StreamEnd event is manual reset so that it cannot be missed even if raised before this
      // code starts waiting for it

      AddEndOfStreamPacket();

      constexpr uint32_t waitSafety = 100; // extra ms wait in case of scheduler issue
      DWORD waitRc =
          WaitForSingleObject(m_voiceCallback.m_StreamEndEvent,
                              m_framesInBuffers * 1000 / m_format.m_sampleRate + waitSafety);

      if (waitRc != WAIT_OBJECT_0)
      {
        if (waitRc == WAIT_FAILED)
        {
          //! @todo use FormatMessage for a human readable error message
          CLog::LogF(LOGERROR,
                     "error WAIT_FAILED while waiting for queued buffers to drain. GetLastError:{}",
                     GetLastError());
        }
        else
        {
          CLog::LogF(LOGERROR, "error {} while waiting for queued buffers to drain.", waitRc);
        }
      }

      m_sourceVoice->Stop();
      ResetEvent(m_voiceCallback.m_StreamEndEvent);

      m_sinkFrames = 0;
      m_framesInBuffers = 0;
    }
    catch (...)
    {
      CLog::Log(LOGERROR, "{}: Invalidated source voice - Releasing", __FUNCTION__);