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

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

Issue 1056333002: Web MIDI: Improve device change monitornig on Windows (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: SystemMonitor in test Created 5 years, 8 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_unittest.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/midi/midi_manager_win.h" 5 #include "media/midi/midi_manager_win.h"
6 6
7 #include <windows.h> 7 #include <windows.h>
8 #include <dbt.h>
9 #include <ks.h> 8 #include <ks.h>
10 #include <ksmedia.h> 9 #include <ksmedia.h>
11 #include <mmreg.h> 10 #include <mmreg.h>
12 // Prevent unnecessary functions from being included from <mmsystem.h> 11 // Prevent unnecessary functions from being included from <mmsystem.h>
13 #define MMNODRV 12 #define MMNODRV
14 #define MMNOSOUND 13 #define MMNOSOUND
15 #define MMNOWAVE 14 #define MMNOWAVE
16 #define MMNOAUX 15 #define MMNOAUX
17 #define MMNOMIXER 16 #define MMNOMIXER
18 #define MMNOTIMER 17 #define MMNOTIMER
(...skipping 10 matching lines...) Expand all
29 #include "base/basictypes.h" 28 #include "base/basictypes.h"
30 #include "base/bind.h" 29 #include "base/bind.h"
31 #include "base/containers/hash_tables.h" 30 #include "base/containers/hash_tables.h"
32 #include "base/memory/scoped_ptr.h" 31 #include "base/memory/scoped_ptr.h"
33 #include "base/message_loop/message_loop.h" 32 #include "base/message_loop/message_loop.h"
34 #include "base/strings/string16.h" 33 #include "base/strings/string16.h"
35 #include "base/strings/string_number_conversions.h" 34 #include "base/strings/string_number_conversions.h"
36 #include "base/strings/string_piece.h" 35 #include "base/strings/string_piece.h"
37 #include "base/strings/stringprintf.h" 36 #include "base/strings/stringprintf.h"
38 #include "base/strings/utf_string_conversions.h" 37 #include "base/strings/utf_string_conversions.h"
38 #include "base/system_monitor/system_monitor.h"
39 #include "base/threading/thread.h" 39 #include "base/threading/thread.h"
40 #include "base/threading/thread_checker.h" 40 #include "base/threading/thread_checker.h"
41 #include "base/timer/timer.h" 41 #include "base/timer/timer.h"
42 #include "base/win/message_window.h" 42 #include "base/win/message_window.h"
43 #include "device/usb/usb_ids.h" 43 #include "device/usb/usb_ids.h"
44 #include "media/midi/midi_manager.h" 44 #include "media/midi/midi_manager.h"
45 #include "media/midi/midi_message_queue.h" 45 #include "media/midi/midi_message_queue.h"
46 #include "media/midi/midi_message_util.h" 46 #include "media/midi/midi_message_util.h"
47 #include "media/midi/midi_port_info.h" 47 #include "media/midi/midi_port_info.h"
48 48
49 namespace media { 49 namespace media {
50 namespace { 50 namespace {
51 51
52 const wchar_t kWindowClassName[] = L"ChromeMidiDeviceMonitorWindow";
53 const int kOnDeviceArrivalDelayMilliSec = 500;
54 static const size_t kBufferLength = 32 * 1024; 52 static const size_t kBufferLength = 32 * 1024;
55 53
56 // We assume that nullpter represents an invalid MIDI handle. 54 // We assume that nullpter represents an invalid MIDI handle.
57 const HMIDIIN kInvalidMidiInHandle = nullptr; 55 const HMIDIIN kInvalidMidiInHandle = nullptr;
58 const HMIDIOUT kInvalidMidiOutHandle = nullptr; 56 const HMIDIOUT kInvalidMidiOutHandle = nullptr;
59 57
60 // Note that |RegisterDeviceNotificationW| returns nullptr as an invalid handle.
61 const HDEVNOTIFY kInvalidDeviceNotifyHandle = nullptr;
62
63 std::string GetInErrorMessage(MMRESULT result) { 58 std::string GetInErrorMessage(MMRESULT result) {
64 wchar_t text[MAXERRORLENGTH]; 59 wchar_t text[MAXERRORLENGTH];
65 MMRESULT get_result = midiInGetErrorText(result, text, arraysize(text)); 60 MMRESULT get_result = midiInGetErrorText(result, text, arraysize(text));
66 if (get_result != MMSYSERR_NOERROR) { 61 if (get_result != MMSYSERR_NOERROR) {
67 DLOG(ERROR) << "Failed to get error message." 62 DLOG(ERROR) << "Failed to get error message."
68 << " original error: " << result 63 << " original error: " << result
69 << " midiInGetErrorText error: " << get_result; 64 << " midiInGetErrorText error: " << get_result;
70 return std::string(); 65 return std::string();
71 } 66 }
72 return base::WideToUTF8(text); 67 return base::WideToUTF8(text);
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
284 return EXTRACT_USBAUDIO_PID(&caps.ProductGuid); 279 return EXTRACT_USBAUDIO_PID(&caps.ProductGuid);
285 } 280 }
286 static uint16 ExtractUsbProductIdIfExists(const MIDIOUTCAPS2W& caps) { 281 static uint16 ExtractUsbProductIdIfExists(const MIDIOUTCAPS2W& caps) {
287 if (!IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid)) 282 if (!IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid))
288 return 0; 283 return 0;
289 return EXTRACT_USBAUDIO_PID(&caps.ProductGuid); 284 return EXTRACT_USBAUDIO_PID(&caps.ProductGuid);
290 } 285 }
291 }; 286 };
292 287
293 std::string GetManufacturerName(const MidiDeviceInfo& info) { 288 std::string GetManufacturerName(const MidiDeviceInfo& info) {
294 if (info.is_usb_device) 289 if (info.is_usb_device) {
295 return device::UsbIds::GetVendorName(info.usb_vendor_id); 290 const char* name = device::UsbIds::GetVendorName(info.usb_vendor_id);
291 return std::string(name ? name : "");
292 }
296 293
297 switch (info.manufacturer_id) { 294 switch (info.manufacturer_id) {
298 case MM_MICROSOFT: 295 case MM_MICROSOFT:
299 return "Microsoft Corporation"; 296 return "Microsoft Corporation";
300 default: 297 default:
301 // TODO(toyoshim): Support other manufacture IDs. crbug.com/472341. 298 // TODO(toyoshim): Support other manufacture IDs. crbug.com/472341.
302 return ""; 299 return "";
303 } 300 }
304 } 301 }
305 302
306 using PortNumberCache = base::hash_map< 303 using PortNumberCache = base::hash_map<
307 MidiDeviceInfo, 304 MidiDeviceInfo,
308 std::priority_queue<uint32, std::vector<uint32>, std::greater<uint32>>, 305 std::priority_queue<uint32, std::vector<uint32>, std::greater<uint32>>,
309 MidiDeviceInfo::Hasher>; 306 MidiDeviceInfo::Hasher>;
310 307
311 class DeviceMonitorWindow final {
312 public:
313 explicit DeviceMonitorWindow(base::Closure on_device_arrival)
314 : on_device_arrival_(on_device_arrival),
315 dev_notify_handle_(kInvalidDeviceNotifyHandle) {
316 window_.reset(new base::win::MessageWindow);
317 if (!window_->CreateNamed(base::Bind(&DeviceMonitorWindow::HandleMessage,
318 base::Unretained(this)),
319 base::string16(kWindowClassName))) {
320 LOG(ERROR) << "Failed to create message window: " << kWindowClassName;
321 window_.reset();
322 }
323 DEV_BROADCAST_DEVICEINTERFACE kBloadcastRequest = {
324 sizeof(DEV_BROADCAST_DEVICEINTERFACE), DBT_DEVTYP_DEVICEINTERFACE,
325 };
326 dev_notify_handle_ = RegisterDeviceNotificationW(
327 window_->hwnd(), &kBloadcastRequest,
328 DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
329 }
330
331 ~DeviceMonitorWindow() {
332 if (dev_notify_handle_ != kInvalidDeviceNotifyHandle) {
333 BOOL result = UnregisterDeviceNotification(dev_notify_handle_);
334 if (!result) {
335 const DWORD last_error = GetLastError();
336 DLOG(ERROR) << "UnregisterDeviceNotification failed. error: "
337 << last_error;
338 }
339 dev_notify_handle_ = kInvalidDeviceNotifyHandle;
340 }
341 }
342
343 private:
344 bool HandleMessage(UINT message,
345 WPARAM wparam,
346 LPARAM lparam,
347 LRESULT* result) {
348 if (message == WM_DEVICECHANGE) {
349 switch (wparam) {
350 case DBT_DEVICEARRIVAL: {
351 device_arrival_timer_.Stop();
352 device_arrival_timer_.Start(
353 FROM_HERE,
354 base::TimeDelta::FromMilliseconds(kOnDeviceArrivalDelayMilliSec),
355 on_device_arrival_);
356 break;
357 }
358 }
359 }
360 return false;
361 }
362
363 base::Closure on_device_arrival_;
364 base::OneShotTimer<DeviceMonitorWindow> device_arrival_timer_;
365 base::ThreadChecker thread_checker_;
366 scoped_ptr<base::win::MessageWindow> window_;
367 HDEVNOTIFY dev_notify_handle_;
368
369 DISALLOW_COPY_AND_ASSIGN(DeviceMonitorWindow);
370 };
371
372 struct MidiInputDeviceState final : base::RefCounted<MidiInputDeviceState> { 308 struct MidiInputDeviceState final : base::RefCounted<MidiInputDeviceState> {
373 explicit MidiInputDeviceState(const MidiDeviceInfo& device_info) 309 explicit MidiInputDeviceState(const MidiDeviceInfo& device_info)
374 : device_info(device_info), 310 : device_info(device_info),
375 midi_handle(kInvalidMidiInHandle), 311 midi_handle(kInvalidMidiInHandle),
376 port_index(0), 312 port_index(0),
377 port_age(0), 313 port_age(0),
378 start_time_initialized(false) {} 314 start_time_initialized(false) {}
379 315
380 const MidiDeviceInfo device_info; 316 const MidiDeviceInfo device_info;
381 HMIDIIN midi_handle; 317 HMIDIIN midi_handle;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 uint64 port_age; 352 uint64 port_age;
417 // True if the device is already closed and |midi_handle| is considered to be 353 // True if the device is already closed and |midi_handle| is considered to be
418 // invalid. 354 // invalid.
419 // TODO(toyoshim): Use std::atomic<bool> when it is allowed in Chromium 355 // TODO(toyoshim): Use std::atomic<bool> when it is allowed in Chromium
420 // project. 356 // project.
421 volatile bool closed; 357 volatile bool closed;
422 }; 358 };
423 359
424 // The core logic of MIDI device handling for Windows. Basically this class is 360 // The core logic of MIDI device handling for Windows. Basically this class is
425 // shared among following 4 threads: 361 // shared among following 4 threads:
426 // 1. Device Monitor Thread 362 // 1. Chrome IO Thread
427 // 2. OS Multimedia Thread 363 // 2. OS Multimedia Thread
428 // 3. Misc. Task Thread 364 // 3. Task Thread
429 // 4. Sender Thread 365 // 4. Sender Thread
430 // 366 //
431 // Device Monitor Thread: 367 // Chrome IO Thread:
432 // This is a standalone UI thread that is dedicated for a message-only window 368 // MidiManager runs on Chrome IO thread. Device change notification is
433 // which will receive WM_DEVICECHANGE Win32 message whose 369 // delivered to the thread through the SystemMonitor service.
434 // wParam == DBT_DEVICEARRIVAL. This event will be used as the trigger to open 370 // OnDevicesChanged() callback is invoked to update the MIDI device list.
435 // all the new MIDI devices. Note that in the current implementation we will 371 // Note that in the current implementation we will try to open all the
436 // try to open all the existing devices in practice. This is OK because trying 372 // existing devices in practice. This is OK because trying to reopen a MIDI
437 // to reopen a MIDI device that is already opened would simply fail, and there 373 // device that is already opened would simply fail, and there is no unwilling
438 // is no unwilling side effect. Note also that this thread isn't responsible 374 // side effect.
439 // for updating the device database. It will be handled by MIM_OPEN/MOM_OPEN
440 // events dispatched to OS Multimedia Thread.
441 // 375 //
442 // OS Multimedia Thread: 376 // OS Multimedia Thread:
443 // This thread is maintained by the OS as a part of MIDI runtime, and 377 // This thread is maintained by the OS as a part of MIDI runtime, and
444 // responsible for receiving all the system initiated events such as device 378 // responsible for receiving all the system initiated events such as device
445 // open and close. For performance reasons, most of potentially blocking 379 // close, and receiving data. For performance reasons, most of potentially
446 // operations will be dispatched into Misc. Task Thread. 380 // blocking operations will be dispatched into Task Thread.
447 // 381 //
448 // Misc. Task Thread: 382 // Task Thread:
449 // This thread will be used to call back following methods of MidiManager. 383 // This thread will be used to call back following methods of MidiManager.
450 // - MidiManager::CompleteInitialization 384 // - MidiManager::CompleteInitialization
451 // - MidiManager::AddInputPort 385 // - MidiManager::AddInputPort
452 // - MidiManager::AddOutputPort 386 // - MidiManager::AddOutputPort
453 // - MidiManager::SetInputPortState 387 // - MidiManager::SetInputPortState
454 // - MidiManager::SetOutputPortState 388 // - MidiManager::SetOutputPortState
455 // - MidiManager::ReceiveMidiData 389 // - MidiManager::ReceiveMidiData
456 // 390 //
457 // Sender Thread: 391 // Sender Thread:
458 // This thread will be used to call Win32 APIs to send MIDI message at the 392 // This thread will be used to call Win32 APIs to send MIDI message at the
459 // specified time. We don't want to call MIDI send APIs on Misc. Task Thread 393 // specified time. We don't want to call MIDI send APIs on Task Thread
460 // because those APIs could be performed synchronously, hence they could block 394 // because those APIs could be performed synchronously, hence they could block
461 // the caller thread for a while. See the comment in 395 // the caller thread for a while. See the comment in
462 // SendLongMidiMessageInternal for details. Currently we expect that the 396 // SendLongMidiMessageInternal for details. Currently we expect that the
463 // blocking time would be less than 1 second. 397 // blocking time would be less than 1 second.
464 class MidiServiceWinImpl : public MidiServiceWin { 398 class MidiServiceWinImpl : public MidiServiceWin,
399 public base::SystemMonitor::DevicesChangedObserver {
465 public: 400 public:
466 MidiServiceWinImpl() 401 MidiServiceWinImpl()
467 : delegate_(nullptr), 402 : delegate_(nullptr),
468 monitor_thread_("Windows MIDI device monitor thread"),
469 sender_thread_("Windows MIDI sender thread"), 403 sender_thread_("Windows MIDI sender thread"),
470 task_thread_("Windows MIDI task thread"), 404 task_thread_("Windows MIDI task thread"),
471 destructor_started(false) {} 405 destructor_started(false) {}
472 406
473 ~MidiServiceWinImpl() final { 407 ~MidiServiceWinImpl() final {
408 // Start() and Stop() of the threads, and AddDevicesChangeObserver() and
409 // RemoveDevicesChangeObserver() should be called on the same thread.
410 CHECK(thread_checker_.CalledOnValidThread());
411
474 destructor_started = true; 412 destructor_started = true;
475 if (monitor_thread_.IsRunning()) { 413 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
476 monitor_thread_.message_loop()->PostTask(
477 FROM_HERE,
478 base::Bind(&MidiServiceWinImpl::StopDeviceMonitorOnMonitorThreadSync,
479 base::Unretained(this)));
480 }
481 { 414 {
482 std::vector<HMIDIIN> input_devices; 415 std::vector<HMIDIIN> input_devices;
483 { 416 {
484 base::AutoLock auto_lock(input_ports_lock_); 417 base::AutoLock auto_lock(input_ports_lock_);
485 for (auto it : input_device_map_) 418 for (auto it : input_device_map_)
486 input_devices.push_back(it.first); 419 input_devices.push_back(it.first);
487 } 420 }
488 { 421 {
489 for (const auto handle : input_devices) { 422 for (const auto handle : input_devices) {
490 MMRESULT result = midiInClose(handle); 423 MMRESULT result = midiInClose(handle);
(...skipping 22 matching lines...) Expand all
513 result = midiOutReset(handle); 446 result = midiOutReset(handle);
514 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) 447 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
515 << "midiOutReset failed: " << GetOutErrorMessage(result); 448 << "midiOutReset failed: " << GetOutErrorMessage(result);
516 result = midiOutClose(handle); 449 result = midiOutClose(handle);
517 } 450 }
518 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) 451 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
519 << "midiOutClose failed: " << GetOutErrorMessage(result); 452 << "midiOutClose failed: " << GetOutErrorMessage(result);
520 } 453 }
521 } 454 }
522 } 455 }
523 monitor_thread_.Stop();
524 sender_thread_.Stop(); 456 sender_thread_.Stop();
525 task_thread_.Stop(); 457 task_thread_.Stop();
526 } 458 }
527 459
528 // MidiServiceWin overrides: 460 // MidiServiceWin overrides:
529 void InitializeAsync(MidiServiceWinDelegate* delegate) final { 461 void InitializeAsync(MidiServiceWinDelegate* delegate) final {
462 // Start() and Stop() of the threads, and AddDevicesChangeObserver() and
463 // RemoveDevicesChangeObserver() should be called on the same thread.
464 CHECK(thread_checker_.CalledOnValidThread());
465
530 delegate_ = delegate; 466 delegate_ = delegate;
531 sender_thread_.StartWithOptions( 467
532 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); 468 sender_thread_.Start();
533 task_thread_.StartWithOptions( 469 task_thread_.Start();
534 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); 470
535 monitor_thread_.StartWithOptions( 471 // Start monitoring device changes. This should start before the
536 base::Thread::Options(base::MessageLoop::TYPE_UI, 0)); 472 // following UpdateDeviceList() call not to miss the event happening
537 monitor_thread_.message_loop()->PostTask( 473 // between the call and the observer registration.
538 FROM_HERE, base::Bind(&MidiServiceWinImpl::InitializeOnMonitorThread, 474 base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
539 base::Unretained(this))); 475
476 UpdateDeviceList();
477
478 task_thread_.message_loop()->PostTask(
479 FROM_HERE,
480 base::Bind(&MidiServiceWinImpl::CompleteInitializationOnTaskThread,
481 base::Unretained(this), MIDI_OK));
540 } 482 }
541 483
542 void SendMidiDataAsync(uint32 port_number, 484 void SendMidiDataAsync(uint32 port_number,
543 const std::vector<uint8>& data, 485 const std::vector<uint8>& data,
544 base::TimeTicks time) final { 486 base::TimeTicks time) final {
545 if (destructor_started) { 487 if (destructor_started) {
546 LOG(ERROR) << "ThreadSafeSendData failed because MidiServiceWinImpl is " 488 LOG(ERROR) << "ThreadSafeSendData failed because MidiServiceWinImpl is "
547 "being destructed. port: " << port_number; 489 "being destructed. port: " << port_number;
548 return; 490 return;
549 } 491 }
(...skipping 17 matching lines...) Expand all
567 state->port_age, data, time), 509 state->port_age, data, time),
568 time - now); 510 time - now);
569 } else { 511 } else {
570 sender_thread_.message_loop()->PostTask( 512 sender_thread_.message_loop()->PostTask(
571 FROM_HERE, base::Bind(&MidiServiceWinImpl::SendOnSenderThread, 513 FROM_HERE, base::Bind(&MidiServiceWinImpl::SendOnSenderThread,
572 base::Unretained(this), port_number, 514 base::Unretained(this), port_number,
573 state->port_age, data, time)); 515 state->port_age, data, time));
574 } 516 }
575 } 517 }
576 518
519 // base::SystemMonitor::DevicesChangedObserver overrides:
520 void OnDevicesChanged(base::SystemMonitor::DeviceType device_type) final {
521 CHECK(thread_checker_.CalledOnValidThread());
522 if (destructor_started)
523 return;
524
525 switch (device_type) {
526 case base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE:
527 case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE:
528 // Add case of other unrelated device types here.
529 return;
530 case base::SystemMonitor::DEVTYPE_UNKNOWN:
531 // Interested in MIDI devices. Try updating the device list.
532 UpdateDeviceList();
533 break;
534 // No default here to capture new DeviceType by compile time.
535 }
536 }
537
577 private: 538 private:
578 scoped_refptr<MidiInputDeviceState> GetInputDeviceFromHandle( 539 scoped_refptr<MidiInputDeviceState> GetInputDeviceFromHandle(
579 HMIDIIN midi_handle) { 540 HMIDIIN midi_handle) {
580 base::AutoLock auto_lock(input_ports_lock_); 541 base::AutoLock auto_lock(input_ports_lock_);
581 const auto it = input_device_map_.find(midi_handle); 542 const auto it = input_device_map_.find(midi_handle);
582 return (it != input_device_map_.end() ? it->second : nullptr); 543 return (it != input_device_map_.end() ? it->second : nullptr);
583 } 544 }
584 545
585 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromHandle( 546 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromHandle(
586 HMIDIOUT midi_handle) { 547 HMIDIOUT midi_handle) {
587 base::AutoLock auto_lock(output_ports_lock_); 548 base::AutoLock auto_lock(output_ports_lock_);
588 const auto it = output_device_map_.find(midi_handle); 549 const auto it = output_device_map_.find(midi_handle);
589 return (it != output_device_map_.end() ? it->second : nullptr); 550 return (it != output_device_map_.end() ? it->second : nullptr);
590 } 551 }
591 552
592 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromPort( 553 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromPort(
593 uint32 port_number) { 554 uint32 port_number) {
594 base::AutoLock auto_lock(output_ports_lock_); 555 base::AutoLock auto_lock(output_ports_lock_);
595 if (output_ports_.size() <= port_number) 556 if (output_ports_.size() <= port_number)
596 return nullptr; 557 return nullptr;
597 return output_ports_[port_number]; 558 return output_ports_[port_number];
598 } 559 }
599 560
561 void UpdateDeviceList() {
562 task_thread_.message_loop()->PostTask(
563 FROM_HERE, base::Bind(&MidiServiceWinImpl::UpdateDeviceListOnTaskThread,
564 base::Unretained(this)));
565 }
566
600 ///////////////////////////////////////////////////////////////////////////// 567 /////////////////////////////////////////////////////////////////////////////
601 // Callbacks on the OS multimedia thread. 568 // Callbacks on the OS multimedia thread.
602 ///////////////////////////////////////////////////////////////////////////// 569 /////////////////////////////////////////////////////////////////////////////
603 570
604 static void CALLBACK OnMidiInEventOnMultimediaThread(HMIDIIN midi_in_handle, 571 static void CALLBACK
605 UINT message, 572 OnMidiInEventOnMainlyMultimediaThread(HMIDIIN midi_in_handle,
606 DWORD_PTR instance, 573 UINT message,
607 DWORD_PTR param1, 574 DWORD_PTR instance,
608 DWORD_PTR param2) { 575 DWORD_PTR param1,
576 DWORD_PTR param2) {
609 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); 577 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance);
610 if (!self) 578 if (!self)
611 return; 579 return;
612 switch (message) { 580 switch (message) {
613 case MIM_OPEN: 581 case MIM_OPEN:
614 self->OnMidiInOpenOnMultimediaThread(midi_in_handle); 582 self->OnMidiInOpen(midi_in_handle);
615 break; 583 break;
616 case MIM_DATA: 584 case MIM_DATA:
617 self->OnMidiInDataOnMultimediaThread(midi_in_handle, param1, param2); 585 self->OnMidiInDataOnMultimediaThread(midi_in_handle, param1, param2);
618 break; 586 break;
619 case MIM_LONGDATA: 587 case MIM_LONGDATA:
620 self->OnMidiInLongDataOnMultimediaThread(midi_in_handle, param1, 588 self->OnMidiInLongDataOnMultimediaThread(midi_in_handle, param1,
621 param2); 589 param2);
622 break; 590 break;
623 case MIM_CLOSE: 591 case MIM_CLOSE:
624 self->OnMidiInCloseOnMultimediaThread(midi_in_handle); 592 self->OnMidiInCloseOnMultimediaThread(midi_in_handle);
625 break; 593 break;
626 } 594 }
627 } 595 }
628 596
629 void OnMidiInOpenOnMultimediaThread(HMIDIIN midi_in_handle) { 597 void OnMidiInOpen(HMIDIIN midi_in_handle) {
630 UINT device_id = 0; 598 UINT device_id = 0;
631 MMRESULT result = midiInGetID(midi_in_handle, &device_id); 599 MMRESULT result = midiInGetID(midi_in_handle, &device_id);
632 if (result != MMSYSERR_NOERROR) { 600 if (result != MMSYSERR_NOERROR) {
633 DLOG(ERROR) << "midiInGetID failed: " << GetInErrorMessage(result); 601 DLOG(ERROR) << "midiInGetID failed: " << GetInErrorMessage(result);
634 return; 602 return;
635 } 603 }
636 MIDIINCAPS2W caps = {}; 604 MIDIINCAPS2W caps = {};
637 result = midiInGetDevCaps(device_id, reinterpret_cast<LPMIDIINCAPSW>(&caps), 605 result = midiInGetDevCaps(device_id, reinterpret_cast<LPMIDIINCAPSW>(&caps),
638 sizeof(caps)); 606 sizeof(caps));
639 if (result != MMSYSERR_NOERROR) { 607 if (result != MMSYSERR_NOERROR) {
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
778 unused_input_ports_[device_info].push(port_number); 746 unused_input_ports_[device_info].push(port_number);
779 } 747 }
780 task_thread_.message_loop()->PostTask( 748 task_thread_.message_loop()->PostTask(
781 FROM_HERE, 749 FROM_HERE,
782 base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread, 750 base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread,
783 base::Unretained(this), port_number, 751 base::Unretained(this), port_number,
784 MIDI_PORT_DISCONNECTED)); 752 MIDI_PORT_DISCONNECTED));
785 } 753 }
786 754
787 static void CALLBACK 755 static void CALLBACK
788 OnMidiOutEventOnMultimediaThread(HMIDIOUT midi_out_handle, 756 OnMidiOutEventOnMainlyMultimediaThread(HMIDIOUT midi_out_handle,
789 UINT message, 757 UINT message,
790 DWORD_PTR instance, 758 DWORD_PTR instance,
791 DWORD_PTR param1, 759 DWORD_PTR param1,
792 DWORD_PTR param2) { 760 DWORD_PTR param2) {
793 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); 761 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance);
794 if (!self) 762 if (!self)
795 return; 763 return;
796 switch (message) { 764 switch (message) {
797 case MOM_OPEN: 765 case MOM_OPEN:
798 self->OnMidiOutOpenOnMultimediaThread(midi_out_handle, param1, param2); 766 self->OnMidiOutOpen(midi_out_handle, param1, param2);
799 break; 767 break;
800 case MOM_DONE: 768 case MOM_DONE:
801 self->OnMidiOutDoneOnMultimediaThread(midi_out_handle, param1); 769 self->OnMidiOutDoneOnMultimediaThread(midi_out_handle, param1);
802 break; 770 break;
803 case MOM_CLOSE: 771 case MOM_CLOSE:
804 self->OnMidiOutCloseOnMultimediaThread(midi_out_handle); 772 self->OnMidiOutCloseOnMultimediaThread(midi_out_handle);
805 break; 773 break;
806 } 774 }
807 } 775 }
808 776
809 void OnMidiOutOpenOnMultimediaThread(HMIDIOUT midi_out_handle, 777 void OnMidiOutOpen(HMIDIOUT midi_out_handle,
810 DWORD_PTR param1, 778 DWORD_PTR param1,
811 DWORD_PTR param2) { 779 DWORD_PTR param2) {
812 UINT device_id = 0; 780 UINT device_id = 0;
813 MMRESULT result = midiOutGetID(midi_out_handle, &device_id); 781 MMRESULT result = midiOutGetID(midi_out_handle, &device_id);
814 if (result != MMSYSERR_NOERROR) { 782 if (result != MMSYSERR_NOERROR) {
815 DLOG(ERROR) << "midiOutGetID failed: " << GetOutErrorMessage(result); 783 DLOG(ERROR) << "midiOutGetID failed: " << GetOutErrorMessage(result);
816 return; 784 return;
817 } 785 }
818 MIDIOUTCAPS2W caps = {}; 786 MIDIOUTCAPS2W caps = {};
819 result = midiOutGetDevCaps( 787 result = midiOutGetDevCaps(
820 device_id, reinterpret_cast<LPMIDIOUTCAPSW>(&caps), sizeof(caps)); 788 device_id, reinterpret_cast<LPMIDIOUTCAPSW>(&caps), sizeof(caps));
821 if (result != MMSYSERR_NOERROR) { 789 if (result != MMSYSERR_NOERROR) {
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
899 state->closed = true; 867 state->closed = true;
900 } 868 }
901 task_thread_.message_loop()->PostTask( 869 task_thread_.message_loop()->PostTask(
902 FROM_HERE, 870 FROM_HERE,
903 base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread, 871 base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread,
904 base::Unretained(this), port_number, 872 base::Unretained(this), port_number,
905 MIDI_PORT_DISCONNECTED)); 873 MIDI_PORT_DISCONNECTED));
906 } 874 }
907 875
908 ///////////////////////////////////////////////////////////////////////////// 876 /////////////////////////////////////////////////////////////////////////////
909 // Callbacks on the monitor thread.
910 /////////////////////////////////////////////////////////////////////////////
911
912 void AssertOnMonitorThread() {
913 DCHECK_EQ(monitor_thread_.thread_id(), base::PlatformThread::CurrentId());
914 }
915
916 void InitializeOnMonitorThread() {
917 AssertOnMonitorThread();
918 // Synchronously open all the existing MIDI devices.
919 TryOpenAllDevicesOnMonitorThreadSync();
920 // Since |TryOpenAllDevicesOnMonitorThreadSync()| is a blocking call, it is
921 // guaranteed that all the initial MIDI ports are already opened here, and
922 // corresponding tasks to call |AddInputPortOnTaskThread()| and
923 // |AddOutputPortOnTaskThread()| are already queued in the Misc. Task
924 // Thread. This means that the following task to call
925 // |CompleteInitializationOnTaskThread()| are always called after all the
926 // pending tasks to call |AddInputPortOnTaskThread()| and
927 // |AddOutputPortOnTaskThread()| are completed.
928 task_thread_.message_loop()->PostTask(
929 FROM_HERE,
930 base::Bind(&MidiServiceWinImpl::CompleteInitializationOnTaskThread,
931 base::Unretained(this), MIDI_OK));
932 // Start monitoring based on notifications generated by
933 // RegisterDeviceNotificationW API.
934 device_monitor_window_.reset(new DeviceMonitorWindow(
935 base::Bind(&MidiServiceWinImpl::TryOpenAllDevicesOnMonitorThreadSync,
936 base::Unretained(this))));
937 }
938
939 void StopDeviceMonitorOnMonitorThreadSync() {
940 device_monitor_window_.reset();
941 }
942
943 // Note that this is a blocking method.
944 void TryOpenAllDevicesOnMonitorThreadSync() {
945 AssertOnMonitorThread();
946 const UINT num_in_devices = midiInGetNumDevs();
947 for (UINT device_id = 0; device_id < num_in_devices; ++device_id) {
948 // Here we use |CALLBACK_FUNCTION| to subscribe MIM_DATA, MIM_LONGDATA,
949 // MIM_OPEN, and MIM_CLOSE events.
950 // - MIM_DATA: This is the only way to get a short MIDI message with
951 // timestamp information.
952 // - MIM_LONGDATA: This is the only way to get a long MIDI message with
953 // timestamp information.
954 // - MIM_OPEN: This event is sent the input device is opened.
955 // - MIM_CLOSE: This event is sent when 1) midiInClose() is called, or 2)
956 // the MIDI device becomes unavailable for some reasons, e.g., the
957 // cable is disconnected. As for the former case, HMIDIOUT will be
958 // invalidated soon after the callback is finished. As for the later
959 // case, however, HMIDIOUT continues to be valid until midiInClose()
960 // is called.
961 HMIDIIN midi_handle = kInvalidMidiInHandle;
962 const MMRESULT result = midiInOpen(
963 &midi_handle, device_id,
964 reinterpret_cast<DWORD_PTR>(&OnMidiInEventOnMultimediaThread),
965 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
966 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED)
967 << "Failed to open output device. "
968 << " id: " << device_id << " message: " << GetInErrorMessage(result);
969 }
970
971 const UINT num_out_devices = midiOutGetNumDevs();
972 for (UINT device_id = 0; device_id < num_out_devices; ++device_id) {
973 // Here we use |CALLBACK_FUNCTION| to subscribe MOM_DONE, MOM_OPEN, and
974 // MOM_CLOSE events.
975 // - MOM_DONE: SendLongMidiMessageInternal() relies on this event to clean
976 // up the backing store where a long MIDI message is stored.
977 // - MOM_OPEN: This event is sent the output device is opened.
978 // - MOM_CLOSE: This event is sent when 1) midiOutClose() is called, or 2)
979 // the MIDI device becomes unavailable for some reasons, e.g., the
980 // cable is disconnected. As for the former case, HMIDIOUT will be
981 // invalidated soon after the callback is finished. As for the later
982 // case, however, HMIDIOUT continues to be valid until midiOutClose()
983 // is called.
984 HMIDIOUT midi_handle = kInvalidMidiOutHandle;
985 const MMRESULT result = midiOutOpen(
986 &midi_handle, device_id,
987 reinterpret_cast<DWORD_PTR>(&OnMidiOutEventOnMultimediaThread),
988 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
989 if (result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED) {
990 DLOG(ERROR) << "Failed to open output device. "
991 << " id: " << device_id
992 << " message: " << GetOutErrorMessage(result);
993 }
994 }
995 }
996
997 /////////////////////////////////////////////////////////////////////////////
998 // Callbacks on the sender thread. 877 // Callbacks on the sender thread.
999 ///////////////////////////////////////////////////////////////////////////// 878 /////////////////////////////////////////////////////////////////////////////
1000 879
1001 void AssertOnSenderThread() { 880 void AssertOnSenderThread() {
1002 DCHECK_EQ(sender_thread_.thread_id(), base::PlatformThread::CurrentId()); 881 DCHECK_EQ(sender_thread_.thread_id(), base::PlatformThread::CurrentId());
1003 } 882 }
1004 883
1005 void SendOnSenderThread(uint32 port_number, 884 void SendOnSenderThread(uint32 port_number,
1006 uint64 port_age, 885 uint64 port_age,
1007 const std::vector<uint8>& data, 886 const std::vector<uint8>& data,
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1051 } 930 }
1052 931
1053 ///////////////////////////////////////////////////////////////////////////// 932 /////////////////////////////////////////////////////////////////////////////
1054 // Callbacks on the task thread. 933 // Callbacks on the task thread.
1055 ///////////////////////////////////////////////////////////////////////////// 934 /////////////////////////////////////////////////////////////////////////////
1056 935
1057 void AssertOnTaskThread() { 936 void AssertOnTaskThread() {
1058 DCHECK_EQ(task_thread_.thread_id(), base::PlatformThread::CurrentId()); 937 DCHECK_EQ(task_thread_.thread_id(), base::PlatformThread::CurrentId());
1059 } 938 }
1060 939
940 void UpdateDeviceListOnTaskThread() {
941 AssertOnTaskThread();
942 const UINT num_in_devices = midiInGetNumDevs();
943 for (UINT device_id = 0; device_id < num_in_devices; ++device_id) {
944 // Here we use |CALLBACK_FUNCTION| to subscribe MIM_DATA, MIM_LONGDATA,
945 // MIM_OPEN, and MIM_CLOSE events.
946 // - MIM_DATA: This is the only way to get a short MIDI message with
947 // timestamp information.
948 // - MIM_LONGDATA: This is the only way to get a long MIDI message with
949 // timestamp information.
950 // - MIM_OPEN: This event is sent the input device is opened. Note that
951 // this message is called on the caller thread.
952 // - MIM_CLOSE: This event is sent when 1) midiInClose() is called, or 2)
953 // the MIDI device becomes unavailable for some reasons, e.g., the
954 // cable is disconnected. As for the former case, HMIDIOUT will be
955 // invalidated soon after the callback is finished. As for the later
956 // case, however, HMIDIOUT continues to be valid until midiInClose()
957 // is called.
958 HMIDIIN midi_handle = kInvalidMidiInHandle;
959 const MMRESULT result = midiInOpen(
960 &midi_handle, device_id,
961 reinterpret_cast<DWORD_PTR>(&OnMidiInEventOnMainlyMultimediaThread),
962 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
963 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED)
964 << "Failed to open output device. "
965 << " id: " << device_id << " message: " << GetInErrorMessage(result);
966 }
967
968 const UINT num_out_devices = midiOutGetNumDevs();
969 for (UINT device_id = 0; device_id < num_out_devices; ++device_id) {
970 // Here we use |CALLBACK_FUNCTION| to subscribe MOM_DONE, MOM_OPEN, and
971 // MOM_CLOSE events.
972 // - MOM_DONE: SendLongMidiMessageInternal() relies on this event to clean
973 // up the backing store where a long MIDI message is stored.
974 // - MOM_OPEN: This event is sent the output device is opened. Note that
975 // this message is called on the caller thread.
976 // - MOM_CLOSE: This event is sent when 1) midiOutClose() is called, or 2)
977 // the MIDI device becomes unavailable for some reasons, e.g., the
978 // cable is disconnected. As for the former case, HMIDIOUT will be
979 // invalidated soon after the callback is finished. As for the later
980 // case, however, HMIDIOUT continues to be valid until midiOutClose()
981 // is called.
982 HMIDIOUT midi_handle = kInvalidMidiOutHandle;
983 const MMRESULT result = midiOutOpen(
984 &midi_handle, device_id,
985 reinterpret_cast<DWORD_PTR>(&OnMidiOutEventOnMainlyMultimediaThread),
986 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
987 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED)
988 << "Failed to open output device. "
989 << " id: " << device_id << " message: " << GetOutErrorMessage(result);
990 }
991 }
992
1061 void StartInputDeviceOnTaskThread(HMIDIIN midi_in_handle) { 993 void StartInputDeviceOnTaskThread(HMIDIIN midi_in_handle) {
1062 AssertOnTaskThread(); 994 AssertOnTaskThread();
1063 auto state = GetInputDeviceFromHandle(midi_in_handle); 995 auto state = GetInputDeviceFromHandle(midi_in_handle);
1064 if (!state) 996 if (!state)
1065 return; 997 return;
1066 MMRESULT result = 998 MMRESULT result =
1067 midiInPrepareHeader(state->midi_handle, state->midi_header.get(), 999 midiInPrepareHeader(state->midi_handle, state->midi_header.get(),
1068 sizeof(*state->midi_header)); 1000 sizeof(*state->midi_header));
1069 if (result != MMSYSERR_NOERROR) { 1001 if (result != MMSYSERR_NOERROR) {
1070 DLOG(ERROR) << "Failed to initialize input buffer: " 1002 DLOG(ERROR) << "Failed to initialize input buffer: "
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
1120 delegate_->OnSetOutputPortState(port_index, state); 1052 delegate_->OnSetOutputPortState(port_index, state);
1121 } 1053 }
1122 1054
1123 ///////////////////////////////////////////////////////////////////////////// 1055 /////////////////////////////////////////////////////////////////////////////
1124 // Fields: 1056 // Fields:
1125 ///////////////////////////////////////////////////////////////////////////// 1057 /////////////////////////////////////////////////////////////////////////////
1126 1058
1127 // Does not take ownership. 1059 // Does not take ownership.
1128 MidiServiceWinDelegate* delegate_; 1060 MidiServiceWinDelegate* delegate_;
1129 1061
1130 base::Thread monitor_thread_; 1062 base::ThreadChecker thread_checker_;
1063
1131 base::Thread sender_thread_; 1064 base::Thread sender_thread_;
1132 base::Thread task_thread_; 1065 base::Thread task_thread_;
1133 1066
1134 scoped_ptr<DeviceMonitorWindow> device_monitor_window_;
1135
1136 base::Lock input_ports_lock_; 1067 base::Lock input_ports_lock_;
1137 base::hash_map<HMIDIIN, scoped_refptr<MidiInputDeviceState>> 1068 base::hash_map<HMIDIIN, scoped_refptr<MidiInputDeviceState>>
1138 input_device_map_; // GUARDED_BY(input_ports_lock_) 1069 input_device_map_; // GUARDED_BY(input_ports_lock_)
1139 PortNumberCache unused_input_ports_; // GUARDED_BY(input_ports_lock_) 1070 PortNumberCache unused_input_ports_; // GUARDED_BY(input_ports_lock_)
1140 std::vector<scoped_refptr<MidiInputDeviceState>> 1071 std::vector<scoped_refptr<MidiInputDeviceState>>
1141 input_ports_; // GUARDED_BY(input_ports_lock_) 1072 input_ports_; // GUARDED_BY(input_ports_lock_)
1142 std::vector<uint64> input_ports_ages_; // GUARDED_BY(input_ports_lock_) 1073 std::vector<uint64> input_ports_ages_; // GUARDED_BY(input_ports_lock_)
1143 1074
1144 base::Lock output_ports_lock_; 1075 base::Lock output_ports_lock_;
1145 base::hash_map<HMIDIOUT, scoped_refptr<MidiOutputDeviceState>> 1076 base::hash_map<HMIDIOUT, scoped_refptr<MidiOutputDeviceState>>
1146 output_device_map_; // GUARDED_BY(output_ports_lock_) 1077 output_device_map_; // GUARDED_BY(output_ports_lock_)
1147 PortNumberCache unused_output_ports_; // GUARDED_BY(output_ports_lock_) 1078 PortNumberCache unused_output_ports_; // GUARDED_BY(output_ports_lock_)
1148 std::vector<scoped_refptr<MidiOutputDeviceState>> 1079 std::vector<scoped_refptr<MidiOutputDeviceState>>
1149 output_ports_; // GUARDED_BY(output_ports_lock_) 1080 output_ports_; // GUARDED_BY(output_ports_lock_)
1150 std::vector<uint64> output_ports_ages_; // GUARDED_BY(output_ports_lock_) 1081 std::vector<uint64> output_ports_ages_; // GUARDED_BY(output_ports_lock_)
1151 1082
1152 // True if one thread reached MidiServiceWinImpl::~MidiServiceWinImpl(). Note 1083 // True if one thread reached MidiServiceWinImpl::~MidiServiceWinImpl(). Note
1153 // that MidiServiceWinImpl::~MidiServiceWinImpl() is blocked until 1084 // that MidiServiceWinImpl::~MidiServiceWinImpl() is blocked until
1154 // |monitor_thread_|, |sender_thread_|, and |task_thread_| are stopped. 1085 // |sender_thread_|, and |task_thread_| are stopped.
1155 // This flag can be used as the signal that when background tasks must be 1086 // This flag can be used as the signal that when background tasks must be
1156 // interrupted. 1087 // interrupted.
1157 // TODO(toyoshim): Use std::atomic<bool> when it is allowed. 1088 // TODO(toyoshim): Use std::atomic<bool> when it is allowed.
1158 volatile bool destructor_started; 1089 volatile bool destructor_started;
1159 1090
1160 DISALLOW_COPY_AND_ASSIGN(MidiServiceWinImpl); 1091 DISALLOW_COPY_AND_ASSIGN(MidiServiceWinImpl);
1161 }; 1092 };
1162 1093
1163 } // namespace 1094 } // namespace
1164 1095
(...skipping 21 matching lines...) Expand all
1186 // TOOD(toyoshim): This calculation should be done when the date is actually 1117 // TOOD(toyoshim): This calculation should be done when the date is actually
1187 // sent. 1118 // sent.
1188 client->AccumulateMidiBytesSent(data.size()); 1119 client->AccumulateMidiBytesSent(data.size());
1189 } 1120 }
1190 1121
1191 MidiManager* MidiManager::Create() { 1122 MidiManager* MidiManager::Create() {
1192 return new MidiManagerWin(); 1123 return new MidiManagerWin();
1193 } 1124 }
1194 1125
1195 } // namespace media 1126 } // namespace media
OLDNEW
« no previous file with comments | « media/midi/midi_manager_unittest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698