| Index: content/renderer/media/media_stream_impl.cc
|
| diff --git a/content/renderer/media/media_stream_impl.cc b/content/renderer/media/media_stream_impl.cc
|
| index 7573e78c92a41037accf09060e1e5548fc4f6ce4..4a59710f648764d45dc7e2578156b0c4d44c89a4 100644
|
| --- a/content/renderer/media/media_stream_impl.cc
|
| +++ b/content/renderer/media/media_stream_impl.cc
|
| @@ -4,12 +4,29 @@
|
|
|
| #include "content/renderer/media/media_stream_impl.h"
|
|
|
| -#include "base/string_util.h"
|
| +#include "base/bind.h"
|
| +#include "base/logging.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| +#include "base/utf_string_conversions.h"
|
| #include "content/renderer/media/capture_video_decoder.h"
|
| +#include "content/renderer/media/media_stream_dependency_factory.h"
|
| +#include "content/renderer/media/media_stream_dispatcher.h"
|
| +#include "content/renderer/media/peer_connection_handler.h"
|
| +#include "content/renderer/media/rtc_video_decoder.h"
|
| #include "content/renderer/media/video_capture_impl_manager.h"
|
| -#include "googleurl/src/gurl.h"
|
| +#include "content/renderer/media/video_capture_module_impl.h"
|
| +#include "content/renderer/media/webrtc_audio_device_impl.h"
|
| +#include "content/renderer/p2p/ipc_network_manager.h"
|
| +#include "content/renderer/p2p/ipc_socket_factory.h"
|
| +#include "content/renderer/p2p/socket_dispatcher.h"
|
| +#include "jingle/glue/thread_wrapper.h"
|
| #include "media/base/message_loop_factory.h"
|
| -#include "media/base/pipeline.h"
|
| +#include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h"
|
| +#include "third_party/libjingle/source/talk/session/phone/dummydevicemanager.h"
|
| +#include "third_party/libjingle/source/talk/session/phone/webrtcmediaengine.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamDescriptor.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamRegistry.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaStreamSource.h"
|
|
|
| namespace {
|
|
|
| @@ -17,24 +34,195 @@ static const int kVideoCaptureWidth = 352;
|
| static const int kVideoCaptureHeight = 288;
|
| static const int kVideoCaptureFramePerSecond = 30;
|
|
|
| -static const int kStartOpenSessionId = 1;
|
| +} // namespace
|
|
|
| -// TODO(wjia): remove this string when full media stream code is checked in.
|
| -static const char kRawMediaScheme[] = "mediastream";
|
| +MediaStreamImpl::MediaStreamImpl(
|
| + MediaStreamDispatcher* media_stream_dispatcher,
|
| + content::P2PSocketDispatcher* p2p_socket_dispatcher,
|
| + VideoCaptureImplManager* vc_manager,
|
| + MediaStreamDependencyFactory* dependency_factory)
|
| + : dependency_factory_(dependency_factory),
|
| + media_stream_dispatcher_(media_stream_dispatcher),
|
| + media_engine_(NULL),
|
| + p2p_socket_dispatcher_(p2p_socket_dispatcher),
|
| + vc_manager_(vc_manager),
|
| + message_loop_proxy_(base::MessageLoopProxy::current()),
|
| + signaling_thread_(NULL),
|
| + worker_thread_(NULL),
|
| + chrome_worker_thread_("Chrome_libJingle_WorkerThread"),
|
| + request_id_counter_(0),
|
| + vcm_created_(false) {
|
| +}
|
|
|
| -} // namespace
|
| +MediaStreamImpl::~MediaStreamImpl() {
|
| + if (dependency_factory_.get())
|
| + dependency_factory_->DeletePeerConnectionFactory();
|
| +}
|
| +
|
| +WebKit::WebPeerConnectionHandler* MediaStreamImpl::CreatePeerConnectionHandler(
|
| + WebKit::WebPeerConnectionHandlerClient* client) {
|
| + if (peer_connection_handler_.get()) {
|
| + VLOG(1) << "A PeerConnection already exists";
|
| + return NULL;
|
| + }
|
| +
|
| + if (!media_engine_) {
|
| + media_engine_ = dependency_factory_->CreateWebRtcMediaEngine();
|
| + }
|
| +
|
| + if (!signaling_thread_) {
|
| + jingle_glue::JingleThreadWrapper::EnsureForCurrentThread();
|
| + jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true);
|
| + signaling_thread_ = jingle_glue::JingleThreadWrapper::current();
|
| + }
|
| +
|
| + if (!worker_thread_) {
|
| + if (!chrome_worker_thread_.IsRunning()) {
|
| + if (!chrome_worker_thread_.Start()) {
|
| + LOG(ERROR) << "Could not start worker thread";
|
| + delete media_engine_;
|
| + media_engine_ = NULL;
|
| + signaling_thread_ = NULL;
|
| + return NULL;
|
| + }
|
| + }
|
| + base::WaitableEvent event(true, false);
|
| + chrome_worker_thread_.message_loop()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&MediaStreamImpl::InitializeWorkerThread, this,
|
| + &worker_thread_, &event));
|
| + event.Wait();
|
| + DCHECK(worker_thread_);
|
| + }
|
| +
|
| + if (!dependency_factory_->PeerConnectionFactoryCreated()) {
|
| + ipc_network_manager_.reset(
|
| + new content::IpcNetworkManager(p2p_socket_dispatcher_));
|
| + ipc_socket_factory_.reset(
|
| + new content::IpcPacketSocketFactory(p2p_socket_dispatcher_));
|
| + cricket::HttpPortAllocator* port_allocator = new cricket::HttpPortAllocator(
|
| + ipc_network_manager_.get(), ipc_socket_factory_.get(),
|
| + "PeerConnection");
|
| + // TODO(mallinath): The following flags were added to solve a crash in
|
| + // HttpClient, we should probably remove them after that issue has been
|
| + // investigated.
|
| + port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
|
| + cricket::PORTALLOCATOR_DISABLE_RELAY);
|
|
|
| -MediaStreamImpl::MediaStreamImpl(VideoCaptureImplManager* vc_manager)
|
| - : vc_manager_(vc_manager) {
|
| + // TODO(mallinath): PeerConnectionFactory constructor changed in latest
|
| + // code and it no more accepts config string. Config string must be parsed
|
| + // here and set in HttpPortAllocator. Now using standard google STUN server
|
| + // address.
|
| + std::vector<talk_base::SocketAddress> stun_hosts;
|
| + stun_hosts.push_back(talk_base::SocketAddress("stun.l.google.com", 19302));
|
| + port_allocator->SetStunHosts(stun_hosts);
|
| +
|
| + if (!dependency_factory_->CreatePeerConnectionFactory(port_allocator,
|
| + media_engine_,
|
| + worker_thread_)) {
|
| + LOG(ERROR) << "Could not initialize PeerConnection factory";
|
| + return NULL;
|
| + }
|
| + }
|
| +
|
| + peer_connection_handler_.reset(new PeerConnectionHandler(
|
| + client,
|
| + this,
|
| + dependency_factory_.get(),
|
| + signaling_thread_));
|
| +
|
| + return peer_connection_handler_.get();
|
| }
|
|
|
| -MediaStreamImpl::~MediaStreamImpl() {}
|
| +void MediaStreamImpl::ClosePeerConnection() {
|
| + rtc_video_decoder_ = NULL;
|
| + media_engine_->SetVideoCaptureModule(NULL);
|
| + vcm_created_ = false;
|
| + peer_connection_handler_.reset();
|
| +}
|
| +
|
| +bool MediaStreamImpl::SetVideoCaptureModule(const std::string& label) {
|
| + if (vcm_created_)
|
| + return true;
|
| + // Set the capture device.
|
| + // TODO(grunell): Instead of using the first track, the selected track
|
| + // should be used.
|
| + int id = media_stream_dispatcher_->video_session_id(label, 0);
|
| + if (id == media_stream::StreamDeviceInfo::kNoId)
|
| + return false;
|
| + webrtc::VideoCaptureModule* vcm =
|
| + new VideoCaptureModuleImpl(id, vc_manager_.get());
|
| + vcm_created_ = true;
|
| + media_engine_->SetVideoCaptureModule(vcm);
|
| + return true;
|
| +}
|
| +
|
| +void MediaStreamImpl::userMediaControllerDestroyed() {
|
| + // TODO(grunell): Implement.
|
| + NOTIMPLEMENTED();
|
| +}
|
| +
|
| +void MediaStreamImpl::requestUserMedia(
|
| + const WebKit::WebUserMediaRequest& user_media_request) {
|
| + DCHECK(!user_media_request.isNull());
|
| +
|
| + int request_id = ++request_id_counter_;
|
| +
|
| + bool audio = user_media_request.audio();
|
| + media_stream::StreamOptions::VideoOption video_option =
|
| + media_stream::StreamOptions::kNoCamera;
|
| + if ((user_media_request.cameraPreferenceUser())
|
| + && (user_media_request.cameraPreferenceEnvironment())) {
|
| + video_option = media_stream::StreamOptions::kFacingBoth;
|
| + } else {
|
| + if (user_media_request.cameraPreferenceEnvironment())
|
| + video_option = media_stream::StreamOptions::kFacingEnvironment;
|
| + if (user_media_request.cameraPreferenceUser())
|
| + video_option = media_stream::StreamOptions::kFacingUser;
|
| + }
|
| +
|
| + std::string security_origin = UTF16ToUTF8(
|
| + user_media_request.securityOrigin().toString());
|
| +
|
| + DVLOG(1) << "MediaStreamImpl::generateStream(" << request_id << ", [ "
|
| + << (audio ? "audio " : "")
|
| + << ((user_media_request.cameraPreferenceUser()) ?
|
| + "video_facing_user " : "")
|
| + << ((user_media_request.cameraPreferenceEnvironment()) ?
|
| + "video_facing_environment " : "") << "], "
|
| + << security_origin << ")";
|
| +
|
| + user_media_requests_.insert(
|
| + std::pair<int, WebKit::WebUserMediaRequest>(
|
| + request_id, user_media_request));
|
| +
|
| + media_stream_dispatcher_->GenerateStream(
|
| + request_id,
|
| + this,
|
| + media_stream::StreamOptions(audio, video_option),
|
| + security_origin);
|
| +}
|
| +
|
| +void MediaStreamImpl::cancelUserMediaRequest(
|
| + const WebKit::WebUserMediaRequest& user_media_request) {
|
| + // TODO(grunell): Implement.
|
| + NOTIMPLEMENTED();
|
| +}
|
|
|
| scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder(
|
| - const GURL& url, media::MessageLoopFactory* message_loop_factory) {
|
| - bool raw_media = (url.spec().find(kRawMediaScheme) == 0);
|
| - media::VideoDecoder* decoder = NULL;
|
| - if (raw_media) {
|
| + const GURL& url,
|
| + media::MessageLoopFactory* message_loop_factory) {
|
| + WebKit::WebMediaStreamDescriptor descriptor(
|
| + WebKit::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url));
|
| + if (descriptor.isNull())
|
| + return NULL; // This is not a valid stream.
|
| + std::string label(UTF16ToUTF8(descriptor.label()));
|
| + DCHECK(!label.empty());
|
| +
|
| + scoped_refptr<media::VideoDecoder> decoder;
|
| + if (media_stream_dispatcher_->IsStream(label)) {
|
| + // It's a local stream.
|
| + int video_session_id = media_stream_dispatcher_->video_session_id(label, 0);
|
| media::VideoCapture::VideoCaptureCapability capability;
|
| capability.width = kVideoCaptureWidth;
|
| capability.height = kVideoCaptureHeight;
|
| @@ -42,10 +230,95 @@ scoped_refptr<media::VideoDecoder> MediaStreamImpl::GetVideoDecoder(
|
| capability.expected_capture_delay = 0;
|
| capability.raw_type = media::VideoFrame::I420;
|
| capability.interlaced = false;
|
| -
|
| decoder = new CaptureVideoDecoder(
|
| - message_loop_factory->GetMessageLoopProxy("CaptureVideoDecoder").get(),
|
| - kStartOpenSessionId, vc_manager_.get(), capability);
|
| + message_loop_factory->GetMessageLoopProxy("CaptureVideoDecoderThread"),
|
| + video_session_id,
|
| + vc_manager_.get(),
|
| + capability);
|
| + } else {
|
| + // It's a remote stream.
|
| + size_t found = label.rfind("-remote");
|
| + if (found != std::string::npos)
|
| + label = label.substr(0, found);
|
| + if (rtc_video_decoder_.get()) {
|
| + // The renderer is used by PeerConnection, release it first.
|
| + if (peer_connection_handler_.get())
|
| + peer_connection_handler_->SetVideoRenderer(label, NULL);
|
| +
|
| + }
|
| + rtc_video_decoder_ = new RTCVideoDecoder(
|
| + message_loop_factory->GetMessageLoop("RtcVideoDecoderThread"),
|
| + url.spec());
|
| + decoder = rtc_video_decoder_;
|
| + if (peer_connection_handler_.get())
|
| + peer_connection_handler_->SetVideoRenderer(label, rtc_video_decoder_);
|
| }
|
| return decoder;
|
| }
|
| +
|
| +void MediaStreamImpl::OnStreamGenerated(
|
| + int request_id,
|
| + const std::string& label,
|
| + const media_stream::StreamDeviceInfoArray& audio_array,
|
| + const media_stream::StreamDeviceInfoArray& video_array) {
|
| + WebKit::WebVector<WebKit::WebMediaStreamSource> source_vector(
|
| + audio_array.size() + video_array.size());
|
| +
|
| + WebKit::WebString track_id(WebKit::WebString::fromUTF8(""));
|
| + WebKit::WebString track_label_audio(
|
| + WebKit::WebString::fromUTF8("AudioDevice"));
|
| + WebKit::WebString track_label_video(
|
| + WebKit::WebString::fromUTF8("VideoCapture"));
|
| +
|
| + size_t track_num = source_vector.size();
|
| + while (track_num--) {
|
| + if (track_num < audio_array.size()) {
|
| + source_vector[track_num].initialize(
|
| + track_id,
|
| + WebKit::WebMediaStreamSource::TypeAudio,
|
| + track_label_audio);
|
| + } else {
|
| + source_vector[track_num].initialize(
|
| + track_id,
|
| + WebKit::WebMediaStreamSource::TypeVideo,
|
| + track_label_video);
|
| + }
|
| + }
|
| +
|
| + MediaRequestMap::iterator it = user_media_requests_.find(request_id);
|
| + WebKit::WebUserMediaRequest user_media_request = it->second;
|
| + user_media_requests_.erase(it);
|
| +
|
| + user_media_request.requestSucceeded(UTF8ToUTF16(label), source_vector);
|
| +}
|
| +
|
| +void MediaStreamImpl::OnStreamGenerationFailed(int request_id) {
|
| + DVLOG(1) << "MediaStreamImpl::OnStreamGenerationFailed("
|
| + << request_id << ")";
|
| + // TODO(grunell): Implement when available in WebKit.
|
| + NOTIMPLEMENTED();
|
| +}
|
| +
|
| +void MediaStreamImpl::OnVideoDeviceFailed(const std::string& label,
|
| + int index) {
|
| + DVLOG(1) << "MediaStreamImpl::OnVideoDeviceFailed("
|
| + << label << ", " << index << ")";
|
| + // TODO(grunell): Implement when available in WebKit.
|
| + NOTIMPLEMENTED();
|
| +}
|
| +
|
| +void MediaStreamImpl::OnAudioDeviceFailed(const std::string& label,
|
| + int index) {
|
| + DVLOG(1) << "MediaStreamImpl::OnAudioDeviceFailed("
|
| + << label << ", " << index << ")";
|
| + // TODO(grunell): Implement when available in WebKit.
|
| + NOTIMPLEMENTED();
|
| +}
|
| +
|
| +void MediaStreamImpl::InitializeWorkerThread(talk_base::Thread** thread,
|
| + base::WaitableEvent* event) {
|
| + jingle_glue::JingleThreadWrapper::EnsureForCurrentThread();
|
| + jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true);
|
| + *thread = jingle_glue::JingleThreadWrapper::current();
|
| + event->Signal();
|
| +}
|
|
|