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

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