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

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: RemovePortEventHandlers() in StopWatcher(), 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;
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 invalid (as in <wrl/event.h>).
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 virtual ~MidiPortManager() { DCHECK(thread_checker_.CalledOnValidThread()); }
Shao-Chuan Lee 2016/08/30 02:37:48 Although we are not destructing Midi{In,Out}PortMa
151
152 bool StartWatcher() {
153 DCHECK(thread_checker_.CalledOnValidThread());
154
155 HRESULT hr;
156
157 midi_port_statics_ =
158 WrlStaticsFactory<StaticsInterfaceType, runtime_class_id>();
159 if (!midi_port_statics_)
160 return false;
161
162 HSTRING device_selector = nullptr;
163 hr = midi_port_statics_->GetDeviceSelector(&device_selector);
164 if (FAILED(hr)) {
165 VLOG(1) << "GetDeviceSelector failed: " << PrintHr(hr);
166 return false;
167 }
168
169 auto dev_info_statics = WrlStaticsFactory<
170 IDeviceInformationStatics,
171 RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>();
172 if (!dev_info_statics)
173 return false;
174
175 hr = dev_info_statics->CreateWatcherAqsFilter(device_selector,
176 watcher_.Receive());
177 if (FAILED(hr)) {
178 VLOG(1) << "CreateWatcherAqsFilter failed: " << PrintHr(hr);
179 return false;
180 }
181
182 // Register callbacks to WinRT that post state-modifying jobs back to COM
183 // thread. |weak_ptr| and |task_runner| are captured by lambda callbacks for
184 // posting jobs. Note that WinRT callback arguments should not be passed
185 // outside the callback since the pointers may be unavailable afterwards.
186 base::WeakPtr<MidiPortManager> weak_ptr = GetWeakPtrFromFactory();
187 scoped_refptr<base::SingleThreadTaskRunner> task_runner = task_runner_;
188
189 hr = watcher_->add_Added(
190 WRL::Callback<ITypedEventHandler<DeviceWatcher*, DeviceInformation*>>(
191 [weak_ptr, task_runner](IDeviceWatcher* watcher,
192 IDeviceInformation* info) {
193 std::string dev_id = GetIdString(info),
194 dev_name = GetNameString(info);
195
196 task_runner->PostTask(
197 FROM_HERE, base::Bind(&MidiPortManager::OnAdded, weak_ptr,
198 dev_id, dev_name));
199
200 return S_OK;
201 })
202 .Get(),
203 &token_Added_);
204 if (FAILED(hr)) {
205 VLOG(1) << "add_Added failed: " << PrintHr(hr);
206 return false;
207 }
208
209 hr = watcher_->add_EnumerationCompleted(
210 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
211 [weak_ptr, task_runner](IDeviceWatcher* watcher,
212 IInspectable* insp) {
213 task_runner->PostTask(
214 FROM_HERE,
215 base::Bind(&MidiPortManager::OnEnumerationCompleted,
216 weak_ptr));
217
218 return S_OK;
219 })
220 .Get(),
221 &token_EnumerationCompleted_);
222 if (FAILED(hr)) {
223 VLOG(1) << "add_EnumerationCompleted failed: " << PrintHr(hr);
224 return false;
225 }
226
227 hr = watcher_->add_Removed(
228 WRL::Callback<
229 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
230 [weak_ptr, task_runner](IDeviceWatcher* watcher,
231 IDeviceInformationUpdate* update) {
232 std::string dev_id = GetIdString(update);
233
234 task_runner->PostTask(
235 FROM_HERE,
236 base::Bind(&MidiPortManager::OnRemoved, weak_ptr, dev_id));
237
238 return S_OK;
239 })
240 .Get(),
241 &token_Removed_);
242 if (FAILED(hr)) {
243 VLOG(1) << "add_Removed failed: " << PrintHr(hr);
244 return false;
245 }
246
247 hr = watcher_->add_Stopped(
248 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
249 [](IDeviceWatcher* watcher, IInspectable* insp) {
250 // Placeholder, does nothing for now.
251 return S_OK;
252 })
253 .Get(),
254 &token_Stopped_);
255 if (FAILED(hr)) {
256 VLOG(1) << "add_Stopped failed: " << PrintHr(hr);
257 return false;
258 }
259
260 hr = watcher_->add_Updated(
261 WRL::Callback<
262 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
263 [](IDeviceWatcher* watcher, IDeviceInformationUpdate* update) {
264 // TODO(shaochuan): Check for fields to be updated here.
265 return S_OK;
266 })
267 .Get(),
268 &token_Updated_);
269 if (FAILED(hr)) {
270 VLOG(1) << "add_Updated failed: " << PrintHr(hr);
271 return false;
272 }
273
274 hr = watcher_->Start();
275 if (FAILED(hr)) {
276 VLOG(1) << "Start failed: " << PrintHr(hr);
277 return false;
278 }
279
280 is_initialized_ = true;
281 return true;
282 }
283
284 void StopWatcher() {
285 DCHECK(thread_checker_.CalledOnValidThread());
286
287 HRESULT hr;
288
289 for (const auto& entry : ports_)
290 RemovePortEventHandlers(entry.second.get());
291
292 if (token_Added_.value != kInvalidTokenValue) {
293 hr = watcher_->remove_Added(token_Added_);
294 VLOG_IF(1, FAILED(hr)) << "remove_Added failed: " << PrintHr(hr);
295 token_Added_.value = kInvalidTokenValue;
296 }
297 if (token_EnumerationCompleted_.value != kInvalidTokenValue) {
298 hr = watcher_->remove_EnumerationCompleted(token_EnumerationCompleted_);
299 VLOG_IF(1, FAILED(hr)) << "remove_EnumerationCompleted failed: "
300 << PrintHr(hr);
301 token_EnumerationCompleted_.value = kInvalidTokenValue;
302 }
303 if (token_Removed_.value != kInvalidTokenValue) {
304 hr = watcher_->remove_Removed(token_Removed_);
305 VLOG_IF(1, FAILED(hr)) << "remove_Removed failed: " << PrintHr(hr);
306 token_Removed_.value = kInvalidTokenValue;
307 }
308 if (token_Stopped_.value != kInvalidTokenValue) {
309 hr = watcher_->remove_Stopped(token_Stopped_);
310 VLOG_IF(1, FAILED(hr)) << "remove_Stopped failed: " << PrintHr(hr);
311 token_Stopped_.value = kInvalidTokenValue;
312 }
313 if (token_Updated_.value != kInvalidTokenValue) {
314 hr = watcher_->remove_Updated(token_Updated_);
315 VLOG_IF(1, FAILED(hr)) << "remove_Updated failed: " << PrintHr(hr);
316 token_Updated_.value = kInvalidTokenValue;
317 }
318
319 if (is_initialized_) {
320 hr = watcher_->Stop();
321 VLOG_IF(1, FAILED(hr)) << "Stop failed: " << PrintHr(hr);
322 is_initialized_ = false;
323 }
324 }
325
326 MidiPort<InterfaceType>* GetPortByDeviceId(std::string dev_id) {
327 DCHECK(thread_checker_.CalledOnValidThread());
328 CHECK(is_initialized_);
329
330 auto it = ports_.find(dev_id);
331 if (it == ports_.end())
332 return nullptr;
333 return it->second.get();
334 }
335
336 MidiPort<InterfaceType>* GetPortByIndex(uint32_t port_index) {
337 DCHECK(thread_checker_.CalledOnValidThread());
338 CHECK(is_initialized_);
339
340 return GetPortByDeviceId(port_ids_[port_index]);
341 }
342
343 protected:
344 // Points to the MidiManagerWinrt instance, which is expected to outlive the
345 // MidiPortManager instance.
346 MidiManagerWinrt* midi_manager_;
347
348 // Task runner of the COM thread.
349 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
350
351 // Ensures all methods are called on the COM thread.
352 base::ThreadChecker thread_checker_;
353
354 private:
355 // DeviceWatcher callbacks:
356 void OnAdded(std::string dev_id, std::string dev_name) {
357 DCHECK(thread_checker_.CalledOnValidThread());
358 CHECK(is_initialized_);
359
360 // TODO(shaochuan): Disable Microsoft GS Wavetable Synth due to security
361 // reasons. http://crbug.com/499279
362
363 port_names_[dev_id] = dev_name;
364
365 WRL::Wrappers::HString dev_id_hstring;
366 HRESULT hr = dev_id_hstring.Set(base::UTF8ToWide(dev_id).c_str());
367 if (FAILED(hr)) {
368 VLOG(1) << "Set failed: " << PrintHr(hr);
369 return;
370 }
371
372 IAsyncOperation<RuntimeType*>* async_op;
373
374 hr = midi_port_statics_->FromIdAsync(dev_id_hstring.Get(), &async_op);
375 if (FAILED(hr)) {
376 VLOG(1) << "FromIdAsync failed: " << PrintHr(hr);
377 return;
378 }
379
380 base::WeakPtr<MidiPortManager> weak_ptr = GetWeakPtrFromFactory();
381 scoped_refptr<base::SingleThreadTaskRunner> task_runner = task_runner_;
382
383 hr = async_op->put_Completed(
384 WRL::Callback<IAsyncOperationCompletedHandler<RuntimeType*>>(
385 [weak_ptr, task_runner](IAsyncOperation<RuntimeType*>* async_op,
386 AsyncStatus status) {
387 // TODO(shaochuan): Check if port open time is accurate.
388 const auto now = base::TimeTicks::Now();
389
390 InterfaceType* handle;
391 HRESULT hr = async_op->GetResults(&handle);
392 if (FAILED(hr)) {
393 VLOG(1) << "GetResults failed: " << PrintHr(hr);
394 return hr;
395 }
396
397 // A reference to |async_op| is kept in |async_ops_|, safe to pass
398 // outside.
399 task_runner->PostTask(
400 FROM_HERE,
401 base::Bind(&MidiPortManager::OnCompletedGetPortFromIdAsync,
402 weak_ptr, handle, now, async_op));
403
404 return S_OK;
405 })
406 .Get());
407 if (FAILED(hr)) {
408 VLOG(1) << "put_Completed failed: " << PrintHr(hr);
409 return;
410 }
411
412 // Keep a reference to incompleted |async_op| for releasing later.
413 async_ops_.insert(async_op);
414 }
415
416 void OnEnumerationCompleted() {
417 DCHECK(thread_checker_.CalledOnValidThread());
418 CHECK(is_initialized_);
419
420 if (async_ops_.empty())
421 midi_manager_->OnPortManagerReady();
422 else
423 enumeration_completed_not_ready_ = true;
424 }
425
426 void OnRemoved(std::string dev_id) {
427 DCHECK(thread_checker_.CalledOnValidThread());
428 CHECK(is_initialized_);
429
430 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
431 if (!port) {
432 VLOG(1) << "Removing non-existent port " << dev_id;
433 return;
434 }
435
436 SetPortState(port->index, MIDI_PORT_DISCONNECTED);
437
438 RemovePortEventHandlers(port);
439 port->handle = nullptr;
440 }
441
442 void OnCompletedGetPortFromIdAsync(InterfaceType* handle,
443 base::TimeTicks start_time,
444 IAsyncOperation<RuntimeType*>* async_op) {
445 DCHECK(thread_checker_.CalledOnValidThread());
446 CHECK(is_initialized_);
447
448 EventRegistrationToken token = {kInvalidTokenValue};
449 if (!RegisterOnMessageReceived(handle, &token))
450 return;
451
452 std::string dev_id = GetDeviceIdString(handle);
453
454 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
455
456 if (port == nullptr) {
457 // TODO(shaochuan): Fill in manufacturer and driver version.
458 AddPort(MidiPortInfo(dev_id, std::string("Manufacturer"),
459 port_names_[dev_id], std::string("DriverVersion"),
460 MIDI_PORT_OPENED));
461
462 port = new MidiPort<InterfaceType>;
463 port->index = static_cast<uint32_t>(port_ids_.size());
464
465 ports_[dev_id].reset(port);
466 port_ids_.push_back(dev_id);
467 } else {
468 SetPortState(port->index, MIDI_PORT_CONNECTED);
469 }
470
471 port->handle = handle;
472 port->start_time = start_time;
473 port->token_MessageReceived = token;
474
475 // Manually release COM interface to completed |async_op|.
476 auto it = async_ops_.find(async_op);
477 CHECK(it != async_ops_.end());
478 (*it)->Release();
479 async_ops_.erase(it);
480
481 if (enumeration_completed_not_ready_ && async_ops_.empty()) {
482 midi_manager_->OnPortManagerReady();
483 enumeration_completed_not_ready_ = false;
484 }
485 }
486
487 // Overrided by MidiInPortManager to listen to input ports.
488 virtual bool RegisterOnMessageReceived(InterfaceType* handle,
489 EventRegistrationToken* p_token) {
490 return true;
491 }
492
493 // Overrided by MidiInPortManager to remove MessageReceived event handler.
494 virtual void RemovePortEventHandlers(MidiPort<InterfaceType>* port) {}
495
496 // Calls midi_manager_->Add{Input,Output}Port.
497 virtual void AddPort(MidiPortInfo info) = 0;
498
499 // Calls midi_manager_->Set{Input,Output}PortState.
500 virtual void SetPortState(uint32_t port_index, MidiPortState state) = 0;
501
502 // WeakPtrFactory has to be declared in derived class, use this method to
503 // retrieve upcasted WeakPtr for posting tasks.
504 virtual base::WeakPtr<MidiPortManager> GetWeakPtrFromFactory() = 0;
505
506 // Midi{In,Out}PortStatics instance.
507 ScopedComPtr<StaticsInterfaceType> midi_port_statics_;
508
509 // DeviceWatcher instance and event registration tokens for unsubscribing
510 // events in destructor.
511 ScopedComPtr<IDeviceWatcher> watcher_;
512 EventRegistrationToken token_Added_ = {kInvalidTokenValue},
513 token_EnumerationCompleted_ = {kInvalidTokenValue},
514 token_Removed_ = {kInvalidTokenValue},
515 token_Stopped_ = {kInvalidTokenValue},
516 token_Updated_ = {kInvalidTokenValue};
517
518 // All manipulations to these fields should be done on COM thread.
519 std::unordered_map<std::string, std::unique_ptr<MidiPort<InterfaceType>>>
520 ports_;
Shao-Chuan Lee 2016/08/30 02:37:48 Moved back to private since we are not using these
521 std::vector<std::string> port_ids_;
522 std::unordered_map<std::string, std::string> port_names_;
523
524 // Keeps AsyncOperation references before the operation completes. Note that
525 // raw pointers are used here and the COM interfaces should be released
526 // manually.
527 std::unordered_set<IAsyncOperation<RuntimeType*>*> async_ops_;
528
529 // Set when device enumeration is completed but OnPortManagerReady() is not
530 // called since some ports are not yet ready (i.e. |async_ops_| is not empty).
531 // In such cases, OnPortManagerReady() will be called in
532 // OnCompletedGetPortFromIdAsync() when the last pending port is ready.
533 bool enumeration_completed_not_ready_ = false;
534
535 // Set if the instance is initialized without error. Should be checked in all
536 // methods on COM thread except StartWatcher().
537 bool is_initialized_ = false;
Shao-Chuan Lee 2016/08/30 02:37:48 ditto
538 };
539
540 class MidiManagerWinrt::MidiInPortManager final
541 : public MidiPortManager<IMidiInPort,
542 MidiInPort,
543 IMidiInPortStatics,
544 RuntimeClass_Windows_Devices_Midi_MidiInPort> {
545 public:
546 MidiInPortManager(MidiManagerWinrt* midi_manager)
547 : MidiPortManager(midi_manager), weak_factory_(this) {}
548
549 private:
550 // MidiPortManager overrides:
551 bool RegisterOnMessageReceived(IMidiInPort* handle,
552 EventRegistrationToken* p_token) override {
553 DCHECK(thread_checker_.CalledOnValidThread());
554
555 base::WeakPtr<MidiInPortManager> weak_ptr = weak_factory_.GetWeakPtr();
556 scoped_refptr<base::SingleThreadTaskRunner> task_runner = task_runner_;
557
558 HRESULT hr = handle->add_MessageReceived(
559 WRL::Callback<
560 ITypedEventHandler<MidiInPort*, MidiMessageReceivedEventArgs*>>(
561 [weak_ptr, task_runner](IMidiInPort* handle,
562 IMidiMessageReceivedEventArgs* args) {
563 std::string dev_id = GetDeviceIdString(handle);
564
565 ScopedComPtr<IMidiMessage> message;
566 HRESULT hr = args->get_Message(message.Receive());
567 if (FAILED(hr)) {
568 VLOG(1) << "get_Message failed: " << PrintHr(hr);
569 return hr;
570 }
571
572 ScopedComPtr<IBuffer> buffer;
573 hr = message->get_RawData(buffer.Receive());
574 if (FAILED(hr)) {
575 VLOG(1) << "get_RawData failed: " << PrintHr(hr);
576 return hr;
577 }
578
579 uint8_t* p_buffer_data = nullptr;
580 hr = GetPointerToBufferData(buffer.get(), &p_buffer_data);
581 if (FAILED(hr))
582 return hr;
583
584 uint32_t data_length = 0;
585 hr = buffer->get_Length(&data_length);
586 if (FAILED(hr)) {
587 VLOG(1) << "get_Length failed: " << PrintHr(hr);
588 return hr;
589 }
590
591 std::vector<uint8_t> data(p_buffer_data,
592 p_buffer_data + data_length);
593
594 // Time since port opened in 100-nanosecond units.
595 TimeSpan time_span;
596 hr = message->get_Timestamp(&time_span);
Shao-Chuan Lee 2016/08/30 04:57:59 According to docs this is time since "the MidiInPo
597 if (FAILED(hr)) {
598 VLOG(1) << "get_Timestamp failed: " << PrintHr(hr);
599 return hr;
600 }
601
602 task_runner->PostTask(
603 FROM_HERE,
604 base::Bind(&MidiInPortManager::OnMessageReceived, weak_ptr,
605 dev_id, data, base::TimeDelta::FromMicroseconds(
606 time_span.Duration / 10)));
607
608 return S_OK;
609 })
610 .Get(),
611 p_token);
612 if (FAILED(hr)) {
613 VLOG(1) << "add_MessageReceived failed: " << PrintHr(hr);
614 return false;
615 }
616
617 return true;
618 }
619
620 void RemovePortEventHandlers(MidiPort<IMidiInPort>* port) override {
621 if (!(port->handle &&
622 port->token_MessageReceived.value != kInvalidTokenValue))
623 return;
624
625 HRESULT hr =
626 port->handle->remove_MessageReceived(port->token_MessageReceived);
627 VLOG_IF(1, FAILED(hr)) << "remove_MessageReceived failed: " << PrintHr(hr);
628 port->token_MessageReceived.value = kInvalidTokenValue;
629 }
630
631 void AddPort(MidiPortInfo info) final { midi_manager_->AddInputPort(info); }
632
633 void SetPortState(uint32_t port_index, MidiPortState state) final {
634 midi_manager_->SetInputPortState(port_index, state);
635 }
636
637 base::WeakPtr<MidiPortManager> GetWeakPtrFromFactory() final {
638 DCHECK(thread_checker_.CalledOnValidThread());
639
640 return weak_factory_.GetWeakPtr();
641 }
642
643 // Callback on receiving MIDI input message.
644 void OnMessageReceived(std::string dev_id,
645 std::vector<uint8_t> data,
646 base::TimeDelta time_since_start) {
647 DCHECK(thread_checker_.CalledOnValidThread());
648
649 MidiPort<IMidiInPort>* port = GetPortByDeviceId(dev_id);
650 CHECK(port);
651
652 midi_manager_->ReceiveMidiData(port->index, &data[0], data.size(),
653 port->start_time + time_since_start);
654 }
655
656 // Last member to ensure destructed first.
657 base::WeakPtrFactory<MidiInPortManager> weak_factory_;
658
659 DISALLOW_COPY_AND_ASSIGN(MidiInPortManager);
660 };
661
662 class MidiManagerWinrt::MidiOutPortManager final
663 : public MidiPortManager<IMidiOutPort,
664 IMidiOutPort,
665 IMidiOutPortStatics,
666 RuntimeClass_Windows_Devices_Midi_MidiOutPort> {
667 public:
668 MidiOutPortManager(MidiManagerWinrt* midi_manager)
669 : MidiPortManager(midi_manager), weak_factory_(this) {}
670
671 private:
672 // MidiPortManager overrides:
673 void AddPort(MidiPortInfo info) final { midi_manager_->AddOutputPort(info); }
674
675 void SetPortState(uint32_t port_index, MidiPortState state) final {
676 midi_manager_->SetOutputPortState(port_index, state);
677 }
678
679 base::WeakPtr<MidiPortManager> GetWeakPtrFromFactory() final {
680 DCHECK(thread_checker_.CalledOnValidThread());
681
682 return weak_factory_.GetWeakPtr();
683 }
684
685 // Last member to ensure destructed first.
686 base::WeakPtrFactory<MidiOutPortManager> weak_factory_;
687
688 DISALLOW_COPY_AND_ASSIGN(MidiOutPortManager);
689 };
690
691 MidiManagerWinrt::MidiManagerWinrt() : com_thread_("Windows MIDI COM Thread") {}
692
693 MidiManagerWinrt::~MidiManagerWinrt() {
694 base::AutoLock auto_lock(lazy_init_member_lock_);
695
696 CHECK(!com_thread_checker_);
697 CHECK(!port_manager_in_);
698 CHECK(!port_manager_out_);
699 CHECK(!scheduler_);
700 }
701
702 void MidiManagerWinrt::StartInitialization() {
703 if (base::win::GetVersion() < base::win::VERSION_WIN10) {
704 VLOG(1) << "WinRT MIDI backend is only supported on Windows 10 or later.";
705 CompleteInitialization(Result::INITIALIZATION_ERROR);
706 return;
707 }
708
709 com_thread_.init_com_with_mta(true);
710 com_thread_.Start();
711
712 com_thread_.task_runner()->PostTask(
713 FROM_HERE, base::Bind(&MidiManagerWinrt::InitializeOnComThread,
714 base::Unretained(this)));
715 }
716
717 void MidiManagerWinrt::Finalize() {
718 com_thread_.task_runner()->PostTask(
719 FROM_HERE, base::Bind(&MidiManagerWinrt::FinalizeOnComThread,
720 base::Unretained(this)));
721
722 // Blocks until FinalizeOnComThread() returns. Delayed MIDI send data tasks
723 // will be ignored.
724 com_thread_.Stop();
725 }
726
727 void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client,
728 uint32_t port_index,
729 const std::vector<uint8_t>& data,
730 double timestamp) {
731 CHECK(scheduler_);
732
733 scheduler_->PostSendDataTask(
734 client, data.size(), timestamp,
735 base::Bind(&MidiManagerWinrt::SendOnComThread, base::Unretained(this),
736 port_index, data));
737 }
738
739 void MidiManagerWinrt::InitializeOnComThread() {
740 base::AutoLock auto_lock(lazy_init_member_lock_);
741
742 com_thread_checker_.reset(new base::ThreadChecker);
743
744 port_manager_in_.reset(new MidiInPortManager(this));
745 port_manager_out_.reset(new MidiOutPortManager(this));
746
747 scheduler_.reset(new MidiScheduler(this));
748
749 if (!(port_manager_in_->StartWatcher() &&
750 port_manager_out_->StartWatcher())) {
751 port_manager_in_->StopWatcher();
752 port_manager_out_->StopWatcher();
753 CompleteInitialization(Result::INITIALIZATION_ERROR);
754 }
755 }
756
757 void MidiManagerWinrt::FinalizeOnComThread() {
758 base::AutoLock auto_lock(lazy_init_member_lock_);
759
760 DCHECK(com_thread_checker_->CalledOnValidThread());
761
762 scheduler_.reset();
763
764 port_manager_in_->StopWatcher();
Shao-Chuan Lee 2016/08/30 02:37:48 RemovePortEventHandlers() moved from dtor to StopW
765 port_manager_in_.reset();
766 port_manager_out_->StopWatcher();
767 port_manager_out_.reset();
768
769 com_thread_checker_.reset();
770 }
771
772 void MidiManagerWinrt::SendOnComThread(uint32_t port_index,
773 const std::vector<uint8_t>& data) {
774 DCHECK(com_thread_checker_->CalledOnValidThread());
775
776 MidiPort<IMidiOutPort>* port = port_manager_out_->GetPortByIndex(port_index);
777 if (!(port && port->handle)) {
778 VLOG(1) << "Port not available: " << port_index;
779 return;
780 }
781
782 auto buffer_factory =
783 WrlStaticsFactory<IBufferFactory,
784 RuntimeClass_Windows_Storage_Streams_Buffer>();
785 if (!buffer_factory)
786 return;
787
788 ScopedComPtr<IBuffer> buffer;
789 HRESULT hr = buffer_factory->Create(static_cast<UINT32>(data.size()),
790 buffer.Receive());
791 if (FAILED(hr)) {
792 VLOG(1) << "Create failed: " << PrintHr(hr);
793 return;
794 }
795
796 hr = buffer->put_Length(static_cast<UINT32>(data.size()));
797 if (FAILED(hr)) {
798 VLOG(1) << "put_Length failed: " << PrintHr(hr);
799 return;
800 }
801
802 uint8_t* p_buffer_data = nullptr;
803 hr = GetPointerToBufferData(buffer.get(), &p_buffer_data);
804 if (FAILED(hr))
805 return;
806
807 std::copy(data.begin(), data.end(), p_buffer_data);
808
809 hr = port->handle->SendBuffer(buffer.get());
810 if (FAILED(hr)) {
811 VLOG(1) << "SendBuffer failed: " << PrintHr(hr);
812 return;
813 }
814 }
815
816 void MidiManagerWinrt::OnPortManagerReady() {
817 DCHECK(com_thread_checker_->CalledOnValidThread());
818 DCHECK(port_manager_ready_count_ < 2);
819
820 if (++port_manager_ready_count_ == 2)
821 CompleteInitialization(Result::OK);
822 }
823
824 MidiManager* MidiManager::Create() {
825 return new MidiManagerWinrt();
826 }
827
828 } // namespace midi
829 } // 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