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

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: one line bug fix Created 6 years, 1 month 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
27 // TODO(crbug.com/425389): Rewrite this class as a RenderFrameObserver.
25 MidiMessageFilter::MidiMessageFilter( 28 MidiMessageFilter::MidiMessageFilter(
26 const scoped_refptr<base::MessageLoopProxy>& io_message_loop) 29 const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
27 : sender_(NULL), 30 : sender_(NULL),
28 io_message_loop_(io_message_loop), 31 io_message_loop_(io_message_loop),
29 main_message_loop_(base::MessageLoopProxy::current()), 32 main_message_loop_(base::MessageLoopProxy::current()),
30 next_available_id_(0), 33 session_result_(media::MIDI_NOT_INITIALIZED),
31 unacknowledged_bytes_sent_(0) { 34 unacknowledged_bytes_sent_(0u) {
32 } 35 }
33 36
34 MidiMessageFilter::~MidiMessageFilter() {} 37 MidiMessageFilter::~MidiMessageFilter() {}
35 38
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));
48 }
49 }
50
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(),
56 client);
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;
61 io_message_loop_->PostTask(FROM_HERE,
62 base::Bind(&MidiMessageFilter::EndSessionOnIOThread, this));
63 }
64 }
65
66 void MidiMessageFilter::SendMidiData(uint32 port,
67 const uint8* data,
68 size_t length,
69 double timestamp) {
70 DCHECK(main_message_loop_->BelongsToCurrentThread());
71 if ((kMaxUnacknowledgedBytesSent - unacknowledged_bytes_sent_) < length) {
72 // TODO(toyoshim): buffer up the data to send at a later time.
73 // For now we're just dropping these bytes on the floor.
74 return;
75 }
76
77 unacknowledged_bytes_sent_ += length;
78 std::vector<uint8> v(data, data + length);
79 io_message_loop_->PostTask(FROM_HERE, base::Bind(
80 &MidiMessageFilter::SendMidiDataOnIOThread, this, port, v, timestamp));
81 }
82
83 void MidiMessageFilter::StartSessionOnIOThread() {
84 TRACE_EVENT0("midi", "MidiMessageFilter::StartSessionOnIOThread");
85 DCHECK(io_message_loop_->BelongsToCurrentThread());
86 Send(new MidiHostMsg_StartSession());
87 }
88
89 void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
90 const std::vector<uint8>& data,
91 double timestamp) {
92 DCHECK(io_message_loop_->BelongsToCurrentThread());
93 Send(new MidiHostMsg_SendData(port, data, timestamp));
94 }
95
96 void MidiMessageFilter::EndSessionOnIOThread() {
97 DCHECK(io_message_loop_->BelongsToCurrentThread());
98 Send(new MidiHostMsg_EndSession());
99 }
100
36 void MidiMessageFilter::Send(IPC::Message* message) { 101 void MidiMessageFilter::Send(IPC::Message* message) {
37 DCHECK(io_message_loop_->BelongsToCurrentThread()); 102 DCHECK(io_message_loop_->BelongsToCurrentThread());
38 if (!sender_) { 103 if (!sender_) {
39 delete message; 104 delete message;
40 } else { 105 } else {
41 sender_->Send(message); 106 sender_->Send(message);
42 } 107 }
43 } 108 }
44 109
45 bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) { 110 bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) {
46 DCHECK(io_message_loop_->BelongsToCurrentThread()); 111 DCHECK(io_message_loop_->BelongsToCurrentThread());
47 bool handled = true; 112 bool handled = true;
48 IPC_BEGIN_MESSAGE_MAP(MidiMessageFilter, message) 113 IPC_BEGIN_MESSAGE_MAP(MidiMessageFilter, message)
49 IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted) 114 IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted)
50 IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived) 115 IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived)
51 IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData) 116 IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData)
52 IPC_MESSAGE_UNHANDLED(handled = false) 117 IPC_MESSAGE_UNHANDLED(handled = false)
53 IPC_END_MESSAGE_MAP() 118 IPC_END_MESSAGE_MAP()
54 return handled; 119 return handled;
55 } 120 }
56 121
57 void MidiMessageFilter::OnFilterAdded(IPC::Sender* sender) { 122 void MidiMessageFilter::OnFilterAdded(IPC::Sender* sender) {
58 DCHECK(io_message_loop_->BelongsToCurrentThread()); 123 DCHECK(io_message_loop_->BelongsToCurrentThread());
59 sender_ = sender; 124 sender_ = sender;
60 } 125 }
61 126
62 void MidiMessageFilter::OnFilterRemoved() { 127 void MidiMessageFilter::OnFilterRemoved() {
63 DCHECK(io_message_loop_->BelongsToCurrentThread()); 128 DCHECK(io_message_loop_->BelongsToCurrentThread());
64
65 // Once removed, a filter will not be used again. At this time all 129 // Once removed, a filter will not be used again. At this time all
66 // delegates must be notified so they release their reference. 130 // delegates must be notified so they release their reference.
67 OnChannelClosing(); 131 OnChannelClosing();
68 } 132 }
69 133
70 void MidiMessageFilter::OnChannelClosing() { 134 void MidiMessageFilter::OnChannelClosing() {
71 DCHECK(io_message_loop_->BelongsToCurrentThread()); 135 DCHECK(io_message_loop_->BelongsToCurrentThread());
72 sender_ = NULL; 136 sender_ = NULL;
73 } 137 }
74 138
75 void MidiMessageFilter::StartSession(blink::WebMIDIAccessorClient* client) { 139 void MidiMessageFilter::OnSessionStarted(media::MidiResult result,
76 // Generate and keep track of a "client id" which is sent to the browser 140 MidiPortInfoList inputs,
77 // to ask permission to talk to MIDI hardware. 141 MidiPortInfoList outputs) {
78 // This id is handed back when we receive the answer in OnAccessApproved(). 142 TRACE_EVENT0("midi", "MidiMessageFilter::OnSessionStarted");
79 if (clients_.find(client) == clients_.end()) { 143 DCHECK(io_message_loop_->BelongsToCurrentThread());
80 int client_id = next_available_id_++; 144 // TODO(toyoshim): |inputs_| and |outputs_| should not be updated on
81 clients_[client] = client_id; 145 // |io_message_loop_|. This should be fixed in a following change not to
82 146 // distribute MidiPortInfo via OnSessionStarted().
83 io_message_loop_->PostTask(FROM_HERE, 147 // For now, this is safe because these are not updated later.
84 base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this, 148 inputs_ = inputs;
85 client_id)); 149 outputs_ = outputs;
86 }
87 }
88
89 void MidiMessageFilter::StartSessionOnIOThread(int client_id) {
90 Send(new MidiHostMsg_StartSession(client_id));
91 }
92
93 void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) {
94 ClientsMap::iterator i = clients_.find(client);
95 if (i != clients_.end())
96 clients_.erase(i);
97 }
98
99 // Received from browser.
100
101 void MidiMessageFilter::OnSessionStarted(
102 int client_id,
103 media::MidiResult result,
104 MidiPortInfoList inputs,
105 MidiPortInfoList outputs) {
106 // Handle on the main JS thread. 150 // Handle on the main JS thread.
107 main_message_loop_->PostTask( 151 main_message_loop_->PostTask(
108 FROM_HERE, 152 FROM_HERE,
109 base::Bind(&MidiMessageFilter::HandleSessionStarted, this, 153 base::Bind(&MidiMessageFilter::HandleClientAdded, this, result));
110 client_id, result, inputs, outputs));
111 } 154 }
112 155
113 void MidiMessageFilter::HandleSessionStarted( 156 void MidiMessageFilter::OnDataReceived(uint32 port,
114 int client_id, 157 const std::vector<uint8>& data,
115 media::MidiResult result, 158 double timestamp) {
116 MidiPortInfoList inputs, 159 TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
117 MidiPortInfoList outputs) { 160 DCHECK(io_message_loop_->BelongsToCurrentThread());
118 blink::WebMIDIAccessorClient* client = GetClientFromId(client_id); 161 // Handle on the main JS thread.
119 if (!client) 162 main_message_loop_->PostTask(
120 return; 163 FROM_HERE,
164 base::Bind(&MidiMessageFilter::HandleDataReceived, this, port, data,
165 timestamp));
166 }
121 167
122 if (result == media::MIDI_OK) { 168 void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
123 // Add the client's input and output ports. 169 DCHECK(io_message_loop_->BelongsToCurrentThread());
124 const bool active = true; 170 main_message_loop_->PostTask(
125 for (size_t i = 0; i < inputs.size(); ++i) { 171 FROM_HERE,
126 client->didAddInputPort( 172 base::Bind(&MidiMessageFilter::HandleAckknowledgeSentData, this,
127 base::UTF8ToUTF16(inputs[i].id), 173 bytes_sent));
128 base::UTF8ToUTF16(inputs[i].manufacturer), 174 }
129 base::UTF8ToUTF16(inputs[i].name),
130 base::UTF8ToUTF16(inputs[i].version),
131 active);
132 }
133 175
134 for (size_t i = 0; i < outputs.size(); ++i) { 176 void MidiMessageFilter::HandleClientAdded(media::MidiResult result) {
135 client->didAddOutputPort( 177 TRACE_EVENT0("midi", "MidiMessageFilter::HandleClientAdded");
136 base::UTF8ToUTF16(outputs[i].id), 178 DCHECK(main_message_loop_->BelongsToCurrentThread());
137 base::UTF8ToUTF16(outputs[i].manufacturer), 179 session_result_ = result;
138 base::UTF8ToUTF16(outputs[i].name),
139 base::UTF8ToUTF16(outputs[i].version),
140 active);
141 }
142 }
143 std::string error; 180 std::string error;
144 std::string message; 181 std::string message;
145 switch (result) { 182 switch (result) {
146 case media::MIDI_OK: 183 case media::MIDI_OK:
147 break; 184 break;
148 case media::MIDI_NOT_SUPPORTED: 185 case media::MIDI_NOT_SUPPORTED:
149 error = "NotSupportedError"; 186 error = "NotSupportedError";
150 break; 187 break;
151 case media::MIDI_INITIALIZATION_ERROR: 188 case media::MIDI_INITIALIZATION_ERROR:
152 error = "InvalidStateError"; 189 error = "InvalidStateError";
153 message = "Platform dependent initialization failed."; 190 message = "Platform dependent initialization failed.";
154 break; 191 break;
155 default: 192 default:
156 NOTREACHED(); 193 NOTREACHED();
157 error = "InvalidStateError"; 194 error = "InvalidStateError";
158 message = "Unknown internal error occurred."; 195 message = "Unknown internal error occurred.";
159 break; 196 break;
160 } 197 }
161 client->didStartSession(result == media::MIDI_OK, base::UTF8ToUTF16(error), 198 base::string16 error16 = base::UTF8ToUTF16(error);
162 base::UTF8ToUTF16(message)); 199 base::string16 message16 = base::UTF8ToUTF16(message);
200 for (blink::WebMIDIAccessorClient* client : clients_waiting_session_queue_) {
201 if (result == media::MIDI_OK) {
202 // Add the client's input and output ports.
203 const bool active = true;
204 for (const auto& info : inputs_) {
205 client->didAddInputPort(
206 base::UTF8ToUTF16(info.id),
207 base::UTF8ToUTF16(info.manufacturer),
208 base::UTF8ToUTF16(info.name),
209 base::UTF8ToUTF16(info.version),
210 active);
211 }
212
213 for (const auto& info : outputs_) {
214 client->didAddOutputPort(
215 base::UTF8ToUTF16(info.id),
216 base::UTF8ToUTF16(info.manufacturer),
217 base::UTF8ToUTF16(info.name),
218 base::UTF8ToUTF16(info.version),
219 active);
220 }
221 }
222 client->didStartSession(result == media::MIDI_OK, error16, message16);
223 clients_.insert(client);
224 }
225 clients_waiting_session_queue_.clear();
163 } 226 }
164 227
165 blink::WebMIDIAccessorClient* 228 void MidiMessageFilter::HandleDataReceived(uint32 port,
166 MidiMessageFilter::GetClientFromId(int client_id) { 229 const std::vector<uint8>& data,
167 // Iterating like this seems inefficient, but in practice there generally 230 double timestamp) {
168 // will be very few clients (usually one). Additionally, this lookup 231 TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
169 // usually happens one time during page load. So the performance hit is 232 DCHECK(main_message_loop_->BelongsToCurrentThread());
170 // negligible. 233 DCHECK(!data.empty());
171 for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i) { 234
172 if ((*i).second == client_id) 235 for (blink::WebMIDIAccessorClient* client : clients_)
173 return (*i).first; 236 client->didReceiveMIDIData(port, &data[0], data.size(), timestamp);
174 }
175 return NULL;
176 } 237 }
177 238
178 void MidiMessageFilter::OnDataReceived(uint32 port, 239 void MidiMessageFilter::HandleAckknowledgeSentData(size_t bytes_sent) {
179 const std::vector<uint8>& data, 240 DCHECK(main_message_loop_->BelongsToCurrentThread());
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); 241 DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
191 if (unacknowledged_bytes_sent_ >= bytes_sent) 242 if (unacknowledged_bytes_sent_ >= bytes_sent)
192 unacknowledged_bytes_sent_ -= bytes_sent; 243 unacknowledged_bytes_sent_ -= bytes_sent;
193 } 244 }
194 245
195 void MidiMessageFilter::HandleDataReceived(uint32 port,
196 const std::vector<uint8>& data,
197 double timestamp) {
198 DCHECK(!data.empty());
199 TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
200
201 for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i)
202 (*i).first->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 }
238
239 } // namespace content 246 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/media/midi_message_filter.h ('k') | content/renderer/media/renderer_webmidiaccessor_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698