Bug 866514. Part 2: Delay delivering getUserMedia stream result until the DOM object has asynchronously acquired the desired tracks. r=jesup

This commit is contained in:
Robert O'Callahan 2013-05-03 17:07:37 +12:00
Родитель 6d42b1300e
Коммит 95251bed50
2 изменённых файлов: 83 добавлений и 27 удалений

Просмотреть файл

@ -326,6 +326,41 @@ public:
~GetUserMediaStreamRunnable() {}
class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback
{
public:
TracksAvailableCallback(MediaManager* aManager,
nsIDOMGetUserMediaSuccessCallback* aSuccess,
uint64_t aWindowID,
DOMMediaStream* aStream)
: mWindowID(aWindowID), mSuccess(aSuccess), mManager(aManager),
mStream(aStream) {}
virtual void NotifyTracksAvailable(DOMMediaStream* aStream) MOZ_OVERRIDE
{
// We're in the main thread, so no worries here.
if (!(mManager->IsWindowStillActive(mWindowID))) {
return;
}
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
LOG(("Returning success for getUserMedia()"));
mSuccess->OnSuccess(aStream);
}
uint64_t mWindowID;
nsRefPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
nsRefPtr<MediaManager> mManager;
// Keep the DOMMediaStream alive until the NotifyTracksAvailable callback
// has fired, otherwise we might immediately destroy the DOMMediaStream and
// shut down the underlying MediaStream prematurely.
// This creates a cycle which is broken when NotifyTracksAvailable
// is fired (which will happen unless the browser shuts down,
// since we only add this callback when we've successfully appended
// the desired tracks in the MediaStreamGraph) or when
// DOMMediaStream::NotifyMediaStreamGraphShutdown is called.
nsRefPtr<DOMMediaStream> mStream;
};
NS_IMETHOD
Run()
{
@ -342,13 +377,14 @@ public:
}
// Create a media stream.
uint32_t hints = (mAudioSource ? DOMMediaStream::HINT_CONTENTS_AUDIO : 0);
hints |= (mVideoSource ? DOMMediaStream::HINT_CONTENTS_VIDEO : 0);
DOMMediaStream::TrackTypeHints hints =
(mAudioSource ? DOMMediaStream::HINT_CONTENTS_AUDIO : 0) |
(mVideoSource ? DOMMediaStream::HINT_CONTENTS_VIDEO : 0);
nsRefPtr<nsDOMUserMediaStream> trackunion =
nsDOMUserMediaStream::CreateTrackUnionStream(window, hints);
if (!trackunion) {
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error = mError.forget();
LOG(("Returning error for getUserMedia() - no stream"));
error->OnError(NS_LITERAL_STRING("NO_STREAM"));
return NS_OK;
@ -372,11 +408,17 @@ public:
// when the page is invalidated (on navigation or close).
mListener->Activate(stream.forget(), mAudioSource, mVideoSource);
TracksAvailableCallback* tracksAvailableCallback =
new TracksAvailableCallback(mManager, mSuccess, mWindowID, trackunion);
// Dispatch to the media thread to ask it to start the sources,
// because that can take a while
// because that can take a while.
// Pass ownership of trackunion to the MediaOperationRunnable
// to ensure it's kept alive until the MediaOperationRunnable runs (at least).
nsIThread *mediaThread = MediaManager::GetThread();
nsRefPtr<MediaOperationRunnable> runnable(
new MediaOperationRunnable(MEDIA_START, mListener,
new MediaOperationRunnable(MEDIA_START, mListener, trackunion,
tracksAvailableCallback,
mAudioSource, mVideoSource, false));
mediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
@ -407,24 +449,14 @@ public:
}
#endif
// We're in the main thread, so no worries here either.
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
if (!(mManager->IsWindowStillActive(mWindowID))) {
return NS_OK;
}
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
LOG(("Returning success for getUserMedia()"));
success->OnSuccess(static_cast<nsIDOMLocalMediaStream*>(trackunion));
// We won't need mError now.
mError = nullptr;
return NS_OK;
}
private:
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
nsRefPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
nsRefPtr<nsIDOMGetUserMediaErrorCallback> mError;
nsRefPtr<MediaEngineSource> mAudioSource;
nsRefPtr<MediaEngineSource> mVideoSource;
uint64_t mWindowID;
@ -1498,7 +1530,8 @@ GetUserMediaCallbackMediaStreamListener::Invalidate()
// Pass a ref to us (which is threadsafe) so it can query us for the
// source stream info.
runnable = new MediaOperationRunnable(MEDIA_STOP,
this, mAudioSource, mVideoSource,
this, nullptr, nullptr,
mAudioSource, mVideoSource,
mFinished);
mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
}

Просмотреть файл

@ -22,6 +22,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/StaticPtr.h"
#include "prlog.h"
#include "DOMMediaStream.h"
#ifdef MOZ_WEBRTC
#include "mtransport/runnable_utils.h"
@ -200,8 +201,11 @@ class GetUserMediaNotificationEvent: public nsRunnable
GetUserMediaStatus aStatus)
: mListener(aListener), mStatus(aStatus) {}
GetUserMediaNotificationEvent(GetUserMediaStatus aStatus)
: mListener(nullptr), mStatus(aStatus) {}
GetUserMediaNotificationEvent(GetUserMediaStatus aStatus,
already_AddRefed<DOMMediaStream> aStream,
DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback)
: mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
mStatus(aStatus) {}
NS_IMETHOD
Run()
@ -216,6 +220,7 @@ class GetUserMediaNotificationEvent: public nsRunnable
switch (mStatus) {
case STARTING:
msg = NS_LITERAL_STRING("starting");
mStream->OnTracksAvailable(mOnTracksAvailableCallback.forget());
break;
case STOPPING:
msg = NS_LITERAL_STRING("shutdown");
@ -237,6 +242,8 @@ class GetUserMediaNotificationEvent: public nsRunnable
protected:
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
nsRefPtr<DOMMediaStream> mStream;
nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
GetUserMediaStatus mStatus;
};
@ -254,10 +261,14 @@ public:
// so we can send Stop without AddRef()ing from the MSG thread
MediaOperationRunnable(MediaOperation aType,
GetUserMediaCallbackMediaStreamListener* aListener,
DOMMediaStream* aStream,
DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
MediaEngineSource* aAudioSource,
MediaEngineSource* aVideoSource,
bool aNeedsFinish)
: mType(aType)
, mStream(aStream)
, mOnTracksAvailableCallback(aOnTracksAvailableCallback)
, mAudioSource(aAudioSource)
, mVideoSource(aVideoSource)
, mListener(aListener)
@ -286,24 +297,34 @@ public:
source->SetPullEnabled(true);
DOMMediaStream::TrackTypeHints expectedTracks = 0;
if (mAudioSource) {
rv = mAudioSource->Start(source, kAudioTrack);
if (NS_FAILED(rv)) {
if (NS_SUCCEEDED(rv)) {
expectedTracks |= DOMMediaStream::HINT_CONTENTS_AUDIO;
} else {
MM_LOG(("Starting audio failed, rv=%d",rv));
}
}
if (mVideoSource) {
rv = mVideoSource->Start(source, kVideoTrack);
if (NS_FAILED(rv)) {
if (NS_SUCCEEDED(rv)) {
expectedTracks |= DOMMediaStream::HINT_CONTENTS_VIDEO;
} else {
MM_LOG(("Starting video failed, rv=%d",rv));
}
}
mOnTracksAvailableCallback->SetExpectedTracks(expectedTracks);
MM_LOG(("started all sources"));
// Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent,
// because mOnTracksAvailableCallback needs to be added to mStream
// on the main thread.
nsRefPtr<GetUserMediaNotificationEvent> event =
new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING);
new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING,
mStream.forget(),
mOnTracksAvailableCallback.forget());
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
break;
@ -339,6 +360,8 @@ public:
private:
MediaOperation mType;
nsRefPtr<DOMMediaStream> mStream;
nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe