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

Side by Side Diff: media/midi/midi_manager_winrt.cc

Issue 2243183002: Web MIDI backend for Windows 10 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: StopWatcher(), invalid token value constant, fixup Created 4 years, 3 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
« no previous file with comments | « media/midi/midi_manager_winrt.h ('k') | media/midi/midi_options.gni » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 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_winrt.h"
6
7 #include <comdef.h>
8 #include <robuffer.h>
9 #include <windows.devices.enumeration.h>
10 #include <windows.devices.midi.h>
11 #include <wrl/event.h>
12
13 #include <iomanip>
14 #include <unordered_map>
15 #include <unordered_set>
16
17 #include "base/bind.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/thread_checker.h"
20 #include "base/threading/thread_task_runner_handle.h"
21 #include "base/timer/timer.h"
22 #include "base/win/scoped_comptr.h"
23 #include "base/win/windows_version.h"
24 #include "media/midi/midi_scheduler.h"
25
26 namespace media {
27 namespace midi {
28 namespace {
29
30 namespace WRL = Microsoft::WRL;
31
32 using namespace ABI::Windows::Devices::Enumeration;
33 using namespace ABI::Windows::Devices::Midi;
34 using namespace ABI::Windows::Foundation;
35 using namespace ABI::Windows::Storage::Streams;
36
37 using base::win::ScopedComPtr;
38
39 // Helpers for printing HRESULTs.
40 struct PrintHr {
41 PrintHr(HRESULT hr) : hr_(hr) {}
42 HRESULT hr_;
Takashi Toyoshima 2016/08/29 16:20:17 I overlooked this style nit, but we do not use und
Shao-Chuan Lee 2016/08/30 02:37:48 Done.
43 };
44
45 std::ostream& operator<<(std::ostream& os, const PrintHr& phr) {
46 std::ios_base::fmtflags ff = os.flags();
47 os << _com_error(phr.hr_).ErrorMessage() << " (0x" << std::hex
48 << std::uppercase << std::setfill('0') << std::setw(8) << phr.hr_ << ")";
49 os.flags(ff);
50 return os;
51 }
52
53 // Factory functions that activate and create WinRT components. The caller takes
54 // ownership of the returning ComPtr.
55 template <typename InterfaceType, base::char16 const* runtime_class_id>
56 ScopedComPtr<InterfaceType> WrlStaticsFactory() {
57 ScopedComPtr<InterfaceType> com_ptr;
58
59 HRESULT hr = GetActivationFactory(
60 WRL::Wrappers::HStringReference(runtime_class_id).Get(),
61 com_ptr.Receive());
62 if (FAILED(hr)) {
63 VLOG(1) << "GetActivationFactory failed: " << PrintHr(hr);
64 com_ptr = nullptr;
65 }
66
67 return com_ptr;
68 }
69
70 template <typename T, HRESULT (T::*method)(HSTRING*)>
71 std::string GetStringFromObjectMethod(T* obj) {
72 HSTRING result;
73 HRESULT hr = (obj->*method)(&result);
74 if (FAILED(hr)) {
75 VLOG(1) << "GetStringFromObjectMethod failed: " << PrintHr(hr);
76 return std::string();
77 }
78
79 // Note: empty HSTRINGs are represent as nullptr, and instantiating
80 // std::string with nullptr (in base::WideToUTF8) is undefined behavior.
81 const base::char16* buffer = WindowsGetStringRawBuffer(result, nullptr);
82 if (buffer)
83 return base::WideToUTF8(buffer);
84 return std::string();
85 }
86
87 template <typename T>
88 std::string GetIdString(T* obj) {
89 return GetStringFromObjectMethod<T, &T::get_Id>(obj);
90 }
91
92 template <typename T>
93 std::string GetDeviceIdString(T* obj) {
94 return GetStringFromObjectMethod<T, &T::get_DeviceId>(obj);
95 }
96
97 std::string GetNameString(IDeviceInformation* info) {
98 return GetStringFromObjectMethod<IDeviceInformation,
99 &IDeviceInformation::get_Name>(info);
100 }
101
102 HRESULT GetPointerToBufferData(IBuffer* buffer, uint8_t** out) {
103 ScopedComPtr<Windows::Storage::Streams::IBufferByteAccess> buffer_byte_access;
104
105 HRESULT hr = buffer_byte_access.QueryFrom(buffer);
106 if (FAILED(hr)) {
107 VLOG(1) << "QueryInterface failed: " << PrintHr(hr);
108 return hr;
109 }
110
111 // Lifetime of the pointing buffer is controlled by the buffer object.
112 hr = buffer_byte_access->Buffer(out);
113 if (FAILED(hr)) {
114 VLOG(1) << "Buffer failed: " << PrintHr(hr);
115 return hr;
116 }
117
118 return S_OK;
119 }
120
121 // Tokens with value = 0 are considered as invalid (as in <wrl/event.h>).
Shao-Chuan Lee 2016/08/30 02:37:48 grammar
122 const int64_t kInvalidTokenValue = 0;
123
124 template <typename InterfaceType>
125 struct MidiPort {
126 MidiPort() = default;
127
128 uint32_t index;
129 ScopedComPtr<InterfaceType> handle;
130 base::TimeTicks start_time;
131 EventRegistrationToken token_MessageReceived;
132
133 private:
134 DISALLOW_COPY_AND_ASSIGN(MidiPort);
135 };
136
137 } // namespace
138
139 template <typename InterfaceType,
140 typename RuntimeType,
141 typename StaticsInterfaceType,
142 base::char16 const* runtime_class_id>
143 class MidiManagerWinrt::MidiPortManager {
144 public:
145 // MidiPortManager instances should be constructed on the COM thread.
146 MidiPortManager(MidiManagerWinrt* midi_manager)
147 : midi_manager_(midi_manager),
148 task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
149
150 bool StartWatcher() {
151 DCHECK(thread_checker_.CalledOnValidThread());
152
153 HRESULT hr;
154
155 midi_port_statics_ =
156 WrlStaticsFactory<StaticsInterfaceType, runtime_class_id>();
157 if (!midi_port_statics_)
158 return false;
159
160 HSTRING device_selector = nullptr;
161 hr = midi_port_statics_->GetDeviceSelector(&device_selector);
162 if (FAILED(hr)) {
163 VLOG(1) << "GetDeviceSelector failed: " << PrintHr(hr);
164 return false;
165 }
166
167 auto dev_info_statics = WrlStaticsFactory<
168 IDeviceInformationStatics,
169 RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>();
170 if (!dev_info_statics)
171 return false;
172
173 hr = dev_info_statics->CreateWatcherAqsFilter(device_selector,
174 watcher_.Receive());
175 if (FAILED(hr)) {
176 VLOG(1) << "CreateWatcherAqsFilter failed: " << PrintHr(hr);
177 return false;
178 }
179
180 // Register callbacks to WinRT that post state-modifying jobs back to COM
181 // thread. |weak_ptr| and |task_runner| are captured by lambda callbacks for
182 // posting jobs. Note that WinRT callback arguments should not be passed
183 // outside the callback since the pointers may be unavailable afterwards.
184 base::WeakPtr<MidiPortManager> weak_ptr = GetWeakPtrFromFactory();
185 scoped_refptr<base::SingleThreadTaskRunner> task_runner = task_runner_;
186
187 hr = watcher_->add_Added(
188 WRL::Callback<ITypedEventHandler<DeviceWatcher*, DeviceInformation*>>(
189 [weak_ptr, task_runner](IDeviceWatcher* watcher,
190 IDeviceInformation* info) {
191 std::string dev_id = GetIdString(info),
192 dev_name = GetNameString(info);
193
194 task_runner->PostTask(
195 FROM_HERE, base::Bind(&MidiPortManager::OnAdded, weak_ptr,
196 dev_id, dev_name));
197
198 return S_OK;
199 })
200 .Get(),
201 &token_Added_);
202 if (FAILED(hr)) {
203 VLOG(1) << "add_Added failed: " << PrintHr(hr);
204 return false;
205 }
206
207 hr = watcher_->add_EnumerationCompleted(
208 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
209 [weak_ptr, task_runner](IDeviceWatcher* watcher,
210 IInspectable* insp) {
211 task_runner->PostTask(
212 FROM_HERE,
213 base::Bind(&MidiPortManager::OnEnumerationCompleted,
214 weak_ptr));
215
216 return S_OK;
217 })
218 .Get(),
219 &token_EnumerationCompleted_);
220 if (FAILED(hr)) {
221 VLOG(1) << "add_EnumerationCompleted failed: " << PrintHr(hr);
222 return false;
223 }
224
225 hr = watcher_->add_Removed(
226 WRL::Callback<
227 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
228 [weak_ptr, task_runner](IDeviceWatcher* watcher,
229 IDeviceInformationUpdate* update) {
230 std::string dev_id = GetIdString(update);
231
232 task_runner->PostTask(
233 FROM_HERE,
234 base::Bind(&MidiPortManager::OnRemoved, weak_ptr, dev_id));
235
236 return S_OK;
237 })
238 .Get(),
239 &token_Removed_);
240 if (FAILED(hr)) {
241 VLOG(1) << "add_Removed failed: " << PrintHr(hr);
242 return false;
243 }
244
245 hr = watcher_->add_Stopped(
246 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
247 [](IDeviceWatcher* watcher, IInspectable* insp) {
248 // Placeholder, does nothing for now.
249 return S_OK;
250 })
251 .Get(),
252 &token_Stopped_);
253 if (FAILED(hr)) {
254 VLOG(1) << "add_Stopped failed: " << PrintHr(hr);
255 return false;
256 }
257
258 hr = watcher_->add_Updated(
259 WRL::Callback<
260 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
261 [](IDeviceWatcher* watcher, IDeviceInformationUpdate* update) {
262 // TODO(shaochuan): Check for fields to be updated here.
263 return S_OK;
264 })
265 .Get(),
266 &token_Updated_);
267 if (FAILED(hr)) {
268 VLOG(1) << "add_Updated failed: " << PrintHr(hr);
269 return false;
270 }
271
272 hr = watcher_->Start();
273 if (FAILED(hr)) {
274 VLOG(1) << "Start failed: " << PrintHr(hr);
275 return false;
276 }
277
278 is_initialized_ = true;
279 return true;
280 }
281
282 void StopWatcher() {
283 DCHECK(thread_checker_.CalledOnValidThread());
284
285 HRESULT hr;
286
287 if (token_Added_.value != kInvalidTokenValue) {
288 hr = watcher_->remove_Added(token_Added_);
289 VLOG_IF(1, FAILED(hr)) << "remove_Added failed: " << PrintHr(hr);
290 token_Added_.value = kInvalidTokenValue;
291 }
292 if (token_EnumerationCompleted_.value != kInvalidTokenValue) {
293 hr = watcher_->remove_EnumerationCompleted(token_EnumerationCompleted_);
294 VLOG_IF(1, FAILED(hr)) << "remove_EnumerationCompleted failed: "
295 << PrintHr(hr);
296 token_EnumerationCompleted_.value = kInvalidTokenValue;
297 }
298 if (token_Removed_.value != kInvalidTokenValue) {
299 hr = watcher_->remove_Removed(token_Removed_);
300 VLOG_IF(1, FAILED(hr)) << "remove_Removed failed: " << PrintHr(hr);
301 token_Removed_.value = kInvalidTokenValue;
302 }
303 if (token_Stopped_.value != kInvalidTokenValue) {
304 hr = watcher_->remove_Stopped(token_Stopped_);
305 VLOG_IF(1, FAILED(hr)) << "remove_Stopped failed: " << PrintHr(hr);
306 token_Stopped_.value = kInvalidTokenValue;
307 }
308 if (token_Updated_.value != kInvalidTokenValue) {
309 hr = watcher_->remove_Updated(token_Updated_);
310 VLOG_IF(1, FAILED(hr)) << "remove_Updated failed: " << PrintHr(hr);
311 token_Updated_.value = kInvalidTokenValue;
312 }
313
314 if (is_initialized_) {
315 hr = watcher_->Stop();
316 VLOG_IF(1, FAILED(hr)) << "Stop failed: " << PrintHr(hr);
317 is_initialized_ = false;
318 }
319 }
320
321 ~MidiPortManager() {
322 DCHECK(thread_checker_.CalledOnValidThread());
323
324 StopWatcher();
325 }
326
327 MidiPort<InterfaceType>* GetPortByDeviceId(std::string dev_id) {
328 DCHECK(thread_checker_.CalledOnValidThread());
329 CHECK(is_initialized_);
330
331 auto it = ports_.find(dev_id);
332 if (it == ports_.end())
333 return nullptr;
334 return it->second.get();
335 }
336
337 MidiPort<InterfaceType>* GetPortByIndex(uint32_t port_index) {
338 DCHECK(thread_checker_.CalledOnValidThread());
339 CHECK(is_initialized_);
340
341 return GetPortByDeviceId(port_ids_[port_index]);
342 }
343
344 protected:
345 // Points to the MidiManagerWinrt instance, which is expected to outlive the
346 // MidiPortManager instance.
347 MidiManagerWinrt* midi_manager_;
348
349 // Task runner of the COM thread.
350 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
351
352 // Ensures all methods are called on the COM thread.
353 base::ThreadChecker thread_checker_;
354
355 // All manipulations to these fields should be done on COM thread.
356 std::unordered_map<std::string, std::unique_ptr<MidiPort<InterfaceType>>>
357 ports_;
358 std::vector<std::string> port_ids_;
359 std::unordered_map<std::string, std::string> port_names_;
360
361 // Set if the instance is initialized without error. Should be checked in all
362 // methods on COM thread except StartWatcher().
363 bool is_initialized_ = false;
364
365 private:
366 // DeviceWatcher callbacks:
367 void OnAdded(std::string dev_id, std::string dev_name) {
368 DCHECK(thread_checker_.CalledOnValidThread());
369 CHECK(is_initialized_);
370
371 // TODO(shaochuan): Disable Microsoft GS Wavetable Synth due to security
372 // reasons. http://crbug.com/499279
373
374 port_names_[dev_id] = dev_name;
375
376 WRL::Wrappers::HString dev_id_hstring;
377 HRESULT hr = dev_id_hstring.Set(base::UTF8ToWide(dev_id).c_str());
378 if (FAILED(hr)) {
379 VLOG(1) << "Set failed: " << PrintHr(hr);
380 return;
381 }
382
383 IAsyncOperation<RuntimeType*>* async_op;
384
385 hr = midi_port_statics_->FromIdAsync(dev_id_hstring.Get(), &async_op);
386 if (FAILED(hr)) {
387 VLOG(1) << "FromIdAsync failed: " << PrintHr(hr);
388 return;
389 }
390
391 base::WeakPtr<MidiPortManager> weak_ptr = GetWeakPtrFromFactory();
392 scoped_refptr<base::SingleThreadTaskRunner> task_runner = task_runner_;
393
394 hr = async_op->put_Completed(
395 WRL::Callback<IAsyncOperationCompletedHandler<RuntimeType*>>(
396 [weak_ptr, task_runner](IAsyncOperation<RuntimeType*>* async_op,
397 AsyncStatus status) {
398 // TODO(shaochuan): Check if port open time is accurate.
399 const auto now = base::TimeTicks::Now();
400
401 InterfaceType* handle;
402 HRESULT hr = async_op->GetResults(&handle);
403 if (FAILED(hr)) {
404 VLOG(1) << "GetResults failed: " << PrintHr(hr);
405 return hr;
406 }
407
408 // A reference to |async_op| is kept in |async_ops_|, safe to pass
409 // outside.
410 task_runner->PostTask(
411 FROM_HERE,
412 base::Bind(&MidiPortManager::OnCompletedGetPortFromIdAsync,
413 weak_ptr, handle, now, async_op));
414
415 return S_OK;
416 })
417 .Get());
418 if (FAILED(hr)) {
419 VLOG(1) << "put_Completed failed: " << PrintHr(hr);
420 return;
421 }
422
423 // Keep a reference to incompleted |async_op| for releasing later.
424 async_ops_.insert(async_op);
425 }
426
427 void OnEnumerationCompleted() {
428 DCHECK(thread_checker_.CalledOnValidThread());
429 CHECK(is_initialized_);
430
431 if (async_ops_.empty())
432 midi_manager_->OnPortManagerReady();
433 else
434 enumeration_completed_not_ready_ = true;
435 }
436
437 void OnRemoved(std::string dev_id) {
438 DCHECK(thread_checker_.CalledOnValidThread());
439 CHECK(is_initialized_);
440
441 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
442 if (!port) {
443 VLOG(1) << "Removing non-existent port " << dev_id;
444 return;
445 }
446
447 SetPortState(port->index, MIDI_PORT_DISCONNECTED);
448
449 RemovePortEventHandlers(port);
450 port->handle = nullptr;
451 }
452
453 void OnCompletedGetPortFromIdAsync(InterfaceType* handle,
454 base::TimeTicks start_time,
455 IAsyncOperation<RuntimeType*>* async_op) {
456 DCHECK(thread_checker_.CalledOnValidThread());
457 CHECK(is_initialized_);
458
459 EventRegistrationToken token = {kInvalidTokenValue};
460 if (!RegisterOnMessageReceived(handle, &token))
461 return;
462
463 std::string dev_id = GetDeviceIdString(handle);
464
465 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
466
467 if (port == nullptr) {
468 // TODO(shaochuan): Fill in manufacturer and driver version.
469 AddPort(MidiPortInfo(dev_id, std::string("Manufacturer"),
470 port_names_[dev_id], std::string("DriverVersion"),
471 MIDI_PORT_OPENED));
472
473 port = new MidiPort<InterfaceType>;
474 port->index = static_cast<uint32_t>(port_ids_.size());
475
476 ports_[dev_id].reset(port);
477 port_ids_.push_back(dev_id);
478 } else {
479 SetPortState(port->index, MIDI_PORT_CONNECTED);
480 }
481
482 port->handle = handle;
483 port->start_time = start_time;
484 port->token_MessageReceived = token;
485
486 // Manually release COM interface to completed |async_op|.
487 auto it = async_ops_.find(async_op);
488 CHECK(it != async_ops_.end());
489 (*it)->Release();
490 async_ops_.erase(it);
491
492 if (enumeration_completed_not_ready_ && async_ops_.empty()) {
493 midi_manager_->OnPortManagerReady();
494 enumeration_completed_not_ready_ = false;
495 }
496 }
497
498 // Overrided by MidiInPortManager to listen to input ports.
499 virtual bool RegisterOnMessageReceived(InterfaceType* handle,
500 EventRegistrationToken* p_token) {
501 return true;
502 }
503
504 // Overrided by MidiInPortManager to remove MessageReceived event handler.
505 virtual void RemovePortEventHandlers(MidiPort<InterfaceType>* port) {}
506
507 // Calls midi_manager_->Add{Input,Output}Port.
508 virtual void AddPort(MidiPortInfo info) = 0;
509
510 // Calls midi_manager_->Set{Input,Output}PortState.
511 virtual void SetPortState(uint32_t port_index, MidiPortState state) = 0;
512
513 // WeakPtrFactory has to be declared in derived class, use this method to
514 // retrieve upcasted WeakPtr for posting tasks.
515 virtual base::WeakPtr<MidiPortManager> GetWeakPtrFromFactory() = 0;
516
517 // Midi{In,Out}PortStatics instance.
518 ScopedComPtr<StaticsInterfaceType> midi_port_statics_;
519
520 // DeviceWatcher instance and event registration tokens for unsubscribing
521 // events in destructor.
522 ScopedComPtr<IDeviceWatcher> watcher_;
523 EventRegistrationToken token_Added_ = {kInvalidTokenValue},
524 token_EnumerationCompleted_ = {kInvalidTokenValue},
525 token_Removed_ = {kInvalidTokenValue},
526 token_Stopped_ = {kInvalidTokenValue},
527 token_Updated_ = {kInvalidTokenValue};
528
529 // Keeps AsyncOperation references before the operation completes. Note that
530 // raw pointers are used here and the COM interfaces should be released
531 // manually.
532 std::unordered_set<IAsyncOperation<RuntimeType*>*> async_ops_;
533
534 // Set when device enumeration is completed but OnPortManagerReady() is not
535 // called since some ports are not yet ready (i.e. |async_ops_| is not empty).
536 // In such cases, OnPortManagerReady() will be called in
537 // OnCompletedGetPortFromIdAsync() when the last pending port is ready.
538 bool enumeration_completed_not_ready_ = false;
539 };
540
541 class MidiManagerWinrt::MidiInPortManager final
542 : public MidiPortManager<IMidiInPort,
543 MidiInPort,
544 IMidiInPortStatics,
545 RuntimeClass_Windows_Devices_Midi_MidiInPort> {
546 public:
547 MidiInPortManager(MidiManagerWinrt* midi_manager)
548 : MidiPortManager(midi_manager), weak_factory_(this) {}
549
550 ~MidiInPortManager() {
551 DCHECK(thread_checker_.CalledOnValidThread());
552 if (!is_initialized_)
553 return;
554
555 for (const auto& entry : ports_)
556 RemovePortEventHandlers(entry.second.get());
557 }
558
559 private:
560 // MidiPortManager overrides:
561 bool RegisterOnMessageReceived(IMidiInPort* handle,
562 EventRegistrationToken* p_token) override {
563 DCHECK(thread_checker_.CalledOnValidThread());
564
565 base::WeakPtr<MidiInPortManager> weak_ptr = weak_factory_.GetWeakPtr();
566 scoped_refptr<base::SingleThreadTaskRunner> task_runner = task_runner_;
567
568 HRESULT hr = handle->add_MessageReceived(
569 WRL::Callback<
570 ITypedEventHandler<MidiInPort*, MidiMessageReceivedEventArgs*>>(
571 [weak_ptr, task_runner](IMidiInPort* handle,
572 IMidiMessageReceivedEventArgs* args) {
573 std::string dev_id = GetDeviceIdString(handle);
574
575 ScopedComPtr<IMidiMessage> message;
576 HRESULT hr = args->get_Message(message.Receive());
577 if (FAILED(hr)) {
578 VLOG(1) << "get_Message failed: " << PrintHr(hr);
579 return hr;
580 }
581
582 ScopedComPtr<IBuffer> buffer;
583 hr = message->get_RawData(buffer.Receive());
584 if (FAILED(hr)) {
585 VLOG(1) << "get_RawData failed: " << PrintHr(hr);
586 return hr;
587 }
588
589 uint8_t* p_buffer_data = nullptr;
590 hr = GetPointerToBufferData(buffer.get(), &p_buffer_data);
591 if (FAILED(hr))
592 return hr;
593
594 uint32_t data_length = 0;
595 hr = buffer->get_Length(&data_length);
596 if (FAILED(hr)) {
597 VLOG(1) << "get_Length failed: " << PrintHr(hr);
598 return hr;
599 }
600
601 std::vector<uint8_t> data(p_buffer_data,
602 p_buffer_data + data_length);
603
604 // Time since port opened in 100-nanosecond units.
605 TimeSpan time_span;
606 hr = message->get_Timestamp(&time_span);
607 if (FAILED(hr)) {
608 VLOG(1) << "get_Timestamp failed: " << PrintHr(hr);
609 return hr;
610 }
611
612 task_runner->PostTask(
613 FROM_HERE,
614 base::Bind(&MidiInPortManager::OnMessageReceived, weak_ptr,
615 dev_id, data, base::TimeDelta::FromMicroseconds(
616 time_span.Duration / 10)));
617
618 return S_OK;
619 })
620 .Get(),
621 p_token);
622 if (FAILED(hr)) {
623 VLOG(1) << "add_MessageReceived failed: " << PrintHr(hr);
624 return false;
625 }
626
627 return true;
628 }
629
630 void RemovePortEventHandlers(MidiPort<IMidiInPort>* port) {
631 if (!(port->handle &&
632 port->token_MessageReceived.value != kInvalidTokenValue))
633 return;
634
635 HRESULT hr =
636 port->handle->remove_MessageReceived(port->token_MessageReceived);
637 VLOG_IF(1, FAILED(hr)) << "remove_MessageReceived failed: " << PrintHr(hr);
638 }
639
640 void AddPort(MidiPortInfo info) final { midi_manager_->AddInputPort(info); }
641
642 void SetPortState(uint32_t port_index, MidiPortState state) final {
643 midi_manager_->SetInputPortState(port_index, state);
644 }
645
646 base::WeakPtr<MidiPortManager> GetWeakPtrFromFactory() final {
647 DCHECK(thread_checker_.CalledOnValidThread());
648
649 return weak_factory_.GetWeakPtr();
650 }
651
652 // Callback on receiving MIDI input message.
653 void OnMessageReceived(std::string dev_id,
654 std::vector<uint8_t> data,
655 base::TimeDelta time_since_start) {
656 DCHECK(thread_checker_.CalledOnValidThread());
657
658 MidiPort<IMidiInPort>* port = GetPortByDeviceId(dev_id);
659 CHECK(port);
660
661 midi_manager_->ReceiveMidiData(port->index, &data[0], data.size(),
662 port->start_time + time_since_start);
663 }
664
665 // Last member to ensure destructed first.
666 base::WeakPtrFactory<MidiInPortManager> weak_factory_;
667
668 DISALLOW_COPY_AND_ASSIGN(MidiInPortManager);
669 };
670
671 class MidiManagerWinrt::MidiOutPortManager final
672 : public MidiPortManager<IMidiOutPort,
673 IMidiOutPort,
674 IMidiOutPortStatics,
675 RuntimeClass_Windows_Devices_Midi_MidiOutPort> {
676 public:
677 MidiOutPortManager(MidiManagerWinrt* midi_manager)
678 : MidiPortManager(midi_manager), weak_factory_(this) {}
679
680 private:
681 // MidiPortManager overrides:
682 void AddPort(MidiPortInfo info) final { midi_manager_->AddOutputPort(info); }
683
684 void SetPortState(uint32_t port_index, MidiPortState state) final {
685 midi_manager_->SetOutputPortState(port_index, state);
686 }
687
688 base::WeakPtr<MidiPortManager> GetWeakPtrFromFactory() final {
689 DCHECK(thread_checker_.CalledOnValidThread());
690
691 return weak_factory_.GetWeakPtr();
692 }
693
694 // Last member to ensure destructed first.
695 base::WeakPtrFactory<MidiOutPortManager> weak_factory_;
696
697 DISALLOW_COPY_AND_ASSIGN(MidiOutPortManager);
698 };
699
700 MidiManagerWinrt::MidiManagerWinrt() : com_thread_("Windows MIDI COM Thread") {}
701
702 MidiManagerWinrt::~MidiManagerWinrt() {
703 base::AutoLock auto_lock(lazy_init_member_lock_);
704
705 CHECK(!com_thread_checker_);
706 CHECK(!port_manager_in_);
707 CHECK(!port_manager_out_);
708 CHECK(!scheduler_);
709 }
710
711 void MidiManagerWinrt::StartInitialization() {
712 if (base::win::GetVersion() < base::win::VERSION_WIN10) {
713 VLOG(1) << "WinRT MIDI backend is only supported on Windows 10 or later.";
714 CompleteInitialization(Result::INITIALIZATION_ERROR);
715 return;
716 }
717
718 com_thread_.init_com_with_mta(true);
719 com_thread_.Start();
720
721 com_thread_.task_runner()->PostTask(
722 FROM_HERE, base::Bind(&MidiManagerWinrt::InitializeOnComThread,
723 base::Unretained(this)));
724 }
725
726 void MidiManagerWinrt::Finalize() {
727 com_thread_.task_runner()->PostTask(
728 FROM_HERE, base::Bind(&MidiManagerWinrt::FinalizeOnComThread,
729 base::Unretained(this)));
730
731 // Blocks until FinalizeOnComThread() returns. Delayed MIDI send data tasks
732 // will be ignored.
733 com_thread_.Stop();
734 }
735
736 void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client,
737 uint32_t port_index,
738 const std::vector<uint8_t>& data,
739 double timestamp) {
740 CHECK(scheduler_);
741
742 scheduler_->PostSendDataTask(
743 client, data.size(), timestamp,
744 base::Bind(&MidiManagerWinrt::SendOnComThread, base::Unretained(this),
745 port_index, data));
746 }
747
748 void MidiManagerWinrt::InitializeOnComThread() {
749 base::AutoLock auto_lock(lazy_init_member_lock_);
750
751 com_thread_checker_.reset(new base::ThreadChecker);
752
753 port_manager_in_.reset(new MidiInPortManager(this));
754 port_manager_out_.reset(new MidiOutPortManager(this));
755
756 scheduler_.reset(new MidiScheduler(this));
757
758 if (!(port_manager_in_->StartWatcher() &&
759 port_manager_out_->StartWatcher())) {
760 CompleteInitialization(Result::INITIALIZATION_ERROR);
Takashi Toyoshima 2016/08/29 16:20:17 It's natural and probably safe to call CompleteIni
Shao-Chuan Lee 2016/08/30 02:37:48 Done. This was actually a copy/paste mistake..
761 port_manager_in_->StopWatcher();
762 port_manager_out_->StopWatcher();
763 }
764 }
765
766 void MidiManagerWinrt::FinalizeOnComThread() {
767 base::AutoLock auto_lock(lazy_init_member_lock_);
768
769 DCHECK(com_thread_checker_->CalledOnValidThread());
770
771 scheduler_.reset();
772
773 port_manager_in_.reset();
774 port_manager_out_.reset();
775
776 com_thread_checker_.reset();
777 }
778
779 void MidiManagerWinrt::SendOnComThread(uint32_t port_index,
780 const std::vector<uint8_t>& data) {
781 DCHECK(com_thread_checker_->CalledOnValidThread());
782
783 MidiPort<IMidiOutPort>* port = port_manager_out_->GetPortByIndex(port_index);
784 if (!(port && port->handle)) {
785 VLOG(1) << "Port not available: " << port_index;
786 return;
787 }
788
789 auto buffer_factory =
790 WrlStaticsFactory<IBufferFactory,
791 RuntimeClass_Windows_Storage_Streams_Buffer>();
792 if (!buffer_factory)
793 return;
794
795 ScopedComPtr<IBuffer> buffer;
796 HRESULT hr = buffer_factory->Create(static_cast<UINT32>(data.size()),
797 buffer.Receive());
798 if (FAILED(hr)) {
799 VLOG(1) << "Create failed: " << PrintHr(hr);
800 return;
801 }
802
803 hr = buffer->put_Length(static_cast<UINT32>(data.size()));
804 if (FAILED(hr)) {
805 VLOG(1) << "put_Length failed: " << PrintHr(hr);
806 return;
807 }
808
809 uint8_t* p_buffer_data = nullptr;
810 hr = GetPointerToBufferData(buffer.get(), &p_buffer_data);
811 if (FAILED(hr))
812 return;
813
814 std::copy(data.begin(), data.end(), p_buffer_data);
815
816 hr = port->handle->SendBuffer(buffer.get());
817 if (FAILED(hr)) {
818 VLOG(1) << "SendBuffer failed: " << PrintHr(hr);
819 return;
820 }
821 }
822
823 void MidiManagerWinrt::OnPortManagerReady() {
824 DCHECK(com_thread_checker_->CalledOnValidThread());
825 DCHECK(port_manager_ready_count_ < 2);
826
827 if (++port_manager_ready_count_ == 2)
828 CompleteInitialization(Result::OK);
829 }
830
831 MidiManager* MidiManager::Create() {
832 return new MidiManagerWinrt();
833 }
834
835 } // namespace midi
836 } // namespace media
OLDNEW
« no previous file with comments | « media/midi/midi_manager_winrt.h ('k') | media/midi/midi_options.gni » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698