Esempio n. 1
0
void CAESinkPULSE::GetDelay(AEDelayStatus& status)
{
  if (!m_IsAllocated)
  {
    status.SetDelay(0);
    return;
  }

  pa_threaded_mainloop_lock(m_MainLoop);
  const pa_timing_info* pti = pa_stream_get_timing_info(m_Stream);
  // only incorporate local sink delay + internal PA transport delay
  double sink_delay = (pti->configured_sink_usec / 1000000.0);
  double transport_delay = pti->transport_usec / 1000000.0;

  uint64_t diff = CurrentHostCounter() - m_lastPackageStamp;
  unsigned int bytes_played = (unsigned int) ((double) diff * (double) m_BytesPerSecond  / (double) CurrentHostFrequency() + 0.5);

  int buffer_delay = m_filled_bytes - bytes_played;
  if (buffer_delay < 0)
    buffer_delay = 0;

  pa_threaded_mainloop_unlock(m_MainLoop);

  double delay = buffer_delay / (double) m_BytesPerSecond + sink_delay + transport_delay;
  status.SetDelay(delay);
}
Esempio n. 2
0
unsigned int CActiveAESink::OutputSamples(CSampleBuffer* samples)
{
  uint8_t **buffer = samples->pkt->data;
  unsigned int frames = samples->pkt->nb_samples;
  unsigned int maxFrames;
  int retry = 0;
  unsigned int written = 0;

  switch(m_swapState)
  {
  case SKIP_SWAP:
    break;
  case NEED_BYTESWAP:
    Endian_Swap16_buf((uint16_t *)buffer[0], (uint16_t *)buffer[0], frames * samples->pkt->config.channels);
    break;
  case CHECK_SWAP:
    SwapInit(samples);
    if (m_swapState == NEED_BYTESWAP)
      Endian_Swap16_buf((uint16_t *)buffer[0], (uint16_t *)buffer[0], frames * samples->pkt->config.channels);
    break;
  default:
    break;
  }

  AEDelayStatus status;

  while(frames > 0)
  {
    maxFrames = std::min(frames, m_sinkFormat.m_frames);
    written = m_sink->AddPackets(buffer, maxFrames, samples->pkt->nb_samples-frames);
    if (written == 0)
    {
      Sleep(500*m_sinkFormat.m_frames/m_sinkFormat.m_sampleRate);
      retry++;
      if (retry > 4)
      {
        m_extError = true;
        CLog::Log(LOGERROR, "CActiveAESink::OutputSamples - failed");
        status.SetDelay(0);
        m_stats->UpdateSinkDelay(status, frames);
        return 0;
      }
      else
        continue;
    }
    else if (written > maxFrames)
    {
      m_extError = true;
      CLog::Log(LOGERROR, "CActiveAESink::OutputSamples - sink returned error");
      status.SetDelay(0);
      m_stats->UpdateSinkDelay(status, samples->pool ? maxFrames : 0);
      return 0;
    }
    frames -= written;

    m_sink->GetDelay(status);
    m_stats->UpdateSinkDelay(status, samples->pool ? written : 0);
  }
  return status.delay;
}
Esempio n. 3
0
void CAESinkPULSE::GetDelay(AEDelayStatus& status)
{
  if (!m_IsAllocated)
  {
    status.SetDelay(0);
    return;
  }
  int error = 0;
  pa_usec_t latency = (pa_usec_t) -1;
  pa_threaded_mainloop_lock(m_MainLoop);
  if ((error = pa_stream_get_latency(m_Stream, &latency, NULL)) < 0)
  {
    if (error == -PA_ERR_NODATA)
    {
      WaitForOperation(pa_stream_update_timing_info(m_Stream, NULL,NULL), m_MainLoop, "Update Timing Information");
      if ((error = pa_stream_get_latency(m_Stream, &latency, NULL)) < 0)
      {
        CLog::Log(LOGDEBUG, "GetDelay - Failed to get Latency %d", error); 
      }
    }
  }
  if (error < 0 )
    latency = (pa_usec_t) 0;

  pa_threaded_mainloop_unlock(m_MainLoop);
  status.SetDelay(latency / 1000000.0);
}
Esempio n. 4
0
void CAESinkPi::Drain()
{
  AEDelayStatus status;
  GetDelay(status);
  int delay = (int)(status.GetDelay() * 1000.0);
  if (delay)
    Sleep(delay);
  CLog::Log(LOGDEBUG, "%s:%s delay:%dms now:%dms", CLASSNAME, __func__, delay, (int)(status.GetDelay() * 1000.0));
}
Esempio n. 5
0
unsigned int CAESinkPi::AddPackets(uint8_t **data, unsigned int frames, unsigned int offset)
{
  if (!m_Initialized || !m_omx_output || !frames)
  {
    Sleep(10);
    return frames;
  }
  OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
  OMX_BUFFERHEADERTYPE *omx_buffer = NULL;

  unsigned int channels    = m_format.m_channelLayout.Count();
  unsigned int sample_size = CAEUtil::DataFormatToBits(m_format.m_dataFormat) >> 3;
  const int planes = AE_IS_PLANAR(m_format.m_dataFormat) ? channels : 1;
  const int chans  = AE_IS_PLANAR(m_format.m_dataFormat) ? 1 : channels;
  const int pitch  = chans * sample_size;

  AEDelayStatus status;
  GetDelay(status);
  double delay = status.GetDelay();
  if (delay <= 0.0 && m_submitted)
    CLog::Log(LOGNOTICE, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);

  omx_buffer = m_omx_output->GetInputBuffer(1000);
  if (omx_buffer == NULL)
  {
    CLog::Log(LOGERROR, "CAESinkPi::AddPackets timeout");
    return 0;
  }

  omx_buffer->nFilledLen = frames * m_format.m_frameSize;
  // must be true
  assert(omx_buffer->nFilledLen <= omx_buffer->nAllocLen);
  omx_buffer->nTimeStamp = ToOMXTime(0);
  omx_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;

  if (omx_buffer->nFilledLen)
  {
    int planesize = omx_buffer->nFilledLen / planes;
    for (int i=0; i < planes; i++)
      memcpy((uint8_t *)omx_buffer->pBuffer + i * planesize, data[i] + offset * pitch, planesize);
  }
  omx_err = m_omx_output->EmptyThisBuffer(omx_buffer);
  if (omx_err != OMX_ErrorNone)
  {
    CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err);
    m_omx_output->DecoderEmptyBufferDone(m_omx_output->GetComponent(), omx_buffer);
  }
  m_submitted++;
  GetDelay(status);
  delay = status.GetDelay();
  if (delay > m_latency)
    Sleep((int)(1000.0f * (delay - m_latency)));
  return frames;
}
Esempio n. 6
0
void CAESinkDirectSound::GetDelay(AEDelayStatus& status)
{
  if (!m_initialized)
  {
    status.SetDelay(0);
    return;
  }

  /* Make sure we know how much data is in the cache */
  if (!UpdateCacheStatus())
    m_isDirtyDS = true;

  /** returns current cached data duration in seconds */
  status.SetDelay((double)m_CacheLen / (double)m_AvgBytesPerSec);
}
Esempio n. 7
0
void CAESinkOSS::GetDelay(AEDelayStatus& status)
{
  if (m_fd == -1)
  {
    status.SetDelay(0);
    return;
  }
  
  int delay;
  if (ioctl(m_fd, SNDCTL_DSP_GETODELAY, &delay) == -1)
  {
    status.SetDelay(0);
    return;
  }

  status.SetDelay((double)delay / (m_format.m_frameSize * m_format.m_sampleRate));
}
Esempio n. 8
0
void CAESinkAUDIOTRACK::GetDelay(AEDelayStatus& status)
{
  if (!m_at_jni)
  {
    status.SetDelay(0);
    return;
  }

  // In their infinite wisdom, Google decided to make getPlaybackHeadPosition
  // return a 32bit "int" that you should "interpret as unsigned."  As such,
  // for wrap saftey, we need to do all ops on it in 32bit integer math.
  uint32_t head_pos = (uint32_t)m_at_jni->getPlaybackHeadPosition();

  double delay = (double)(m_frames_written - head_pos) / m_format.m_sampleRate;

  status.SetDelay(delay);
}
Esempio n. 9
0
void CAESinkPULSE::GetDelay(AEDelayStatus& status)
{
  if (!m_IsAllocated)
  {
    status.SetDelay(0);
    return;
  }

  pa_threaded_mainloop_lock(m_MainLoop);
  pa_usec_t r_usec;
  int negative;

  if (pa_stream_get_latency(m_Stream, &r_usec, &negative) < 0)
    r_usec = 0;

  pa_threaded_mainloop_unlock(m_MainLoop);
  status.SetDelay(r_usec / 1000000.0);
}
Esempio n. 10
0
void CAESinkPi::GetDelay(AEDelayStatus& status)
{
  OMX_PARAM_U32TYPE param;
  OMX_INIT_STRUCTURE(param);

  if (!m_Initialized)
  {
    status.SetDelay(0);
    return;
  }

  param.nPortIndex = m_omx_render.GetInputPort();

  OMX_ERRORTYPE omx_err = m_omx_render.GetConfig(OMX_IndexConfigAudioRenderingLatency, &param);

  if (omx_err != OMX_ErrorNone)
  {
    CLog::Log(LOGERROR, "%s::%s - error getting OMX_IndexConfigAudioRenderingLatency error 0x%08x",
      CLASSNAME, __func__, omx_err);
  }
  double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * param.nU32 * m_format.m_frameSize;
  status.SetDelay(sinkbuffer_seconds_to_empty);
}
Esempio n. 11
0
void CAESinkSNDIO::GetDelay(AEDelayStatus& status)
{
  unsigned int frameSize = m_par.bps * m_par.pchan;
  double delay = 1.0 * ((m_written / frameSize) - m_played) / m_par.rate;
  status.SetDelay(delay);
}
Esempio n. 12
0
double CActiveAEStream::GetDelay()
{
  AEDelayStatus status;
  AE.GetDelay(status, this);
  return status.GetDelay();
}
Esempio n. 13
0
unsigned int CActiveAESink::OutputSamples(CSampleBuffer* samples)
{
  uint8_t **buffer = samples->pkt->data;
  uint8_t *packBuffer;
  unsigned int frames = samples->pkt->nb_samples;
  unsigned int totalFrames = frames;
  unsigned int maxFrames;
  int retry = 0;
  unsigned int written = 0;
  std::unique_ptr<uint8_t[]> mergebuffer;
  uint8_t* p_mergebuffer = NULL;
  AEDelayStatus status;

  if (m_requestedFormat.m_dataFormat == AE_FMT_RAW)
  {
    if (m_requestedFormat.m_streamInfo.m_IECPacked)
    {
      if (frames > 0)
      {
        m_packer->Reset();
        if (m_sinkFormat.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_TRUEHD)
        {
          if (frames == 61440)
          {
            int offset;
            int len;
            m_packer->GetBuffer();
            for (int i=0; i<24; i++)
            {
              offset = i*2560;
              len = (*(buffer[0] + offset+2560-2) << 8) + *(buffer[0] + offset+2560-1);
              m_packer->Pack(m_sinkFormat.m_streamInfo, buffer[0] + offset, len);
            }
          }
          else
          {
            m_extError = true;
            CLog::Log(LOGERROR, "CActiveAESink::OutputSamples - incomplete TrueHD buffer");
            return 0;
          }
        }
        else
          m_packer->Pack(m_sinkFormat.m_streamInfo, buffer[0], frames);
      }
      else if (samples->pkt->pause_burst_ms > 0)
      {
        // construct a pause burst
        m_packer->PackPause(m_sinkFormat.m_streamInfo, samples->pkt->pause_burst_ms);
      }
      else
        m_packer->Reset();

      unsigned int size = m_packer->GetSize();
      packBuffer = m_packer->GetBuffer();
      buffer = &packBuffer;
      totalFrames = size / m_sinkFormat.m_frameSize;
      frames = totalFrames;

      switch(m_swapState)
      {
        case SKIP_SWAP:
          break;
        case NEED_BYTESWAP:
          Endian_Swap16_buf((uint16_t *)buffer[0], (uint16_t *)buffer[0], size / 2);
          break;
        case CHECK_SWAP:
          SwapInit(samples);
          if (m_swapState == NEED_BYTESWAP)
            Endian_Swap16_buf((uint16_t *)buffer[0], (uint16_t *)buffer[0], size / 2);
          break;
        default:
          break;
      }
    }
    else
    {
      if (m_sinkFormat.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_TRUEHD && frames == 61440)
      {
        int offset;
        int len;
        unsigned int size = 0;
        mergebuffer.reset(new uint8_t[MAX_IEC61937_PACKET]);
        p_mergebuffer = mergebuffer.get();
        for (int i=0; i<24; i++)
        {
          offset = i*2560;
          len = (*(buffer[0] + offset+2560-2) << 8) + *(buffer[0] + offset+2560-1);
          memcpy(&(mergebuffer.get())[size], buffer[0] + offset, len);
          size += len;
        }
        buffer = &p_mergebuffer;
        totalFrames = size / m_sinkFormat.m_frameSize;
        frames = totalFrames;
      }
      if (samples->pkt->pause_burst_ms > 0)
      {
        m_sink->AddPause(samples->pkt->pause_burst_ms);
        m_sink->GetDelay(status);
        m_stats->UpdateSinkDelay(status, samples->pool ? 1 : 0);
        return status.delay * 1000;
      }
    }
  }

  int framesOrPackets;

  while (frames > 0)
  {
    maxFrames = std::min(frames, m_sinkFormat.m_frames);
    written = m_sink->AddPackets(buffer, maxFrames, totalFrames - frames);
    if (written == 0)
    {
      Sleep(500*m_sinkFormat.m_frames/m_sinkFormat.m_sampleRate);
      retry++;
      if (retry > 4)
      {
        m_extError = true;
        CLog::Log(LOGERROR, "CActiveAESink::OutputSamples - failed");
        status.SetDelay(0);
        framesOrPackets = frames;
        if (m_requestedFormat.m_dataFormat == AE_FMT_RAW)
          framesOrPackets = 1;
        m_stats->UpdateSinkDelay(status, samples->pool ? framesOrPackets : 0);
        return 0;
      }
      else
        continue;
    }
    else if (written > maxFrames)
    {
      m_extError = true;
      CLog::Log(LOGERROR, "CActiveAESink::OutputSamples - sink returned error");
      status.SetDelay(0);
      framesOrPackets = frames;
      if (m_requestedFormat.m_dataFormat == AE_FMT_RAW)
        framesOrPackets = 1;
      m_stats->UpdateSinkDelay(status, samples->pool ? framesOrPackets : 0);
      return 0;
    }
    frames -= written;

    m_sink->GetDelay(status);

    if (m_requestedFormat.m_dataFormat != AE_FMT_RAW)
      m_stats->UpdateSinkDelay(status, samples->pool ? written : 0);
  }

  if (m_requestedFormat.m_dataFormat == AE_FMT_RAW)
    m_stats->UpdateSinkDelay(status, samples->pool ? 1 : 0);

  return status.delay * 1000;
}
Esempio n. 14
0
void CAESinkAUDIOTRACK::GetDelay(AEDelayStatus& status)
{
  if (!m_at_jni)
  {
    status.SetDelay(0);
    return;
  }

  // In their infinite wisdom, Google decided to make getPlaybackHeadPosition
  // return a 32bit "int" that you should "interpret as unsigned."  As such,
  // for wrap safety, we need to do all ops on it in 32bit integer math.

  uint32_t head_pos = (uint32_t)m_at_jni->getPlaybackHeadPosition();

  // Wraparound
  if ((uint32_t)(m_headPos & UINT64_LOWER_BYTES) > head_pos) // need to compute wraparound
    m_headPos += (1ULL << 32); // add wraparound, e.g. 0x0000 FFFF FFFF -> 0x0001 FFFF FFFF
  // clear lower 32 bit values, e.g. 0x0001 FFFF FFFF -> 0x0001 0000 0000
  // and add head_pos which wrapped around, e.g. 0x0001 0000 0000 -> 0x0001 0000 0004
  m_headPos = (m_headPos & UINT64_UPPER_BYTES) | (uint64_t)head_pos;

  // head_pos does not necessarily start at the beginning
  if (m_offset == -1 && m_at_jni->getPlayState() == CJNIAudioTrack::PLAYSTATE_PLAYING)
  {
    m_offset = m_headPos;
  }

  if (m_offset != -1 && (uint64_t) m_offset > m_headPos)
  {
    CLog::Log(LOGDEBUG, "You did it wrong man - fully wrong! offset %lld head pos %llu", m_offset, m_headPos);
    m_offset = 0;
  }

  // we might not yet be running here, but we need m_offset to track first PT fillup
  uint64_t normHead_pos = m_headPos;
  if (m_offset > 0)
    m_headPos -= m_offset;

  // this makes EAC3 working even when AML is not enabled
  if (aml_present() && m_info.m_wantsIECPassthrough &&
      (m_encoding == CJNIAudioFormat::ENCODING_DTS_HD ||
       m_encoding == CJNIAudioFormat::ENCODING_E_AC3 ||
       m_encoding == CJNIAudioFormat::ENCODING_DOLBY_TRUEHD))
    normHead_pos /= m_sink_frameSize;  // AML wants sink in 48k but returns pos in 192k

  if (m_passthrough && !m_info.m_wantsIECPassthrough)
  {
    if (m_extTimer.MillisLeft() > 0)
    {
      const double d = GetMovingAverageDelay(GetCacheTotal());
      status.SetDelay(d);
      return;
    }
  }

  double gone = (double) normHead_pos / (double) m_sink_sampleRate;

  // if sink is run dry without buffer time written anymore
  if (gone > m_duration_written)
    gone = m_duration_written;

  double delay = m_duration_written - gone;
  if (delay < 0)
    delay = 0;

  const double d = GetMovingAverageDelay(delay);

  status.SetDelay(d);
}
Esempio n. 15
0
void CAESinkAUDIOTRACK::GetDelay(AEDelayStatus& status)
{
  if (!m_at_jni)
  {
    status.SetDelay(0);
    return;
  }

  // In their infinite wisdom, Google decided to make getPlaybackHeadPosition
  // return a 32bit "int" that you should "interpret as unsigned."  As such,
  // for wrap saftey, we need to do all ops on it in 32bit integer math.

  uint32_t head_pos = (uint32_t)m_at_jni->getPlaybackHeadPosition();

  // head_pos does not necessarily start at the beginning
  if (m_offset == -1 && m_at_jni->getPlayState() == CJNIAudioTrack::PLAYSTATE_PLAYING)
  {
    CLog::Log(LOGDEBUG, "Offset updated to %u", head_pos);
    m_offset = head_pos;
  }

  if (m_offset > head_pos)
  {
    CLog::Log(LOGDEBUG, "You did it wrong man - fully wrong! offset %lld head pos %u", m_offset, head_pos);
    m_offset = 0;
  }
  uint32_t normHead_pos = head_pos - m_offset;

  if (m_passthrough && !m_info.m_wantsIECPassthrough)
  {
    if (m_pause_time > 0)
    {
      const double d = GetMovingAverageDelay(GetCacheTotal());
      CLog::Log(LOGDEBUG, "Faking Delay: smooth %lf measured: %lf", d * 1000, GetCacheTotal() * 1000);
      status.SetDelay(d);
      return;
    }
  }
  if (normHead_pos > m_lastPlaybackHeadPosition)
  {
    unsigned int differencehead = normHead_pos - m_lastPlaybackHeadPosition;
    CLog::Log(LOGDEBUG, "Sink advanced: %u", differencehead);
    m_lastPlaybackHeadPosition = normHead_pos;
  }

  double gone = (double) normHead_pos / (double) m_sink_sampleRate;

  // if sink is run dry without buffer time written anymore
  if (gone > m_duration_written)
    gone = m_duration_written;

  double delay = m_duration_written - gone;
  if (delay < 0)
    delay = 0;

  const double d = GetMovingAverageDelay(delay);

  CLog::Log(LOGDEBUG, "Calculations duration written: %lf sampleRate: %u gone: %lf", m_duration_written, m_sink_sampleRate, gone);

  bool playing = m_at_jni->getPlayState() == CJNIAudioTrack::PLAYSTATE_PLAYING;

  CLog::Log(LOGDEBUG, "Current-Delay: smoothed: %lf measured: %lf Head Pos: %u Playing: %s", d * 1000, delay * 1000,
                       normHead_pos, playing ? "yes" : "no");

  status.SetDelay(d);
}
Esempio n. 16
0
void CAESinkAUDIOTRACK::GetDelay(AEDelayStatus& status)
{
  if (!m_at_jni)
  {
    status.SetDelay(0);
    return;
  }

  // In their infinite wisdom, Google decided to make getPlaybackHeadPosition
  // return a 32bit "int" that you should "interpret as unsigned."  As such,
  // for wrap saftey, we need to do all ops on it in 32bit integer math.

  uint32_t head_pos = (uint32_t)m_at_jni->getPlaybackHeadPosition();

  // head_pos does not necessarily start at the beginning
  if (m_offset == -1 && m_at_jni->getPlayState() == CJNIAudioTrack::PLAYSTATE_PLAYING)
  {
    m_offset = head_pos;
  }

  if (m_offset > head_pos)
  {
    CLog::Log(LOGDEBUG, "You did it wrong man - fully wrong! offset %lld head pos %u", m_offset, head_pos);
    m_offset = 0;
  }
  uint32_t normHead_pos = head_pos - m_offset;

#if defined(HAS_LIBAMCODEC)
  if (aml_present() &&
      (m_encoding == CJNIAudioFormat::ENCODING_DTS_HD ||
       m_encoding == CJNIAudioFormat::ENCODING_E_AC3 ||
       m_encoding == CJNIAudioFormat::ENCODING_DOLBY_TRUEHD))
    normHead_pos /= m_sink_frameSize;  // AML wants sink in 48k but returns pos in 192k
#endif

  if (m_passthrough && !m_info.m_wantsIECPassthrough)
  {
    if (m_extTimer.MillisLeft() > 0)
    {
      const double d = GetMovingAverageDelay(GetCacheTotal());
      status.SetDelay(d);
      return;
    }
  }
  if (normHead_pos > m_lastPlaybackHeadPosition)
  {
    unsigned int differencehead = normHead_pos - m_lastPlaybackHeadPosition;
    m_lastPlaybackHeadPosition = normHead_pos;
  }

  double gone = (double) normHead_pos / (double) m_sink_sampleRate;

  // if sink is run dry without buffer time written anymore
  if (gone > m_duration_written)
    gone = m_duration_written;

  double delay = m_duration_written - gone;
  if (delay < 0)
    delay = 0;

  const double d = GetMovingAverageDelay(delay);

  status.SetDelay(d);
}
Esempio n. 17
0
void CAESinkIntelSMD::GetDelay(AEDelayStatus& status)
{
  status.SetDelay(GetCacheTime());
}