root/content/renderer/media/media_stream_dispatcher.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. RemoveStreamDeviceFromArray
  2. next_ipc_id_
  3. GenerateStream
  4. CancelGenerateStream
  5. StopStreamDevice
  6. EnumerateDevices
  7. StopEnumerateDevices
  8. OpenDevice
  9. CancelOpenDevice
  10. CloseDevice
  11. Send
  12. OnMessageReceived
  13. OnStreamGenerated
  14. OnStreamGenerationFailed
  15. OnDeviceStopped
  16. OnDevicesEnumerated
  17. OnDeviceOpened
  18. OnDeviceOpenFailed
  19. audio_session_id
  20. IsStream
  21. video_session_id

// Copyright (c) 2012 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/media_stream_dispatcher.h"

#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "content/common/media/media_stream_messages.h"
#include "content/renderer/media/media_stream_dispatcher_eventhandler.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/render_view_impl.h"
#include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
#include "url/gurl.h"

namespace content {

namespace {

bool RemoveStreamDeviceFromArray(const StreamDeviceInfo device_info,
                                 StreamDeviceInfoArray* array) {
  for (StreamDeviceInfoArray::iterator device_it = array->begin();
       device_it != array->end(); ++device_it) {
    if (StreamDeviceInfo::IsEqual(*device_it, device_info)) {
      array->erase(device_it);
      return true;
    }
  }
  return false;
}

}  // namespace

// A request is identified by pair (request_id, handler), or ipc_request.
// There could be multiple clients making requests and each has its own
// request_id sequence.
// The ipc_request is garanteed to be unique when it's created in
// MediaStreamDispatcher.
struct MediaStreamDispatcher::Request {
  Request(const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler,
          int request_id,
          int ipc_request)
      : handler(handler),
        request_id(request_id),
        ipc_request(ipc_request) {
  }
  bool IsThisRequest(
      int request_id1,
      const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler1) {
    return (request_id1 == request_id && handler1.get() == handler.get());
  }
  base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
  int request_id;
  int ipc_request;
};

struct MediaStreamDispatcher::Stream {
  Stream() {}
  ~Stream() {}
  base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
  StreamDeviceInfoArray audio_array;
  StreamDeviceInfoArray video_array;
};

MediaStreamDispatcher::MediaStreamDispatcher(RenderViewImpl* render_view)
    : RenderViewObserver(render_view),
      main_loop_(base::MessageLoopProxy::current()),
      next_ipc_id_(0) {
}

MediaStreamDispatcher::~MediaStreamDispatcher() {}

void MediaStreamDispatcher::GenerateStream(
    int request_id,
    const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
    const StreamOptions& components,
    const GURL& security_origin) {
  DCHECK(main_loop_->BelongsToCurrentThread());
  DVLOG(1) << "MediaStreamDispatcher::GenerateStream(" << request_id << ")";

  requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
  Send(new MediaStreamHostMsg_GenerateStream(
      routing_id(), next_ipc_id_++, components, security_origin,
      blink::WebUserGestureIndicator::isProcessingUserGesture()));
}

void MediaStreamDispatcher::CancelGenerateStream(
    int request_id,
    const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
  DCHECK(main_loop_->BelongsToCurrentThread());
  DVLOG(1) << "MediaStreamDispatcher::CancelGenerateStream"
           << ", {request_id = " << request_id << "}";

  RequestList::iterator it = requests_.begin();
  for (; it != requests_.end(); ++it) {
    if (it->IsThisRequest(request_id, event_handler)) {
      int ipc_request = it->ipc_request;
      requests_.erase(it);
      Send(new MediaStreamHostMsg_CancelGenerateStream(routing_id(),
                                                       ipc_request));
      break;
    }
  }
}

void MediaStreamDispatcher::StopStreamDevice(
    const StreamDeviceInfo& device_info) {
  DVLOG(1) << "MediaStreamDispatcher::StopStreamDevice"
           << ", {device_id = " << device_info.device.id << "}";
  // Remove |device_info| from all streams in |label_stream_map_|.
  bool device_found = false;
  LabelStreamMap::iterator stream_it = label_stream_map_.begin();
  while (stream_it != label_stream_map_.end()) {
    StreamDeviceInfoArray& audio_array = stream_it->second.audio_array;
    StreamDeviceInfoArray& video_array = stream_it->second.video_array;

    if (RemoveStreamDeviceFromArray(device_info, &audio_array) ||
        RemoveStreamDeviceFromArray(device_info, &video_array)) {
      device_found = true;
      if (audio_array.empty() && video_array.empty()) {
        label_stream_map_.erase(stream_it++);
        continue;
      }
    }
    ++stream_it;
  }
  DCHECK(device_found);

  Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(),
                                               device_info.device.id));
}

void MediaStreamDispatcher::EnumerateDevices(
    int request_id,
    const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
    MediaStreamType type,
    const GURL& security_origin) {
  DCHECK(main_loop_->BelongsToCurrentThread());
  DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
         type == MEDIA_DEVICE_VIDEO_CAPTURE);
  DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices("
           << request_id << ")";

  for (RequestList::iterator it = requests_.begin(); it != requests_.end();
       ++it) {
    DCHECK(!it->IsThisRequest(request_id, event_handler));
  }

  requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
  Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(),
                                               next_ipc_id_++,
                                               type,
                                               security_origin));
}

void MediaStreamDispatcher::StopEnumerateDevices(
    int request_id,
    const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
  DCHECK(main_loop_->BelongsToCurrentThread());
  DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices("
           << request_id << ")";
  for (RequestList::iterator it = requests_.begin(); it != requests_.end();
       ++it) {
    if (it->IsThisRequest(request_id, event_handler)) {
      Send(new MediaStreamHostMsg_CancelEnumerateDevices(routing_id(),
                                                         it->ipc_request));
      requests_.erase(it);
      break;
    }
  }
}

void MediaStreamDispatcher::OpenDevice(
    int request_id,
    const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
    const std::string& device_id,
    MediaStreamType type,
    const GURL& security_origin) {
  DCHECK(main_loop_->BelongsToCurrentThread());
  DVLOG(1) << "MediaStreamDispatcher::OpenDevice(" << request_id << ")";

  requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
  Send(new MediaStreamHostMsg_OpenDevice(routing_id(),
                                         next_ipc_id_++,
                                         device_id,
                                         type,
                                         security_origin));
}

void MediaStreamDispatcher::CancelOpenDevice(
    int request_id,
    const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
  CancelGenerateStream(request_id, event_handler);
}

void MediaStreamDispatcher::CloseDevice(const std::string& label) {
  DCHECK(main_loop_->BelongsToCurrentThread());
  DCHECK(!label.empty());
  DVLOG(1) << "MediaStreamDispatcher::CloseDevice"
           << ", {label = " << label << "}";

  LabelStreamMap::iterator it = label_stream_map_.find(label);
  if (it == label_stream_map_.end())
    return;
  label_stream_map_.erase(it);

  Send(new MediaStreamHostMsg_CloseDevice(routing_id(), label));
}

bool MediaStreamDispatcher::Send(IPC::Message* message) {
  if (!RenderThread::Get()) {
    delete message;
    return false;
  }

  return RenderThread::Get()->Send(message);
}

bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message)
    IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated,
                        OnStreamGenerated)
    IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed,
                        OnStreamGenerationFailed)
    IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped,
                        OnDeviceStopped)
    IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated,
                        OnDevicesEnumerated)
    IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened,
                        OnDeviceOpened)
    IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed,
                        OnDeviceOpenFailed)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void MediaStreamDispatcher::OnStreamGenerated(
    int request_id,
    const std::string& label,
    const StreamDeviceInfoArray& audio_array,
    const StreamDeviceInfoArray& video_array) {
  DCHECK(main_loop_->BelongsToCurrentThread());

  for (RequestList::iterator it = requests_.begin();
       it != requests_.end(); ++it) {
    Request& request = *it;
    if (request.ipc_request == request_id) {
      Stream new_stream;
      new_stream.handler = request.handler;
      new_stream.audio_array = audio_array;
      new_stream.video_array = video_array;
      label_stream_map_[label] = new_stream;
      if (request.handler.get()) {
        request.handler->OnStreamGenerated(
            request.request_id, label, audio_array, video_array);
        DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated("
                 << request.request_id << ", " << label << ")";
      }
      requests_.erase(it);
      break;
    }
  }
}

void MediaStreamDispatcher::OnStreamGenerationFailed(
    int request_id,
    content::MediaStreamRequestResult result) {
  DCHECK(main_loop_->BelongsToCurrentThread());
  for (RequestList::iterator it = requests_.begin();
       it != requests_.end(); ++it) {
    Request& request = *it;
    if (request.ipc_request == request_id) {
      if (request.handler.get()) {
        request.handler->OnStreamGenerationFailed(request.request_id, result);
        DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed("
                 << request.request_id << ")\n";
      }
      requests_.erase(it);
      break;
    }
  }
}

void MediaStreamDispatcher::OnDeviceStopped(
    const std::string& label,
    const StreamDeviceInfo& device_info) {
  DCHECK(main_loop_->BelongsToCurrentThread());
  DVLOG(1) << "MediaStreamDispatcher::OnDeviceStopped("
           << "{label = " << label << "})"
           << ", {device_id = " << device_info.device.id << "})";

  LabelStreamMap::iterator it = label_stream_map_.find(label);
  if (it == label_stream_map_.end()) {
    // This can happen if a user happen stop a the device from JS at the same
    // time as the underlying media device is unplugged from the system.
    return;
  }
  Stream* stream = &it->second;
  if (IsAudioMediaType(device_info.device.type))
    RemoveStreamDeviceFromArray(device_info, &stream->audio_array);
  else
    RemoveStreamDeviceFromArray(device_info, &stream->video_array);

  if (stream->handler.get())
    stream->handler->OnDeviceStopped(label, device_info);

  if (stream->audio_array.empty() && stream->video_array.empty())
    label_stream_map_.erase(it);
}

void MediaStreamDispatcher::OnDevicesEnumerated(
    int request_id,
    const StreamDeviceInfoArray& device_array) {
  DCHECK(main_loop_->BelongsToCurrentThread());
  DCHECK_GE(request_id, 0);

  for (RequestList::iterator it = requests_.begin(); it != requests_.end();
       ++it) {
    if (it->ipc_request == request_id && it->handler.get()) {
      it->handler->OnDevicesEnumerated(it->request_id, device_array);
      break;
    }
  }
}

void MediaStreamDispatcher::OnDeviceOpened(
    int request_id,
    const std::string& label,
    const StreamDeviceInfo& device_info) {
  DCHECK(main_loop_->BelongsToCurrentThread());
  for (RequestList::iterator it = requests_.begin();
       it != requests_.end(); ++it) {
    Request& request = *it;
    if (request.ipc_request == request_id) {
      Stream new_stream;
      new_stream.handler = request.handler;
      if (IsAudioMediaType(device_info.device.type)) {
        new_stream.audio_array.push_back(device_info);
      } else if (IsVideoMediaType(device_info.device.type)) {
        new_stream.video_array.push_back(device_info);
      } else {
        NOTREACHED();
      }
      label_stream_map_[label] = new_stream;
      if (request.handler.get()) {
        request.handler->OnDeviceOpened(request.request_id, label, device_info);
        DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened("
                 << request.request_id << ", " << label << ")";
      }
      requests_.erase(it);
      break;
    }
  }
}

void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id) {
  DCHECK(main_loop_->BelongsToCurrentThread());
  for (RequestList::iterator it = requests_.begin();
       it != requests_.end(); ++it) {
    Request& request = *it;
    if (request.ipc_request == request_id) {
      if (request.handler.get()) {
        request.handler->OnDeviceOpenFailed(request.request_id);
        DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed("
                 << request.request_id << ")\n";
      }
      requests_.erase(it);
      break;
    }
  }
}

int MediaStreamDispatcher::audio_session_id(const std::string& label,
                                            int index) {
  LabelStreamMap::iterator it = label_stream_map_.find(label);
  if (it == label_stream_map_.end() ||
      it->second.audio_array.size() <= static_cast<size_t>(index)) {
    return StreamDeviceInfo::kNoId;
  }
  return it->second.audio_array[index].session_id;
}

bool MediaStreamDispatcher::IsStream(const std::string& label) {
  return label_stream_map_.find(label) != label_stream_map_.end();
}

int MediaStreamDispatcher::video_session_id(const std::string& label,
                                            int index) {
  LabelStreamMap::iterator it = label_stream_map_.find(label);
  if (it == label_stream_map_.end() ||
      it->second.video_array.size() <= static_cast<size_t>(index)) {
    return StreamDeviceInfo::kNoId;
  }
  return it->second.video_array[index].session_id;
}

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */