Home | History | Annotate | Download | only in media
      1 // Copyright (c) 2013 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 "content/renderer/media/midi_message_filter.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/debug/trace_event.h"
      9 #include "base/message_loop/message_loop_proxy.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "content/common/media/midi_messages.h"
     12 #include "content/renderer/render_thread_impl.h"
     13 #include "ipc/ipc_logging.h"
     14 
     15 using media::MidiPortInfoList;
     16 using base::AutoLock;
     17 
     18 // The maximum number of bytes which we're allowed to send to the browser
     19 // before getting acknowledgement back from the browser that they've been
     20 // successfully sent.
     21 static const size_t kMaxUnacknowledgedBytesSent = 10 * 1024 * 1024;  // 10 MB.
     22 
     23 namespace content {
     24 
     25 MidiMessageFilter::MidiMessageFilter(
     26     const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
     27     : sender_(NULL),
     28       io_message_loop_(io_message_loop),
     29       main_message_loop_(base::MessageLoopProxy::current()),
     30       next_available_id_(0),
     31       unacknowledged_bytes_sent_(0) {
     32 }
     33 
     34 MidiMessageFilter::~MidiMessageFilter() {}
     35 
     36 void MidiMessageFilter::Send(IPC::Message* message) {
     37   DCHECK(io_message_loop_->BelongsToCurrentThread());
     38   if (!sender_) {
     39     delete message;
     40   } else {
     41     sender_->Send(message);
     42   }
     43 }
     44 
     45 bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) {
     46   DCHECK(io_message_loop_->BelongsToCurrentThread());
     47   bool handled = true;
     48   IPC_BEGIN_MESSAGE_MAP(MidiMessageFilter, message)
     49     IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted)
     50     IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived)
     51     IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData)
     52     IPC_MESSAGE_UNHANDLED(handled = false)
     53   IPC_END_MESSAGE_MAP()
     54   return handled;
     55 }
     56 
     57 void MidiMessageFilter::OnFilterAdded(IPC::Sender* sender) {
     58   DCHECK(io_message_loop_->BelongsToCurrentThread());
     59   sender_ = sender;
     60 }
     61 
     62 void MidiMessageFilter::OnFilterRemoved() {
     63   DCHECK(io_message_loop_->BelongsToCurrentThread());
     64 
     65   // Once removed, a filter will not be used again.  At this time all
     66   // delegates must be notified so they release their reference.
     67   OnChannelClosing();
     68 }
     69 
     70 void MidiMessageFilter::OnChannelClosing() {
     71   DCHECK(io_message_loop_->BelongsToCurrentThread());
     72   sender_ = NULL;
     73 }
     74 
     75 void MidiMessageFilter::StartSession(blink::WebMIDIAccessorClient* client) {
     76   // Generate and keep track of a "client id" which is sent to the browser
     77   // to ask permission to talk to MIDI hardware.
     78   // This id is handed back when we receive the answer in OnAccessApproved().
     79   if (clients_.find(client) == clients_.end()) {
     80     int client_id = next_available_id_++;
     81     clients_[client] = client_id;
     82 
     83     io_message_loop_->PostTask(FROM_HERE,
     84         base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this,
     85                    client_id));
     86   }
     87 }
     88 
     89 void MidiMessageFilter::StartSessionOnIOThread(int client_id) {
     90   Send(new MidiHostMsg_StartSession(client_id));
     91 }
     92 
     93 void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) {
     94   ClientsMap::iterator i = clients_.find(client);
     95   if (i != clients_.end())
     96     clients_.erase(i);
     97 }
     98 
     99 // Received from browser.
    100 
    101 void MidiMessageFilter::OnSessionStarted(
    102     int client_id,
    103     media::MidiResult result,
    104     MidiPortInfoList inputs,
    105     MidiPortInfoList outputs) {
    106   // Handle on the main JS thread.
    107   main_message_loop_->PostTask(
    108       FROM_HERE,
    109       base::Bind(&MidiMessageFilter::HandleSessionStarted, this,
    110                  client_id, result, inputs, outputs));
    111 }
    112 
    113 void MidiMessageFilter::HandleSessionStarted(
    114     int client_id,
    115     media::MidiResult result,
    116     MidiPortInfoList inputs,
    117     MidiPortInfoList outputs) {
    118   blink::WebMIDIAccessorClient* client = GetClientFromId(client_id);
    119   if (!client)
    120     return;
    121 
    122   if (result == media::MIDI_OK) {
    123     // Add the client's input and output ports.
    124     for (size_t i = 0; i < inputs.size(); ++i) {
    125       client->didAddInputPort(
    126           base::UTF8ToUTF16(inputs[i].id),
    127           base::UTF8ToUTF16(inputs[i].manufacturer),
    128           base::UTF8ToUTF16(inputs[i].name),
    129           base::UTF8ToUTF16(inputs[i].version));
    130     }
    131 
    132     for (size_t i = 0; i < outputs.size(); ++i) {
    133       client->didAddOutputPort(
    134           base::UTF8ToUTF16(outputs[i].id),
    135           base::UTF8ToUTF16(outputs[i].manufacturer),
    136           base::UTF8ToUTF16(outputs[i].name),
    137           base::UTF8ToUTF16(outputs[i].version));
    138     }
    139   }
    140   std::string error;
    141   std::string message;
    142   switch (result) {
    143     case media::MIDI_OK:
    144       break;
    145     case media::MIDI_NOT_SUPPORTED:
    146       error = "NotSupportedError";
    147       break;
    148     case media::MIDI_INITIALIZATION_ERROR:
    149       error = "InvalidStateError";
    150       message = "Platform dependent initialization failed.";
    151       break;
    152     default:
    153       NOTREACHED();
    154       error = "InvalidStateError";
    155       message = "Unknown internal error occurred.";
    156       break;
    157   }
    158   client->didStartSession(result == media::MIDI_OK, base::UTF8ToUTF16(error),
    159                           base::UTF8ToUTF16(message));
    160 }
    161 
    162 blink::WebMIDIAccessorClient*
    163 MidiMessageFilter::GetClientFromId(int client_id) {
    164   // Iterating like this seems inefficient, but in practice there generally
    165   // will be very few clients (usually one).  Additionally, this lookup
    166   // usually happens one time during page load. So the performance hit is
    167   // negligible.
    168   for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i) {
    169     if ((*i).second == client_id)
    170       return (*i).first;
    171   }
    172   return NULL;
    173 }
    174 
    175 void MidiMessageFilter::OnDataReceived(uint32 port,
    176                                        const std::vector<uint8>& data,
    177                                        double timestamp) {
    178   TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
    179 
    180   main_message_loop_->PostTask(
    181       FROM_HERE,
    182       base::Bind(&MidiMessageFilter::HandleDataReceived, this,
    183                  port, data, timestamp));
    184 }
    185 
    186 void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
    187   DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
    188   if (unacknowledged_bytes_sent_ >= bytes_sent)
    189     unacknowledged_bytes_sent_ -= bytes_sent;
    190 }
    191 
    192 void MidiMessageFilter::HandleDataReceived(uint32 port,
    193                                            const std::vector<uint8>& data,
    194                                            double timestamp) {
    195   DCHECK(!data.empty());
    196   TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
    197 
    198   for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i)
    199     (*i).first->didReceiveMIDIData(port, &data[0], data.size(), timestamp);
    200 }
    201 
    202 void MidiMessageFilter::SendMidiData(uint32 port,
    203                                      const uint8* data,
    204                                      size_t length,
    205                                      double timestamp) {
    206   if (length > kMaxUnacknowledgedBytesSent) {
    207     // TODO(toyoshim): buffer up the data to send at a later time.
    208     // For now we're just dropping these bytes on the floor.
    209     return;
    210   }
    211 
    212   std::vector<uint8> v(data, data + length);
    213   io_message_loop_->PostTask(FROM_HERE,
    214       base::Bind(&MidiMessageFilter::SendMidiDataOnIOThread, this,
    215                  port, v, timestamp));
    216 }
    217 
    218 void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
    219                                                const std::vector<uint8>& data,
    220                                                double timestamp) {
    221   size_t n = data.size();
    222   if (n > kMaxUnacknowledgedBytesSent ||
    223       unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent ||
    224       n + unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent) {
    225     // TODO(toyoshim): buffer up the data to send at a later time.
    226     // For now we're just dropping these bytes on the floor.
    227     return;
    228   }
    229 
    230   unacknowledged_bytes_sent_ += n;
    231 
    232   // Send to the browser.
    233   Send(new MidiHostMsg_SendData(port, data, timestamp));
    234 }
    235 
    236 }  // namespace content
    237