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

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

Powered by Google App Engine
This is Rietveld 408576698