blob: 55b5a3777d19531f3155f2771389786df1e11a08 [file] [log] [blame]
crogers@google.com27356e42013-06-22 04:03:03 +00001// 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.
4
5#include "media/midi/midi_manager.h"
6
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +00007#include "base/bind.h"
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +00008#include "base/message_loop/message_loop.h"
toyoshimf1b88962015-07-09 14:14:51 -07009#include "base/metrics/histogram_macros.h"
ssid91344442015-01-28 04:13:15 -080010#include "base/trace_event/trace_event.h"
avi793390d2015-12-22 22:22:36 -080011#include "build/build_config.h"
crogers@google.com542a43a2013-07-31 05:16:49 +000012
crogers@google.com27356e42013-06-22 04:03:03 +000013namespace media {
toyoshime147c5e2015-05-07 21:58:31 -070014namespace midi {
crogers@google.com27356e42013-06-22 04:03:03 +000015
toyoshim715385c2015-08-09 23:00:26 -070016namespace {
17
18using Sample = base::HistogramBase::Sample;
19
20// If many users have more devices, this number will be increased.
21// But the number is expected to be big enough for now.
22const Sample kMaxUmaDevices = 31;
23
24// Used to count events for usage histogram.
25enum class Usage {
26 CREATED,
27 CREATED_ON_UNSUPPORTED_PLATFORMS,
28 SESSION_STARTED,
29 SESSION_ENDED,
30 INITIALIZED,
31 INPUT_PORT_ADDED,
32 OUTPUT_PORT_ADDED,
33
34 // New items should be inserted here, and |MAX| should point the last item.
35 MAX = INITIALIZED,
36};
37
toyoshim7a809662015-10-06 00:54:21 -070038// Used in StartSession.
39enum class Completion {
40 COMPLETE_SYNCHRONOUSLY,
41 INVOKE_INITIALIZATION,
42 COMPLETE_ASYNCHRONOUSLY,
43};
44
toyoshim715385c2015-08-09 23:00:26 -070045void ReportUsage(Usage usage) {
46 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage",
47 static_cast<Sample>(usage),
48 static_cast<Sample>(Usage::MAX) + 1);
49}
50
51} // namespace
52
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000053MidiManager::MidiManager()
toyoshim7a809662015-10-06 00:54:21 -070054 : initialized_(false), finalized_(false), result_(Result::NOT_INITIALIZED) {
toyoshim715385c2015-08-09 23:00:26 -070055 ReportUsage(Usage::CREATED);
crogers@google.com27356e42013-06-22 04:03:03 +000056}
57
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000058MidiManager::~MidiManager() {
toyoshim7a809662015-10-06 00:54:21 -070059 // Make sure that Finalize() is called to clean up resources allocated on
60 // the Chrome_IOThread.
61 DCHECK(finalized_);
yukawa@chromium.orgdb7ad8b2013-12-04 15:42:41 +000062}
crogers@google.com27356e42013-06-22 04:03:03 +000063
agoode7de413f2015-04-24 00:13:39 -070064#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
65 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000066MidiManager* MidiManager::Create() {
toyoshim715385c2015-08-09 23:00:26 -070067 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000068 return new MidiManager;
69}
70#endif
71
toyoshim7a809662015-10-06 00:54:21 -070072void MidiManager::Shutdown() {
73 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown",
74 static_cast<int>(result_),
75 static_cast<int>(Result::MAX) + 1);
76 base::AutoLock auto_lock(lock_);
77 if (session_thread_runner_) {
78 session_thread_runner_->PostTask(
79 FROM_HERE, base::Bind(&MidiManager::ShutdownOnSessionThread,
80 base::Unretained(this)));
81 session_thread_runner_ = nullptr;
82 } else {
83 finalized_ = true;
84 }
85}
86
toyoshima485ff92014-10-23 00:17:59 -070087void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -070088 ReportUsage(Usage::SESSION_STARTED);
89
toyoshim7a809662015-10-06 00:54:21 -070090 Completion completion = Completion::COMPLETE_SYNCHRONOUSLY;
91 Result result = Result::NOT_INITIALIZED;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000092
93 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000094 base::AutoLock auto_lock(lock_);
toyoshima485ff92014-10-23 00:17:59 -070095 if (clients_.find(client) != clients_.end() ||
96 pending_clients_.find(client) != pending_clients_.end()) {
97 // Should not happen. But just in case the renderer is compromised.
98 NOTREACHED();
99 return;
100 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000101
toyoshim7a809662015-10-06 00:54:21 -0700102 if (initialized_) {
103 // Platform dependent initialization was already finished for previously
104 // initialized clients.
105 if (result_ == Result::OK) {
106 AddInitialPorts(client);
107 clients_.insert(client);
108 }
109 // Complete synchronously with |result_|;
110 result = result_;
111 } else {
112 bool too_many_pending_clients_exist =
113 pending_clients_.size() >= kMaxPendingClientCount;
114 // Do not accept a new request if the pending client list contains too
115 // many clients, or Shutdown() was already called.
116 if (too_many_pending_clients_exist || finalized_) {
117 result = Result::INITIALIZATION_ERROR;
118 } else {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000119 // Call StartInitialization() only for the first request.
toyoshim7a809662015-10-06 00:54:21 -0700120 if (pending_clients_.empty()) {
121 completion = Completion::INVOKE_INITIALIZATION;
122 session_thread_runner_ = base::MessageLoop::current()->task_runner();
123 } else {
124 completion = Completion::COMPLETE_ASYNCHRONOUSLY;
125 }
toyoshima485ff92014-10-23 00:17:59 -0700126 pending_clients_.insert(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000127 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000128 }
ochangae3bae92016-01-12 19:42:10 -0800129
130 if (completion == Completion::COMPLETE_SYNCHRONOUSLY) {
131 client->CompleteStartSession(result);
132 return;
133 }
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000134 }
crogers@google.com27356e42013-06-22 04:03:03 +0000135
ochangae3bae92016-01-12 19:42:10 -0800136 if (completion == Completion::INVOKE_INITIALIZATION) {
toyoshim7a809662015-10-06 00:54:21 -0700137 // Lazily initialize the MIDI back-end.
138 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000139 // CompleteInitialization() will be called asynchronously when platform
140 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700141 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000142 }
crogers@google.com27356e42013-06-22 04:03:03 +0000143}
144
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000145void MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700146 ReportUsage(Usage::SESSION_ENDED);
147
toyoshima485ff92014-10-23 00:17:59 -0700148 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800149 // any method of |client| is dangerous. Calls on clients *must* be protected
150 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000151 base::AutoLock auto_lock(lock_);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000152 clients_.erase(client);
153 pending_clients_.erase(client);
crogers@google.com27356e42013-06-22 04:03:03 +0000154}
155
toyoshim7dd12482015-04-07 07:02:49 -0700156void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
agoodead116b22015-12-07 00:00:35 -0800157 base::AutoLock auto_lock(lock_);
158 if (clients_.find(client) == clients_.end())
159 return;
160
agoodeb02d0962015-12-07 19:45:54 -0800161 // Continue to hold lock_ here in case another thread is currently doing
162 // EndSession.
toyoshim7dd12482015-04-07 07:02:49 -0700163 client->AccumulateMidiBytesSent(n);
164}
165
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000166void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500167 uint32_t port_index,
168 const std::vector<uint8_t>& data,
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000169 double timestamp) {
170 NOTREACHED();
171}
172
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000173void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700174 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000175}
176
toyoshimf1b88962015-07-09 14:14:51 -0700177void MidiManager::CompleteInitialization(Result result) {
toyoshim7a809662015-10-06 00:54:21 -0700178 base::AutoLock auto_lock(lock_);
179 if (session_thread_runner_) {
180 session_thread_runner_->PostTask(
181 FROM_HERE, base::Bind(&MidiManager::CompleteInitializationInternal,
182 base::Unretained(this), result));
183 }
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000184}
185
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000186void MidiManager::AddInputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700187 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700188 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000189 input_ports_.push_back(info);
toyoshima62d6742014-10-23 09:05:03 -0700190 for (auto client : clients_)
191 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000192}
193
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000194void MidiManager::AddOutputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700195 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700196 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000197 output_ports_.push_back(info);
toyoshima62d6742014-10-23 09:05:03 -0700198 for (auto client : clients_)
199 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000200}
201
Avi Drissman3528fd02015-12-18 20:11:31 -0500202void MidiManager::SetInputPortState(uint32_t port_index, MidiPortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800203 base::AutoLock auto_lock(lock_);
204 DCHECK_LT(port_index, input_ports_.size());
205 input_ports_[port_index].state = state;
206 for (auto client : clients_)
207 client->SetInputPortState(port_index, state);
208}
209
Avi Drissman3528fd02015-12-18 20:11:31 -0500210void MidiManager::SetOutputPortState(uint32_t port_index, MidiPortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800211 base::AutoLock auto_lock(lock_);
212 DCHECK_LT(port_index, output_ports_.size());
213 output_ports_[port_index].state = state;
214 for (auto client : clients_)
215 client->SetOutputPortState(port_index, state);
216}
217
Avi Drissman3528fd02015-12-18 20:11:31 -0500218void MidiManager::ReceiveMidiData(uint32_t port_index,
219 const uint8_t* data,
220 size_t length,
221 double timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000222 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000223
toyoshima62d6742014-10-23 09:05:03 -0700224 for (auto client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700225 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000226}
227
toyoshimf1b88962015-07-09 14:14:51 -0700228void MidiManager::CompleteInitializationInternal(Result result) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000229 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
toyoshim715385c2015-08-09 23:00:26 -0700230 ReportUsage(Usage::INITIALIZED);
231 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts",
232 static_cast<Sample>(input_ports_.size()),
233 kMaxUmaDevices + 1);
234 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts",
235 static_cast<Sample>(output_ports_.size()),
236 kMaxUmaDevices + 1);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000237
238 base::AutoLock auto_lock(lock_);
239 DCHECK(clients_.empty());
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000240 DCHECK(!initialized_);
241 initialized_ = true;
242 result_ = result;
243
toyoshima62d6742014-10-23 09:05:03 -0700244 for (auto client : pending_clients_) {
toyoshimf1b88962015-07-09 14:14:51 -0700245 if (result_ == Result::OK) {
toyoshima62d6742014-10-23 09:05:03 -0700246 AddInitialPorts(client);
toyoshima485ff92014-10-23 00:17:59 -0700247 clients_.insert(client);
toyoshima62d6742014-10-23 09:05:03 -0700248 }
toyoshima485ff92014-10-23 00:17:59 -0700249 client->CompleteStartSession(result_);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000250 }
251 pending_clients_.clear();
252}
253
toyoshima62d6742014-10-23 09:05:03 -0700254void MidiManager::AddInitialPorts(MidiManagerClient* client) {
255 lock_.AssertAcquired();
256
257 for (const auto& info : input_ports_)
258 client->AddInputPort(info);
259 for (const auto& info : output_ports_)
260 client->AddOutputPort(info);
261}
262
toyoshim7a809662015-10-06 00:54:21 -0700263void MidiManager::ShutdownOnSessionThread() {
264 Finalize();
265 base::AutoLock auto_lock(lock_);
266 finalized_ = true;
267
268 // Detach all clients so that they do not call MidiManager methods any more.
269 for (auto client : clients_)
270 client->Detach();
271}
272
toyoshime147c5e2015-05-07 21:58:31 -0700273} // namespace midi
crogers@google.com27356e42013-06-22 04:03:03 +0000274} // namespace media