2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "MediaStreamFrameController.h"
28 #if ENABLE(MEDIA_STREAM)
30 #include "DOMWindow.h"
33 #include "LocalMediaStream.h"
34 #include "MediaStreamController.h"
35 #include "MediaStreamTrackList.h"
36 #include "NavigatorUserMediaErrorCallback.h"
37 #include "NavigatorUserMediaSuccessCallback.h"
39 #include "PeerConnection.h"
40 #include "SecurityOrigin.h"
41 #include "SignalingCallback.h"
42 #include <wtf/RefCounted.h>
46 class MediaStreamFrameController::Request : public RefCounted<Request> {
47 WTF_MAKE_NONCOPYABLE(Request);
49 virtual ~Request() { }
51 ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext; }
52 virtual bool isGenerateStreamRequest() const { return false; }
53 virtual bool isRecordedDataRequest() const { return false; }
54 virtual bool isSignalingRequest() const { return false; }
56 virtual void abort() = 0;
59 Request(ScriptExecutionContext* scriptExecutionContext)
60 : m_scriptExecutionContext(scriptExecutionContext) { }
63 // This is guaranteed to have the lifetime of the Frame, and it's only used to make
64 // the callback asynchronous. The original callback context is used in the call.
65 ScriptExecutionContext* m_scriptExecutionContext;
68 class MediaStreamFrameController::GenerateStreamRequest : public Request {
69 WTF_MAKE_NONCOPYABLE(GenerateStreamRequest);
71 static PassRefPtr<GenerateStreamRequest> create(ScriptExecutionContext* scriptExecutionContext,
72 PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback,
73 PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback)
75 return adoptRef(new GenerateStreamRequest(scriptExecutionContext, successCallback, errorCallback));
78 virtual ~GenerateStreamRequest() { }
80 virtual bool isGenerateStreamRequest() const { return true; }
84 if (m_errorCallback) {
85 RefPtr<NavigatorUserMediaError> error = NavigatorUserMediaError::create(NavigatorUserMediaError::PERMISSION_DENIED);
86 // The callback itself is made with the JS callback's context, not with the frame's context.
87 m_errorCallback->scheduleCallback(scriptExecutionContext(), error);
91 PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback() const { return m_successCallback; }
92 PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback() const { return m_errorCallback; }
95 GenerateStreamRequest(ScriptExecutionContext* scriptExecutionContext,
96 PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback,
97 PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback)
98 : Request(scriptExecutionContext)
99 , m_successCallback(successCallback)
100 , m_errorCallback(errorCallback) { }
102 RefPtr<NavigatorUserMediaSuccessCallback> m_successCallback;
103 RefPtr<NavigatorUserMediaErrorCallback> m_errorCallback;
106 void MediaStreamFrameController::RequestMap::abort(int requestId)
108 get(requestId)->abort();
112 void MediaStreamFrameController::RequestMap::abortAll()
115 begin()->second->abort();
120 template <typename T>
121 void MediaStreamFrameController::ClientMapBase<T>::unregisterAll()
123 while (!this->isEmpty()) {
124 T key = this->begin()->first;
125 // unregister should remove the element from the map.
126 this->begin()->second->unregister();
127 ASSERT_UNUSED(key, !this->contains(key));
131 template <typename T>
132 void MediaStreamFrameController::ClientMapBase<T>::detachEmbedder()
134 for (typename MapType::iterator it = this->begin(); it != this->end(); ++it)
135 it->second->detachEmbedder();
138 MediaStreamFrameController::MediaStreamFrameController(Frame* frame)
140 , m_isInDetachedState(false)
142 if (!isClientAvailable())
143 enterDetachedState();
146 MediaStreamFrameController::~MediaStreamFrameController()
150 SecurityOrigin* MediaStreamFrameController::securityOrigin() const
152 return m_frame ? m_frame->existingDOMWindow()->securityOrigin() : 0;
155 ScriptExecutionContext* MediaStreamFrameController::scriptExecutionContext() const
157 return m_frame ? m_frame->existingDOMWindow()->scriptExecutionContext() : 0;
160 MediaStreamController* MediaStreamFrameController::pageController() const
162 return m_frame && m_frame->page() ? m_frame->page()->mediaStreamController() : 0;
165 void MediaStreamFrameController::unregister(MediaStreamClient* client)
167 ASSERT(m_streams.contains(client->clientId()));
169 // Assuming we should stop any live streams when losing access to the embedder.
170 if (client->isLocalMediaStream()) {
171 LocalMediaStream* stream = static_cast<LocalMediaStream*>(client);
172 if (stream->readyState() == MediaStream::LIVE)
173 stopGeneratedStream(stream->label());
176 m_streams.remove(client->clientId());
179 void MediaStreamFrameController::unregister(GenericClient* client)
181 ASSERT(m_clients.contains(client->clientId()));
182 m_clients.remove(client->clientId());
185 MediaStream* MediaStreamFrameController::getStreamFromLabel(const String& label) const
187 ASSERT(m_streams.contains(label));
188 ASSERT(m_streams.get(label)->isMediaStream());
189 return static_cast<MediaStream*>(m_streams.get(label));
192 void MediaStreamFrameController::enterDetachedState()
194 if (m_isInDetachedState) {
195 ASSERT(m_requests.isEmpty());
199 m_requests.abortAll();
200 m_streams.detachEmbedder();
201 m_clients.detachEmbedder();
202 m_isInDetachedState = true;
205 bool MediaStreamFrameController::isClientAvailable() const
207 if (m_isInDetachedState)
210 MediaStreamController* controller = pageController();
211 return controller && controller->isClientAvailable();
214 // Called also when the frame is detached from the page, in which case the page controller will remain alive.
215 void MediaStreamFrameController::disconnectPage()
217 if (pageController())
218 pageController()->unregisterFrameController(this);
220 enterDetachedState();
223 // Called when the frame is being destroyed. Since the frame controller is owned by the frame it will die shortly after this.
224 void MediaStreamFrameController::disconnectFrame()
228 ASSERT(m_requests.isEmpty());
229 m_streams.unregisterAll();
230 m_clients.unregisterAll();
235 void MediaStreamFrameController::transferToNewPage(Page*)
237 // FIXME: In the future we should keep running the media stream services while transfering frames between pages.
238 // However, until a proper way to do this is decided, we're shutting down services.
242 GenerateStreamOptionFlags MediaStreamFrameController::parseGenerateStreamOptions(const String& options)
244 GenerateStreamOptionFlags flags = 0;
245 Vector<String> optionList;
246 options.split(',', optionList);
248 for (Vector<String>::const_iterator option = optionList.begin(); option != optionList.end(); ++option) {
249 Vector<String> suboptionList;
250 option->split(' ', suboptionList);
252 if (suboptionList.first() == "audio")
253 flags |= GenerateStreamRequestAudio;
254 else if (suboptionList.first() == "video") {
255 bool videoSuboptions = false;
256 Vector<String>::const_iterator suboption = suboptionList.begin();
257 for (++suboption; suboption != suboptionList.end(); ++suboption)
258 if (*suboption == "user") {
259 flags |= GenerateStreamRequestVideoFacingUser;
260 videoSuboptions = true;
261 } else if (*suboption == "environment") {
262 flags |= GenerateStreamRequestVideoFacingEnvironment;
263 videoSuboptions = true;
266 // Ask for all kind of cameras if no suboption was specified.
267 if (!videoSuboptions)
268 flags |= GenerateStreamRequestVideoFacingUser | GenerateStreamRequestVideoFacingEnvironment;
275 // Implements the getUserMedia method from http://www.whatwg.org/specs/web-apps/current-work/#dom-navigator-getusermedia.
276 void MediaStreamFrameController::generateStream(const String& options,
277 PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback,
278 PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback,
282 if (!successCallback)
285 GenerateStreamOptionFlags flags = parseGenerateStreamOptions(options);
287 ec = NOT_SUPPORTED_ERR;
291 int requestId = m_requests.getNextId();
292 m_requests.add(requestId, GenerateStreamRequest::create(scriptExecutionContext(), successCallback, errorCallback));
294 if (!isClientAvailable()) {
295 // This makes sure to call the error callback if provided.
296 m_requests.abort(requestId);
300 pageController()->generateStream(this, requestId, flags, securityOrigin());
303 void MediaStreamFrameController::stopGeneratedStream(const String& streamLabel)
305 ASSERT(m_streams.contains(streamLabel));
306 ASSERT(m_streams.get(streamLabel)->isLocalMediaStream());
308 if (isClientAvailable())
309 pageController()->stopGeneratedStream(streamLabel);
312 void MediaStreamFrameController::setMediaStreamTrackEnabled(const String& trackId, bool enabled)
314 if (isClientAvailable())
315 pageController()->setMediaStreamTrackEnabled(trackId, enabled);
318 void MediaStreamFrameController::streamGenerated(int requestId, const String& label, PassRefPtr<MediaStreamTrackList> tracksParam)
320 // Don't assert since the request can have been aborted as a result of embedder detachment.
321 if (!m_requests.contains(requestId))
324 ASSERT(m_requests.get(requestId)->isGenerateStreamRequest());
325 ASSERT(!label.isNull());
328 RefPtr<MediaStreamTrackList> tracks = tracksParam;
330 for (unsigned i = 0; i < tracks->length(); ++i) {
331 int trackClientId = m_clients.getNextId();
332 RefPtr<MediaStreamTrack> track = tracks->item(i);
333 track->associateFrameController(this, trackClientId);
334 m_clients.add(trackClientId, track.get());
337 RefPtr<GenerateStreamRequest> streamRequest = static_cast<GenerateStreamRequest*>(m_requests.get(requestId).get());
338 RefPtr<LocalMediaStream> generatedStream = LocalMediaStream::create(this, label, tracks.release());
339 m_streams.add(label, generatedStream.get());
340 m_requests.remove(requestId);
341 streamRequest->successCallback()->handleEvent(generatedStream.get());
344 void MediaStreamFrameController::streamGenerationFailed(int requestId, NavigatorUserMediaError::ErrorCode code)
346 // Don't assert since the request can have been aborted as a result of embedder detachment.
347 if (!m_requests.contains(requestId))
350 ASSERT(m_requests.get(requestId)->isGenerateStreamRequest());
352 RefPtr<GenerateStreamRequest> streamRequest = static_cast<GenerateStreamRequest*>(m_requests.get(requestId).get());
353 m_requests.remove(requestId);
355 if (streamRequest->errorCallback()) {
356 RefPtr<NavigatorUserMediaError> error = NavigatorUserMediaError::create(code);
357 streamRequest->errorCallback()->handleEvent(error.get());
361 void MediaStreamFrameController::streamFailed(const String& label)
363 getStreamFromLabel(label)->streamEnded();
366 PassRefPtr<PeerConnection> MediaStreamFrameController::createPeerConnection(const String& configuration, PassRefPtr<SignalingCallback> signalingCallback)
368 int clientId = m_clients.getNextId();
369 RefPtr<PeerConnection> peerConnection = PeerConnection::create(this, clientId, configuration, signalingCallback);
371 m_clients.add(clientId, peerConnection.get());
372 return peerConnection.release();
375 void MediaStreamFrameController::newPeerConnection(int peerConnectionId, const String& configuration)
377 if (pageController())
378 pageController()->newPeerConnection(this, peerConnectionId, configuration);
381 void MediaStreamFrameController::closePeerConnection(int peerConnectionId)
383 if (pageController())
384 pageController()->closePeerConnection(this, peerConnectionId);
387 void MediaStreamFrameController::startNegotiation(int peerConnectionId)
389 if (pageController())
390 pageController()->startNegotiation(this, peerConnectionId);
393 void MediaStreamFrameController::processSignalingMessage(int peerConnectionId, const String& message)
395 if (pageController())
396 pageController()->processSignalingMessage(this, peerConnectionId, message);
399 void MediaStreamFrameController::message(int peerConnectionId, const String& message)
401 if (pageController())
402 pageController()->message(this, peerConnectionId, message);
405 void MediaStreamFrameController::addStream(int peerConnectionId, PassRefPtr<MediaStream> prpStream)
407 RefPtr<MediaStream> stream = prpStream;
408 ASSERT(m_streams.contains(stream->label()));
410 if (pageController())
411 pageController()->addStream(this, peerConnectionId, stream->label());
414 void MediaStreamFrameController::removeStream(int peerConnectionId, PassRefPtr<MediaStream> prpStream)
416 RefPtr<MediaStream> stream = prpStream;
417 ASSERT(m_streams.contains(stream->label()));
419 if (pageController())
420 pageController()->removeStream(this, peerConnectionId, stream->label());
423 void MediaStreamFrameController::onMessage(int peerConnectionId, const String& message)
425 ASSERT(m_clients.contains(peerConnectionId));
426 ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
428 RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
430 peerConnection->onMessage(message);
433 void MediaStreamFrameController::onAddStream(int peerConnectionId, const String& streamLabel, PassRefPtr<MediaStreamTrackList> tracks)
435 ASSERT(!streamLabel.isEmpty());
436 ASSERT(m_clients.contains(peerConnectionId));
437 ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
439 RefPtr<MediaStream> stream = MediaStream::create(this, streamLabel, tracks);
441 RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
442 peerConnection->streamAdded(stream.release());
445 void MediaStreamFrameController::onRemoveStream(int peerConnectionId, const String& streamLabel)
447 ASSERT(!streamLabel.isEmpty());
448 ASSERT(m_clients.contains(peerConnectionId));
449 ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
451 RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
452 peerConnection->streamRemoved(streamLabel);
455 void MediaStreamFrameController::onSignalingMessage(int peerConnectionId, const String& message)
457 ASSERT(m_clients.contains(peerConnectionId));
458 ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
460 RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
461 peerConnection->onSignalingMessage(message);
464 void MediaStreamFrameController::onNegotiationStarted(int peerConnectionId)
466 // FIXME: Assert or not?
467 ASSERT(m_clients.contains(peerConnectionId));
468 ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
470 RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
472 peerConnection->onNegotiationStarted();
475 void MediaStreamFrameController::onNegotiationDone(int peerConnectionId)
477 // FIXME: Assert or not?
478 ASSERT(m_clients.contains(peerConnectionId));
479 ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
481 RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
483 peerConnection->onNegotiationDone();
486 } // namespace WebCore
488 #endif // ENABLE(MEDIA_STREAM)