Commit 40405201 authored by toyoshim's avatar toyoshim Committed by Commit bot

Web MIDI: make MidiManagerMac notify device connection status

To handle device connection status, MidiManager and MidiMessageFilter
hold all devices as a list. These lists are modified by event
notifications that are delivered asynchronously.

They do not remove disconnected devices, but just mark as disconnected.
This is useful when JavaScript has a reference to a device
that got disconnected. The reference can be availalbe if the device
gets connected again.

BUG=422333
TEST=media_unittests

Review URL: https://codereview.chromium.org/662233002

Cr-Commit-Position: refs/heads/master@{#317000}
parent f3a85349
......@@ -145,6 +145,14 @@ void MidiHost::AddOutputPort(const media::MidiPortInfo& info) {
Send(new MidiMsg_AddOutputPort(info));
}
void MidiHost::SetInputPortState(uint32 port, media::MidiPortState state) {
Send(new MidiMsg_SetInputPortState(port, state));
}
void MidiHost::SetOutputPortState(uint32 port, media::MidiPortState state) {
Send(new MidiMsg_SetOutputPortState(port, state));
}
void MidiHost::ReceiveMidiData(
uint32 port,
const uint8* data,
......
......@@ -16,6 +16,7 @@
#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/browser_thread.h"
#include "media/midi/midi_manager.h"
#include "media/midi/midi_port_info.h"
namespace media {
class MidiManager;
......@@ -39,6 +40,8 @@ class CONTENT_EXPORT MidiHost
void CompleteStartSession(media::MidiResult result) override;
void AddInputPort(const media::MidiPortInfo& info) override;
void AddOutputPort(const media::MidiPortInfo& info) override;
void SetInputPortState(uint32 port, media::MidiPortState state) override;
void SetOutputPortState(uint32 port, media::MidiPortState state) override;
void ReceiveMidiData(uint32 port,
const uint8* data,
size_t length,
......
......@@ -17,11 +17,15 @@
#define IPC_MESSAGE_EXPORT CONTENT_EXPORT
#define IPC_MESSAGE_START MidiMsgStart
IPC_ENUM_TRAITS_MAX_VALUE(media::MidiPortState,
media::MIDI_PORT_STATE_LAST)
IPC_STRUCT_TRAITS_BEGIN(media::MidiPortInfo)
IPC_STRUCT_TRAITS_MEMBER(id)
IPC_STRUCT_TRAITS_MEMBER(manufacturer)
IPC_STRUCT_TRAITS_MEMBER(name)
IPC_STRUCT_TRAITS_MEMBER(version)
IPC_STRUCT_TRAITS_MEMBER(state)
IPC_STRUCT_TRAITS_END()
IPC_ENUM_TRAITS_MAX_VALUE(media::MidiResult, media::MIDI_RESULT_LAST)
......@@ -46,6 +50,14 @@ IPC_MESSAGE_CONTROL1(MidiMsg_AddInputPort,
IPC_MESSAGE_CONTROL1(MidiMsg_AddOutputPort,
media::MidiPortInfo /* output port */)
IPC_MESSAGE_CONTROL2(MidiMsg_SetInputPortState,
uint32 /* port */,
media::MidiPortState /* state */)
IPC_MESSAGE_CONTROL2(MidiMsg_SetOutputPortState,
uint32 /* port */,
media::MidiPortState /* state */)
IPC_MESSAGE_CONTROL1(MidiMsg_SessionStarted,
media::MidiResult /* result */)
......
......@@ -116,6 +116,8 @@ bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted)
IPC_MESSAGE_HANDLER(MidiMsg_AddInputPort, OnAddInputPort)
IPC_MESSAGE_HANDLER(MidiMsg_AddOutputPort, OnAddOutputPort)
IPC_MESSAGE_HANDLER(MidiMsg_SetInputPortState, OnSetInputPortState)
IPC_MESSAGE_HANDLER(MidiMsg_SetOutputPortState, OnSetOutputPortState)
IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived)
IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData)
IPC_MESSAGE_UNHANDLED(handled = false)
......@@ -163,6 +165,24 @@ void MidiMessageFilter::OnAddOutputPort(media::MidiPortInfo info) {
base::Bind(&MidiMessageFilter::HandleAddOutputPort, this, info));
}
void MidiMessageFilter::OnSetInputPortState(uint32 port,
media::MidiPortState state) {
DCHECK(io_message_loop_->BelongsToCurrentThread());
main_message_loop_->PostTask(
FROM_HERE,
base::Bind(&MidiMessageFilter::HandleSetInputPortState,
this, port, state));
}
void MidiMessageFilter::OnSetOutputPortState(uint32 port,
media::MidiPortState state) {
DCHECK(io_message_loop_->BelongsToCurrentThread());
main_message_loop_->PostTask(
FROM_HERE,
base::Bind(&MidiMessageFilter::HandleSetOutputPortState,
this, port, state));
}
void MidiMessageFilter::OnDataReceived(uint32 port,
const std::vector<uint8>& data,
double timestamp) {
......@@ -265,4 +285,18 @@ void MidiMessageFilter::HandleAckknowledgeSentData(size_t bytes_sent) {
unacknowledged_bytes_sent_ -= bytes_sent;
}
void MidiMessageFilter::HandleSetInputPortState(uint32 port,
media::MidiPortState state) {
DCHECK(main_message_loop_->BelongsToCurrentThread());
inputs_[port].state = state;
// TODO(toyoshim): Notify to all clients.
}
void MidiMessageFilter::HandleSetOutputPortState(uint32 port,
media::MidiPortState state) {
DCHECK(main_message_loop_->BelongsToCurrentThread());
outputs_[port].state = state;
// TODO(toyoshim): Notify to all clients.
}
} // namespace content
......@@ -77,6 +77,12 @@ class CONTENT_EXPORT MidiMessageFilter : public IPC::MessageFilter {
void OnAddInputPort(media::MidiPortInfo info);
void OnAddOutputPort(media::MidiPortInfo info);
// These functions are called to notify the recipient that a device that is
// notified via OnAddInputPort() or OnAddOutputPort() gets disconnected, or
// connected again.
void OnSetInputPortState(uint32 port, media::MidiPortState state);
void OnSetOutputPortState(uint32 port, media::MidiPortState state);
// Called when the browser process has sent MIDI data containing one or
// more messages.
void OnDataReceived(uint32 port,
......@@ -93,6 +99,8 @@ class CONTENT_EXPORT MidiMessageFilter : public IPC::MessageFilter {
void HandleAddInputPort(media::MidiPortInfo info);
void HandleAddOutputPort(media::MidiPortInfo info);
void HandleSetInputPortState(uint32 port, media::MidiPortState state);
void HandleSetOutputPortState(uint32 port, media::MidiPortState state);
void HandleDataReceived(uint32 port,
const std::vector<uint8>& data,
......
......@@ -130,6 +130,22 @@ void MidiManager::AddOutputPort(const MidiPortInfo& info) {
client->AddOutputPort(info);
}
void MidiManager::SetInputPortState(uint32 port_index, MidiPortState state) {
base::AutoLock auto_lock(lock_);
DCHECK_LT(port_index, input_ports_.size());
input_ports_[port_index].state = state;
for (auto client : clients_)
client->SetInputPortState(port_index, state);
}
void MidiManager::SetOutputPortState(uint32 port_index, MidiPortState state) {
base::AutoLock auto_lock(lock_);
DCHECK_LT(port_index, output_ports_.size());
output_ports_[port_index].state = state;
for (auto client : clients_)
client->SetOutputPortState(port_index, state);
}
void MidiManager::ReceiveMidiData(
uint32 port_index,
const uint8* data,
......
......@@ -35,10 +35,10 @@ class MEDIA_EXPORT MidiManagerClient {
virtual void AddInputPort(const MidiPortInfo& info) = 0;
virtual void AddOutputPort(const MidiPortInfo& info) = 0;
// TODO(toyoshim): DisableInputPort(const MidiPortInfo& info) and
// DisableOutputPort(const MidiPortInfo& info) should be added.
// On DisableInputPort(), internal states, e.g. received_messages_queues in
// MidiHost, should be reset.
// SetInputPortState() and SetOutputPortState() are called to notify a known
// device gets disconnected, or connected again.
virtual void SetInputPortState(uint32 port_index, MidiPortState state) = 0;
virtual void SetOutputPortState(uint32 port_index, MidiPortState state) = 0;
// CompleteStartSession() is called when platform dependent preparation is
// finished.
......@@ -122,6 +122,8 @@ class MEDIA_EXPORT MidiManager {
void AddInputPort(const MidiPortInfo& info);
void AddOutputPort(const MidiPortInfo& info);
void SetInputPortState(uint32 port_index, MidiPortState state);
void SetOutputPortState(uint32 port_index, MidiPortState state);
// Dispatches to all clients.
// TODO(toyoshim): Fix the mac implementation to use
......
......@@ -252,7 +252,8 @@ void MidiManagerAlsa::StartInitialization() {
VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
} else {
source_map_[AddrToInt(sender)] = current_input++;
AddInputPort(MidiPortInfo(id, manufacturer, name, version));
AddInputPort(MidiPortInfo(
id, manufacturer, name, version, MIDI_PORT_OPENED));
}
}
if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) {
......@@ -286,7 +287,8 @@ void MidiManagerAlsa::StartInitialization() {
snd_midi_event_new(kSendBufferSize, &encoder);
encoders_.push_back(encoder);
out_ports_.push_back(out_port);
AddOutputPort(MidiPortInfo(id, manufacturer, name, version));
AddOutputPort(MidiPortInfo(
id, manufacturer, name, version, MIDI_PORT_OPENED));
}
}
}
......
......@@ -4,6 +4,7 @@
#include "media/midi/midi_manager_mac.h"
#include <algorithm>
#include <string>
#include "base/bind.h"
......@@ -25,8 +26,7 @@ namespace media {
namespace {
MidiPortInfo GetPortInfoFromEndpoint(
MIDIEndpointRef endpoint) {
MidiPortInfo GetPortInfoFromEndpoint(MIDIEndpointRef endpoint) {
SInt32 id_number = 0;
MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &id_number);
string id = IntToString(id_number);
......@@ -65,7 +65,8 @@ MidiPortInfo GetPortInfoFromEndpoint(
<< result;
}
return MidiPortInfo(id, manufacturer, name, version);
const MidiPortState state = MIDI_PORT_OPENED;
return MidiPortInfo(id, manufacturer, name, version, state);
}
double MIDITimeStampToSeconds(MIDITimeStamp timestamp) {
......@@ -140,7 +141,8 @@ void MidiManagerMac::InitializeCoreMIDI() {
// TODO(toyoshim): Set MIDINotifyProc to receive CoreMIDI event notifications.
midi_client_ = 0;
OSStatus result =
MIDIClientCreate(CFSTR("Chrome"), NULL, NULL, &midi_client_);
MIDIClientCreate(CFSTR("Chrome"), ReceiveMidiNotifyDispatch, this,
&midi_client_);
if (result != noErr)
return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
......@@ -199,6 +201,61 @@ void MidiManagerMac::InitializeCoreMIDI() {
CompleteInitialization(MIDI_OK);
}
// static
void MidiManagerMac::ReceiveMidiNotifyDispatch(const MIDINotification* message,
void* refcon) {
MidiManagerMac* manager = static_cast<MidiManagerMac*>(refcon);
manager->ReceiveMidiNotify(message);
}
void MidiManagerMac::ReceiveMidiNotify(const MIDINotification* message) {
DCHECK(client_thread_.message_loop_proxy()->BelongsToCurrentThread());
if (kMIDIMsgObjectAdded == message->messageID) {
const MIDIObjectAddRemoveNotification* notification =
reinterpret_cast<const MIDIObjectAddRemoveNotification*>(message);
MIDIEndpointRef endpoint =
static_cast<MIDIEndpointRef>(notification->child);
if (notification->childType == kMIDIObjectType_Source) {
SourceMap::iterator it = source_map_.find(endpoint);
if (it == source_map_.end()) {
uint32 index = source_map_.size();
source_map_[endpoint] = index;
MidiPortInfo info = GetPortInfoFromEndpoint(endpoint);
AddInputPort(info);
} else {
uint32 index = it->second;
SetInputPortState(index, MIDI_PORT_OPENED);
}
} else if (notification->childType == kMIDIObjectType_Destination) {
auto i = std::find(destinations_.begin(), destinations_.end(), endpoint);
if (i != destinations_.end()) {
SetOutputPortState(i - destinations_.begin(), MIDI_PORT_OPENED);
} else {
destinations_.push_back(endpoint);
MidiPortInfo info = GetPortInfoFromEndpoint(endpoint);
AddOutputPort(info);
}
}
} else if (kMIDIMsgObjectRemoved == message->messageID) {
const MIDIObjectAddRemoveNotification* notification =
reinterpret_cast<const MIDIObjectAddRemoveNotification*>(message);
MIDIEndpointRef endpoint =
static_cast<MIDIEndpointRef>(notification->child);
if (notification->childType == kMIDIObjectType_Source) {
SourceMap::iterator it = source_map_.find(endpoint);
if (it != source_map_.end()) {
uint32 index = it->second;
SetInputPortState(index, MIDI_PORT_DISCONNECTED);
}
} else if (notification->childType == kMIDIObjectType_Destination) {
auto i = std::find(destinations_.begin(), destinations_.end(), endpoint);
if (i != destinations_.end())
SetOutputPortState(i - destinations_.begin(), MIDI_PORT_DISCONNECTED);
}
}
}
// static
void MidiManagerMac::ReadMidiDispatch(const MIDIPacketList* packet_list,
void* read_proc_refcon,
......
......@@ -42,13 +42,18 @@ class MEDIA_EXPORT MidiManagerMac : public MidiManager {
// StartInitialization().
void InitializeCoreMIDI();
// CoreMIDI callback for MIDI notification.
// Receives MIDI related event notifications from CoreMIDI.
static void ReceiveMidiNotifyDispatch(const MIDINotification* message,
void* refcon);
void ReceiveMidiNotify(const MIDINotification* message);
// CoreMIDI callback for MIDI data.
// Each callback can contain multiple packets, each of which can contain
// multiple MIDI messages.
static void ReadMidiDispatch(
const MIDIPacketList *pktlist,
void *read_proc_refcon,
void *src_conn_refcon);
static void ReadMidiDispatch(const MIDIPacketList* packet_list,
void* read_proc_refcon,
void* src_conn_refcon);
virtual void ReadMidi(MIDIEndpointRef source, const MIDIPacketList *pktlist);
// An internal callback that runs on MidiSendThread.
......@@ -67,13 +72,13 @@ class MEDIA_EXPORT MidiManagerMac : public MidiManager {
MIDIPacketList* packet_list_;
MIDIPacket* midi_packet_;
typedef std::map<MIDIEndpointRef, uint32> SourceMap;
// Keeps track of the index (0-based) for each of our sources.
typedef std::map<MIDIEndpointRef, uint32> SourceMap;
SourceMap source_map_;
// Keeps track of all destinations.
std::vector<MIDIEndpointRef> destinations_;
typedef std::vector<MIDIEndpointRef> DestinationVector;
DestinationVector destinations_;
// |client_thread_| is used to handle platform dependent operations.
base::Thread client_thread_;
......
......@@ -62,6 +62,8 @@ class FakeMidiManagerClient : public MidiManagerClient {
// MidiManagerClient implementation.
void AddInputPort(const MidiPortInfo& info) override {}
void AddOutputPort(const MidiPortInfo& info) override {}
void SetInputPortState(uint32 port_index, MidiPortState state) override {}
void SetOutputPortState(uint32 port_index, MidiPortState state) override {}
void CompleteStartSession(MidiResult result) override {
EXPECT_TRUE(wait_for_result_);
......
......@@ -106,6 +106,7 @@ void MidiManagerUsb::OnEnumerateDevicesDone(bool result,
// setting is sufficiently unique although there is no user-friendly
// meaning.
MidiPortInfo port;
port.state = MIDI_PORT_OPENED;
port.id = base::StringPrintf("port-%ld-%ld",
static_cast<long>(i),
static_cast<long>(j));
......@@ -115,6 +116,7 @@ void MidiManagerUsb::OnEnumerateDevicesDone(bool result,
input_jacks.push_back(jacks[j]);
// TODO(yhirano): Set appropriate properties.
MidiPortInfo port;
port.state = MIDI_PORT_OPENED;
port.id = base::StringPrintf("port-%ld-%ld",
static_cast<long>(i),
static_cast<long>(j));
......
......@@ -86,6 +86,10 @@ class FakeMidiManagerClient : public MidiManagerClient {
output_ports_.push_back(info);
}
void SetInputPortState(uint32 port_index, MidiPortState state) override {}
void SetOutputPortState(uint32 port_index, MidiPortState state) override {}
void CompleteStartSession(MidiResult result) override {
complete_start_session_ = true;
result_ = result;
......
......@@ -515,7 +515,8 @@ void MidiManagerWin::StartInitialization() {
base::IntToString(static_cast<int>(device_id)),
"",
base::WideToUTF8(caps.szPname),
base::IntToString(static_cast<int>(caps.vDriverVersion)));
base::IntToString(static_cast<int>(caps.vDriverVersion)),
MIDI_PORT_OPENED);
AddInputPort(info);
in_device->set_port_index(inport_index++);
in_devices_.push_back(in_device.release());
......@@ -538,7 +539,8 @@ void MidiManagerWin::StartInitialization() {
base::IntToString(static_cast<int>(device_id)),
"",
base::WideToUTF8(caps.szPname),
base::IntToString(static_cast<int>(caps.vDriverVersion)));
base::IntToString(static_cast<int>(caps.vDriverVersion)),
MIDI_PORT_OPENED);
AddOutputPort(info);
out_devices_.push_back(out_port.release());
}
......
......@@ -11,11 +11,13 @@ MidiPortInfo::MidiPortInfo() {}
MidiPortInfo::MidiPortInfo(const std::string& in_id,
const std::string& in_manufacturer,
const std::string& in_name,
const std::string& in_version)
const std::string& in_version,
MidiPortState in_state)
: id(in_id),
manufacturer(in_manufacturer),
name(in_name),
version(in_version) {}
version(in_version),
state(in_state) {}
MidiPortInfo::~MidiPortInfo() {}
......@@ -23,6 +25,7 @@ MidiPortInfo::MidiPortInfo(const MidiPortInfo& info)
: id(info.id),
manufacturer(info.manufacturer),
name(info.name),
version(info.version) {}
version(info.version),
state(info.state) {}
} // namespace media
......@@ -13,12 +13,20 @@
namespace media {
enum MidiPortState {
MIDI_PORT_DISCONNECTED,
MIDI_PORT_CONNECTED,
MIDI_PORT_OPENED,
MIDI_PORT_STATE_LAST = MIDI_PORT_OPENED,
};
struct MEDIA_EXPORT MidiPortInfo {
MidiPortInfo();
MidiPortInfo(const std::string& in_id,
const std::string& in_manufacturer,
const std::string& in_name,
const std::string& in_version);
const std::string& in_version,
MidiPortState in_state);
MidiPortInfo(const MidiPortInfo& info);
~MidiPortInfo();
......@@ -27,6 +35,7 @@ struct MEDIA_EXPORT MidiPortInfo {
std::string manufacturer;
std::string name;
std::string version;
MidiPortState state;
};
typedef std::vector<MidiPortInfo> MidiPortInfoList;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment