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 @@
#include "device/time_zone_monitor/time_zone_monitor.h"
#include "media/base/media.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/scoped_ipc_support.h"
#include "net/base/network_change_notifier.h"
......@@ -1140,9 +1140,9 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
resource_dispatcher_host_->Shutdown();
}
// Request shutdown to clean up allocated resources on the IO thread.
if (midi_manager_) {
TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:MidiManager");
midi_manager_->Shutdown();
if (midi_service_) {
TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:MidiService");
midi_service_->Shutdown();
}
memory_pressure_monitor_.reset();
......@@ -1405,8 +1405,8 @@ int BrowserMainLoop::BrowserThreadsStarted() {
}
{
TRACE_EVENT0("startup", "BrowserThreadsStarted::Subsystem:MidiManager");
midi_manager_.reset(midi::MidiManager::Create());
TRACE_EVENT0("startup", "BrowserThreadsStarted::Subsystem:MidiService");
midi_service_.reset(new midi::MidiService);
}
#if defined(OS_WIN)
......
......@@ -52,7 +52,7 @@ class DeviceMonitorMac;
} // namespace media
namespace midi {
class MidiManager;
class MidiService;
} // namespace midi
namespace mojo {
......@@ -145,7 +145,7 @@ class CONTENT_EXPORT BrowserMainLoop {
device::TimeZoneMonitor* time_zone_monitor() const {
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(); }
bool is_tracing_startup_for_duration() const {
......@@ -291,7 +291,7 @@ class CONTENT_EXPORT BrowserMainLoop {
std::unique_ptr<AudioManagerThread> audio_thread_;
media::ScopedAudioManagerPtr audio_manager_;
std::unique_ptr<midi::MidiManager> midi_manager_;
std::unique_ptr<midi::MidiService> midi_service_;
#if defined(OS_WIN)
std::unique_ptr<media::SystemMessageWindowWin> system_message_window_;
......
......@@ -15,8 +15,8 @@
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/user_metrics.h"
#include "media/midi/message_util.h"
#include "media/midi/midi_manager.h"
#include "media/midi/midi_message_queue.h"
#include "media/midi/midi_service.h"
namespace content {
namespace {
......@@ -42,30 +42,29 @@ using midi::kEndOfSysExByte;
using midi::mojom::PortState;
using midi::mojom::Result;
MidiHost::MidiHost(int renderer_process_id,
midi::MidiManager* midi_manager)
MidiHost::MidiHost(int renderer_process_id, midi::MidiService* midi_service)
: BrowserMessageFilter(MidiMsgStart),
renderer_process_id_(renderer_process_id),
has_sys_ex_permission_(false),
is_session_requested_(false),
midi_manager_(midi_manager),
midi_service_(midi_service),
sent_bytes_in_flight_(0),
bytes_sent_since_last_acknowledgement_(0),
output_port_count_(0) {
DCHECK(midi_manager_);
DCHECK(midi_service_);
}
MidiHost::~MidiHost() = default;
void MidiHost::OnChannelClosing() {
// If we get here the MidiHost is going to be destroyed soon. Prevent any
// subsequent calls from MidiManager by closing our session.
// If we get here the MidiHost is going to be destroyed soon. Prevent any
// subsequent calls from MidiService by closing our session.
// 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
// have already entered OnDestruct().
if (is_session_requested_ && midi_manager_) {
midi_manager_->EndSession(this);
if (is_session_requested_ && midi_service_) {
midi_service_->EndSession(this);
is_session_requested_ = false;
}
}
......@@ -89,8 +88,8 @@ bool MidiHost::OnMessageReceived(const IPC::Message& message) {
void MidiHost::OnStartSession() {
is_session_requested_ = true;
if (midi_manager_)
midi_manager_->StartSession(this);
if (midi_service_)
midi_service_->StartSession(this);
}
void MidiHost::OnSendData(uint32_t port,
......@@ -128,14 +127,14 @@ void MidiHost::OnSendData(uint32_t port,
return;
sent_bytes_in_flight_ += data.size();
}
if (midi_manager_)
midi_manager_->DispatchSendMidiData(this, port, data, timestamp);
if (midi_service_)
midi_service_->DispatchSendMidiData(this, port, data, timestamp);
}
void MidiHost::OnEndSession() {
is_session_requested_ = false;
if (midi_manager_)
midi_manager_->EndSession(this);
if (midi_service_)
midi_service_->EndSession(this);
}
void MidiHost::CompleteStartSession(Result result) {
......@@ -222,7 +221,7 @@ void MidiHost::AccumulateMidiBytesSent(size_t n) {
}
void MidiHost::Detach() {
midi_manager_ = nullptr;
midi_service_ = nullptr;
}
} // namespace content
......@@ -24,7 +24,7 @@
#include "media/midi/midi_service.mojom.h"
namespace midi {
class MidiManager;
class MidiService;
class MidiMessageQueue;
} // namespace midi
......@@ -34,7 +34,7 @@ class CONTENT_EXPORT MidiHost : public BrowserMessageFilter,
public midi::MidiManagerClient {
public:
// 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.
void OnChannelClosing() override;
......@@ -80,12 +80,9 @@ class CONTENT_EXPORT MidiHost : public BrowserMessageFilter,
// Represents if a session is requested to start.
bool is_session_requested_;
// |midi_manager_| talks to the platform-specific MIDI APIs.
// It can be NULL if the platform (or our current implementation)
// does not support MIDI. If not supported then a call to
// OnRequestAccess() will always refuse access and a call to
// OnSendData() will do nothing.
midi::MidiManager* midi_manager_;
// |midi_service_| manages a MidiManager instance that talks to
// platform-specific MIDI APIs. It can be nullptr after detached.
midi::MidiService* midi_service_;
// Buffers where data sent from each MIDI input port is stored.
ScopedVector<midi::MidiMessageQueue> received_messages_queues_;
......
......@@ -8,12 +8,14 @@
#include <stdint.h>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "content/common/media/midi_messages.h"
#include "content/public/test/test_browser_thread.h"
#include "media/midi/midi_manager.h"
#include "media/midi/midi_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
......@@ -60,9 +62,8 @@ class FakeMidiManager : public midi::MidiManager {
class MidiHostForTesting : public MidiHost {
public:
MidiHostForTesting(int renderer_process_id,
midi::MidiManager* midi_manager)
: MidiHost(renderer_process_id, midi_manager) {}
MidiHostForTesting(int renderer_process_id, midi::MidiService* midi_service)
: MidiHost(renderer_process_id, midi_service) {}
private:
~MidiHostForTesting() override {}
......@@ -78,11 +79,14 @@ class MidiHostTest : public testing::Test {
public:
MidiHostTest()
: io_browser_thread_(BrowserThread::IO, &message_loop_),
host_(new MidiHostForTesting(kRenderProcessId, &manager_)),
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 {
manager_.Shutdown();
service_->Shutdown();
RunLoopUntilIdle();
}
......@@ -104,15 +108,13 @@ class MidiHostTest : public testing::Test {
host_->OnMessageReceived(*message.get());
}
size_t GetEventSize() const {
return manager_.events_.size();
}
size_t GetEventSize() const { return manager_->events_.size(); }
void CheckSendEventAt(size_t at, uint32_t port) {
EXPECT_EQ(DISPATCH_SEND_MIDI_DATA, manager_.events_[at].type);
EXPECT_EQ(port, manager_.events_[at].port_index);
EXPECT_EQ(data_, manager_.events_[at].data);
EXPECT_EQ(0.0, manager_.events_[at].timestamp);
EXPECT_EQ(DISPATCH_SEND_MIDI_DATA, manager_->events_[at].type);
EXPECT_EQ(port, manager_->events_[at].port_index);
EXPECT_EQ(data_, manager_->events_[at].data);
EXPECT_EQ(0.0, manager_->events_[at].timestamp);
}
void RunLoopUntilIdle() {
......@@ -124,10 +126,11 @@ class MidiHostTest : public testing::Test {
base::MessageLoop message_loop_;
TestBrowserThread io_browser_thread_;
FakeMidiManager manager_;
scoped_refptr<MidiHostForTesting> host_;
std::vector<uint8_t> data_;
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);
};
......
......@@ -1074,7 +1074,7 @@ void RenderProcessHostImpl::CreateMessageFilters() {
browser_context->GetResourceContext()->GetMediaDeviceIDSalt());
AddFilter(audio_renderer_host_.get());
AddFilter(
new MidiHost(GetID(), BrowserMainLoop::GetInstance()->midi_manager()));
new MidiHost(GetID(), BrowserMainLoop::GetInstance()->midi_service()));
AddFilter(new AppCacheDispatcherHost(
storage_partition_impl_->GetAppCacheService(), GetID()));
AddFilter(new ClipboardMessageFilter(blob_storage_context));
......
......@@ -88,6 +88,8 @@ component("midi") {
"midi_port_info.h",
"midi_scheduler.cc",
"midi_scheduler.h",
"midi_service.cc",
"midi_service.h",
"midi_switches.cc",
"midi_switches.h",
]
......
......@@ -28,6 +28,7 @@ namespace midi {
// A MidiManagerClient registers with the MidiManager to receive MIDI data.
// See MidiManager::RequestAccess() and MidiManager::ReleaseAccess()
// for details.
// TODO(toyoshim): Consider to have a MidiServiceClient interface.
class MIDI_EXPORT MidiManagerClient {
public:
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