Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(42)

Side by Side Diff: content/renderer/media/midi_message_filter.cc

Issue 662853003: Manage MIDI related objects and sessions' lifecycles correctly (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: [rebase not for review, but for trybots] Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/media/midi_message_filter.h" 5 #include "content/renderer/media/midi_message_filter.h"
6 6
7 #include <algorithm>
8
7 #include "base/bind.h" 9 #include "base/bind.h"
8 #include "base/debug/trace_event.h" 10 #include "base/debug/trace_event.h"
9 #include "base/message_loop/message_loop_proxy.h" 11 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/strings/utf_string_conversions.h" 12 #include "base/strings/utf_string_conversions.h"
11 #include "content/common/media/midi_messages.h" 13 #include "content/common/media/midi_messages.h"
12 #include "content/renderer/render_thread_impl.h" 14 #include "content/renderer/render_thread_impl.h"
13 #include "ipc/ipc_logging.h" 15 #include "ipc/ipc_logging.h"
14 16
15 using media::MidiPortInfoList; 17 using media::MidiPortInfoList;
16 using base::AutoLock; 18 using base::AutoLock;
17 19
18 // The maximum number of bytes which we're allowed to send to the browser 20 // 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 // before getting acknowledgement back from the browser that they've been
20 // successfully sent. 22 // successfully sent.
21 static const size_t kMaxUnacknowledgedBytesSent = 10 * 1024 * 1024; // 10 MB. 23 static const size_t kMaxUnacknowledgedBytesSent = 10 * 1024 * 1024; // 10 MB.
22 24
23 namespace content { 25 namespace content {
24 26
25 MidiMessageFilter::MidiMessageFilter( 27 MidiMessageFilter::MidiMessageFilter(
26 const scoped_refptr<base::MessageLoopProxy>& io_message_loop) 28 const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
27 : sender_(NULL), 29 : sender_(NULL),
28 io_message_loop_(io_message_loop), 30 io_message_loop_(io_message_loop),
29 main_message_loop_(base::MessageLoopProxy::current()), 31 main_message_loop_(base::MessageLoopProxy::current()),
30 next_available_id_(0), 32 session_result_(media::MIDI_NOT_INITIALIZED),
31 unacknowledged_bytes_sent_(0) { 33 unacknowledged_bytes_sent_(0) {
32 } 34 }
33 35
34 MidiMessageFilter::~MidiMessageFilter() {} 36 MidiMessageFilter::~MidiMessageFilter() {}
35 37
38 void MidiMessageFilter::AddClient(blink::WebMIDIAccessorClient* client) {
39 TRACE_EVENT0("midi", "MidiMessageFilter::AddClient");
40 base::AutoLock auto_lock(lock_);
41 if (session_result_ != media::MIDI_NOT_INITIALIZED) {
42 main_message_loop_->PostTask(FROM_HERE,
43 base::Bind(&MidiMessageFilter::HandleClientAdded, this, client));
44 } else if (!clients_waiting_session_queue_.empty()) {
45 clients_waiting_session_queue_.push_back(client);
46 } else {
47 clients_waiting_session_queue_.push_back(client);
48 io_message_loop_->PostTask(FROM_HERE,
49 base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this));
50 }
51 }
52
53 void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) {
54 base::AutoLock auto_lock(lock_);
55 clients_.erase(client);
56 ClientsQueue::iterator it = std::find(clients_waiting_session_queue_.begin(),
57 clients_waiting_session_queue_.end(),
58 client);
59 if (it != clients_waiting_session_queue_.end())
60 clients_waiting_session_queue_.erase(it);
61 if (clients_.empty() && clients_waiting_session_queue_.empty()) {
62 session_result_ = media::MIDI_NOT_INITIALIZED;
63 io_message_loop_->PostTask(FROM_HERE,
64 base::Bind(&MidiMessageFilter::EndSessionOnIOThread, this));
65 }
66 }
67
68 void MidiMessageFilter::SendMidiData(uint32 port,
scherkus (not reviewing) 2014/10/20 18:05:48 which thread is this typically called on? it seem
Takashi Toyoshima 2014/10/21 01:24:07 This function should be called from main thread.
scherkus (not reviewing) 2014/10/21 17:06:17 Sounds good. I'm not guaranteeing it'll work but
69 const uint8* data,
70 size_t length,
71 double timestamp) {
72 if (length > kMaxUnacknowledgedBytesSent) {
73 // TODO(toyoshim): buffer up the data to send at a later time.
74 // For now we're just dropping these bytes on the floor.
75 return;
76 }
77
78 std::vector<uint8> v(data, data + length);
79 io_message_loop_->PostTask(FROM_HERE,
80 base::Bind(&MidiMessageFilter::SendMidiDataOnIOThread, this,
81 port, v, timestamp));
82 }
83
84 void MidiMessageFilter::StartSessionOnIOThread() {
85 TRACE_EVENT0("midi", "MidiMessageFilter::StartSessionOnIOThread");
86 DCHECK(io_message_loop_->BelongsToCurrentThread());
87 Send(new MidiHostMsg_StartSession());
88 }
89
90 void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
91 const std::vector<uint8>& data,
92 double timestamp) {
93 DCHECK(io_message_loop_->BelongsToCurrentThread());
94 size_t n = data.size();
95 if (n > kMaxUnacknowledgedBytesSent ||
96 unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent ||
97 n + unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent) {
98 // TODO(toyoshim): buffer up the data to send at a later time.
99 // For now we're just dropping these bytes on the floor.
100 return;
101 }
102
103 unacknowledged_bytes_sent_ += n;
104
105 // Send to the browser.
106 Send(new MidiHostMsg_SendData(port, data, timestamp));
107 }
108
109 void MidiMessageFilter::EndSessionOnIOThread() {
110 DCHECK(io_message_loop_->BelongsToCurrentThread());
111 Send(new MidiHostMsg_EndSession());
112 }
113
36 void MidiMessageFilter::Send(IPC::Message* message) { 114 void MidiMessageFilter::Send(IPC::Message* message) {
37 DCHECK(io_message_loop_->BelongsToCurrentThread()); 115 DCHECK(io_message_loop_->BelongsToCurrentThread());
38 if (!sender_) { 116 if (!sender_) {
39 delete message; 117 delete message;
40 } else { 118 } else {
41 sender_->Send(message); 119 sender_->Send(message);
42 } 120 }
43 } 121 }
44 122
45 bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) { 123 bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) {
46 DCHECK(io_message_loop_->BelongsToCurrentThread()); 124 DCHECK(io_message_loop_->BelongsToCurrentThread());
47 bool handled = true; 125 bool handled = true;
48 IPC_BEGIN_MESSAGE_MAP(MidiMessageFilter, message) 126 IPC_BEGIN_MESSAGE_MAP(MidiMessageFilter, message)
49 IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted) 127 IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted)
50 IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived) 128 IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived)
51 IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData) 129 IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData)
52 IPC_MESSAGE_UNHANDLED(handled = false) 130 IPC_MESSAGE_UNHANDLED(handled = false)
53 IPC_END_MESSAGE_MAP() 131 IPC_END_MESSAGE_MAP()
54 return handled; 132 return handled;
55 } 133 }
56 134
57 void MidiMessageFilter::OnFilterAdded(IPC::Sender* sender) { 135 void MidiMessageFilter::OnFilterAdded(IPC::Sender* sender) {
58 DCHECK(io_message_loop_->BelongsToCurrentThread()); 136 DCHECK(io_message_loop_->BelongsToCurrentThread());
59 sender_ = sender; 137 sender_ = sender;
60 } 138 }
61 139
62 void MidiMessageFilter::OnFilterRemoved() { 140 void MidiMessageFilter::OnFilterRemoved() {
63 DCHECK(io_message_loop_->BelongsToCurrentThread()); 141 DCHECK(io_message_loop_->BelongsToCurrentThread());
64
65 // Once removed, a filter will not be used again. At this time all 142 // Once removed, a filter will not be used again. At this time all
66 // delegates must be notified so they release their reference. 143 // delegates must be notified so they release their reference.
67 OnChannelClosing(); 144 OnChannelClosing();
68 } 145 }
69 146
70 void MidiMessageFilter::OnChannelClosing() { 147 void MidiMessageFilter::OnChannelClosing() {
71 DCHECK(io_message_loop_->BelongsToCurrentThread()); 148 DCHECK(io_message_loop_->BelongsToCurrentThread());
72 sender_ = NULL; 149 sender_ = NULL;
73 } 150 }
74 151
75 void MidiMessageFilter::StartSession(blink::WebMIDIAccessorClient* client) { 152 void MidiMessageFilter::OnSessionStarted(media::MidiResult result,
76 // Generate and keep track of a "client id" which is sent to the browser 153 MidiPortInfoList inputs,
77 // to ask permission to talk to MIDI hardware. 154 MidiPortInfoList outputs) {
78 // This id is handed back when we receive the answer in OnAccessApproved(). 155 TRACE_EVENT0("midi", "MidiMessageFilter::OnSessionStarted");
79 if (clients_.find(client) == clients_.end()) { 156 DCHECK(io_message_loop_->BelongsToCurrentThread());
80 int client_id = next_available_id_++; 157 base::AutoLock auto_lock(lock_);
81 clients_[client] = client_id; 158 session_result_ = result;
82 159 inputs_ = inputs;
83 io_message_loop_->PostTask(FROM_HERE, 160 outputs_ = outputs;
84 base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this, 161 // Handle on the main JS thread.
85 client_id)); 162 for (blink::WebMIDIAccessorClient* client : clients_waiting_session_queue_) {
163 main_message_loop_->PostTask(FROM_HERE,
164 base::Bind(&MidiMessageFilter::HandleClientAdded, this, client));
86 } 165 }
166 clients_waiting_session_queue_.clear();
87 } 167 }
88 168
89 void MidiMessageFilter::StartSessionOnIOThread(int client_id) { 169 void MidiMessageFilter::OnDataReceived(uint32 port,
90 Send(new MidiHostMsg_StartSession(client_id)); 170 const std::vector<uint8>& data,
171 double timestamp) {
172 TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
173 DCHECK(io_message_loop_->BelongsToCurrentThread());
174 main_message_loop_->PostTask(
175 FROM_HERE,
176 base::Bind(&MidiMessageFilter::HandleDataReceived, this,
177 port, data, timestamp));
91 } 178 }
92 179
93 void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) { 180 void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
94 ClientsMap::iterator i = clients_.find(client); 181 DCHECK(io_message_loop_->BelongsToCurrentThread());
95 if (i != clients_.end()) 182 DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
96 clients_.erase(i); 183 if (unacknowledged_bytes_sent_ >= bytes_sent)
184 unacknowledged_bytes_sent_ -= bytes_sent;
97 } 185 }
98 186
99 // Received from browser. 187 void MidiMessageFilter::HandleClientAdded(
100 188 blink::WebMIDIAccessorClient* client) {
101 void MidiMessageFilter::OnSessionStarted( 189 TRACE_EVENT0("midi", "MidiMessageFilter::HandleClientAdded");
102 int client_id, 190 DCHECK(main_message_loop_->BelongsToCurrentThread());
103 media::MidiResult result, 191 media::MidiResult result;
104 MidiPortInfoList inputs, 192 {
105 MidiPortInfoList outputs) { 193 base::AutoLock auto_lock(lock_);
106 // Handle on the main JS thread. 194 result = session_result_;
107 main_message_loop_->PostTask( 195 }
108 FROM_HERE,
109 base::Bind(&MidiMessageFilter::HandleSessionStarted, this,
110 client_id, result, inputs, outputs));
111 }
112
113 void MidiMessageFilter::HandleSessionStarted(
114 int client_id,
115 media::MidiResult result,
116 MidiPortInfoList inputs,
117 MidiPortInfoList outputs) {
118 blink::WebMIDIAccessorClient* client = GetClientFromId(client_id);
119 if (!client)
120 return;
121
122 if (result == media::MIDI_OK) { 196 if (result == media::MIDI_OK) {
123 // Add the client's input and output ports. 197 // Add the client's input and output ports.
124 const bool active = true; 198 const bool active = true;
125 for (size_t i = 0; i < inputs.size(); ++i) { 199 for (media::MidiPortInfo info : inputs_) {
scherkus (not reviewing) 2014/10/20 18:05:48 won't this create a copy? you could use const aut
Takashi Toyoshima 2014/10/21 01:49:46 Done.
126 client->didAddInputPort( 200 client->didAddInputPort(
127 base::UTF8ToUTF16(inputs[i].id), 201 base::UTF8ToUTF16(info.id),
128 base::UTF8ToUTF16(inputs[i].manufacturer), 202 base::UTF8ToUTF16(info.manufacturer),
129 base::UTF8ToUTF16(inputs[i].name), 203 base::UTF8ToUTF16(info.name),
130 base::UTF8ToUTF16(inputs[i].version), 204 base::UTF8ToUTF16(info.version),
131 active); 205 true);
132 } 206 }
133 207
134 for (size_t i = 0; i < outputs.size(); ++i) { 208 for (media::MidiPortInfo info : outputs_) {
scherkus (not reviewing) 2014/10/20 18:05:48 ditto
Takashi Toyoshima 2014/10/21 01:49:46 Done.
135 client->didAddOutputPort( 209 client->didAddOutputPort(
136 base::UTF8ToUTF16(outputs[i].id), 210 base::UTF8ToUTF16(info.id),
137 base::UTF8ToUTF16(outputs[i].manufacturer), 211 base::UTF8ToUTF16(info.manufacturer),
138 base::UTF8ToUTF16(outputs[i].name), 212 base::UTF8ToUTF16(info.name),
139 base::UTF8ToUTF16(outputs[i].version), 213 base::UTF8ToUTF16(info.version),
140 active); 214 true);
141 } 215 }
142 } 216 }
217 {
218 base::AutoLock auto_lock(lock_);
219 clients_.insert(client);
220 }
143 std::string error; 221 std::string error;
144 std::string message; 222 std::string message;
145 switch (result) { 223 switch (result) {
146 case media::MIDI_OK: 224 case media::MIDI_OK:
147 break; 225 break;
148 case media::MIDI_NOT_SUPPORTED: 226 case media::MIDI_NOT_SUPPORTED:
149 error = "NotSupportedError"; 227 error = "NotSupportedError";
150 break; 228 break;
151 case media::MIDI_INITIALIZATION_ERROR: 229 case media::MIDI_INITIALIZATION_ERROR:
152 error = "InvalidStateError"; 230 error = "InvalidStateError";
153 message = "Platform dependent initialization failed."; 231 message = "Platform dependent initialization failed.";
154 break; 232 break;
155 default: 233 default:
156 NOTREACHED(); 234 NOTREACHED();
157 error = "InvalidStateError"; 235 error = "InvalidStateError";
158 message = "Unknown internal error occurred."; 236 message = "Unknown internal error occurred.";
159 break; 237 break;
160 } 238 }
161 client->didStartSession(result == media::MIDI_OK, base::UTF8ToUTF16(error), 239 client->didStartSession(result == media::MIDI_OK, base::UTF8ToUTF16(error),
162 base::UTF8ToUTF16(message)); 240 base::UTF8ToUTF16(message));
163 } 241 }
164 242
165 blink::WebMIDIAccessorClient*
166 MidiMessageFilter::GetClientFromId(int client_id) {
167 // Iterating like this seems inefficient, but in practice there generally
168 // will be very few clients (usually one). Additionally, this lookup
169 // usually happens one time during page load. So the performance hit is
170 // negligible.
171 for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i) {
172 if ((*i).second == client_id)
173 return (*i).first;
174 }
175 return NULL;
176 }
177
178 void MidiMessageFilter::OnDataReceived(uint32 port,
179 const std::vector<uint8>& data,
180 double timestamp) {
181 TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
182
183 main_message_loop_->PostTask(
184 FROM_HERE,
185 base::Bind(&MidiMessageFilter::HandleDataReceived, this,
186 port, data, timestamp));
187 }
188
189 void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
190 DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
191 if (unacknowledged_bytes_sent_ >= bytes_sent)
192 unacknowledged_bytes_sent_ -= bytes_sent;
193 }
194
195 void MidiMessageFilter::HandleDataReceived(uint32 port, 243 void MidiMessageFilter::HandleDataReceived(uint32 port,
196 const std::vector<uint8>& data, 244 const std::vector<uint8>& data,
197 double timestamp) { 245 double timestamp) {
246 TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
247 DCHECK(main_message_loop_->BelongsToCurrentThread());
198 DCHECK(!data.empty()); 248 DCHECK(!data.empty());
199 TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
200 249
201 for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i) 250 for (blink::WebMIDIAccessorClient* client : clients_)
202 (*i).first->didReceiveMIDIData(port, &data[0], data.size(), timestamp); 251 client->didReceiveMIDIData(port, &data[0], data.size(), timestamp);
203 }
204
205 void MidiMessageFilter::SendMidiData(uint32 port,
206 const uint8* data,
207 size_t length,
208 double timestamp) {
209 if (length > kMaxUnacknowledgedBytesSent) {
210 // TODO(toyoshim): buffer up the data to send at a later time.
211 // For now we're just dropping these bytes on the floor.
212 return;
213 }
214
215 std::vector<uint8> v(data, data + length);
216 io_message_loop_->PostTask(FROM_HERE,
217 base::Bind(&MidiMessageFilter::SendMidiDataOnIOThread, this,
218 port, v, timestamp));
219 }
220
221 void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
222 const std::vector<uint8>& data,
223 double timestamp) {
224 size_t n = data.size();
225 if (n > kMaxUnacknowledgedBytesSent ||
226 unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent ||
227 n + unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent) {
228 // TODO(toyoshim): buffer up the data to send at a later time.
229 // For now we're just dropping these bytes on the floor.
230 return;
231 }
232
233 unacknowledged_bytes_sent_ += n;
234
235 // Send to the browser.
236 Send(new MidiHostMsg_SendData(port, data, timestamp));
237 } 252 }
238 253
239 } // namespace content 254 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698