summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoragoode <agoode@chromium.org>2015-03-08 12:40:17 -0700
committerCommit bot <commit-bot@chromium.org>2015-03-08 19:40:54 +0000
commit2e1bef845184269974b3dc42f7864add125b8466 (patch)
treecaeb5b98c89e348bcf753acfa25c275c5838a6c2
parent30816d9aa5e2a8143d16dfb3f8110ff458c7354f (diff)
downloadchromium_src-2e1bef845184269974b3dc42f7864add125b8466.zip
chromium_src-2e1bef845184269974b3dc42f7864add125b8466.tar.gz
chromium_src-2e1bef845184269974b3dc42f7864add125b8466.tar.bz2
Improve MidiManagerAlsa manufacturer reporting
This is dependent on https://codereview.chromium.org/965903003/ BUG=377250 Review URL: https://codereview.chromium.org/968663004 Cr-Commit-Position: refs/heads/master@{#319597}
-rw-r--r--media/BUILD.gn8
-rw-r--r--media/DEPS1
-rw-r--r--media/media.gyp10
-rw-r--r--media/midi/midi_manager_alsa.cc172
-rw-r--r--media/midi/midi_manager_alsa.h51
-rw-r--r--media/midi/midi_manager_alsa_unittest.cc77
6 files changed, 285 insertions, 34 deletions
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 31a6efd..a19ec54 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -361,6 +361,10 @@ component("media") {
]
}
+ if (use_udev) {
+ deps += [ "//device/udev_linux" ]
+ }
+
# A simple WebM encoder for animated avatars on ChromeOS.
if (use_ozone) {
@@ -671,6 +675,10 @@ test("media_unittests") {
]
}
+ if (use_alsa) {
+ sources += [ "midi/midi_manager_alsa_unittest.cc" ]
+ }
+
# include_dirs += [
# # Needed by media_drm_bridge.cc.
# target_gen_dir,
diff --git a/media/DEPS b/media/DEPS
index cad0352..13abdcf 100644
--- a/media/DEPS
+++ b/media/DEPS
@@ -1,5 +1,6 @@
# Do NOT add net/ or ui/base without a great reason, they're huge!
include_rules = [
+ "+device/udev_linux",
"+gpu",
"+jni",
"+skia/ext",
diff --git a/media/media.gyp b/media/media.gyp
index ea044b9..d012137 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -1087,6 +1087,11 @@
'filters/source_buffer_platform.h',
]
}],
+ ['use_udev==1', {
+ 'dependencies': [
+ '../device/udev_linux/udev.gyp:udev_linux',
+ ],
+ }],
], # conditions
'target_conditions': [
['OS == "ios" and _toolset != "host"', {
@@ -1401,6 +1406,11 @@
'video/capture/mac/video_capture_device_factory_mac_unittest.mm',
]
}],
+ ['use_alsa==1', {
+ 'sources': [
+ 'midi/midi_manager_alsa_unittest.cc',
+ ]
+ }],
],
},
{
diff --git a/media/midi/midi_manager_alsa.cc b/media/midi/midi_manager_alsa.cc
index 9043f1f..477f220 100644
--- a/media/midi/midi_manager_alsa.cc
+++ b/media/midi/midi_manager_alsa.cc
@@ -15,6 +15,7 @@
#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
@@ -41,16 +42,15 @@ int AddrToInt(const snd_seq_addr_t* addr) {
return (addr->client << 8) | addr->port;
}
-class CardInfo {
- public:
- CardInfo(const std::string name, const std::string manufacturer,
- const std::string driver)
- : name_(name), manufacturer_(manufacturer), driver_(driver) {
- }
- const std::string name_;
- const std::string manufacturer_;
- const std::string driver_;
-};
+#if defined(USE_UDEV)
+// Copied from components/storage_monitor/udev_util_linux.cc.
+// TODO(agoode): Move this into a common place. Maybe device/udev_linux?
+std::string GetUdevDevicePropertyValue(udev_device* udev_device,
+ const char* key) {
+ const char* value = device::udev_device_get_property_value(udev_device, key);
+ return value ? value : std::string();
+}
+#endif // defined(USE_UDEV)
} // namespace
@@ -60,6 +60,9 @@ MidiManagerAlsa::MidiManagerAlsa()
out_client_id_(-1),
in_port_(-1),
decoder_(NULL),
+#if defined(USE_UDEV)
+ udev_(device::udev_new()),
+#endif // defined(USE_UDEV)
send_thread_("MidiSendThread"),
event_thread_("MidiEventThread"),
event_thread_shutdown_(false) {
@@ -126,11 +129,9 @@ void MidiManagerAlsa::StartInitialization() {
return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
}
- // Use a heuristic to extract the list of manufacturers for the hardware MIDI
+ // Extract the list of manufacturers for the hardware MIDI
// devices. This won't work for all devices. It is also brittle until
- // hotplug is implemented. (See http://crbug.com/279097.)
- // TODO(agoode): Make manufacturer extraction simple and reliable.
- // http://crbug.com/377250.
+ // hotplug is implemented. (See http://crbug.com/431489.)
ScopedVector<CardInfo> cards;
snd_ctl_card_info_t* card;
snd_rawmidi_info_t* midi_out;
@@ -138,8 +139,8 @@ void MidiManagerAlsa::StartInitialization() {
snd_ctl_card_info_alloca(&card);
snd_rawmidi_info_alloca(&midi_out);
snd_rawmidi_info_alloca(&midi_in);
- for (int index = -1; !snd_card_next(&index) && index >= 0; ) {
- const std::string id = base::StringPrintf("hw:CARD=%i", index);
+ for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0; ) {
+ const std::string id = base::StringPrintf("hw:CARD=%i", card_index);
snd_ctl_t* handle;
int err = snd_ctl_open(&handle, id.c_str(), 0);
if (err != 0) {
@@ -168,21 +169,14 @@ void MidiManagerAlsa::StartInitialization() {
if (!output && !input)
continue;
+ // Compute and save Alsa and udev properties.
snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in;
- const std::string name = snd_rawmidi_info_get_name(midi);
- // We assume that card longname is in the format of
- // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
- // a manufacturer name here.
- std::string manufacturer;
- const std::string card_name = snd_ctl_card_info_get_longname(card);
- size_t at_index = card_name.rfind(" at ");
- if (std::string::npos != at_index) {
- size_t name_index = card_name.rfind(name, at_index - 1);
- if (std::string::npos != name_index)
- manufacturer = card_name.substr(0, name_index - 1);
- }
- const std::string driver = snd_ctl_card_info_get_driver(card);
- cards.push_back(new CardInfo(name, manufacturer, driver));
+ cards.push_back(new CardInfo(
+ this,
+ snd_rawmidi_info_get_name(midi),
+ snd_ctl_card_info_get_longname(card),
+ snd_ctl_card_info_get_driver(card),
+ card_index));
}
snd_ctl_close(handle);
}
@@ -214,9 +208,9 @@ void MidiManagerAlsa::StartInitialization() {
if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) &&
(current_card < cards.size())) {
const CardInfo* info = cards[current_card];
- if (info->name_ == client_name) {
- manufacturer = info->manufacturer_;
- driver = info->driver_;
+ if (info->alsa_name() == client_name) {
+ manufacturer = info->manufacturer();
+ driver = info->alsa_driver();
current_card++;
}
}
@@ -335,6 +329,62 @@ MidiManagerAlsa::~MidiManagerAlsa() {
snd_midi_event_free(*i);
}
+MidiManagerAlsa::CardInfo::CardInfo(
+ const MidiManagerAlsa* outer,
+ const std::string& alsa_name, const std::string& alsa_longname,
+ const std::string& alsa_driver, int card_index)
+ : alsa_name_(alsa_name), alsa_driver_(alsa_driver) {
+ // Get udev properties if available.
+ std::string udev_id_vendor_enc;
+ std::string udev_id_vendor_id;
+ std::string udev_id_vendor_from_database;
+
+#if defined(USE_UDEV)
+ const std::string sysname = base::StringPrintf("card%i", card_index);
+ device::ScopedUdevDevicePtr udev_device(
+ device::udev_device_new_from_subsystem_sysname(
+ outer->udev_.get(), "sound", sysname.c_str()));
+ udev_id_vendor_enc = GetUdevDevicePropertyValue(
+ udev_device.get(), "ID_VENDOR_ENC");
+ udev_id_vendor_id = GetUdevDevicePropertyValue(
+ udev_device.get(), "ID_VENDOR_ID");
+ udev_id_vendor_from_database = GetUdevDevicePropertyValue(
+ udev_device.get(), "ID_VENDOR_FROM_DATABASE");
+
+ udev_id_path_ = GetUdevDevicePropertyValue(
+ udev_device.get(), "ID_PATH");
+ udev_id_id_ = GetUdevDevicePropertyValue(
+ udev_device.get(), "ID_ID");
+#endif // defined(USE_UDEV)
+
+ manufacturer_ = ExtractManufacturerString(
+ udev_id_vendor_enc, udev_id_vendor_id, udev_id_vendor_from_database,
+ alsa_name, alsa_longname);
+}
+
+MidiManagerAlsa::CardInfo::~CardInfo() {
+}
+
+const std::string MidiManagerAlsa::CardInfo::alsa_name() const {
+ return alsa_name_;
+}
+
+const std::string MidiManagerAlsa::CardInfo::manufacturer() const {
+ return manufacturer_;
+}
+
+const std::string MidiManagerAlsa::CardInfo::alsa_driver() const {
+ return alsa_driver_;
+}
+
+const std::string MidiManagerAlsa::CardInfo::udev_id_path() const {
+ return udev_id_path_;
+}
+
+const std::string MidiManagerAlsa::CardInfo::udev_id_id() const {
+ return udev_id_id_;
+}
+
void MidiManagerAlsa::SendMidiData(uint32 port_index,
const std::vector<uint8>& data) {
DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread());
@@ -447,6 +497,62 @@ void MidiManagerAlsa::EventLoop() {
base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
}
+// static
+std::string MidiManagerAlsa::CardInfo::ExtractManufacturerString(
+ const std::string& udev_id_vendor_enc,
+ const std::string& udev_id_vendor_id,
+ const std::string& udev_id_vendor_from_database,
+ const std::string& alsa_name,
+ const std::string& alsa_longname) {
+ // Let's try to determine the manufacturer. Here is the ordered preference
+ // in extraction:
+ // 1. Vendor name from the USB device iManufacturer string, stored in
+ // udev_id_vendor_enc.
+ // 2. Vendor name from the udev hwid database.
+ // 3. Heuristic from ALSA.
+
+ // Is the vendor string not just the USB vendor hex id?
+ std::string udev_id_vendor = UnescapeUdev(udev_id_vendor_enc);
+ if (udev_id_vendor != udev_id_vendor_id) {
+ return udev_id_vendor;
+ }
+
+ // Is there a vendor string in the hardware database?
+ if (!udev_id_vendor_from_database.empty()) {
+ return udev_id_vendor_from_database;
+ }
+
+ // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
+ // We assume that card longname is in the format of
+ // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
+ // a manufacturer name here.
+ size_t at_index = alsa_longname.rfind(" at ");
+ if (std::string::npos != at_index) {
+ size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
+ if (std::string::npos != name_index)
+ return alsa_longname.substr(0, name_index - 1);
+ }
+
+ // Failure.
+ return "";
+}
+
+// static
+std::string MidiManagerAlsa::CardInfo::UnescapeUdev(const std::string& s) {
+ std::string unescaped;
+ const size_t size = s.size();
+ for (size_t i = 0; i < size; ++i) {
+ char c = s[i];
+ if ((i + 3 < size) && c == '\\' && s[i + 1] == 'x') {
+ c = (HexDigitToInt(s[i + 2]) << 4) +
+ HexDigitToInt(s[i + 3]);
+ i += 3;
+ }
+ unescaped.push_back(c);
+ }
+ return unescaped;
+}
+
MidiManager* MidiManager::Create() {
return new MidiManagerAlsa();
}
diff --git a/media/midi/midi_manager_alsa.h b/media/midi/midi_manager_alsa.h
index cbe03c5..d06ff76 100644
--- a/media/midi/midi_manager_alsa.h
+++ b/media/midi/midi_manager_alsa.h
@@ -10,14 +10,19 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
#include "media/midi/midi_manager.h"
+#if defined(USE_UDEV)
+#include "device/udev_linux/scoped_udev.h"
+#endif // defined(USE_UDEV)
+
namespace media {
-class MidiManagerAlsa : public MidiManager {
+class MEDIA_EXPORT MidiManagerAlsa : public MidiManager {
public:
MidiManagerAlsa();
~MidiManagerAlsa() override;
@@ -30,6 +35,45 @@ class MidiManagerAlsa : public MidiManager {
double timestamp) override;
private:
+ FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest, ExtractManufacturer);
+ FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest, UdevEscape);
+
+ class CardInfo {
+ public:
+ CardInfo(const MidiManagerAlsa* outer,
+ const std::string& alsa_name, const std::string& alsa_longname,
+ const std::string& alsa_driver, int card_index);
+ ~CardInfo();
+
+ const std::string alsa_name() const;
+ const std::string manufacturer() const;
+ const std::string alsa_driver() const;
+ const std::string udev_id_path() const;
+ const std::string udev_id_id() const;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest, ExtractManufacturer);
+ FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest, UdevEscape);
+
+ // Extracts the manufacturer using heuristics and a variety of sources.
+ static std::string ExtractManufacturerString(
+ const std::string& udev_id_vendor_enc,
+ const std::string& udev_id_vendor_id,
+ const std::string& udev_id_vendor_from_database,
+ const std::string& alsa_name,
+ const std::string& alsa_longname);
+
+ // TODO(agoode): Move this into a common place. Maybe device/udev_linux?
+ // Decodes just \xXX in strings.
+ static std::string UnescapeUdev(const std::string& s);
+
+ std::string alsa_name_;
+ std::string manufacturer_;
+ std::string alsa_driver_;
+ std::string udev_id_path_;
+ std::string udev_id_id_;
+ };
+
// An internal callback that runs on MidiSendThread.
void SendMidiData(uint32 port_index,
const std::vector<uint8>& data);
@@ -55,6 +99,11 @@ class MidiManagerAlsa : public MidiManager {
typedef std::vector<snd_midi_event_t*> EncoderList;
EncoderList encoders_;
+ // udev, for querying hardware devices.
+#if defined(USE_UDEV)
+ device::ScopedUdevPtr udev_;
+#endif // defined(USE_UDEV)
+
base::Thread send_thread_;
base::Thread event_thread_;
diff --git a/media/midi/midi_manager_alsa_unittest.cc b/media/midi/midi_manager_alsa_unittest.cc
new file mode 100644
index 0000000..6589827
--- /dev/null
+++ b/media/midi/midi_manager_alsa_unittest.cc
@@ -0,0 +1,77 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/midi/midi_manager_alsa.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+TEST(MidiManagerAlsaTest, UdevEscape) {
+ ASSERT_EQ("", MidiManagerAlsa::CardInfo::UnescapeUdev(""));
+ ASSERT_EQ("\\", MidiManagerAlsa::CardInfo::UnescapeUdev("\\x5c"));
+ ASSERT_EQ("\\x5", MidiManagerAlsa::CardInfo::UnescapeUdev("\\x5"));
+ ASSERT_EQ("049f", MidiManagerAlsa::CardInfo::UnescapeUdev("049f"));
+ ASSERT_EQ("HD Pro Webcam C920",
+ MidiManagerAlsa::CardInfo::UnescapeUdev(
+ "HD\\x20Pro\\x20Webcam\\x20C920"));
+ ASSERT_EQ("E-MU Systems,Inc.",
+ MidiManagerAlsa::CardInfo::UnescapeUdev(
+ "E-MU\\x20Systems\\x2cInc."));
+}
+
+TEST(MidiManagerAlsaTest, ExtractManufacturer) {
+ ASSERT_EQ("My Vendor",
+ MidiManagerAlsa::CardInfo::ExtractManufacturerString(
+ "My\\x20Vendor",
+ "1234",
+ "My Vendor, Inc.",
+ "Card",
+ "My Vendor Inc Card at bus"));
+ ASSERT_EQ("My Vendor, Inc.",
+ MidiManagerAlsa::CardInfo::ExtractManufacturerString(
+ "1234",
+ "1234",
+ "My Vendor, Inc.",
+ "Card",
+ "My Vendor Inc Card at bus"));
+ ASSERT_EQ("My Vendor Inc",
+ MidiManagerAlsa::CardInfo::ExtractManufacturerString(
+ "1234",
+ "1234",
+ "",
+ "Card",
+ "My Vendor Inc Card at bus"));
+ ASSERT_EQ("My Vendor Inc",
+ MidiManagerAlsa::CardInfo::ExtractManufacturerString(
+ "",
+ "",
+ "",
+ "Card",
+ "My Vendor Inc Card at bus"));
+ ASSERT_EQ("",
+ MidiManagerAlsa::CardInfo::ExtractManufacturerString("1234",
+ "1234",
+ "",
+ "Card",
+ "Longname"));
+ ASSERT_EQ("Keystation Mini 32",
+ MidiManagerAlsa::CardInfo::ExtractManufacturerString(
+ "Keystation\\x20Mini\\x2032",
+ "129d",
+ "Evolution Electronics, Ltd",
+ "Keystation Mini 32",
+ "Keystation Mini 32 Keystation Mini 32 at"
+ " usb-0000:00:14.0-2.4.4, full speed"));
+ ASSERT_EQ("Keystation Mini 32",
+ MidiManagerAlsa::CardInfo::ExtractManufacturerString(
+ "",
+ "",
+ "",
+ "Keystation Mini 32",
+ "Keystation Mini 32 Keystation Mini 32 at"
+ " usb-0000:00:14.0-2.4.4, full speed"));
+}
+
+} // namespace media