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"
10 #include "base/debug/trace_event.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "content/common/media/midi_messages.h"
14 #include "content/renderer/render_thread_impl.h"
15 #include "ipc/ipc_logging.h"
17 using media::MidiPortInfoList;
20 // The maximum number of bytes which we're allowed to send to the browser
21 // before getting acknowledgement back from the browser that they've been
23 static const size_t kMaxUnacknowledgedBytesSent = 10 * 1024 * 1024; // 10 MB.
27 // TODO(crbug.com/425389): Rewrite this class as a RenderFrameObserver.
28 MidiMessageFilter::MidiMessageFilter(
29 const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
31 io_message_loop_(io_message_loop),
32 main_message_loop_(base::MessageLoopProxy::current()),
33 session_result_(media::MIDI_NOT_INITIALIZED),
34 unacknowledged_bytes_sent_(0u) {
37 MidiMessageFilter::~MidiMessageFilter() {}
39 void MidiMessageFilter::AddClient(blink::WebMIDIAccessorClient* client) {
40 DCHECK(main_message_loop_->BelongsToCurrentThread());
41 TRACE_EVENT0("midi", "MidiMessageFilter::AddClient");
42 clients_waiting_session_queue_.push_back(client);
43 if (session_result_ != media::MIDI_NOT_INITIALIZED) {
44 HandleClientAdded(session_result_);
45 } else if (clients_waiting_session_queue_.size() == 1u) {
46 io_message_loop_->PostTask(FROM_HERE,
47 base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this));
51 void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) {
52 DCHECK(main_message_loop_->BelongsToCurrentThread());
53 clients_.erase(client);
54 ClientsQueue::iterator it = std::find(clients_waiting_session_queue_.begin(),
55 clients_waiting_session_queue_.end(),
57 if (it != clients_waiting_session_queue_.end())
58 clients_waiting_session_queue_.erase(it);
59 if (clients_.empty() && clients_waiting_session_queue_.empty()) {
60 session_result_ = media::MIDI_NOT_INITIALIZED;
63 io_message_loop_->PostTask(FROM_HERE,
64 base::Bind(&MidiMessageFilter::EndSessionOnIOThread, this));
68 void MidiMessageFilter::SendMidiData(uint32 port,
72 DCHECK(main_message_loop_->BelongsToCurrentThread());
73 if ((kMaxUnacknowledgedBytesSent - unacknowledged_bytes_sent_) < length) {
74 // TODO(toyoshim): buffer up the data to send at a later time.
75 // For now we're just dropping these bytes on the floor.
79 unacknowledged_bytes_sent_ += length;
80 std::vector<uint8> v(data, data + length);
81 io_message_loop_->PostTask(FROM_HERE, base::Bind(
82 &MidiMessageFilter::SendMidiDataOnIOThread, this, port, v, timestamp));
85 void MidiMessageFilter::StartSessionOnIOThread() {
86 TRACE_EVENT0("midi", "MidiMessageFilter::StartSessionOnIOThread");
87 DCHECK(io_message_loop_->BelongsToCurrentThread());
88 Send(new MidiHostMsg_StartSession());
91 void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
92 const std::vector<uint8>& data,
94 DCHECK(io_message_loop_->BelongsToCurrentThread());
95 Send(new MidiHostMsg_SendData(port, data, timestamp));
98 void MidiMessageFilter::EndSessionOnIOThread() {
99 DCHECK(io_message_loop_->BelongsToCurrentThread());
100 Send(new MidiHostMsg_EndSession());
103 void MidiMessageFilter::Send(IPC::Message* message) {
104 DCHECK(io_message_loop_->BelongsToCurrentThread());
108 sender_->Send(message);
112 bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) {
113 DCHECK(io_message_loop_->BelongsToCurrentThread());
115 IPC_BEGIN_MESSAGE_MAP(MidiMessageFilter, message)
116 IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted)
117 IPC_MESSAGE_HANDLER(MidiMsg_AddInputPort, OnAddInputPort)
118 IPC_MESSAGE_HANDLER(MidiMsg_AddOutputPort, OnAddOutputPort)
119 IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived)
120 IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData)
121 IPC_MESSAGE_UNHANDLED(handled = false)
122 IPC_END_MESSAGE_MAP()
126 void MidiMessageFilter::OnFilterAdded(IPC::Sender* sender) {
127 DCHECK(io_message_loop_->BelongsToCurrentThread());
131 void MidiMessageFilter::OnFilterRemoved() {
132 DCHECK(io_message_loop_->BelongsToCurrentThread());
133 // Once removed, a filter will not be used again. At this time all
134 // delegates must be notified so they release their reference.
138 void MidiMessageFilter::OnChannelClosing() {
139 DCHECK(io_message_loop_->BelongsToCurrentThread());
143 void MidiMessageFilter::OnSessionStarted(media::MidiResult result) {
144 TRACE_EVENT0("midi", "MidiMessageFilter::OnSessionStarted");
145 DCHECK(io_message_loop_->BelongsToCurrentThread());
146 // Handle on the main JS thread.
147 main_message_loop_->PostTask(
149 base::Bind(&MidiMessageFilter::HandleClientAdded, this, result));
152 void MidiMessageFilter::OnAddInputPort(media::MidiPortInfo info) {
153 DCHECK(io_message_loop_->BelongsToCurrentThread());
154 main_message_loop_->PostTask(
156 base::Bind(&MidiMessageFilter::HandleAddInputPort, this, info));
159 void MidiMessageFilter::OnAddOutputPort(media::MidiPortInfo info) {
160 DCHECK(io_message_loop_->BelongsToCurrentThread());
161 main_message_loop_->PostTask(
163 base::Bind(&MidiMessageFilter::HandleAddOutputPort, this, info));
166 void MidiMessageFilter::OnDataReceived(uint32 port,
167 const std::vector<uint8>& data,
169 TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
170 DCHECK(io_message_loop_->BelongsToCurrentThread());
171 // Handle on the main JS thread.
172 main_message_loop_->PostTask(
174 base::Bind(&MidiMessageFilter::HandleDataReceived, this, port, data,
178 void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
179 DCHECK(io_message_loop_->BelongsToCurrentThread());
180 main_message_loop_->PostTask(
182 base::Bind(&MidiMessageFilter::HandleAckknowledgeSentData, this,
186 void MidiMessageFilter::HandleClientAdded(media::MidiResult result) {
187 TRACE_EVENT0("midi", "MidiMessageFilter::HandleClientAdded");
188 DCHECK(main_message_loop_->BelongsToCurrentThread());
189 session_result_ = result;
195 case media::MIDI_NOT_SUPPORTED:
196 error = "NotSupportedError";
198 case media::MIDI_INITIALIZATION_ERROR:
199 error = "InvalidStateError";
200 message = "Platform dependent initialization failed.";
204 error = "InvalidStateError";
205 message = "Unknown internal error occurred.";
208 base::string16 error16 = base::UTF8ToUTF16(error);
209 base::string16 message16 = base::UTF8ToUTF16(message);
210 for (blink::WebMIDIAccessorClient* client : clients_waiting_session_queue_) {
211 if (result == media::MIDI_OK) {
212 // Add the client's input and output ports.
213 const bool active = true;
214 for (const auto& info : inputs_) {
215 client->didAddInputPort(
216 base::UTF8ToUTF16(info.id),
217 base::UTF8ToUTF16(info.manufacturer),
218 base::UTF8ToUTF16(info.name),
219 base::UTF8ToUTF16(info.version),
223 for (const auto& info : outputs_) {
224 client->didAddOutputPort(
225 base::UTF8ToUTF16(info.id),
226 base::UTF8ToUTF16(info.manufacturer),
227 base::UTF8ToUTF16(info.name),
228 base::UTF8ToUTF16(info.version),
232 client->didStartSession(result == media::MIDI_OK, error16, message16);
233 clients_.insert(client);
235 clients_waiting_session_queue_.clear();
238 void MidiMessageFilter::HandleAddInputPort(media::MidiPortInfo info) {
239 DCHECK(main_message_loop_->BelongsToCurrentThread());
240 inputs_.push_back(info);
241 // TODO(toyoshim): Notify to clients that were already added.
244 void MidiMessageFilter::HandleAddOutputPort(media::MidiPortInfo info) {
245 DCHECK(main_message_loop_->BelongsToCurrentThread());
246 outputs_.push_back(info);
247 // TODO(toyoshim): Notify to clients that were already added.
250 void MidiMessageFilter::HandleDataReceived(uint32 port,
251 const std::vector<uint8>& data,
253 TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
254 DCHECK(main_message_loop_->BelongsToCurrentThread());
255 DCHECK(!data.empty());
257 for (blink::WebMIDIAccessorClient* client : clients_)
258 client->didReceiveMIDIData(port, &data[0], data.size(), timestamp);
261 void MidiMessageFilter::HandleAckknowledgeSentData(size_t bytes_sent) {
262 DCHECK(main_message_loop_->BelongsToCurrentThread());
263 DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
264 if (unacknowledged_bytes_sent_ >= bytes_sent)
265 unacknowledged_bytes_sent_ -= bytes_sent;
268 } // namespace content