blob: 3c97a0f37285dcac60c92df45acd82af7ee9c03f [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"
crogers@google.com542a43a2013-07-31 05:16:49 +000011
crogers@google.com27356e42013-06-22 04:03:03 +000012namespace media {
toyoshime147c5e2015-05-07 21:58:31 -070013namespace midi {
crogers@google.com27356e42013-06-22 04:03:03 +000014
toyoshim715385c2015-08-09 23:00:26 -070015namespace {
16
17using Sample = base::HistogramBase::Sample;
18
19// If many users have more devices, this number will be increased.
20// But the number is expected to be big enough for now.
21const Sample kMaxUmaDevices = 31;
22
23// Used to count events for usage histogram.
24enum class Usage {
25 CREATED,
26 CREATED_ON_UNSUPPORTED_PLATFORMS,
27 SESSION_STARTED,
28 SESSION_ENDED,
29 INITIALIZED,
30 INPUT_PORT_ADDED,
31 OUTPUT_PORT_ADDED,
32
33 // New items should be inserted here, and |MAX| should point the last item.
34 MAX = INITIALIZED,
35};
36
toyoshim7a809662015-10-06 00:54:21 -070037// Used in StartSession.
38enum class Completion {
39 COMPLETE_SYNCHRONOUSLY,
40 INVOKE_INITIALIZATION,
41 COMPLETE_ASYNCHRONOUSLY,
42};
43
toyoshim715385c2015-08-09 23:00:26 -070044void ReportUsage(Usage usage) {
45 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage",
46 static_cast<Sample>(usage),
47 static_cast<Sample>(Usage::MAX) + 1);
48}
49
50} // namespace
51
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000052MidiManager::MidiManager()
toyoshim7a809662015-10-06 00:54:21 -070053 : initialized_(false), finalized_(false), result_(Result::NOT_INITIALIZED) {
toyoshim715385c2015-08-09 23:00:26 -070054 ReportUsage(Usage::CREATED);
crogers@google.com27356e42013-06-22 04:03:03 +000055}
56
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000057MidiManager::~MidiManager() {
toyoshim7a809662015-10-06 00:54:21 -070058 // Make sure that Finalize() is called to clean up resources allocated on
59 // the Chrome_IOThread.
60 DCHECK(finalized_);
yukawa@chromium.orgdb7ad8b2013-12-04 15:42:41 +000061}
crogers@google.com27356e42013-06-22 04:03:03 +000062
agoode7de413f2015-04-24 00:13:39 -070063#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
64 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000065MidiManager* MidiManager::Create() {
toyoshim715385c2015-08-09 23:00:26 -070066 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000067 return new MidiManager;
68}
69#endif
70
toyoshim7a809662015-10-06 00:54:21 -070071void MidiManager::Shutdown() {
72 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown",
73 static_cast<int>(result_),
74 static_cast<int>(Result::MAX) + 1);
75 base::AutoLock auto_lock(lock_);
76 if (session_thread_runner_) {
77 session_thread_runner_->PostTask(
78 FROM_HERE, base::Bind(&MidiManager::ShutdownOnSessionThread,
79 base::Unretained(this)));
80 session_thread_runner_ = nullptr;
81 } else {
82 finalized_ = true;
83 }
84}
85
toyoshima485ff92014-10-23 00:17:59 -070086void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -070087 ReportUsage(Usage::SESSION_STARTED);
88
toyoshim7a809662015-10-06 00:54:21 -070089 Completion completion = Completion::COMPLETE_SYNCHRONOUSLY;
90 Result result = Result::NOT_INITIALIZED;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000091
92 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000093 base::AutoLock auto_lock(lock_);
toyoshima485ff92014-10-23 00:17:59 -070094 if (clients_.find(client) != clients_.end() ||
95 pending_clients_.find(client) != pending_clients_.end()) {
96 // Should not happen. But just in case the renderer is compromised.
97 NOTREACHED();
98 return;
99 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000100
toyoshim7a809662015-10-06 00:54:21 -0700101 if (initialized_) {
102 // Platform dependent initialization was already finished for previously
103 // initialized clients.
104 if (result_ == Result::OK) {
105 AddInitialPorts(client);
106 clients_.insert(client);
107 }
108 // Complete synchronously with |result_|;
109 result = result_;
110 } else {
111 bool too_many_pending_clients_exist =
112 pending_clients_.size() >= kMaxPendingClientCount;
113 // Do not accept a new request if the pending client list contains too
114 // many clients, or Shutdown() was already called.
115 if (too_many_pending_clients_exist || finalized_) {
116 result = Result::INITIALIZATION_ERROR;
117 } else {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000118 // Call StartInitialization() only for the first request.
toyoshim7a809662015-10-06 00:54:21 -0700119 if (pending_clients_.empty()) {
120 completion = Completion::INVOKE_INITIALIZATION;
121 session_thread_runner_ = base::MessageLoop::current()->task_runner();
122 } else {
123 completion = Completion::COMPLETE_ASYNCHRONOUSLY;
124 }
toyoshima485ff92014-10-23 00:17:59 -0700125 pending_clients_.insert(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000126 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000127 }
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000128 }
crogers@google.com27356e42013-06-22 04:03:03 +0000129
toyoshim7a809662015-10-06 00:54:21 -0700130 if (completion == Completion::COMPLETE_SYNCHRONOUSLY) {
131 client->CompleteStartSession(result);
132 } else if (completion == Completion::INVOKE_INITIALIZATION) {
133 // Lazily initialize the MIDI back-end.
134 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000135 // CompleteInitialization() will be called asynchronously when platform
136 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700137 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000138 }
crogers@google.com27356e42013-06-22 04:03:03 +0000139}
140
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000141void MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700142 ReportUsage(Usage::SESSION_ENDED);
143
toyoshima485ff92014-10-23 00:17:59 -0700144 // At this point, |client| can be in the destruction process, and calling
145 // any method of |client| is dangerous.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000146 base::AutoLock auto_lock(lock_);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000147 clients_.erase(client);
148 pending_clients_.erase(client);
crogers@google.com27356e42013-06-22 04:03:03 +0000149}
150
toyoshim7dd12482015-04-07 07:02:49 -0700151void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
agoodead116b22015-12-07 00:00:35 -0800152 base::AutoLock auto_lock(lock_);
153 if (clients_.find(client) == clients_.end())
154 return;
155
agoodeb02d0962015-12-07 19:45:54 -0800156 // Continue to hold lock_ here in case another thread is currently doing
157 // EndSession.
158 // Note that if we are in EndSession, then a destructor is being called and
159 // it isn't really safe to call this method. But we don't have another way to
160 // check this right now.
toyoshim7dd12482015-04-07 07:02:49 -0700161 client->AccumulateMidiBytesSent(n);
162}
163
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000164void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500165 uint32_t port_index,
166 const std::vector<uint8_t>& data,
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000167 double timestamp) {
168 NOTREACHED();
169}
170
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000171void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700172 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000173}
174
toyoshimf1b88962015-07-09 14:14:51 -0700175void MidiManager::CompleteInitialization(Result result) {
toyoshim7a809662015-10-06 00:54:21 -0700176 base::AutoLock auto_lock(lock_);
177 if (session_thread_runner_) {
178 session_thread_runner_->PostTask(
179 FROM_HERE, base::Bind(&MidiManager::CompleteInitializationInternal,
180 base::Unretained(this), result));
181 }
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000182}
183
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000184void MidiManager::AddInputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700185 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700186 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000187 input_ports_.push_back(info);
toyoshima62d6742014-10-23 09:05:03 -0700188 for (auto client : clients_)
189 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000190}
191
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000192void MidiManager::AddOutputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700193 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700194 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000195 output_ports_.push_back(info);
toyoshima62d6742014-10-23 09:05:03 -0700196 for (auto client : clients_)
197 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000198}
199
Avi Drissman3528fd02015-12-18 20:11:31 -0500200void MidiManager::SetInputPortState(uint32_t port_index, MidiPortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800201 base::AutoLock auto_lock(lock_);
202 DCHECK_LT(port_index, input_ports_.size());
203 input_ports_[port_index].state = state;
204 for (auto client : clients_)
205 client->SetInputPortState(port_index, state);
206}
207
Avi Drissman3528fd02015-12-18 20:11:31 -0500208void MidiManager::SetOutputPortState(uint32_t port_index, MidiPortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800209 base::AutoLock auto_lock(lock_);
210 DCHECK_LT(port_index, output_ports_.size());
211 output_ports_[port_index].state = state;
212 for (auto client : clients_)
213 client->SetOutputPortState(port_index, state);
214}
215
Avi Drissman3528fd02015-12-18 20:11:31 -0500216void MidiManager::ReceiveMidiData(uint32_t port_index,
217 const uint8_t* data,
218 size_t length,
219 double timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000220 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000221
toyoshima62d6742014-10-23 09:05:03 -0700222 for (auto client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700223 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000224}
225
toyoshimf1b88962015-07-09 14:14:51 -0700226void MidiManager::CompleteInitializationInternal(Result result) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000227 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
toyoshim715385c2015-08-09 23:00:26 -0700228 ReportUsage(Usage::INITIALIZED);
229 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts",
230 static_cast<Sample>(input_ports_.size()),
231 kMaxUmaDevices + 1);
232 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts",
233 static_cast<Sample>(output_ports_.size()),
234 kMaxUmaDevices + 1);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000235
236 base::AutoLock auto_lock(lock_);
237 DCHECK(clients_.empty());
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000238 DCHECK(!initialized_);
239 initialized_ = true;
240 result_ = result;
241
toyoshima62d6742014-10-23 09:05:03 -0700242 for (auto client : pending_clients_) {
toyoshimf1b88962015-07-09 14:14:51 -0700243 if (result_ == Result::OK) {
toyoshima62d6742014-10-23 09:05:03 -0700244 AddInitialPorts(client);
toyoshima485ff92014-10-23 00:17:59 -0700245 clients_.insert(client);
toyoshima62d6742014-10-23 09:05:03 -0700246 }
toyoshima485ff92014-10-23 00:17:59 -0700247 client->CompleteStartSession(result_);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000248 }
249 pending_clients_.clear();
250}
251
toyoshima62d6742014-10-23 09:05:03 -0700252void MidiManager::AddInitialPorts(MidiManagerClient* client) {
253 lock_.AssertAcquired();
254
255 for (const auto& info : input_ports_)
256 client->AddInputPort(info);
257 for (const auto& info : output_ports_)
258 client->AddOutputPort(info);
259}
260
toyoshim7a809662015-10-06 00:54:21 -0700261void MidiManager::ShutdownOnSessionThread() {
262 Finalize();
263 base::AutoLock auto_lock(lock_);
264 finalized_ = true;
265
266 // Detach all clients so that they do not call MidiManager methods any more.
267 for (auto client : clients_)
268 client->Detach();
269}
270
toyoshime147c5e2015-05-07 21:58:31 -0700271} // namespace midi
crogers@google.com27356e42013-06-22 04:03:03 +0000272} // namespace media