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(WebKit::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(WebKit::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 WebKit::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 UTF8ToUTF16(inputs[i].id),
127 UTF8ToUTF16(inputs[i].manufacturer),
128 UTF8ToUTF16(inputs[i].name),
129 UTF8ToUTF16(inputs[i].version));
132 for (size_t i = 0; i < outputs.size(); ++i) {
133 client->didAddOutputPort(
134 UTF8ToUTF16(outputs[i].id),
135 UTF8ToUTF16(outputs[i].manufacturer),
136 UTF8ToUTF16(outputs[i].name),
137 UTF8ToUTF16(outputs[i].version));
140 client->didStartSession(success);
143 WebKit::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(crogers): 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(crogers): 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