blob: b625d7c264edf5c9b710c8f720a628b0f1b24d7f [file] [log] [blame]
// Copyright (c) 2013 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 "content/renderer/media/midi/midi_session_client_impl.h"
#include <algorithm>
#include "base/bind.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "content/public/common/service_names.mojom.h"
#include "content/renderer/render_thread_impl.h"
#include "ipc/ipc_logging.h"
#include "services/service_manager/public/cpp/connector.h"
using base::AutoLock;
using blink::WebString;
using midi::mojom::PortState;
// The maximum number of bytes which we're allowed to send to the browser
// before getting acknowledgement back from the browser that they've been
// successfully sent.
static const size_t kMaxUnacknowledgedBytesSent = 10 * 1024 * 1024; // 10 MB.
namespace content {
MidiSessionClientImpl::MidiSessionClientImpl()
: session_result_(midi::mojom::Result::NOT_INITIALIZED),
unacknowledged_bytes_sent_(0u),
binding_(this) {}
MidiSessionClientImpl::~MidiSessionClientImpl() {}
void MidiSessionClientImpl::AddInputPort(midi::mojom::PortInfoPtr info) {
inputs_.push_back(*info);
const WebString id = WebString::FromUTF8(info->id);
const WebString manufacturer = WebString::FromUTF8(info->manufacturer);
const WebString name = WebString::FromUTF8(info->name);
const WebString version = WebString::FromUTF8(info->version);
for (auto* client : clients_)
client->DidAddInputPort(id, manufacturer, name, version, info->state);
}
void MidiSessionClientImpl::AddOutputPort(midi::mojom::PortInfoPtr info) {
outputs_.push_back(*info);
const WebString id = WebString::FromUTF8(info->id);
const WebString manufacturer = WebString::FromUTF8(info->manufacturer);
const WebString name = WebString::FromUTF8(info->name);
const WebString version = WebString::FromUTF8(info->version);
for (auto* client : clients_)
client->DidAddOutputPort(id, manufacturer, name, version, info->state);
}
void MidiSessionClientImpl::SetInputPortState(uint32_t port,
midi::mojom::PortState state) {
if (inputs_[port].state == state)
return;
inputs_[port].state = state;
for (auto* client : clients_)
client->DidSetInputPortState(port, state);
}
void MidiSessionClientImpl::SetOutputPortState(uint32_t port,
midi::mojom::PortState state) {
if (outputs_[port].state == state)
return;
outputs_[port].state = state;
for (auto* client : clients_)
client->DidSetOutputPortState(port, state);
}
void MidiSessionClientImpl::SessionStarted(midi::mojom::Result result) {
TRACE_EVENT0("midi", "MidiSessionClientImpl::OnSessionStarted");
session_result_ = result;
// A for-loop using iterators does not work because |client| may touch
// |clients_waiting_session_queue_| in callbacks.
while (!clients_waiting_session_queue_.empty()) {
auto* client = clients_waiting_session_queue_.back();
clients_waiting_session_queue_.pop_back();
if (result == midi::mojom::Result::OK) {
// Add the client's input and output ports.
for (const auto& info : inputs_) {
client->DidAddInputPort(WebString::FromUTF8(info.id),
WebString::FromUTF8(info.manufacturer),
WebString::FromUTF8(info.name),
WebString::FromUTF8(info.version), info.state);
}
for (const auto& info : outputs_) {
client->DidAddOutputPort(WebString::FromUTF8(info.id),
WebString::FromUTF8(info.manufacturer),
WebString::FromUTF8(info.name),
WebString::FromUTF8(info.version), info.state);
}
}
client->DidStartSession(result);
clients_.insert(client);
}
}
void MidiSessionClientImpl::AcknowledgeSentData(uint32_t bytes_sent) {
DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
if (unacknowledged_bytes_sent_ >= bytes_sent)
unacknowledged_bytes_sent_ -= bytes_sent;
}
void MidiSessionClientImpl::DataReceived(uint32_t port,
const std::vector<uint8_t>& data,
base::TimeTicks timestamp) {
TRACE_EVENT0("midi", "MidiSessionClientImpl::OnDataReceived");
DCHECK(!data.empty());
for (auto* client : clients_)
client->DidReceiveMIDIData(port, &data[0], data.size(), timestamp);
}
void MidiSessionClientImpl::AddClient(blink::WebMIDIAccessorClient* client) {
TRACE_EVENT0("midi", "MidiSessionClientImpl::AddClient");
clients_waiting_session_queue_.push_back(client);
if (session_result_ != midi::mojom::Result::NOT_INITIALIZED) {
SessionStarted(session_result_);
} else if (clients_waiting_session_queue_.size() == 1u) {
midi::mojom::MidiSessionClientPtr client_ptr;
binding_.Bind(mojo::MakeRequest(&client_ptr));
midi::mojom::MidiSessionRequest request = mojo::MakeRequest(&midi_session_);
GetMidiSessionProvider().StartSession(std::move(request),
std::move(client_ptr));
}
}
void MidiSessionClientImpl::RemoveClient(blink::WebMIDIAccessorClient* client) {
DCHECK(clients_.find(client) != clients_.end() ||
base::ContainsValue(clients_waiting_session_queue_, client))
<< "RemoveClient call was not ballanced with AddClient call";
clients_.erase(client);
auto it = std::find(clients_waiting_session_queue_.begin(),
clients_waiting_session_queue_.end(), client);
if (it != clients_waiting_session_queue_.end())
clients_waiting_session_queue_.erase(it);
if (clients_.empty() && clients_waiting_session_queue_.empty()) {
session_result_ = midi::mojom::Result::NOT_INITIALIZED;
inputs_.clear();
outputs_.clear();
midi_session_.reset();
binding_.Close();
}
}
void MidiSessionClientImpl::SendMidiData(uint32_t port,
const uint8_t* data,
size_t length,
base::TimeTicks timestamp) {
if ((kMaxUnacknowledgedBytesSent - unacknowledged_bytes_sent_) < length) {
// TODO(toyoshim): buffer up the data to send at a later time.
// For now we're just dropping these bytes on the floor.
return;
}
unacknowledged_bytes_sent_ += length;
std::vector<uint8_t> v(data, data + length);
GetMidiSession().SendData(port, v, timestamp);
}
midi::mojom::MidiSession& MidiSessionClientImpl::GetMidiSession() {
DCHECK(midi_session_);
return *midi_session_;
}
midi::mojom::MidiSessionProvider&
MidiSessionClientImpl::GetMidiSessionProvider() {
if (!midi_session_provider_) {
ChildThreadImpl::current()->GetConnector()->BindInterface(
mojom::kBrowserServiceName, mojo::MakeRequest(&midi_session_provider_));
}
return *midi_session_provider_;
}
} // namespace content