예제 #1
0
void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
{
  /* ensure that ALSA has been initialized */
  snd_lib_error_set_handler(sndLibErrorHandler);
  if(!snd_config || force)
  {
    if(force)
      snd_config_update_free_global();

    snd_config_update();
  }

  snd_config_t *config;
  snd_config_copy(&config, snd_config);

  /* Always enumerate the default device.
   * Note: If "default" is a stereo device, EnumerateDevice()
   * will automatically add "@" instead to enable surroundXX mangling.
   * We don't want to do that if "default" can handle multichannel
   * itself (e.g. in case of a pulseaudio server). */
  EnumerateDevice(list, "default", "", config);

  void **hints;

  if (snd_device_name_hint(-1, "pcm", &hints) < 0)
  {
    CLog::Log(LOGINFO, "CAESinkALSA - Unable to get a list of devices");
    return;
  }

  std::string defaultDescription;

  for (void** hint = hints; *hint != NULL; ++hint)
  {
    char *io = snd_device_name_get_hint(*hint, "IOID");
    char *name = snd_device_name_get_hint(*hint, "NAME");
    char *desc = snd_device_name_get_hint(*hint, "DESC");
    if ((!io || strcmp(io, "Output") == 0) && name
        && strcmp(name, "null") != 0)
    {
      std::string baseName = std::string(name);
      baseName = baseName.substr(0, baseName.find(':'));

      if (strcmp(name, "default") == 0)
      {
        /* added already, but lets get the description if we have one */
        if (desc)
          defaultDescription = desc;
      }
      else if (baseName == "front")
      {
        /* Enumerate using the surroundXX mangling */
        /* do not enumerate basic "front", it is already handled
         * by the default "@" entry added in the very beginning */
        if (strcmp(name, "front") != 0)
          EnumerateDevice(list, std::string("@") + (name+5), desc ? desc : name, config);
      }

      /* Do not enumerate "default", it is already enumerated above. */

      /* Do not enumerate the sysdefault or surroundXX devices, those are
       * always accompanied with a "front" device and it is handled above
       * as "@". The below devices will be automatically used if available
       * for a "@" device. */

      /* Ubuntu has patched their alsa-lib so that "defaults.namehint.extended"
       * defaults to "on" instead of upstream "off", causing lots of unwanted
       * extra devices (many of which are not actually routed properly) to be
       * found by the enumeration process. Skip them as well ("hw", "dmix",
       * "plughw", "dsnoop"). */

      else if (baseName != "default"
            && baseName != "sysdefault"
            && baseName != "surround40"
            && baseName != "surround41"
            && baseName != "surround50"
            && baseName != "surround51"
            && baseName != "surround71"
            && baseName != "hw"
            && baseName != "dmix"
            && baseName != "plughw"
            && baseName != "dsnoop")
      {
        EnumerateDevice(list, name, desc ? desc : name, config);
      }
    }
    free(io);
    free(name);
    free(desc);
  }
  snd_device_name_free_hint(hints);

  /* set the displayname for default device */
  if (!list.empty() && list[0].m_deviceName == "default")
  {
    /* If we have one from a hint (DESC), use it */
    if (!defaultDescription.empty())
      list[0].m_displayName = defaultDescription;
    /* Otherwise use the discovered name or (unlikely) "Default" */
    else if (list[0].m_displayName.empty())
      list[0].m_displayName = "Default";
  }

  /* lets check uniqueness, we may need to append DEV or CARD to DisplayName */
  /* If even a single device of card/dev X clashes with Y, add suffixes to
   * all devices of both them, for clarity. */

  /* clashing card names, e.g. "NVidia", "NVidia_2" */
  std::set<std::string> cardsToAppend;

  /* clashing basename + cardname combinations, e.g. ("hdmi","Nvidia") */
  std::set<std::pair<std::string, std::string> > devsToAppend;

  for (AEDeviceInfoList::iterator it1 = list.begin(); it1 != list.end(); ++it1)
  {
    for (AEDeviceInfoList::iterator it2 = it1+1; it2 != list.end(); ++it2)
    {
      if (it1->m_displayName == it2->m_displayName
       && it1->m_displayNameExtra == it2->m_displayNameExtra)
      {
        /* something needs to be done */
        std::string cardString1 = GetParamFromName(it1->m_deviceName, "CARD");
        std::string cardString2 = GetParamFromName(it2->m_deviceName, "CARD");

        if (cardString1 != cardString2)
        {
          /* card name differs, add identifiers to all devices */
          cardsToAppend.insert(cardString1);
          cardsToAppend.insert(cardString2);
          continue;
        }

        std::string devString1 = GetParamFromName(it1->m_deviceName, "DEV");
        std::string devString2 = GetParamFromName(it2->m_deviceName, "DEV");

        if (devString1 != devString2)
        {
          /* device number differs, add identifiers to all such devices */
          devsToAppend.insert(std::make_pair(it1->m_deviceName.substr(0, it1->m_deviceName.find(':')), cardString1));
          devsToAppend.insert(std::make_pair(it2->m_deviceName.substr(0, it2->m_deviceName.find(':')), cardString2));
          continue;
        }

        /* if we got here, the configuration is really weird, just append the whole device string */
        it1->m_displayName += " (" + it1->m_deviceName + ")";
        it2->m_displayName += " (" + it2->m_deviceName + ")";
      }
    }
  }

  for (std::set<std::string>::iterator it = cardsToAppend.begin();
       it != cardsToAppend.end(); ++it)
  {
    for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl)
    {
      std::string cardString = GetParamFromName(itl->m_deviceName, "CARD");
      if (cardString == *it)
        /* "HDA NVidia (NVidia)", "HDA NVidia (NVidia_2)", ... */
        itl->m_displayName += " (" + cardString + ")";
    }
  }

  for (std::set<std::pair<std::string, std::string> >::iterator it = devsToAppend.begin();
       it != devsToAppend.end(); ++it)
  {
    for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl)
    {
      std::string baseName = itl->m_deviceName.substr(0, itl->m_deviceName.find(':'));
      std::string cardString = GetParamFromName(itl->m_deviceName, "CARD");
      if (baseName == it->first && cardString == it->second)
      {
        std::string devString = GetParamFromName(itl->m_deviceName, "DEV");
        /* "HDMI #0", "HDMI #1" ... */
        itl->m_displayNameExtra += " #" + devString;
      }
    }
  }
}