示例#1
0
bool CAESinkDirectSound::Initialize(AEAudioFormat &format, std::string &device)
{
  if (m_initialized)
    return false;

  LPGUID deviceGUID = NULL;
  RPC_CSTR wszUuid  = NULL;
  HRESULT hr;
  std::list<DSDevice> DSDeviceList;
  std::string deviceFriendlyName;
  DirectSoundEnumerate(DSEnumCallback, &DSDeviceList);

  for (std::list<DSDevice>::iterator itt = DSDeviceList.begin(); itt != DSDeviceList.end(); itt++)
  {
    if ((*itt).lpGuid)
    {
      hr = (UuidToString((*itt).lpGuid, &wszUuid));
      std::string sztmp = (char*)wszUuid;
      std::string szGUID = "{" + std::string(sztmp.begin(), sztmp.end()) + "}";
      if (strcasecmp(szGUID.c_str(), device.c_str()) == 0)
      {
        deviceGUID = (*itt).lpGuid;
        deviceFriendlyName = (*itt).name.c_str();
        break;
      }
    }
  if (hr == RPC_S_OK) RpcStringFree(&wszUuid);
  }

 hr = DirectSoundCreate(deviceGUID, &m_pDSound, NULL);

  if (FAILED(hr))
  {
    CLog::Log(LOGERROR, __FUNCTION__": Failed to create the DirectSound device.");
    CLog::Log(LOGERROR, __FUNCTION__": DSErr: %s", dserr2str(hr));
    return false;
  }

  HWND tmp_hWnd;

  /* Dodge the null handle on first init by using desktop handle */
  if (g_hWnd == NULL)
    tmp_hWnd = GetDesktopWindow();
  else
    tmp_hWnd = g_hWnd;

  CLog::Log(LOGDEBUG, __FUNCTION__": Using Window handle: %d", tmp_hWnd);

  hr = m_pDSound->SetCooperativeLevel(tmp_hWnd, DSSCL_PRIORITY);

  if (FAILED(hr))
  {
    CLog::Log(LOGERROR, __FUNCTION__": Failed to create the DirectSound device cooperative level.");
    CLog::Log(LOGERROR, __FUNCTION__": DSErr: %s", dserr2str(hr));
    m_pDSound->Release();
    return false;
  }

  WAVEFORMATEXTENSIBLE wfxex = {0};

  //fill waveformatex
  ZeroMemory(&wfxex, sizeof(WAVEFORMATEXTENSIBLE));
  wfxex.Format.cbSize          = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
  wfxex.Format.nChannels       = format.m_channelLayout.Count();
  wfxex.Format.nSamplesPerSec  = format.m_sampleRate;
  if (AE_IS_RAW(format.m_dataFormat))
  {
    wfxex.dwChannelMask          = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
    wfxex.Format.wFormatTag      = WAVE_FORMAT_DOLBY_AC3_SPDIF;
    wfxex.SubFormat              = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
    wfxex.Format.wBitsPerSample  = 16;
    wfxex.Format.nChannels       = 2;
  }
  else
  {
    wfxex.dwChannelMask          = SpeakerMaskFromAEChannels(format.m_channelLayout);
    wfxex.Format.wFormatTag      = WAVE_FORMAT_EXTENSIBLE;
    wfxex.SubFormat              = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
    wfxex.Format.wBitsPerSample  = 32;
  }

  wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample;
  wfxex.Format.nBlockAlign          = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
  wfxex.Format.nAvgBytesPerSec      = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;

  m_AvgBytesPerSec = wfxex.Format.nAvgBytesPerSec;

  unsigned int uiFrameCount = (int)(format.m_sampleRate * 0.01); //default to 10ms chunks
  m_dwFrameSize = wfxex.Format.nBlockAlign;
  m_dwChunkSize = m_dwFrameSize * uiFrameCount;
  m_dwBufferLen = m_dwChunkSize * 12; //120ms total buffer

  // fill in the secondary sound buffer descriptor
  DSBUFFERDESC dsbdesc;
  memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
  dsbdesc.dwSize = sizeof(DSBUFFERDESC);
  dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
                  | DSBCAPS_GLOBALFOCUS;         /** Allows background playing */

  if (!g_sysinfo.IsVistaOrHigher())
    dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;     /** Needed for 5.1 on emu101k, fails by design on Vista */

  dsbdesc.dwBufferBytes = m_dwBufferLen;
  dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wfxex;

  // now create the stream buffer
  HRESULT res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
  if (res != DS_OK)
  {
    if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE)
    {
      SAFE_RELEASE(m_pBuffer);
      CLog::Log(LOGDEBUG, __FUNCTION__": Couldn't create secondary buffer (%s). Trying without LOCHARDWARE.", dserr2str(res));
      // Try without DSBCAPS_LOCHARDWARE
      dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
      res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
    }
    if (res != DS_OK)
    {
      SAFE_RELEASE(m_pBuffer);
      CLog::Log(LOGERROR, __FUNCTION__": cannot create secondary buffer (%s)", dserr2str(res));
      return false;
    }
  }
  CLog::Log(LOGDEBUG, __FUNCTION__": secondary buffer created");

  m_pBuffer->Stop();

  AEChannelsFromSpeakerMask(wfxex.dwChannelMask);
  format.m_channelLayout = m_channelLayout;
  format.m_frames = uiFrameCount;
  format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
  format.m_frameSize = (AE_IS_RAW(format.m_dataFormat) ? wfxex.Format.wBitsPerSample >> 3 : sizeof(float)) * format.m_channelLayout.Count();
  format.m_dataFormat = AE_IS_RAW(format.m_dataFormat) ? AE_FMT_S16NE : AE_FMT_FLOAT;

  m_format = format;
  m_device = device;

  m_BufferOffset = 0;
  m_CacheLen = 0;
  m_LastCacheCheck = XbmcThreads::SystemClockMillis();
  m_initialized = true;

  CLog::Log(LOGDEBUG, __FUNCTION__": Initializing DirectSound with the following parameters:");
  CLog::Log(LOGDEBUG, "  Audio Device    : %s", ((std::string)deviceFriendlyName).c_str());
  CLog::Log(LOGDEBUG, "  Sample Rate     : %d", wfxex.Format.nSamplesPerSec);
  CLog::Log(LOGDEBUG, "  Sample Format   : %s", CAEUtil::DataFormatToStr(format.m_dataFormat));
  CLog::Log(LOGDEBUG, "  Bits Per Sample : %d", wfxex.Format.wBitsPerSample);
  CLog::Log(LOGDEBUG, "  Valid Bits/Samp : %d", wfxex.Samples.wValidBitsPerSample);
  CLog::Log(LOGDEBUG, "  Channel Count   : %d", wfxex.Format.nChannels);
  CLog::Log(LOGDEBUG, "  Block Align     : %d", wfxex.Format.nBlockAlign);
  CLog::Log(LOGDEBUG, "  Avg. Bytes Sec  : %d", wfxex.Format.nAvgBytesPerSec);
  CLog::Log(LOGDEBUG, "  Samples/Block   : %d", wfxex.Samples.wSamplesPerBlock);
  CLog::Log(LOGDEBUG, "  Format cBSize   : %d", wfxex.Format.cbSize);
  CLog::Log(LOGDEBUG, "  Channel Layout  : %s", ((std::string)format.m_channelLayout).c_str());
  CLog::Log(LOGDEBUG, "  Channel Mask    : %d", wfxex.dwChannelMask);
  CLog::Log(LOGDEBUG, "  Frames          : %d", format.m_frames);
  CLog::Log(LOGDEBUG, "  Frame Samples   : %d", format.m_frameSamples);
  CLog::Log(LOGDEBUG, "  Frame Size      : %d", format.m_frameSize);

  return true;
}
示例#2
0
void CAESinkALSA::EnumerateDevice(AEDeviceInfoList &list, const std::string &device, const std::string &description, snd_config_t *config)
{
  snd_pcm_t *pcmhandle = NULL;
  if (!OpenPCMDevice(device, "", ALSA_MAX_CHANNELS, &pcmhandle, config, false))
    return;

  snd_pcm_info_t *pcminfo;
  snd_pcm_info_alloca(&pcminfo);
  memset(pcminfo, 0, snd_pcm_info_sizeof());

  int err = snd_pcm_info(pcmhandle, pcminfo);
  if (err < 0)
  {
    CLog::Log(LOGINFO, "CAESinkALSA - Unable to get pcm_info for \"%s\"", device.c_str());
    snd_pcm_close(pcmhandle);
  }

  int cardNr = snd_pcm_info_get_card(pcminfo);

  CAEDeviceInfo info;
  info.m_deviceName = device;
  info.m_deviceType = AEDeviceTypeFromName(device);

  if (cardNr >= 0)
  {
    /* "HDA NVidia", "HDA Intel", "HDA ATI HDMI", "SB Live! 24-bit External", ... */
    char *cardName;
    if (snd_card_get_name(cardNr, &cardName) == 0)
      info.m_displayName = cardName;

    if (info.m_deviceType == AE_DEVTYPE_HDMI && info.m_displayName.size() > 5 &&
        info.m_displayName.substr(info.m_displayName.size()-5) == " HDMI")
    {
      /* We already know this is HDMI, strip it */
      info.m_displayName.erase(info.m_displayName.size()-5);
    }

    /* "CONEXANT Analog", "USB Audio", "HDMI 0", "ALC889 Digital" ... */
    std::string pcminfoName = snd_pcm_info_get_name(pcminfo);

    /*
     * Filter "USB Audio", in those cases snd_card_get_name() is more
     * meaningful already
     */
    if (pcminfoName != "USB Audio")
      info.m_displayNameExtra = pcminfoName;

    if (info.m_deviceType == AE_DEVTYPE_HDMI)
    {
      /* replace, this was likely "HDMI 0" */
      info.m_displayNameExtra = "HDMI";

      int dev = snd_pcm_info_get_device(pcminfo);

      if (dev >= 0)
      {
        /* lets see if we can get ELD info */

        snd_ctl_t *ctlhandle;
        std::stringstream sstr;
        sstr << "hw:" << cardNr;
        std::string strHwName = sstr.str();

        if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) == 0)
        {
          snd_hctl_t *hctl;
          if (snd_hctl_open_ctl(&hctl, ctlhandle) == 0)
          {
            snd_hctl_load(hctl);
            bool badHDMI = false;
            if (!GetELD(hctl, dev, info, badHDMI))
              CLog::Log(LOGDEBUG, "CAESinkALSA - Unable to obtain ELD information for device \"%s\" (not supported by device, or kernel older than 3.2)",
                        device.c_str());

            /* snd_hctl_close also closes ctlhandle */
            snd_hctl_close(hctl);

            if (badHDMI)
            {
              /* 
               * Warn about disconnected devices, but keep them enabled 
               * Detection can go wrong on Intel, Nvidia and on all 
               * AMD (fglrx) hardware, so it is not safe to close those
               * handles
               */
              CLog::Log(LOGDEBUG, "CAESinkALSA - HDMI device \"%s\" may be unconnected (no ELD data)", device.c_str());
            }
          }
          else
          {
            snd_ctl_close(ctlhandle);
          }
        }
      }
    }
    else if (info.m_deviceType == AE_DEVTYPE_IEC958)
    {
      /* append instead of replace, pcminfoName is useful for S/PDIF */
      if (!info.m_displayNameExtra.empty())
        info.m_displayNameExtra += ' ';
      info.m_displayNameExtra += "S/PDIF";
    }
    else if (info.m_displayNameExtra.empty())
    {
      /* for USB audio, it gets a bit confusing as there is
       * - "SB Live! 24-bit External"
       * - "SB Live! 24-bit External, S/PDIF"
       * so add "Analog" qualifier to the first one */
      info.m_displayNameExtra = "Analog";
    }

    /* "default" is a device that will be used for all inputs, while
     * "@" will be mangled to front/default/surroundXX as necessary */
    if (device == "@" || device == "default")
    {
      /* Make it "Default (whatever)" */
      info.m_displayName = "Default (" + info.m_displayName + (info.m_displayNameExtra.empty() ? "" : " " + info.m_displayNameExtra + ")");
      info.m_displayNameExtra = "";
    }

  }
  else
  {
    /* virtual devices: "default", "pulse", ... */
    /* description can be e.g. "PulseAudio Sound Server" - for hw devices it is
     * normally uninteresting, like "HDMI Audio Output" or "Default Audio Device",
     * so we only use it for virtual devices that have no better display name */
    info.m_displayName = description;
  }

  snd_pcm_hw_params_t *hwparams;
  snd_pcm_hw_params_alloca(&hwparams);
  memset(hwparams, 0, snd_pcm_hw_params_sizeof());

  /* ensure we can get a playback configuration for the device */
  if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0)
  {
    CLog::Log(LOGINFO, "CAESinkALSA - No playback configurations available for device \"%s\"", device.c_str());
    snd_pcm_close(pcmhandle);
    return;
  }

  /* detect the available sample rates */
  for (unsigned int *rate = ALSASampleRateList; *rate != 0; ++rate)
    if (snd_pcm_hw_params_test_rate(pcmhandle, hwparams, *rate, 0) >= 0)
      info.m_sampleRates.push_back(*rate);

  /* detect the channels available */
  int channels = 0;
  for (int i = ALSA_MAX_CHANNELS; i >= 1; --i)
  {
    /* Reopen the device if needed on the special "surroundXX" cases */
    if (info.m_deviceType == AE_DEVTYPE_PCM && (i == 8 || i == 6 || i == 4))
      OpenPCMDevice(device, "", i, &pcmhandle, config, false);

    if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0)
    {
      channels = i;
      break;
    }
  }

  if (device == "default" && channels == 2)
  {
    /* This looks like the ALSA standard default stereo dmix device, we
     * probably want to use "@" instead to get surroundXX. */
    snd_pcm_close(pcmhandle);
    EnumerateDevice(list, "@", description, config);
    return;
  }

  CAEChannelInfo alsaChannels;
  for (int i = 0; i < channels; ++i)
  {
    if (!info.m_channels.HasChannel(ALSAChannelMap[i]))
      info.m_channels += ALSAChannelMap[i];
    alsaChannels += ALSAChannelMap[i];
  }

  /* remove the channels from m_channels that we cant use */
  info.m_channels.ResolveChannels(alsaChannels);

  /* detect the PCM sample formats that are available */
  for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1))
  {
    if (AE_IS_RAW(i) || i == AE_FMT_MAX)
      continue;
    snd_pcm_format_t fmt = AEFormatToALSAFormat(i);
    if (fmt == SND_PCM_FORMAT_UNKNOWN)
      continue;

    if (snd_pcm_hw_params_test_format(pcmhandle, hwparams, fmt) >= 0)
      info.m_dataFormats.push_back(i);
  }

  snd_pcm_close(pcmhandle);
  list.push_back(info);
}
示例#3
0
bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn/* = true */, bool job /* = false */)
{
  StreamInfo *si = new StreamInfo();

  // check if we advance a track of a CUE sheet
  // if this is the case we don't need to open a new stream
  std::string newURL = file.GetMusicInfoTag() ? file.GetMusicInfoTag()->GetURL() : file.GetPath();
  std::string oldURL = m_FileItem->GetMusicInfoTag() ? m_FileItem->GetMusicInfoTag()->GetURL() : m_FileItem->GetPath();
  if (newURL.compare(oldURL) == 0 &&
      file.m_lStartOffset &&
      file.m_lStartOffset == m_FileItem->m_lEndOffset &&
      m_currentStream && m_currentStream->m_prepareTriggered)
  {
    m_continueStream = true;
    m_upcomingCrossfadeMS = 0;
    *m_FileItem = file;
    return true;
  }
  else
  {
    m_continueStream = false;
  }

  if (!si->m_decoder.Create(file, (file.m_lStartOffset * 1000) / 75))
  {
    CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder");

    delete si;
    // advance playlist
    if (job)
      m_callback.OnPlayBackStarted();
    m_callback.OnQueueNextItem();
    return false;
  }

  /* decode until there is data-available */
  si->m_decoder.Start();
  while(si->m_decoder.GetDataSize() == 0)
  {
    int status = si->m_decoder.GetStatus();
    if (status == STATUS_ENDED   ||
        status == STATUS_NO_FILE ||
        si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
    {
      CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples");

      si->m_decoder.Destroy();
      delete si;
      // advance playlist
      if (job)
        m_callback.OnPlayBackStarted();
      m_callback.OnQueueNextItem();
      return false;
    }

    /* yield our time so that the main PAP thread doesnt stall */
    CThread::Sleep(1);
  }

  /* init the streaminfo struct */
  si->m_decoder.GetDataFormat(&si->m_channelInfo, &si->m_sampleRate, &si->m_encodedSampleRate, &si->m_dataFormat);
  si->m_startOffset        = file.m_lStartOffset * 1000 / 75;
  si->m_endOffset          = file.m_lEndOffset   * 1000 / 75;
  si->m_bytesPerSample     = CAEUtil::DataFormatToBits(si->m_dataFormat) >> 3;
  si->m_bytesPerFrame      = si->m_bytesPerSample * si->m_channelInfo.Count();
  si->m_started            = false;
  si->m_finishing          = false;
  si->m_framesSent         = 0;
  si->m_seekNextAtFrame    = 0;
  si->m_seekFrame          = -1;
  si->m_stream             = NULL;
  si->m_volume             = (fadeIn && m_upcomingCrossfadeMS) ? 0.0f : 1.0f;
  si->m_fadeOutTriggered   = false;
  si->m_isSlaved           = false;

  int64_t streamTotalTime = si->m_decoder.TotalTime();
  if (si->m_endOffset)
    streamTotalTime = si->m_endOffset - si->m_startOffset;
  
  si->m_prepareNextAtFrame = 0;
  // cd drives don't really like it to be crossfaded or prepared
  if(!file.IsCDDA())
  {
    if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
      si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_sampleRate / 1000.0f);
  }

  if (m_currentStream && (AE_IS_RAW(m_currentStream->m_dataFormat) || AE_IS_RAW(si->m_dataFormat)))
  {
    m_currentStream->m_prepareTriggered = false;
    m_currentStream->m_waitOnDrain = true;
    m_currentStream->m_prepareNextAtFrame = 0;
    si->m_decoder.Destroy();
    delete si;
    return false;
  }

  UpdateCrossfadeTime(file);

  si->m_prepareTriggered = false;
  si->m_playNextAtFrame = 0;
  si->m_playNextTriggered = false;
  si->m_waitOnDrain = false;

  if (!PrepareStream(si))
  {
    CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream");
    
    si->m_decoder.Destroy();
    delete si;
    // advance playlist
    if (job)
      m_callback.OnPlayBackStarted();
    m_callback.OnQueueNextItem();
    return false;
  }

  /* add the stream to the list */
  CExclusiveLock lock(m_streamsLock);
  m_streams.push_back(si);
  //update the current stream to start playing the next track at the correct frame.
  UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);

  *m_FileItem = file;

  return true;
}
示例#4
0
bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device)
{
  m_initDevice = device;
  m_initFormat = format;

  /* if we are raw, correct the data format */
  if (AE_IS_RAW(format.m_dataFormat))
  {
    m_channelLayout     = GetChannelLayout(format);
    format.m_dataFormat = AE_FMT_S16NE;
    m_passthrough       = true;
  }
  else
  {
    m_channelLayout = GetChannelLayout(format);
    m_passthrough   = false;
  }
#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC)
  if (aml_present())
  {
    aml_set_audio_passthrough(m_passthrough);
    device = "default";
  }
#endif

  if (m_channelLayout.Count() == 0)
  {
    CLog::Log(LOGERROR, "CAESinkALSA::Initialize - Unable to open the requested channel layout");
    return false;
  }

  format.m_channelLayout = m_channelLayout;

  AEDeviceType devType = AEDeviceTypeFromName(device);

  std::string AESParams;
  /* digital interfaces should have AESx set, though in practice most
   * receivers don't care */
  if (m_passthrough || devType == AE_DEVTYPE_HDMI || devType == AE_DEVTYPE_IEC958)
    GetAESParams(format, AESParams);

  CLog::Log(LOGINFO, "CAESinkALSA::Initialize - Attempting to open device \"%s\"", device.c_str());

  /* get the sound config */
  snd_config_t *config;
  snd_config_copy(&config, snd_config);

  snd_config_t *dmixRateConf;
  long dmixRate;

  if (snd_config_search(config, "defaults.pcm.dmix.rate", &dmixRateConf) < 0
      || snd_config_get_integer(dmixRateConf, &dmixRate) < 0)
    dmixRate = 48000; /* assume default */


  /* Prefer dmix for non-passthrough stereo when sample rate matches */
  if (!OpenPCMDevice(device, AESParams, m_channelLayout.Count(), &m_pcm, config, format.m_sampleRate == (unsigned int) dmixRate && !m_passthrough))
  {
    CLog::Log(LOGERROR, "CAESinkALSA::Initialize - failed to initialize device \"%s\"", device.c_str());
    snd_config_delete(config);
    return false;
  }

  /* get the actual device name that was used */
  device = snd_pcm_name(m_pcm);
  m_device = device;

  CLog::Log(LOGINFO, "CAESinkALSA::Initialize - Opened device \"%s\"", device.c_str());

  /* free the sound config */
  snd_config_delete(config);

  if (!InitializeHW(format) || !InitializeSW(format))
    return false;

  snd_pcm_nonblock(m_pcm, 1);
  snd_pcm_prepare (m_pcm);

  m_format              = format;
  m_formatSampleRateMul = 1.0 / (double)m_format.m_sampleRate;

  return true;
}
示例#5
0
bool CAESinkALSA::InitializeHW(AEAudioFormat &format)
{
  snd_pcm_hw_params_t *hw_params;

  snd_pcm_hw_params_alloca(&hw_params);
  memset(hw_params, 0, snd_pcm_hw_params_sizeof());

  snd_pcm_hw_params_any(m_pcm, hw_params);
  snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);

  unsigned int sampleRate   = format.m_sampleRate;
  unsigned int channelCount = format.m_channelLayout.Count();
  snd_pcm_hw_params_set_rate_near    (m_pcm, hw_params, &sampleRate, NULL);
  snd_pcm_hw_params_set_channels_near(m_pcm, hw_params, &channelCount);

  /* ensure we opened X channels or more */
  if (format.m_channelLayout.Count() > channelCount)
  {
    CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Unable to open the required number of channels");
  }

  /* update the channelLayout to what we managed to open */
  format.m_channelLayout.Reset();
  for (unsigned int i = 0; i < channelCount; ++i)
    format.m_channelLayout += ALSAChannelMap[i];

  snd_pcm_format_t fmt = AEFormatToALSAFormat(format.m_dataFormat);
  if (fmt == SND_PCM_FORMAT_UNKNOWN)
  {
    /* if we dont support the requested format, fallback to float */
    format.m_dataFormat = AE_FMT_FLOAT;
    fmt                 = SND_PCM_FORMAT_FLOAT;
  }

  /* try the data format */
  if (snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0)
  {
    /* if the chosen format is not supported, try each one in decending order */
    CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(format.m_dataFormat));
    for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1))
    {
      if (AE_IS_RAW(i) || i == AE_FMT_MAX)
        continue;

      if (m_passthrough && i != AE_FMT_S16BE && i != AE_FMT_S16LE)
	continue;

      fmt = AEFormatToALSAFormat(i);

      if (fmt == SND_PCM_FORMAT_UNKNOWN || snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0)
      {
        fmt = SND_PCM_FORMAT_UNKNOWN;
        continue;
      }

      int fmtBits = CAEUtil::DataFormatToBits(i);
      int bits    = snd_pcm_hw_params_get_sbits(hw_params);
      if (bits != fmtBits)
      {
        /* if we opened in 32bit and only have 24bits, pack into 24 */
        if (fmtBits == 32 && bits == 24)
          i = AE_FMT_S24NE4;
        else
          continue;
      }

      /* record that the format fell back to X */
      format.m_dataFormat = i;
      CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format %s", CAEUtil::DataFormatToStr(format.m_dataFormat));
      break;
    }

    /* if we failed to find a valid output format */
    if (fmt == SND_PCM_FORMAT_UNKNOWN)
    {
      CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Unable to find a suitable output format");
      return false;
    }
  }

  unsigned int periods;

  snd_pcm_uframes_t periodSize, bufferSize;
  snd_pcm_hw_params_get_buffer_size_max(hw_params, &bufferSize);

  bufferSize  = std::max(bufferSize, (snd_pcm_uframes_t)8192);
  periodSize  = bufferSize / ALSA_PERIODS;
  periods     = ALSA_PERIODS;

  CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize);

  /* work on a copy of the hw params */
  snd_pcm_hw_params_t *hw_params_copy;
  snd_pcm_hw_params_alloca(&hw_params_copy);

  /* try to set the buffer size then the period size */
  snd_pcm_hw_params_copy(hw_params_copy, hw_params);
  snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize);
  snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL);
  snd_pcm_hw_params_set_periods_near    (m_pcm, hw_params_copy, &periods   , NULL);
  if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
  {
    /* try to set the period size then the buffer size */
    snd_pcm_hw_params_copy(hw_params_copy, hw_params);
    snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL);
    snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize);
    snd_pcm_hw_params_set_periods_near    (m_pcm, hw_params_copy, &periods   , NULL);
    if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
    {
      /* try to just set the buffer size */
      snd_pcm_hw_params_copy(hw_params_copy, hw_params);
      snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize);
      snd_pcm_hw_params_set_periods_near    (m_pcm, hw_params_copy, &periods   , NULL);
      if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
      {
        /* try to just set the period size */
        snd_pcm_hw_params_copy(hw_params_copy, hw_params);
        snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL);
        snd_pcm_hw_params_set_periods_near    (m_pcm, hw_params_copy, &periods   , NULL);
        if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
        {
          CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Failed to set the parameters");
          return false;
        }
      }
    }
  }

  snd_pcm_hw_params_get_period_size(hw_params_copy, &periodSize, NULL);
  snd_pcm_hw_params_get_buffer_size(hw_params_copy, &bufferSize);
  

  CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize);

  /* set the format parameters */
  format.m_sampleRate   = sampleRate;
  format.m_frames       = periodSize;
  format.m_frameSamples = periodSize * format.m_channelLayout.Count();
  format.m_frameSize    = snd_pcm_frames_to_bytes(m_pcm, 1);

  m_bufferSize = (unsigned int)bufferSize;
  m_timeout    = std::ceil((double)(bufferSize * 1000) / (double)sampleRate);

  CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Setting timeout to %d ms", m_timeout);

  return true;
}
示例#6
0
bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device)
{
  CAEChannelInfo channelLayout = GetChannelLayout(format, 2, 8);
  m_initDevice = device;
  m_initFormat = format;
  ALSAConfig inconfig, outconfig;
  inconfig.format = format.m_dataFormat;
  inconfig.sampleRate = format.m_sampleRate;
  inconfig.channels = channelLayout.Count();

  /* if we are raw, correct the data format */
  if (AE_IS_RAW(format.m_dataFormat))
  {
    inconfig.format   = AE_FMT_S16NE;
    m_passthrough     = true;
  }
  else
  {
    m_passthrough   = false;
  }
#if defined(HAS_LIBAMCODEC)
  if (aml_present())
  {
    aml_set_audio_passthrough(m_passthrough);
    device = "default";
  }
#endif

  if (inconfig.channels == 0)
  {
    CLog::Log(LOGERROR, "CAESinkALSA::Initialize - Unable to open the requested channel layout");
    return false;
  }

  AEDeviceType devType = AEDeviceTypeFromName(device);

  std::string AESParams;
  /* digital interfaces should have AESx set, though in practice most
   * receivers don't care */
  if (m_passthrough || devType == AE_DEVTYPE_HDMI || devType == AE_DEVTYPE_IEC958)
    GetAESParams(format, AESParams);

  CLog::Log(LOGINFO, "CAESinkALSA::Initialize - Attempting to open device \"%s\"", device.c_str());

  /* get the sound config */
  snd_config_t *config;
  snd_config_copy(&config, snd_config);

  if (!OpenPCMDevice(device, AESParams, inconfig.channels, &m_pcm, config))
  {
    CLog::Log(LOGERROR, "CAESinkALSA::Initialize - failed to initialize device \"%s\"", device.c_str());
    snd_config_delete(config);
    return false;
  }

  /* get the actual device name that was used */
  device = snd_pcm_name(m_pcm);
  m_device = device;

  CLog::Log(LOGINFO, "CAESinkALSA::Initialize - Opened device \"%s\"", device.c_str());

  /* free the sound config */
  snd_config_delete(config);

  if (!InitializeHW(inconfig, outconfig) || !InitializeSW(outconfig))
    return false;

  // we want it blocking
  snd_pcm_nonblock(m_pcm, 0);
  snd_pcm_prepare (m_pcm);

  if (m_passthrough && inconfig.channels != outconfig.channels)
  {
    CLog::Log(LOGINFO, "CAESinkALSA::Initialize - could not open required number of channels");
    return false;
  }
  // adjust format to the configuration we got
  format.m_channelLayout = GetChannelLayout(format, outconfig.channels, outconfig.channels);
  format.m_sampleRate = outconfig.sampleRate;
  format.m_frames = outconfig.periodSize;
  format.m_frameSize = outconfig.frameSize;
  format.m_frameSamples = outconfig.periodSize * outconfig.channels;
  format.m_dataFormat = outconfig.format;

  m_format              = format;
  m_formatSampleRateMul = 1.0 / (double)m_format.m_sampleRate;

  return true;
}
示例#7
0
bool CAESinkALSA::InitializeHW(const ALSAConfig &inconfig, ALSAConfig &outconfig)
{
  snd_pcm_hw_params_t *hw_params;

  snd_pcm_hw_params_alloca(&hw_params);
  memset(hw_params, 0, snd_pcm_hw_params_sizeof());

  snd_pcm_hw_params_any(m_pcm, hw_params);
  snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);

  unsigned int sampleRate   = inconfig.sampleRate;
  unsigned int channelCount = inconfig.channels;
  snd_pcm_hw_params_set_rate_near    (m_pcm, hw_params, &sampleRate, NULL);
  snd_pcm_hw_params_set_channels_near(m_pcm, hw_params, &channelCount);

  /* ensure we opened X channels or more */
  if (inconfig.channels > channelCount)
  {
    CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Unable to open the required number of channels");
  }

  /* update outconfig */
  outconfig.channels = channelCount;

  snd_pcm_format_t fmt = AEFormatToALSAFormat(inconfig.format);
  outconfig.format = inconfig.format;

  if (fmt == SND_PCM_FORMAT_UNKNOWN)
  {
    /* if we dont support the requested format, fallback to float */
    fmt = SND_PCM_FORMAT_FLOAT;
    outconfig.format = AE_FMT_FLOAT;
  }

  /* try the data format */
  if (snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0)
  {
    /* if the chosen format is not supported, try each one in decending order */
    CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(outconfig.format));
    for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1))
    {
      if (AE_IS_RAW(i) || i == AE_FMT_MAX)
        continue;

      if (m_passthrough && i != AE_FMT_S16BE && i != AE_FMT_S16LE)
	continue;

      fmt = AEFormatToALSAFormat(i);

      if (fmt == SND_PCM_FORMAT_UNKNOWN || snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0)
      {
        fmt = SND_PCM_FORMAT_UNKNOWN;
        continue;
      }

      int fmtBits = CAEUtil::DataFormatToBits(i);
      int bits    = snd_pcm_hw_params_get_sbits(hw_params);
      if (bits != fmtBits)
      {
        /* if we opened in 32bit and only have 24bits, pack into 24 */
        if (fmtBits == 32 && bits == 24)
          i = AE_FMT_S24NE4;
        else
          continue;
      }

      /* record that the format fell back to X */
      outconfig.format = i;
      CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format %s", CAEUtil::DataFormatToStr(outconfig.format));
      break;
    }

    /* if we failed to find a valid output format */
    if (fmt == SND_PCM_FORMAT_UNKNOWN)
    {
      CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Unable to find a suitable output format");
      return false;
    }
  }

  snd_pcm_uframes_t periodSize, bufferSize;
  snd_pcm_hw_params_get_buffer_size_max(hw_params, &bufferSize);
  snd_pcm_hw_params_get_period_size_max(hw_params, &periodSize, NULL);

  /* 
   We want to make sure, that we have max 200 ms Buffer with 
   a periodSize of approx 50 ms. Choosing a higher bufferSize
   will cause problems with menu sounds. Buffer will be increased
   after those are fixed.
  */
  periodSize  = std::min(periodSize, (snd_pcm_uframes_t) sampleRate / 20);
  bufferSize  = std::min(bufferSize, (snd_pcm_uframes_t) sampleRate / 5);
  
  /* 
   According to upstream we should set buffer size first - so make sure it is always at least
   4x period size to not get underruns (some systems seem to have issues with only 2 periods)
  */
  periodSize = std::min(periodSize, bufferSize / 4);

  CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: periodSize %lu, bufferSize %lu", periodSize, bufferSize);

  snd_pcm_hw_params_t *hw_params_copy;
  snd_pcm_hw_params_alloca(&hw_params_copy);
  snd_pcm_hw_params_copy(hw_params_copy, hw_params); // copy what we have and is already working

  // Make sure to not initialize too large to not cause underruns
  snd_pcm_uframes_t periodSizeMax = bufferSize / 3;
  if(snd_pcm_hw_params_set_period_size_max(m_pcm, hw_params_copy, &periodSizeMax, NULL) != 0)
  {
    snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy
    CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: Failed to limit periodSize to %lu", periodSizeMax);
  }
  
  // first trying bufferSize, PeriodSize
  // for more info see here:
  // http://mailman.alsa-project.org/pipermail/alsa-devel/2009-September/021069.html
  // the last three tries are done as within pulseaudio

  // backup periodSize and bufferSize first. Restore them after every failed try
  snd_pcm_uframes_t periodSizeTemp, bufferSizeTemp;
  periodSizeTemp = periodSize;
  bufferSizeTemp = bufferSize;
  if (snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0
    || snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0
    || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
  {
    bufferSize = bufferSizeTemp;
    periodSize = periodSizeTemp;
    // retry with PeriodSize, bufferSize
    snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy
    if (snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0
      || snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0
      || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
    {
      // try only periodSize
      periodSize = periodSizeTemp;
      snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy
      if(snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0 
        || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
      {
        // try only BufferSize
        bufferSize = bufferSizeTemp;
        snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restory working copy
        if (snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0
          || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
        {
          // set default that Alsa would choose
          CLog::Log(LOGWARNING, "CAESinkAlsa::IntializeHW - Using default alsa values - set failed");
          if (snd_pcm_hw_params(m_pcm, hw_params) != 0)
          {
            CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Could not init a valid sink");
            return false;
          }
        }
      }
      // reread values when alsa default was kept
      snd_pcm_get_params(m_pcm, &bufferSize, &periodSize);
    }
  }
  
  CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, bufferSize %lu", periodSize, bufferSize);

  /* set the format parameters */
  outconfig.sampleRate   = sampleRate;
  outconfig.periodSize   = periodSize;
  outconfig.frameSize    = snd_pcm_frames_to_bytes(m_pcm, 1);

  m_bufferSize = (unsigned int)bufferSize;
  m_timeout    = std::ceil((double)(bufferSize * 1000) / (double)sampleRate);

  CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Setting timeout to %d ms", m_timeout);

  return true;
}
示例#8
0
文件: AESinkOSS.cpp 项目: bild/xbmc
bool CAESinkOSS::Initialize(AEAudioFormat &format, std::string &device)
{
  m_initFormat = format;
  format.m_channelLayout = GetChannelLayout(format);
  device = GetDeviceUse(format, device);

#ifdef __linux__
  /* try to open in exclusive mode first (no software mixing) */
  m_fd = open(device.c_str(), O_WRONLY | O_EXCL, 0);
  if (m_fd == -1)
#endif
    m_fd = open(device.c_str(), O_WRONLY, 0);
  if (m_fd == -1)
  {
    CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to open the audio device: %s", device.c_str());
    return false;
  }

  int format_mask;
  if (ioctl(m_fd, SNDCTL_DSP_GETFMTS, &format_mask) == -1)
  {
    close(m_fd);
    CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to get supported formats, assuming AFMT_S16_NE");
    return false;
  }

#ifdef OSS4
  bool useCooked = true;
#endif

  int oss_fmt = 0;
#ifdef AFMT_FLOAT
  if ((format.m_dataFormat == AE_FMT_FLOAT) && (format_mask & AFMT_FLOAT ))
    oss_fmt = AFMT_FLOAT;
  else
#endif
#ifdef AFMT_S32_NE
  if ((format.m_dataFormat == AE_FMT_S32NE) && (format_mask & AFMT_S32_NE))
    oss_fmt = AFMT_S32_NE;
  else if ((format.m_dataFormat == AE_FMT_S32BE) && (format_mask & AFMT_S32_BE))
    oss_fmt = AFMT_S32_BE;
  else if ((format.m_dataFormat == AE_FMT_S32LE) && (format_mask & AFMT_S32_LE))
    oss_fmt = AFMT_S32_LE;
  else
#endif
  if ((format.m_dataFormat == AE_FMT_S16NE) && (format_mask & AFMT_S16_NE))
    oss_fmt = AFMT_S16_NE;
  else if ((format.m_dataFormat == AE_FMT_S16BE) && (format_mask & AFMT_S16_BE))
    oss_fmt = AFMT_S16_BE;
  else if ((format.m_dataFormat == AE_FMT_S16LE) && (format_mask & AFMT_S16_LE))
    oss_fmt = AFMT_S16_LE;
  else if ((format.m_dataFormat == AE_FMT_S8   ) && (format_mask & AFMT_S8    ))
    oss_fmt = AFMT_S8;
  else if ((format.m_dataFormat == AE_FMT_U8   ) && (format_mask & AFMT_U8    ))
    oss_fmt = AFMT_U8;
  else if ((AE_IS_RAW(format.m_dataFormat)     ) && (format_mask & AFMT_AC3   ))
    oss_fmt = AFMT_AC3;
  else if (AE_IS_RAW(format.m_dataFormat))
  {
    close(m_fd);
    CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to find a suitable RAW output format");
    return false;
  }
  else
  {
    CLog::Log(LOGINFO, "CAESinkOSS::Initialize - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(format.m_dataFormat));

    /* fallback to the best supported format */
#ifdef AFMT_FLOAT
    if (format_mask & AFMT_FLOAT )
    {
      oss_fmt = AFMT_FLOAT;
      format.m_dataFormat = AE_FMT_FLOAT;
    }
    else
#endif
#ifdef AFMT_S32_NE
    if (format_mask & AFMT_S32_NE)
    {
      oss_fmt = AFMT_S32_NE;
      format.m_dataFormat = AE_FMT_S32NE;
    }
    else if (format_mask & AFMT_S32_BE)
    {
      oss_fmt = AFMT_S32_BE;
      format.m_dataFormat = AE_FMT_S32BE;
    }
    else if (format_mask & AFMT_S32_LE)
    {
      oss_fmt = AFMT_S32_LE;
      format.m_dataFormat = AE_FMT_S32LE;
    }
    else
#endif
    if (format_mask & AFMT_S16_NE)
    {
      oss_fmt = AFMT_S16_NE;
      format.m_dataFormat = AE_FMT_S16NE;
    }
    else if (format_mask & AFMT_S16_BE)
    {
      oss_fmt = AFMT_S16_BE;
      format.m_dataFormat = AE_FMT_S16BE;
    }
    else if (format_mask & AFMT_S16_LE)
    {
      oss_fmt = AFMT_S16_LE;
      format.m_dataFormat = AE_FMT_S16LE;
    }
    else if (format_mask & AFMT_S8    )
    {
      oss_fmt = AFMT_S8;
      format.m_dataFormat = AE_FMT_S8;
    }
    else if (format_mask & AFMT_U8    )
    {
      oss_fmt = AFMT_U8;
      format.m_dataFormat = AE_FMT_U8;
    }
    else
    {
      CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to find a suitable native output format, will try to use AE_FMT_S16NE anyway");
      oss_fmt             = AFMT_S16_NE;
      format.m_dataFormat = AE_FMT_S16NE;
#ifdef OSS4
      /* dont use cooked if we did not find a native format, OSS might be able to convert */
      useCooked           = false;
#endif
    }
  }

#ifdef OSS4
  if (useCooked)
  {
    int oss_cooked = 1;
    if (ioctl(m_fd, SNDCTL_DSP_COOKEDMODE, &oss_cooked) == -1)
      CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set cooked mode");
  }
#endif

  if (ioctl(m_fd, SNDCTL_DSP_SETFMT, &oss_fmt) == -1)
  {
    close(m_fd);
    CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set the data format (%s)", CAEUtil::DataFormatToStr(format.m_dataFormat));
    return false;
  }

  /* find the number we need to open to access the channels we need */
  bool found = false;
  int oss_ch = 0;
  for (int ch = format.m_channelLayout.Count(); ch < 9; ++ch)
  {
    oss_ch = ch;
    if (ioctl(m_fd, SNDCTL_DSP_CHANNELS, &oss_ch) != -1 && oss_ch >= (int)format.m_channelLayout.Count())
    {
      found = true;
      break;
    }
  }

  if (!found)
    CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to access the number of channels required, falling back");

#if defined(TARGET_FREEBSD)
  /* fix hdmi 8 channels order */
  if (!AE_IS_RAW(format.m_dataFormat) && 8 == oss_ch)
  {
    unsigned long long order = 0x0000000087346521ULL;

    if (ioctl(m_fd, SNDCTL_DSP_SET_CHNORDER, &order) == -1)
      CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set the channel order");
  }
#elif defined(OSS4)
  unsigned long long order = 0;

  for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
    switch (format.m_channelLayout[i])
    {
      case AE_CH_FL : order = (order << 4) | CHID_L  ; break;
      case AE_CH_FR : order = (order << 4) | CHID_R  ; break;
      case AE_CH_FC : order = (order << 4) | CHID_C  ; break;
      case AE_CH_LFE: order = (order << 4) | CHID_LFE; break;
      case AE_CH_SL : order = (order << 4) | CHID_LS ; break;
      case AE_CH_SR : order = (order << 4) | CHID_RS ; break;
      case AE_CH_BL : order = (order << 4) | CHID_LR ; break;
      case AE_CH_BR : order = (order << 4) | CHID_RR ; break;

      default:
        continue;
    }

  if (ioctl(m_fd, SNDCTL_DSP_SET_CHNORDER, &order) == -1)
  {
    if (ioctl(m_fd, SNDCTL_DSP_GET_CHNORDER, &order) == -1)
    {
      CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to get the channel order, assuming CHNORDER_NORMAL");
    }
  }
#endif

  int tmp = (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3) * format.m_channelLayout.Count() * OSS_FRAMES;
  int pos = 0;
  while ((tmp & 0x1) == 0x0)
  {
    tmp = tmp >> 1;
    ++pos;
  }

  int oss_frag = (4 << 16) | pos;
  if (ioctl(m_fd, SNDCTL_DSP_SETFRAGMENT, &oss_frag) == -1)
    CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set the fragment size");

  int oss_sr = format.m_sampleRate;
  if (ioctl(m_fd, SNDCTL_DSP_SPEED, &oss_sr) == -1)
  {
    close(m_fd);
    CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set the sample rate");
    return false;
  }

  audio_buf_info bi;
  if (ioctl(m_fd, SNDCTL_DSP_GETOSPACE, &bi) == -1)
  {
    close(m_fd);
    CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to get the output buffer size");
    return false;
  }

  if (fcntl(m_fd, F_SETFL,  fcntl(m_fd, F_GETFL, 0) | O_NONBLOCK) == -1)
  {
    close(m_fd);
    CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set non blocking writes");
    return false;
  }

  format.m_sampleRate    = oss_sr;
  format.m_frameSize     = (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3) * format.m_channelLayout.Count();
  format.m_frames        = bi.fragsize / format.m_frameSize;
  format.m_frameSamples  = format.m_frames * format.m_channelLayout.Count();

  m_device = device;
  m_format = format;
  return true;
}
示例#9
0
bool CAESinkPULSE::Initialize(AEAudioFormat &format, std::string &device)
{
  {
    CSingleLock lock(m_sec);
    m_IsAllocated = false;
  }
  m_passthrough = false;
  m_BytesPerSecond = 0;
  m_BufferSize = 0;
  m_Channels = 0;
  m_Stream = NULL;
  m_Context = NULL;
  m_periodSize = 0;

  if (!SetupContext(NULL, &m_Context, &m_MainLoop))
  {
    CLog::Log(LOGNOTICE, "PulseAudio might not be running. Context was not created.");
    Deinitialize();
    return false;
  }

  pa_threaded_mainloop_lock(m_MainLoop);

  struct pa_channel_map map;
  pa_channel_map_init(&map);

   // PULSE cannot cope with e.g. planar formats so we fallback to FLOAT
   // when we receive an invalid pulse format
   if (AEFormatToPulseFormat(format.m_dataFormat) == PA_SAMPLE_INVALID)
   {
     CLog::Log(LOGDEBUG, "PULSE does not support format: %s - will fallback to AE_FMT_FLOAT", CAEUtil::DataFormatToStr(format.m_dataFormat));
     format.m_dataFormat = AE_FMT_FLOAT;
   }

  m_passthrough = AE_IS_RAW(format.m_dataFormat);

  if(m_passthrough)
  {
    map.channels = 2;
    format.m_channelLayout = AE_CH_LAYOUT_2_0;
  }
  else
  {
    map = AEChannelMapToPAChannel(format.m_channelLayout);
    // if count has changed we need to fit the AE Map
    if(map.channels != format.m_channelLayout.Count())
      format.m_channelLayout = PAChannelToAEChannelMap(map);
  }
  m_Channels = format.m_channelLayout.Count();

  // store information about current sink
  SinkInfoStruct sinkStruct;
  sinkStruct.mainloop = m_MainLoop;
  sinkStruct.device_found = false;

  // get real sample rate of the device we want to open - to avoid resampling
  bool isDefaultDevice = (device == "Default");
  WaitForOperation(pa_context_get_sink_info_by_name(m_Context, isDefaultDevice ? NULL : device.c_str(), SinkInfoCallback, &sinkStruct), m_MainLoop, "Get Sink Info");
  // only check if the device is existing - don't alter the sample rate
  if (!sinkStruct.device_found)
  {
    CLog::Log(LOGERROR, "PulseAudio: Sink %s not found", device.c_str());
    pa_threaded_mainloop_unlock(m_MainLoop);
    Deinitialize();
    return false;
  }

  // Pulse can resample everything between 1 hz and 192000 hz
  // Make sure we are in the range that we originally added
  format.m_sampleRate = std::max(5512U, std::min(format.m_sampleRate, 192000U));

  pa_format_info *info[1];
  info[0] = pa_format_info_new();
  info[0]->encoding = AEFormatToPulseEncoding(format.m_dataFormat);
  if(!m_passthrough)
  {
    pa_format_info_set_sample_format(info[0], AEFormatToPulseFormat(format.m_dataFormat));
    pa_format_info_set_channel_map(info[0], &map);
  }
  pa_format_info_set_channels(info[0], m_Channels);

  // PA requires m_encodedRate in order to do EAC3
  unsigned int samplerate = format.m_sampleRate;
  if (m_passthrough && (AEFormatToPulseEncoding(format.m_dataFormat) == PA_ENCODING_EAC3_IEC61937))
  {
    // this is only used internally for PA to use EAC3
    samplerate = format.m_encodedRate;
  }

  pa_format_info_set_rate(info[0], samplerate);

  if (!pa_format_info_valid(info[0]))
  {
    CLog::Log(LOGERROR, "PulseAudio: Invalid format info");
    pa_format_info_free(info[0]);
    pa_threaded_mainloop_unlock(m_MainLoop);
    Deinitialize();
    return false;
  }

  pa_sample_spec spec;
  #if PA_CHECK_VERSION(2,0,0)
    pa_format_info_to_sample_spec(info[0], &spec, NULL);
  #else
    spec.rate = (AEFormatToPulseEncoding(format.m_dataFormat) == PA_ENCODING_EAC3_IEC61937) ? 4 * samplerate : samplerate;
    spec.format = AEFormatToPulseFormat(format.m_dataFormat);
    spec.channels = m_Channels;
  #endif
  if (!pa_sample_spec_valid(&spec))
  {
    CLog::Log(LOGERROR, "PulseAudio: Invalid sample spec");
    pa_format_info_free(info[0]);
    pa_threaded_mainloop_unlock(m_MainLoop);
    Deinitialize();
    return false;
  }

  m_BytesPerSecond = pa_bytes_per_second(&spec);
  unsigned int frameSize = pa_frame_size(&spec);

  m_Stream = pa_stream_new_extended(m_Context, "kodi audio stream", info, 1, NULL);
  pa_format_info_free(info[0]);

  if (m_Stream == NULL)
  {
    CLog::Log(LOGERROR, "PulseAudio: Could not create a stream");
    pa_threaded_mainloop_unlock(m_MainLoop);
    Deinitialize();
    return false;
  }

  pa_stream_set_state_callback(m_Stream, StreamStateCallback, m_MainLoop);
  pa_stream_set_write_callback(m_Stream, StreamRequestCallback, m_MainLoop);
  pa_stream_set_latency_update_callback(m_Stream, StreamLatencyUpdateCallback, m_MainLoop);

  // default buffer construction
  // align with AE's max buffer
  unsigned int latency = m_BytesPerSecond / 2.5; // 400 ms
  unsigned int process_time = latency / 4; // 100 ms
  if(sinkStruct.isHWDevice)
  {
    // on hw devices buffers can be further reduced
    // 200ms max latency
    // 50ms min packet size
    latency = m_BytesPerSecond / 5;
    process_time = latency / 4;
  }

  pa_buffer_attr buffer_attr;
  buffer_attr.fragsize = latency;
  buffer_attr.maxlength = (uint32_t) -1;
  buffer_attr.minreq = process_time;
  buffer_attr.prebuf = (uint32_t) -1;
  buffer_attr.tlength = latency;

  if (pa_stream_connect_playback(m_Stream, isDefaultDevice ? NULL : device.c_str(), &buffer_attr, ((pa_stream_flags)(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY)), NULL, NULL) < 0)
  {
    CLog::Log(LOGERROR, "PulseAudio: Failed to connect stream to output");
    pa_threaded_mainloop_unlock(m_MainLoop);
    Deinitialize();
    return false;
  }

  /* Wait until the stream is ready */
  do
  {
    pa_threaded_mainloop_wait(m_MainLoop);
    CLog::Log(LOGDEBUG, "PulseAudio: Stream %s", StreamStateToString(pa_stream_get_state(m_Stream)));
  }
  while (pa_stream_get_state(m_Stream) != PA_STREAM_READY && pa_stream_get_state(m_Stream) != PA_STREAM_FAILED);

  if (pa_stream_get_state(m_Stream) == PA_STREAM_FAILED)
  {
    CLog::Log(LOGERROR, "PulseAudio: Waited for the stream but it failed");
    pa_threaded_mainloop_unlock(m_MainLoop);
    Deinitialize();
    return false;
  }

  const pa_buffer_attr *a;

  if (!(a = pa_stream_get_buffer_attr(m_Stream)))
  {
    CLog::Log(LOGERROR, "PulseAudio: %s", pa_strerror(pa_context_errno(m_Context)));
    pa_threaded_mainloop_unlock(m_MainLoop);
    Deinitialize();
    return false;
  }
  else
  {
    unsigned int packetSize = a->minreq;
    m_BufferSize = a->tlength;
    m_periodSize = a->minreq;

    format.m_frames = packetSize / frameSize;
  }

  {
    CSingleLock lock(m_sec);
    // Register Callback for Sink changes
    pa_context_set_subscribe_callback(m_Context, SinkChangedCallback, this);
    const pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SINK;
    pa_operation *op = pa_context_subscribe(m_Context, mask, NULL, this);
    if (op != NULL)
      pa_operation_unref(op);

    // Register Callback for Sink Info changes - this handles volume
    pa_context_set_subscribe_callback(m_Context, SinkInputInfoChangedCallback, this);
    const pa_subscription_mask_t mask_input = PA_SUBSCRIPTION_MASK_SINK_INPUT;
    pa_operation* op_sinfo = pa_context_subscribe(m_Context, mask_input, NULL, this);
    if (op_sinfo != NULL)
      pa_operation_unref(op_sinfo);
  }

  pa_threaded_mainloop_unlock(m_MainLoop);
  
  format.m_frameSize = frameSize;
  format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
  m_format = format;
  format.m_dataFormat = m_passthrough ? AE_FMT_S16NE : format.m_dataFormat;

  CLog::Log(LOGNOTICE, "PulseAudio: Opened device %s in %s mode with Buffersize %u ms",
                      device.c_str(), m_passthrough ? "passthrough" : "pcm",
                      (unsigned int) ((m_BufferSize / (float) m_BytesPerSecond) * 1000));

  // Cork stream will resume when adding first package
  Pause(true);
  {
    CSingleLock lock(m_sec);
    m_IsAllocated = true;
  }

  return true;
}
示例#10
0
void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list)
{
  /* ensure that ALSA has been initialized */
  if(!snd_config)
    snd_config_update();

  snd_ctl_t *ctlhandle;
  snd_pcm_t *pcmhandle;

  snd_ctl_card_info_t *ctlinfo;
  snd_ctl_card_info_alloca(&ctlinfo);
  memset(ctlinfo, 0, snd_ctl_card_info_sizeof());

  snd_pcm_hw_params_t *hwparams;
  snd_pcm_hw_params_alloca(&hwparams);
  memset(hwparams, 0, snd_pcm_hw_params_sizeof());

  snd_pcm_info_t *pcminfo;
  snd_pcm_info_alloca(&pcminfo);
  memset(pcminfo, 0, snd_pcm_info_sizeof());

  /* get the sound config */
  snd_config_t *config;
  snd_config_copy(&config, snd_config);

  std::string strHwName;
  int n_cards = -1;
  while (snd_card_next(&n_cards) == 0 && n_cards != -1)
  {
    std::stringstream sstr;
    sstr << "hw:" << n_cards;
    std::string strHwName = sstr.str();

    if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) != 0)
    {
      CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to open control for device %s", strHwName.c_str());
      continue;
    }

    if (snd_ctl_card_info(ctlhandle, ctlinfo) != 0)
    {
      CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to get card control info for device %s", strHwName.c_str());
      snd_ctl_close(ctlhandle);
      continue;
    }

    snd_hctl_t *hctl;
    if (snd_hctl_open_ctl(&hctl, ctlhandle) != 0)
      hctl = NULL;
    snd_hctl_load(hctl);

    int pcm_index    = 0;
    int iec958_index = 0;
    int hdmi_index   = 0;

    int dev = -1;
    while (snd_ctl_pcm_next_device(ctlhandle, &dev) == 0 && dev != -1)
    {
      snd_pcm_info_set_device   (pcminfo, dev);
      snd_pcm_info_set_subdevice(pcminfo, 0  );
      snd_pcm_info_set_stream   (pcminfo, SND_PCM_STREAM_PLAYBACK);

      if (snd_ctl_pcm_info(ctlhandle, pcminfo) < 0)
      {
        CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping device %s,%d as it does not have PCM playback ability", strHwName.c_str(), dev);
        continue;
      }

      int dev_index;
      sstr.str(std::string());
      CAEDeviceInfo info;
      std::string devname = snd_pcm_info_get_name(pcminfo);

      bool maybeHDMI = false;

      /* detect HDMI */
      if (devname.find("HDMI") != std::string::npos)
      { 
        info.m_deviceType = AE_DEVTYPE_HDMI;
        dev_index = hdmi_index++;
        sstr << "hdmi";
      }
      else
      {
        /* detect IEC958 */

        /* some HDMI devices (intel) report Digital for HDMI also */
        if (devname.find("Digital") != std::string::npos)
          maybeHDMI = true;

        if (maybeHDMI || devname.find("IEC958" ) != std::string::npos)
        {
          info.m_deviceType = AE_DEVTYPE_IEC958;
          dev_index = iec958_index; /* dont increment, it might be HDMI */
          sstr << "iec958";
        }
        else
        {
          info.m_deviceType = AE_DEVTYPE_PCM;
          dev_index = pcm_index++;
          sstr << "hw";
        }
      }

      /* build the driver string to pass to ALSA */
      sstr << ":CARD=" << snd_ctl_card_info_get_id(ctlinfo) << ",DEV=" << dev_index;
      info.m_deviceName = sstr.str();

      /* get the friendly display name*/
      info.m_displayName      = snd_ctl_card_info_get_name(ctlinfo);
      info.m_displayNameExtra = devname;

      /* open the device for testing */
      int err = snd_pcm_open_lconf(&pcmhandle, info.m_deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0, config);

      /* if open of possible IEC958 failed and it could be HDMI, try as HDMI */
      if (err < 0 && maybeHDMI)
      {
        /* check for HDMI if it failed */
        sstr.str(std::string());
        dev_index = hdmi_index;

        sstr << "hdmi";
        sstr << ":CARD=" << snd_ctl_card_info_get_id(ctlinfo) << ",DEV=" << dev_index;
        info.m_deviceName = sstr.str();
        err = snd_pcm_open_lconf(&pcmhandle, info.m_deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0, config);

        /* if it was valid, increment the index and set the type */
        if (err >= 0)
        {
          ++hdmi_index;
          info.m_deviceType = AE_DEVTYPE_HDMI;
        }
      }

      /* if it's still IEC958, increment the index */
      if (info.m_deviceType == AE_DEVTYPE_IEC958)
        ++iec958_index;

      /* final error check */
      if (err < 0)
      {
        CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - Unable to open %s for capability detection", strHwName.c_str());
        continue;
      }

      /* see if we can get ELD for this device */
      if (info.m_deviceType == AE_DEVTYPE_HDMI)
      {
        bool badHDMI = false;
        if (hctl && !GetELD(hctl, dev, info, badHDMI))
          CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to obtain ELD information for device %s, make sure you have ALSA >= 1.0.25", info.m_deviceName.c_str());

        if (badHDMI)
        {
          CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping HDMI device %s as it has no ELD data", info.m_deviceName.c_str());
          continue;
        }
      }

      /* ensure we can get a playback configuration for the device */
      if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0)
      {
        CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - No playback configurations available for device %s", info.m_deviceName.c_str());
        snd_pcm_close(pcmhandle);
        continue;
      }

      /* detect the available sample rates */
      for (unsigned int *rate = ALSASampleRateList; *rate != 0; ++rate)
        if (snd_pcm_hw_params_test_rate(pcmhandle, hwparams, *rate, 0) >= 0)
          info.m_sampleRates.push_back(*rate);

      /* detect the channels available */
      int channels = 0;
      for (int i = 1; i <= ALSA_MAX_CHANNELS; ++i)
        if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0)
          channels = i;

      CAEChannelInfo alsaChannels;
      for (int i = 0; i < channels; ++i)
      {
        if (!info.m_channels.HasChannel(ALSAChannelMap[i]))
          info.m_channels += ALSAChannelMap[i];
        alsaChannels += ALSAChannelMap[i];
      }

      /* remove the channels from m_channels that we cant use */
      info.m_channels.ResolveChannels(alsaChannels);

      /* detect the PCM sample formats that are available */
      for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1))
      {
        if (AE_IS_RAW(i) || i == AE_FMT_MAX)
          continue;
        snd_pcm_format_t fmt = AEFormatToALSAFormat(i);
        if (fmt == SND_PCM_FORMAT_UNKNOWN)
          continue;

        if (snd_pcm_hw_params_test_format(pcmhandle, hwparams, fmt) >= 0)
          info.m_dataFormats.push_back(i);
      }

      snd_pcm_close(pcmhandle);
      list.push_back(info);
    }

    /* snd_hctl_close also closes ctlhandle */
    if (hctl)
      snd_hctl_close(hctl);
    else
      snd_ctl_close(ctlhandle);
  }
}
示例#11
0
bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
{
  char response[80];
  /* if we are raw need to let gpu know */
  if (AE_IS_RAW(format.m_dataFormat))
  {
    vc_gencmd(response, sizeof response, "hdmi_stream_channels 1");
    m_passthrough = true;
  }
  else
  {
    vc_gencmd(response, sizeof response, "hdmi_stream_channels 0");
    m_passthrough = false;
  }

  m_initDevice = device;
  m_initFormat = format;
  // setup for a 50ms sink feed from SoftAE
  format.m_dataFormat    = AE_FMT_S16NE;
  format.m_frames        = format.m_sampleRate * AUDIO_PLAYBUFFER;
  format.m_frameSamples  = format.m_channelLayout.Count();
  format.m_frameSize     = format.m_frameSamples * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
  format.m_sampleRate    = std::max(8000U, std::min(96000U, format.m_sampleRate));

  m_format = format;

  m_sinkbuffer_size = format.m_frameSize * format.m_frames * NUM_OMX_BUFFERS;
  m_sinkbuffer_sec_per_byte = 1.0 / (double)(format.m_frameSize * format.m_sampleRate);

  CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__,
                format.m_dataFormat, format.m_channelLayout.Count(), format.m_sampleRate, format.m_frameSize, m_sinkbuffer_size, 1.0/m_sinkbuffer_sec_per_byte);

  // This may be called before Application calls g_RBP.Initialise, so call it here too
  g_RBP.Initialize();

  CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);

  OMX_ERRORTYPE omx_err   = OMX_ErrorNone;

  if (!m_omx_render.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
    CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);

  OMX_INIT_STRUCTURE(m_pcm_input);
  m_pcm_input.nPortIndex            = m_omx_render.GetInputPort();
  m_pcm_input.eNumData              = OMX_NumericalDataSigned;
  m_pcm_input.eEndian               = OMX_EndianLittle;
  m_pcm_input.bInterleaved          = OMX_TRUE;
  m_pcm_input.nBitPerSample         = 16;
  m_pcm_input.ePCMMode              = OMX_AUDIO_PCMModeLinear;
  m_pcm_input.nChannels             = m_format.m_frameSamples;
  m_pcm_input.nSamplingRate         = m_format.m_sampleRate;
  m_pcm_input.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
  m_pcm_input.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
  m_pcm_input.eChannelMapping[2] = OMX_AUDIO_ChannelMax;

  omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
  if (omx_err != OMX_ErrorNone)
    CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);

  m_omx_render.ResetEos();

  SetAudioDest();

  // set up the number/size of buffers for decoder input
  OMX_PARAM_PORTDEFINITIONTYPE port_param;
  OMX_INIT_STRUCTURE(port_param);
  port_param.nPortIndex = m_omx_render.GetInputPort();

  omx_err = m_omx_render.GetParameter(OMX_IndexParamPortDefinition, &port_param);
  if (omx_err != OMX_ErrorNone)
    CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);

  port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS);
  port_param.nBufferSize = m_sinkbuffer_size / port_param.nBufferCountActual;

  omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param);
  if (omx_err != OMX_ErrorNone)
    CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (intput) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);

  omx_err = m_omx_render.AllocInputBuffers();
  if (omx_err != OMX_ErrorNone)
    CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);

  omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
  if (omx_err != OMX_ErrorNone)
    CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);

  m_Initialized = true;
  return true;
}
示例#12
0
bool CAESinkIntelSMD::Initialize(AEAudioFormat &format, std::string &device)
{
  VERBOSE2();

  CLog::Log(LOGDEBUG, "%s: device: %s, data format: %s, sample rate: %d, channel count: %d, frame size: %d", __DEBUG_ID__, device.c_str(), CAEUtil::DataFormatToStr(format.m_dataFormat), format.m_sampleRate, format.m_channelLayout.Count(), format.m_frameSize);

  CSingleLock lock(m_SMDAudioLock);

  bool bIsHDMI = isHDMI(device);
  bool bIsSPDIF = isSPDIF(device);
  bool bIsAnalog = isAnalog(device);
  int deviceType = getDeviceType(device);

  ismd_result_t result;
  AEDataFormat inputDataFormat = format.m_dataFormat;

  bool bSPDIFPassthrough = false;
  bool bHDMIPassthrough = false;
  bool bIsRawCodec = AE_IS_RAW(inputDataFormat);

  format.m_sampleRate = getOutputSampleRate(deviceType, format.m_sampleRate);
  format.m_dataFormat = getAEDataFormat(deviceType, format.m_dataFormat, format.m_frameSize);

  int channels = format.m_channelLayout.Count();
  // can not support more than 2 channels on anything other than HDMI
  if (channels > 2 && (bIsSPDIF || bIsAnalog))
    channels = 2;
  // support for more than 8 channels not supported
  else if (channels > 8)
    channels = 8;


  ismd_audio_processor_t  audioProcessor = -1;
  ismd_audio_format_t     ismdAudioInputFormat = ISMD_AUDIO_MEDIA_FMT_INVALID;

  audioProcessor = g_IntelSMDGlobals.GetAudioProcessor();
  if(audioProcessor == -1)
  {
    CLog::Log(LOGERROR, "%s audioProcessor is not valid", __DEBUG_ID__);
    return false;
  }

  // disable all outputs
  g_IntelSMDGlobals.DisableAudioOutput(g_IntelSMDGlobals.GetHDMIOutput());
  g_IntelSMDGlobals.DisableAudioOutput(g_IntelSMDGlobals.GetSPDIFOutput());
  g_IntelSMDGlobals.DisableAudioOutput(g_IntelSMDGlobals.GetI2SOutput());

  m_audioDevice = g_IntelSMDGlobals.CreateAudioInput(false);
  if(m_audioDevice == -1)
  {
    CLog::Log(LOGERROR, "%s failed to create audio input", __DEBUG_ID__);
    return false;
  }

  g_IntelSMDGlobals.SetPrimaryAudioDevice(m_audioDevice);
  m_audioDeviceInput = g_IntelSMDGlobals.GetAudioDevicePort(m_audioDevice);
  if(m_audioDeviceInput == -1)
  {
    CLog::Log(LOGERROR, "%s failed to create audio input port", __DEBUG_ID__);
    return false;
  }

  ismdAudioInputFormat = GetISMDFormat(inputDataFormat);

  unsigned int uiBitsPerSample = CAEUtil::DataFormatToBits(format.m_dataFormat);
  unsigned int uiUsedBitsPerSample = CAEUtil::DataFormatToUsedBits(format.m_dataFormat);

  // Are we doing DD+ -> DD mode
  bool bAC3Encode = false;
  if (bIsHDMI)
  {
    unsigned int sampleRate = format.m_sampleRate;
    unsigned int bitsPerSample = uiUsedBitsPerSample;
    if (format.m_encodedRate != 0)
      sampleRate = format.m_encodedRate;

    unsigned int suggSampleRate = sampleRate;
    if (!CheckEDIDSupport(ismdAudioInputFormat, channels, suggSampleRate, bitsPerSample, bAC3Encode))
    {
      if (suggSampleRate != sampleRate)
        format.m_sampleRate = suggSampleRate;

      if (bitsPerSample != uiUsedBitsPerSample)
      {
        if (uiUsedBitsPerSample == 24)
        {
          format.m_dataFormat = AE_FMT_S24NE4;
          uiUsedBitsPerSample = bitsPerSample;
        }
        else if (uiUsedBitsPerSample == 32)
        {
          format.m_dataFormat = AE_FMT_S32LE;
          uiUsedBitsPerSample = bitsPerSample;
        }
        else
        {
          format.m_dataFormat = AE_FMT_S16LE;
          uiUsedBitsPerSample = 16;
        }
        
        uiBitsPerSample = CAEUtil::DataFormatToBits(format.m_dataFormat);
      }
      //format.m_frameSize = uiBitsPerSample/4;
    }  
  }
  else if (bIsSPDIF && ismdAudioInputFormat == ISMD_AUDIO_MEDIA_FMT_DD_PLUS && ISMD_SUCCESS == ismd_audio_codec_available((ismd_audio_format_t) ISMD_AUDIO_ENCODE_FMT_AC3))
  {
    bAC3Encode = true;
  }

  unsigned int outputSampleRate = format.m_sampleRate;
  
  // for raw codecs, send as PCM passthrough
  if (!bAC3Encode && bIsRawCodec && ismdAudioInputFormat != ISMD_AUDIO_MEDIA_FMT_DD && ismdAudioInputFormat != ISMD_AUDIO_MEDIA_FMT_TRUE_HD)
  {
    format.m_dataFormat = AE_FMT_S16NE;
    ismdAudioInputFormat = ISMD_AUDIO_MEDIA_FMT_PCM;
    bHDMIPassthrough = bSPDIFPassthrough = true;
  }

  format.m_channelLayout.Reset();
  if (bIsRawCodec)
  {
    for (int i = 0; i < channels; ++i)
      format.m_channelLayout += AE_CH_RAW;
  }
  // TODO: This currently handles Mono,Stereo, 5.1, 7.1 correctly
  // Handle the other cases (i.e. 6.1 DTS)
  else
  {
    for (int i = 0; i < channels; ++i)
      format.m_channelLayout += s_chMap[i];
  }

  //TODO: Handle non normal channel configs (3 channel, etc).
  int inputChannelConfig = AUDIO_CHAN_CONFIG_2_CH;
  if (format.m_channelLayout.Count() == 1)
    inputChannelConfig = AUDIO_CHAN_CONFIG_1_CH;
  else if (format.m_channelLayout.Count() == 6)
    inputChannelConfig = AUDIO_CHAN_CONFIG_6_CH;
  else if (format.m_channelLayout.Count() == 8)
    inputChannelConfig = AUDIO_CHAN_CONFIG_8_CH;

  format.m_frameSize = channels * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);

  // if standard audio keep buffer small so delay is short
  if (!bIsRawCodec)
  {
    // try to keep roughly 5ms buffer using multiples of a 1024 buffer size
    int numBuffers = ((0.005*((double)(format.m_sampleRate*format.m_frameSize))) / 1024.0) + 0.5;
    if (numBuffers == 0)
      numBuffers = 1;
    else if (numBuffers > 8)
      numBuffers = 8;
    m_dwChunkSize = numBuffers*1024;
  }
  else
  {
    m_dwChunkSize = 8*1024;
  }

  m_dwBufferLen = m_dwChunkSize;

  format.m_frames = m_dwChunkSize/format.m_frameSize;
  format.m_frameSamples = format.m_frames*channels;
  m_frameSize = format.m_frameSize;

  CLog::Log(LOGINFO, "%s ismdAudioInputFormat %d\n", __DEBUG_ID__,
      ismdAudioInputFormat);

  int counter = 0;
  while(counter < 5)
  {
    result = ismd_audio_input_set_data_format(audioProcessor, m_audioDevice, ismdAudioInputFormat);
    if (result != ISMD_SUCCESS)
    {
      CLog::Log(LOGERROR, "%s ismd_audio_input_set_data_format failed. retrying %d %d", __DEBUG_ID__, counter, result);
      counter++;
      usleep(1000);
    }
    else
      break;
  }
  
  switch( ismdAudioInputFormat )
  {
    case ISMD_AUDIO_MEDIA_FMT_DD:
      CLog::Log(LOGDEBUG, "%s: Initialize DD detected", __DEBUG_ID__);
      bHDMIPassthrough = bSPDIFPassthrough = true;
      break;
    case ISMD_AUDIO_MEDIA_FMT_DD_PLUS:
      CLog::Log(LOGDEBUG, "%s: Initialize DD Plus detected", __DEBUG_ID__);

      bHDMIPassthrough = true;
      // check special case for DD+->DD using DDCO 
      if(bAC3Encode)
      {
        CLog::Log(LOGDEBUG, "%s: Initialize EAC3->AC3 transcoding is on", __DEBUG_ID__);
        bHDMIPassthrough = false;
        bAC3Encode = true; 
        ConfigureDolbyPlusModes(audioProcessor, m_audioDevice, bAC3Encode);       
      }
      break;
    case ISMD_AUDIO_MEDIA_FMT_DTS:
    case ISMD_AUDIO_MEDIA_FMT_DTS_LBR:
      CLog::Log(LOGDEBUG, "%s: Initialize DTS detected", __DEBUG_ID__);
      bHDMIPassthrough = bSPDIFPassthrough = true;
      break;
    case ISMD_AUDIO_MEDIA_FMT_DTS_HD:
    case ISMD_AUDIO_MEDIA_FMT_DTS_HD_MA:
    case ISMD_AUDIO_MEDIA_FMT_DTS_HD_HRA:
      CLog::Log(LOGDEBUG, "%s: Initialize DTS-HD detected", __DEBUG_ID__);
      bHDMIPassthrough = true;
      outputSampleRate = format.m_encodedRate;
      channels = 2;
      break;
    case ISMD_AUDIO_MEDIA_FMT_TRUE_HD:
      CLog::Log(LOGDEBUG, "%s: Initialize TrueHD detected", __DEBUG_ID__);
      bHDMIPassthrough = true;
      outputSampleRate = format.m_encodedRate;
      channels = 2;
      break;
    case ISMD_AUDIO_MEDIA_FMT_PCM:
      result = ismd_audio_input_set_pcm_format(audioProcessor, m_audioDevice, uiBitsPerSample, format.m_sampleRate, inputChannelConfig);
      if (result != ISMD_SUCCESS)
      {
        CLog::Log(LOGERROR, "%s - ismd_audio_input_set_pcm_format: %d", __DEBUG_ID__, result);
    //    return false;
      }
      break;
    default:
      break;
  }
  
  
  // I2S. Nothing to touch here. we always use defaults

  // SPIDF
   if(bIsSPDIF)
   {
     ismd_audio_output_t OutputSPDIF = g_IntelSMDGlobals.GetSPDIFOutput();
     ismd_audio_output_config_t spdif_output_config;

     ConfigureAudioOutputParams(spdif_output_config, AE_DEVTYPE_IEC958, uiUsedBitsPerSample,
        outputSampleRate, channels, ismdAudioInputFormat, bSPDIFPassthrough, bAC3Encode);
     if(!g_IntelSMDGlobals.ConfigureAudioOutput(OutputSPDIF, spdif_output_config))
     {
       CLog::Log(LOGERROR, "%s ConfigureAudioOutput SPDIF failed %d", __DEBUG_ID__, result);
 //      return false;
     }
     
     //format.m_sampleRate = spdif_output_config.sample_rate;
   }

  // HDMI
  if(bIsHDMI)
  {
    ismd_audio_output_t OutputHDMI = g_IntelSMDGlobals.GetHDMIOutput();
    ismd_audio_output_config_t hdmi_output_config;
    ConfigureAudioOutputParams(hdmi_output_config, AE_DEVTYPE_HDMI, uiUsedBitsPerSample,
      outputSampleRate, channels, ismdAudioInputFormat, bHDMIPassthrough, bAC3Encode);
    if(!g_IntelSMDGlobals.ConfigureAudioOutput(OutputHDMI, hdmi_output_config))
    {
      CLog::Log(LOGERROR, "%s ConfigureAudioOutput HDMI failed %d", __DEBUG_ID__, result);
      return false;
    }
    //format.m_sampleRate = hdmi_output_config.sample_rate;
  }

  // Configure the master clock frequency

  CLog::Log(LOGINFO, "%s ConfigureMasterClock %d", __DEBUG_ID__, format.m_sampleRate);
  g_IntelSMDGlobals.ConfigureMasterClock(format.m_sampleRate);
  
  bSPDIFPassthrough = bIsSPDIF && bSPDIFPassthrough;
  bHDMIPassthrough = bIsHDMI && bHDMIPassthrough;
  ismd_audio_input_pass_through_config_t passthrough_config;
  memset(&passthrough_config, 0, sizeof(&passthrough_config));
  if (bSPDIFPassthrough || bHDMIPassthrough)
  {
    passthrough_config.is_pass_through = TRUE;
    passthrough_config.supported_format_count = 1;
    passthrough_config.supported_formats[0] = ismdAudioInputFormat;
  }

  result = ismd_audio_input_set_as_primary(audioProcessor, m_audioDevice, passthrough_config);
  if (result != ISMD_SUCCESS)
  {
    CLog::Log(LOGERROR, "%s  ismd_audio_input_set_as_primary failed %d", __DEBUG_ID__, result);
//      return false;
  }

  if(!g_IntelSMDGlobals.EnableAudioInput(m_audioDevice))
  {
    CLog::Log(LOGERROR, "%s  EnableAudioInput", __DEBUG_ID__);
//    return false;
  }

  // enable outputs
  if (bIsHDMI)
  {
    if(!g_IntelSMDGlobals.EnableAudioOutput(g_IntelSMDGlobals.GetHDMIOutput()))
    {
      CLog::Log(LOGERROR, "%s  EnableAudioOutput HDMI failed", __DEBUG_ID__);
     //   return false;
    }
  }
  
  if (bIsSPDIF)
  {
    if(!g_IntelSMDGlobals.EnableAudioOutput(g_IntelSMDGlobals.GetSPDIFOutput()))
    {
      CLog::Log(LOGERROR, "%s  EnableAudioOutput SPDIF failed", __DEBUG_ID__);
//     return false;
    }
  }
  
  if (bIsAnalog)
  {
    if(!g_IntelSMDGlobals.EnableAudioOutput(g_IntelSMDGlobals.GetI2SOutput()))
    {
      CLog::Log(LOGERROR, "%s  EnableAudioOutput I2S failed", __DEBUG_ID__);
 //      return false;
    }
  }
  
  g_IntelSMDGlobals.SetAudioDeviceState(ISMD_DEV_STATE_PLAY, m_audioDevice);

//  m_fCurrentVolume = g_settings.m_fVolumeLevel;
  //g_IntelSMDGlobals.SetMasterVolume(m_fCurrentVolume);

  m_bPause = false;
  m_dSampleRate = format.m_sampleRate;
  m_bIsAllocated = true;

  // set latency when using passthrough since we are not using a timed audio interface
  if (bAC3Encode || ismdAudioInputFormat == ISMD_AUDIO_MEDIA_FMT_DD)
    m_latency = 0.675;//0.45;

  CLog::Log(LOGINFO, "%s done", __DEBUG_ID__);

  return true;
}