Commit 967340a9 authored by toyoshim's avatar toyoshim Committed by Commit bot

Web MIDI: introduce MidiService class

In this patch, the class does nothing interesting,
but would have a dynamic instance management in
following changes.

Migration would happen per platform behind a
field study.

See crbug.com/672793 for rough design and plan.

BUG=672793

Review-Url: https://codereview.chromium.org/2566673002
Cr-Commit-Position: refs/heads/master@{#438765}
parent cc8c7225
...@@ -93,7 +93,7 @@ ...@@ -93,7 +93,7 @@
#include "device/time_zone_monitor/time_zone_monitor.h" #include "device/time_zone_monitor/time_zone_monitor.h"
#include "media/base/media.h" #include "media/base/media.h"
#include "media/base/user_input_monitor.h" #include "media/base/user_input_monitor.h"
#include "media/midi/midi_manager.h" #include "media/midi/midi_service.h"
#include "mojo/edk/embedder/embedder.h" #include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/scoped_ipc_support.h" #include "mojo/edk/embedder/scoped_ipc_support.h"
#include "net/base/network_change_notifier.h" #include "net/base/network_change_notifier.h"
...@@ -1140,9 +1140,9 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { ...@@ -1140,9 +1140,9 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
resource_dispatcher_host_->Shutdown(); resource_dispatcher_host_->Shutdown();
} }
// Request shutdown to clean up allocated resources on the IO thread. // Request shutdown to clean up allocated resources on the IO thread.
if (midi_manager_) { if (midi_service_) {
TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:MidiManager"); TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:MidiService");
midi_manager_->Shutdown(); midi_service_->Shutdown();
} }
memory_pressure_monitor_.reset(); memory_pressure_monitor_.reset();
...@@ -1405,8 +1405,8 @@ int BrowserMainLoop::BrowserThreadsStarted() { ...@@ -1405,8 +1405,8 @@ int BrowserMainLoop::BrowserThreadsStarted() {
} }
{ {
TRACE_EVENT0("startup", "BrowserThreadsStarted::Subsystem:MidiManager"); TRACE_EVENT0("startup", "BrowserThreadsStarted::Subsystem:MidiService");
midi_manager_.reset(midi::MidiManager::Create()); midi_service_.reset(new midi::MidiService);
} }
#if defined(OS_WIN) #if defined(OS_WIN)
......
...@@ -52,7 +52,7 @@ class DeviceMonitorMac; ...@@ -52,7 +52,7 @@ class DeviceMonitorMac;
} // namespace media } // namespace media
namespace midi { namespace midi {
class MidiManager; class MidiService;
} // namespace midi } // namespace midi
namespace mojo { namespace mojo {
...@@ -145,7 +145,7 @@ class CONTENT_EXPORT BrowserMainLoop { ...@@ -145,7 +145,7 @@ class CONTENT_EXPORT BrowserMainLoop {
device::TimeZoneMonitor* time_zone_monitor() const { device::TimeZoneMonitor* time_zone_monitor() const {
return time_zone_monitor_.get(); return time_zone_monitor_.get();
} }
midi::MidiManager* midi_manager() const { return midi_manager_.get(); } midi::MidiService* midi_service() const { return midi_service_.get(); }
base::Thread* indexed_db_thread() const { return indexed_db_thread_.get(); } base::Thread* indexed_db_thread() const { return indexed_db_thread_.get(); }
bool is_tracing_startup_for_duration() const { bool is_tracing_startup_for_duration() const {
...@@ -291,7 +291,7 @@ class CONTENT_EXPORT BrowserMainLoop { ...@@ -291,7 +291,7 @@ class CONTENT_EXPORT BrowserMainLoop {
std::unique_ptr<AudioManagerThread> audio_thread_; std::unique_ptr<AudioManagerThread> audio_thread_;
media::ScopedAudioManagerPtr audio_manager_; media::ScopedAudioManagerPtr audio_manager_;
std::unique_ptr<midi::MidiManager> midi_manager_; std::unique_ptr<midi::MidiService> midi_service_;
#if defined(OS_WIN) #if defined(OS_WIN)
std::unique_ptr<media::SystemMessageWindowWin> system_message_window_; std::unique_ptr<media::SystemMessageWindowWin> system_message_window_;
......
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
#include "content/public/browser/content_browser_client.h" #include "content/public/browser/content_browser_client.h"
#include "content/public/browser/user_metrics.h" #include "content/public/browser/user_metrics.h"
#include "media/midi/message_util.h" #include "media/midi/message_util.h"
#include "media/midi/midi_manager.h"
#include "media/midi/midi_message_queue.h" #include "media/midi/midi_message_queue.h"
#include "media/midi/midi_service.h"
namespace content { namespace content {
namespace { namespace {
...@@ -42,30 +42,29 @@ using midi::kEndOfSysExByte; ...@@ -42,30 +42,29 @@ using midi::kEndOfSysExByte;
using midi::mojom::PortState; using midi::mojom::PortState;
using midi::mojom::Result; using midi::mojom::Result;
MidiHost::MidiHost(int renderer_process_id, MidiHost::MidiHost(int renderer_process_id, midi::MidiService* midi_service)
midi::MidiManager* midi_manager)
: BrowserMessageFilter(MidiMsgStart), : BrowserMessageFilter(MidiMsgStart),
renderer_process_id_(renderer_process_id), renderer_process_id_(renderer_process_id),
has_sys_ex_permission_(false), has_sys_ex_permission_(false),
is_session_requested_(false), is_session_requested_(false),
midi_manager_(midi_manager), midi_service_(midi_service),
sent_bytes_in_flight_(0), sent_bytes_in_flight_(0),
bytes_sent_since_last_acknowledgement_(0), bytes_sent_since_last_acknowledgement_(0),
output_port_count_(0) { output_port_count_(0) {
DCHECK(midi_manager_); DCHECK(midi_service_);
} }
MidiHost::~MidiHost() = default; MidiHost::~MidiHost() = default;
void MidiHost::OnChannelClosing() { void MidiHost::OnChannelClosing() {
// If we get here the MidiHost is going to be destroyed soon. Prevent any // If we get here the MidiHost is going to be destroyed soon. Prevent any
// subsequent calls from MidiManager by closing our session. // subsequent calls from MidiService by closing our session.
// If Send() is called from a different thread (e.g. a separate thread owned // If Send() is called from a different thread (e.g. a separate thread owned
// by the MidiManager implementation), it will get posted to the IO thread. // by the MidiService implementation), it will get posted to the IO thread.
// There is a race condition here if our refcount is 0 and we're about to or // There is a race condition here if our refcount is 0 and we're about to or
// have already entered OnDestruct(). // have already entered OnDestruct().
if (is_session_requested_ && midi_manager_) { if (is_session_requested_ && midi_service_) {
midi_manager_->EndSession(this); midi_service_->EndSession(this);
is_session_requested_ = false; is_session_requested_ = false;
} }
} }
...@@ -89,8 +88,8 @@ bool MidiHost::OnMessageReceived(const IPC::Message& message) { ...@@ -89,8 +88,8 @@ bool MidiHost::OnMessageReceived(const IPC::Message& message) {
void MidiHost::OnStartSession() { void MidiHost::OnStartSession() {
is_session_requested_ = true; is_session_requested_ = true;
if (midi_manager_) if (midi_service_)
midi_manager_->StartSession(this); midi_service_->StartSession(this);
} }
void MidiHost::OnSendData(uint32_t port, void MidiHost::OnSendData(uint32_t port,
...@@ -128,14 +127,14 @@ void MidiHost::OnSendData(uint32_t port, ...@@ -128,14 +127,14 @@ void MidiHost::OnSendData(uint32_t port,
return; return;
sent_bytes_in_flight_ += data.size(); sent_bytes_in_flight_ += data.size();
} }
if (midi_manager_) if (midi_service_)
midi_manager_->DispatchSendMidiData(this, port, data, timestamp); midi_service_->DispatchSendMidiData(this, port, data, timestamp);
} }
void MidiHost::OnEndSession() { void MidiHost::OnEndSession() {
is_session_requested_ = false; is_session_requested_ = false;
if (midi_manager_) if (midi_service_)
midi_manager_->EndSession(this); midi_service_->EndSession(this);
} }
void MidiHost::CompleteStartSession(Result result) { void MidiHost::CompleteStartSession(Result result) {
...@@ -222,7 +221,7 @@ void MidiHost::AccumulateMidiBytesSent(size_t n) { ...@@ -222,7 +221,7 @@ void MidiHost::AccumulateMidiBytesSent(size_t n) {
} }
void MidiHost::Detach() { void MidiHost::Detach() {
midi_manager_ = nullptr; midi_service_ = nullptr;
} }
} // namespace content } // namespace content
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include "media/midi/midi_service.mojom.h" #include "media/midi/midi_service.mojom.h"
namespace midi { namespace midi {
class MidiManager; class MidiService;
class MidiMessageQueue; class MidiMessageQueue;
} // namespace midi } // namespace midi
...@@ -34,7 +34,7 @@ class CONTENT_EXPORT MidiHost : public BrowserMessageFilter, ...@@ -34,7 +34,7 @@ class CONTENT_EXPORT MidiHost : public BrowserMessageFilter,
public midi::MidiManagerClient { public midi::MidiManagerClient {
public: public:
// Called from UI thread from the owner of this object. // Called from UI thread from the owner of this object.
MidiHost(int renderer_process_id, midi::MidiManager* midi_manager); MidiHost(int renderer_process_id, midi::MidiService* midi_service);
// BrowserMessageFilter implementation. // BrowserMessageFilter implementation.
void OnChannelClosing() override; void OnChannelClosing() override;
...@@ -80,12 +80,9 @@ class CONTENT_EXPORT MidiHost : public BrowserMessageFilter, ...@@ -80,12 +80,9 @@ class CONTENT_EXPORT MidiHost : public BrowserMessageFilter,
// Represents if a session is requested to start. // Represents if a session is requested to start.
bool is_session_requested_; bool is_session_requested_;
// |midi_manager_| talks to the platform-specific MIDI APIs. // |midi_service_| manages a MidiManager instance that talks to
// It can be NULL if the platform (or our current implementation) // platform-specific MIDI APIs. It can be nullptr after detached.
// does not support MIDI. If not supported then a call to midi::MidiService* midi_service_;
// OnRequestAccess() will always refuse access and a call to
// OnSendData() will do nothing.
midi::MidiManager* midi_manager_;
// Buffers where data sent from each MIDI input port is stored. // Buffers where data sent from each MIDI input port is stored.
ScopedVector<midi::MidiMessageQueue> received_messages_queues_; ScopedVector<midi::MidiMessageQueue> received_messages_queues_;
......
...@@ -8,12 +8,14 @@ ...@@ -8,12 +8,14 @@
#include <stdint.h> #include <stdint.h>
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "content/common/media/midi_messages.h" #include "content/common/media/midi_messages.h"
#include "content/public/test/test_browser_thread.h" #include "content/public/test/test_browser_thread.h"
#include "media/midi/midi_manager.h" #include "media/midi/midi_manager.h"
#include "media/midi/midi_service.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace content { namespace content {
...@@ -60,9 +62,8 @@ class FakeMidiManager : public midi::MidiManager { ...@@ -60,9 +62,8 @@ class FakeMidiManager : public midi::MidiManager {
class MidiHostForTesting : public MidiHost { class MidiHostForTesting : public MidiHost {
public: public:
MidiHostForTesting(int renderer_process_id, MidiHostForTesting(int renderer_process_id, midi::MidiService* midi_service)
midi::MidiManager* midi_manager) : MidiHost(renderer_process_id, midi_service) {}
: MidiHost(renderer_process_id, midi_manager) {}
private: private:
~MidiHostForTesting() override {} ~MidiHostForTesting() override {}
...@@ -78,11 +79,14 @@ class MidiHostTest : public testing::Test { ...@@ -78,11 +79,14 @@ class MidiHostTest : public testing::Test {
public: public:
MidiHostTest() MidiHostTest()
: io_browser_thread_(BrowserThread::IO, &message_loop_), : io_browser_thread_(BrowserThread::IO, &message_loop_),
host_(new MidiHostForTesting(kRenderProcessId, &manager_)),
data_(kNoteOn, kNoteOn + arraysize(kNoteOn)), data_(kNoteOn, kNoteOn + arraysize(kNoteOn)),
port_id_(0) {} port_id_(0) {
manager_ = new FakeMidiManager;
service_.reset(new midi::MidiService(base::WrapUnique(manager_)));
host_ = new MidiHostForTesting(kRenderProcessId, service_.get());
}
~MidiHostTest() override { ~MidiHostTest() override {
manager_.Shutdown(); service_->Shutdown();
RunLoopUntilIdle(); RunLoopUntilIdle();
} }
...@@ -104,15 +108,13 @@ class MidiHostTest : public testing::Test { ...@@ -104,15 +108,13 @@ class MidiHostTest : public testing::Test {
host_->OnMessageReceived(*message.get()); host_->OnMessageReceived(*message.get());
} }
size_t GetEventSize() const { size_t GetEventSize() const { return manager_->events_.size(); }
return manager_.events_.size();
}
void CheckSendEventAt(size_t at, uint32_t port) { void CheckSendEventAt(size_t at, uint32_t port) {
EXPECT_EQ(DISPATCH_SEND_MIDI_DATA, manager_.events_[at].type); EXPECT_EQ(DISPATCH_SEND_MIDI_DATA, manager_->events_[at].type);
EXPECT_EQ(port, manager_.events_[at].port_index); EXPECT_EQ(port, manager_->events_[at].port_index);
EXPECT_EQ(data_, manager_.events_[at].data); EXPECT_EQ(data_, manager_->events_[at].data);
EXPECT_EQ(0.0, manager_.events_[at].timestamp); EXPECT_EQ(0.0, manager_->events_[at].timestamp);
} }
void RunLoopUntilIdle() { void RunLoopUntilIdle() {
...@@ -124,10 +126,11 @@ class MidiHostTest : public testing::Test { ...@@ -124,10 +126,11 @@ class MidiHostTest : public testing::Test {
base::MessageLoop message_loop_; base::MessageLoop message_loop_;
TestBrowserThread io_browser_thread_; TestBrowserThread io_browser_thread_;
FakeMidiManager manager_;
scoped_refptr<MidiHostForTesting> host_;
std::vector<uint8_t> data_; std::vector<uint8_t> data_;
int32_t port_id_; int32_t port_id_;
FakeMidiManager* manager_; // Raw pointer for testing, owned by |service_|.
std::unique_ptr<midi::MidiService> service_;
scoped_refptr<MidiHostForTesting> host_;
DISALLOW_COPY_AND_ASSIGN(MidiHostTest); DISALLOW_COPY_AND_ASSIGN(MidiHostTest);
}; };
......
...@@ -1074,7 +1074,7 @@ void RenderProcessHostImpl::CreateMessageFilters() { ...@@ -1074,7 +1074,7 @@ void RenderProcessHostImpl::CreateMessageFilters() {
browser_context->GetResourceContext()->GetMediaDeviceIDSalt()); browser_context->GetResourceContext()->GetMediaDeviceIDSalt());
AddFilter(audio_renderer_host_.get()); AddFilter(audio_renderer_host_.get());
AddFilter( AddFilter(
new MidiHost(GetID(), BrowserMainLoop::GetInstance()->midi_manager())); new MidiHost(GetID(), BrowserMainLoop::GetInstance()->midi_service()));
AddFilter(new AppCacheDispatcherHost( AddFilter(new AppCacheDispatcherHost(
storage_partition_impl_->GetAppCacheService(), GetID())); storage_partition_impl_->GetAppCacheService(), GetID()));
AddFilter(new ClipboardMessageFilter(blob_storage_context)); AddFilter(new ClipboardMessageFilter(blob_storage_context));
......
...@@ -88,6 +88,8 @@ component("midi") { ...@@ -88,6 +88,8 @@ component("midi") {
"midi_port_info.h", "midi_port_info.h",
"midi_scheduler.cc", "midi_scheduler.cc",
"midi_scheduler.h", "midi_scheduler.h",
"midi_service.cc",
"midi_service.h",
"midi_switches.cc", "midi_switches.cc",
"midi_switches.h", "midi_switches.h",
] ]
......
...@@ -28,6 +28,7 @@ namespace midi { ...@@ -28,6 +28,7 @@ namespace midi {
// A MidiManagerClient registers with the MidiManager to receive MIDI data. // A MidiManagerClient registers with the MidiManager to receive MIDI data.
// See MidiManager::RequestAccess() and MidiManager::ReleaseAccess() // See MidiManager::RequestAccess() and MidiManager::ReleaseAccess()
// for details. // for details.
// TODO(toyoshim): Consider to have a MidiServiceClient interface.
class MIDI_EXPORT MidiManagerClient { class MIDI_EXPORT MidiManagerClient {
public: public:
virtual ~MidiManagerClient() {} virtual ~MidiManagerClient() {}
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/midi/midi_service.h"
#include "media/midi/midi_manager.h"
namespace midi {
MidiService::MidiService(std::unique_ptr<MidiManager> manager) {
base::AutoLock lock(lock_);
if (manager.get())
manager_ = std::move(manager);
else
manager_.reset(MidiManager::Create());
}
MidiService::~MidiService() {
base::AutoLock lock(lock_);
manager_.reset();
}
void MidiService::Shutdown() {
base::AutoLock lock(lock_);
manager_->Shutdown();
}
void MidiService::StartSession(MidiManagerClient* client) {
base::AutoLock lock(lock_);
manager_->StartSession(client);
}
void MidiService::EndSession(MidiManagerClient* client) {
base::AutoLock lock(lock_);
manager_->EndSession(client);
}
void MidiService::DispatchSendMidiData(MidiManagerClient* client,
uint32_t port_index,
const std::vector<uint8_t>& data,
double timestamp) {
base::AutoLock lock(lock_);
manager_->DispatchSendMidiData(client, port_index, data, timestamp);
}
} // namespace midi
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_MIDI_MIDI_SERVICE_H_
#define MEDIA_MIDI_MIDI_SERVICE_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/macros.h"
#include "base/synchronization/lock.h"
#include "media/midi/midi_export.h"
#include "media/midi/midi_manager.h"
namespace midi {
// Manages MidiManager backends. This class expects to be constructed and
// destructed on the browser main thread, but methods can be called on both
// the main thread and the I/O thread.
class MIDI_EXPORT MidiService final {
public:
// |MidiManager| can be explicitly specified in the constructor for testing.
explicit MidiService(std::unique_ptr<MidiManager> manager = nullptr);
~MidiService();
// Called on the browser main thread to notify the I/O thread will stop and
// the instance will be destructed on the main thread soon.
void Shutdown();
// A client calls StartSession() to receive and send MIDI data.
void StartSession(MidiManagerClient* client);
// A client calls EndSession() to stop receiving MIDI data.
void EndSession(MidiManagerClient* client);
// A client calls DispatchSendMidiData() to send MIDI data.
virtual void DispatchSendMidiData(MidiManagerClient* client,
uint32_t port_index,
const std::vector<uint8_t>& data,
double timestamp);
std::unique_ptr<MidiManager> manager_;
base::Lock lock_;
DISALLOW_COPY_AND_ASSIGN(MidiService);
};
} // namespace midi
#endif // MEDIA_MIDI_MIDI_SERVICE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment