blob: 21f6c2309d77e684373f2ebb5ad01c9c8f9a574c [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 }
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000129 }
crogers@google.com27356e42013-06-22 04:03:03 +0000130
toyoshim7a809662015-10-06 00:54:21 -0700131 if (completion == Completion::COMPLETE_SYNCHRONOUSLY) {
132 client->CompleteStartSession(result);
133 } else if (completion == Completion::INVOKE_INITIALIZATION) {
134 // Lazily initialize the MIDI back-end.
135 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000136 // CompleteInitialization() will be called asynchronously when platform
137 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700138 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000139 }
crogers@google.com27356e42013-06-22 04:03:03 +0000140}
141
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000142void MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700143 ReportUsage(Usage::SESSION_ENDED);
144
toyoshima485ff92014-10-23 00:17:59 -0700145 // At this point, |client| can be in the destruction process, and calling
146 // any method of |client| is dangerous.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000147 base::AutoLock auto_lock(lock_);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000148 clients_.erase(client);
149 pending_clients_.erase(client);
crogers@google.com27356e42013-06-22 04:03:03 +0000150}
151
toyoshim7dd12482015-04-07 07:02:49 -0700152void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
agoodead116b22015-12-07 00:00:35 -0800153 base::AutoLock auto_lock(lock_);
154 if (clients_.find(client) == clients_.end())
155 return;
156
agoodeb02d0962015-12-07 19:45:54 -0800157 // Continue to hold lock_ here in case another thread is currently doing
158 // EndSession.
159 // Note that if we are in EndSession, then a destructor is being called and
160 // it isn't really safe to call this method. But we don't have another way to
161 // check this right now.
toyoshim7dd12482015-04-07 07:02:49 -0700162 client->AccumulateMidiBytesSent(n);
163}
164
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000165void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500166 uint32_t port_index,
167 const std::vector<uint8_t>& data,
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000168 double timestamp) {
169 NOTREACHED();
170}
171
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000172void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700173 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000174}
175
toyoshimf1b88962015-07-09 14:14:51 -0700176void MidiManager::CompleteInitialization(Result result) {
toyoshim7a809662015-10-06 00:54:21 -0700177 base::AutoLock auto_lock(lock_);
178 if (session_thread_runner_) {
179 session_thread_runner_->PostTask(
180 FROM_HERE, base::Bind(&MidiManager::CompleteInitializationInternal,
181 base::Unretained(this), result));
182 }
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000183}
184
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000185void MidiManager::AddInputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700186 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700187 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000188 input_ports_.push_back(info);
toyoshima62d6742014-10-23 09:05:03 -0700189 for (auto client : clients_)
190 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000191}
192
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000193void MidiManager::AddOutputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700194 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700195 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000196 output_ports_.push_back(info);
toyoshima62d6742014-10-23 09:05:03 -0700197 for (auto client : clients_)
198 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000199}
200
Avi Drissman3528fd02015-12-18 20:11:31 -0500201void MidiManager::SetInputPortState(uint32_t port_index, MidiPortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800202 base::AutoLock auto_lock(lock_);
203 DCHECK_LT(port_index, input_ports_.size());
204 input_ports_[port_index].state = state;
205 for (auto client : clients_)
206 client->SetInputPortState(port_index, state);
207}
208
Avi Drissman3528fd02015-12-18 20:11:31 -0500209void MidiManager::SetOutputPortState(uint32_t port_index, MidiPortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800210 base::AutoLock auto_lock(lock_);
211 DCHECK_LT(port_index, output_ports_.size());
212 output_ports_[port_index].state = state;
213 for (auto client : clients_)
214 client->SetOutputPortState(port_index, state);
215}
216
Avi Drissman3528fd02015-12-18 20:11:31 -0500217void MidiManager::ReceiveMidiData(uint32_t port_index,
218 const uint8_t* data,
219 size_t length,
220 double timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000221 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000222
toyoshima62d6742014-10-23 09:05:03 -0700223 for (auto client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700224 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000225}
226
toyoshimf1b88962015-07-09 14:14:51 -0700227void MidiManager::CompleteInitializationInternal(Result result) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000228 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
toyoshim715385c2015-08-09 23:00:26 -0700229 ReportUsage(Usage::INITIALIZED);
230 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts",
231 static_cast<Sample>(input_ports_.size()),
232 kMaxUmaDevices + 1);
233 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts",
234 static_cast<Sample>(output_ports_.size()),
235 kMaxUmaDevices + 1);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000236
237 base::AutoLock auto_lock(lock_);
238 DCHECK(clients_.empty());
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000239 DCHECK(!initialized_);
240 initialized_ = true;
241 result_ = result;
242
toyoshima62d6742014-10-23 09:05:03 -0700243 for (auto client : pending_clients_) {
toyoshimf1b88962015-07-09 14:14:51 -0700244 if (result_ == Result::OK) {
toyoshima62d6742014-10-23 09:05:03 -0700245 AddInitialPorts(client);
toyoshima485ff92014-10-23 00:17:59 -0700246 clients_.insert(client);
toyoshima62d6742014-10-23 09:05:03 -0700247 }
toyoshima485ff92014-10-23 00:17:59 -0700248 client->CompleteStartSession(result_);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000249 }
250 pending_clients_.clear();
251}
252
toyoshima62d6742014-10-23 09:05:03 -0700253void MidiManager::AddInitialPorts(MidiManagerClient* client) {
254 lock_.AssertAcquired();
255
256 for (const auto& info : input_ports_)
257 client->AddInputPort(info);
258 for (const auto& info : output_ports_)
259 client->AddOutputPort(info);
260}
261
toyoshim7a809662015-10-06 00:54:21 -0700262void MidiManager::ShutdownOnSessionThread() {
263 Finalize();
264 base::AutoLock auto_lock(lock_);
265 finalized_ = true;
266
267 // Detach all clients so that they do not call MidiManager methods any more.
268 for (auto client : clients_)
269 client->Detach();
270}
271
toyoshime147c5e2015-05-07 21:58:31 -0700272} // namespace midi
crogers@google.com27356e42013-06-22 04:03:03 +0000273} // namespace media