1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/media/midi_message_filter.h"
8 #include "base/debug/trace_event.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/common/media/midi_messages.h"
12 #include "content/renderer/render_thread_impl.h"
13 #include "ipc/ipc_logging.h"
15 using media::MidiPortInfoList;
18 // The maximum number of bytes which we're allowed to send to the browser
19 // before getting acknowledgement back from the browser that they've been
21 static const size_t kMaxUnacknowledgedBytesSent = 10 * 1024 * 1024; // 10 MB.
25 MidiMessageFilter::MidiMessageFilter(
26 const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
28 io_message_loop_(io_message_loop),
29 main_message_loop_(base::MessageLoopProxy::current()),
30 next_available_id_(0),
31 unacknowledged_bytes_sent_(0) {
34 MidiMessageFilter::~MidiMessageFilter() {}
36 void MidiMessageFilter::Send(IPC::Message* message) {
37 DCHECK(io_message_loop_->BelongsToCurrentThread());
41 channel_->Send(message);
45 bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) {
46 DCHECK(io_message_loop_->BelongsToCurrentThread());
48 IPC_BEGIN_MESSAGE_MAP(MidiMessageFilter, message)
49 IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted)
50 IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived)
51 IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData)
52 IPC_MESSAGE_UNHANDLED(handled = false)
57 void MidiMessageFilter::OnFilterAdded(IPC::Channel* channel) {
58 DCHECK(io_message_loop_->BelongsToCurrentThread());
62 void MidiMessageFilter::OnFilterRemoved() {
63 DCHECK(io_message_loop_->BelongsToCurrentThread());
65 // Once removed, a filter will not be used again. At this time all
66 // delegates must be notified so they release their reference.
70 void MidiMessageFilter::OnChannelClosing() {
71 DCHECK(io_message_loop_->BelongsToCurrentThread());
75 void MidiMessageFilter::StartSession(blink::WebMIDIAccessorClient* client) {
76 // Generate and keep track of a "client id" which is sent to the browser
77 // to ask permission to talk to MIDI hardware.
78 // This id is handed back when we receive the answer in OnAccessApproved().
79 if (clients_.find(client) == clients_.end()) {
80 int client_id = next_available_id_++;
81 clients_[client] = client_id;
83 io_message_loop_->PostTask(FROM_HERE,
84 base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this,
89 void MidiMessageFilter::StartSessionOnIOThread(int client_id) {
90 Send(new MidiHostMsg_StartSession(client_id));
93 void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) {
94 ClientsMap::iterator i = clients_.find(client);
95 if (i != clients_.end())
99 // Received from browser.
101 void MidiMessageFilter::OnSessionStarted(
104 MidiPortInfoList inputs,
105 MidiPortInfoList outputs) {
106 // Handle on the main JS thread.
107 main_message_loop_->PostTask(
109 base::Bind(&MidiMessageFilter::HandleSessionStarted, this,
110 client_id, success, inputs, outputs));
113 void MidiMessageFilter::HandleSessionStarted(
116 MidiPortInfoList inputs,
117 MidiPortInfoList outputs) {
118 blink::WebMIDIAccessorClient* client = GetClientFromId(client_id);
123 // Add the client's input and output ports.
124 for (size_t i = 0; i < inputs.size(); ++i) {
125 client->didAddInputPort(
126 base::UTF8ToUTF16(inputs[i].id),
127 base::UTF8ToUTF16(inputs[i].manufacturer),
128 base::UTF8ToUTF16(inputs[i].name),
129 base::UTF8ToUTF16(inputs[i].version));
132 for (size_t i = 0; i < outputs.size(); ++i) {
133 client->didAddOutputPort(
134 base::UTF8ToUTF16(outputs[i].id),
135 base::UTF8ToUTF16(outputs[i].manufacturer),
136 base::UTF8ToUTF16(outputs[i].name),
137 base::UTF8ToUTF16(outputs[i].version));
140 client->didStartSession(success);
143 blink::WebMIDIAccessorClient*
144 MidiMessageFilter::GetClientFromId(int client_id) {
145 // Iterating like this seems inefficient, but in practice there generally
146 // will be very few clients (usually one). Additionally, this lookup
147 // usually happens one time during page load. So the performance hit is
149 for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i) {
150 if ((*i).second == client_id)
156 void MidiMessageFilter::OnDataReceived(uint32 port,
157 const std::vector<uint8>& data,
159 TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
161 main_message_loop_->PostTask(
163 base::Bind(&MidiMessageFilter::HandleDataReceived, this,
164 port, data, timestamp));
167 void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
168 DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
169 if (unacknowledged_bytes_sent_ >= bytes_sent)
170 unacknowledged_bytes_sent_ -= bytes_sent;
173 void MidiMessageFilter::HandleDataReceived(uint32 port,
174 const std::vector<uint8>& data,
176 DCHECK(!data.empty());
177 TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
179 for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i)
180 (*i).first->didReceiveMIDIData(port, &data[0], data.size(), timestamp);
183 void MidiMessageFilter::SendMidiData(uint32 port,
187 if (length > kMaxUnacknowledgedBytesSent) {
188 // TODO(toyoshim): buffer up the data to send at a later time.
189 // For now we're just dropping these bytes on the floor.
193 std::vector<uint8> v(data, data + length);
194 io_message_loop_->PostTask(FROM_HERE,
195 base::Bind(&MidiMessageFilter::SendMidiDataOnIOThread, this,
196 port, v, timestamp));
199 void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
200 const std::vector<uint8>& data,
202 size_t n = data.size();
203 if (n > kMaxUnacknowledgedBytesSent ||
204 unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent ||
205 n + unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent) {
206 // TODO(toyoshim): buffer up the data to send at a later time.
207 // For now we're just dropping these bytes on the floor.
211 unacknowledged_bytes_sent_ += n;
213 // Send to the browser.
214 Send(new MidiHostMsg_SendData(port, data, timestamp));
217 } // namespace content