Bug 969372: part2: 1. Let Session hold a reference to MediaRecorder. 2. MediaRecorder implement nsIDocumentActivity. r=roc

This commit is contained in:
Benjamin Chen 2014-06-19 10:11:34 +08:00
parent e1204d9e47
commit 1f65ce65cb
2 changed files with 73 additions and 22 deletions

View File

@ -37,6 +37,7 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaRecorder, DOMEventTargetHelper,
mStream)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaRecorder)
NS_INTERFACE_MAP_ENTRY(nsIDocumentActivity)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(MediaRecorder, DOMEventTargetHelper)
@ -68,10 +69,15 @@ NS_IMPL_RELEASE_INHERITED(MediaRecorder, DOMEventTargetHelper)
* Switch from Extract stage to Destroy stage by calling Session::Stop.
* Release session resource and remove associated streams from MSG.
*
* Lifetime of a Session object.
* Lifetime of MediaRecorder and Session objects.
* 1) MediaRecorder creates a Session in MediaRecorder::Start function.
* And the Session registers itself to ShutdownObserver and also holds a
* MediaRecorder. Therefore, the reference dependency in gecko is:
* ShutdownObserver -> Session -> MediaRecorder
* 2) A Session is destroyed in DestroyRunnable after MediaRecorder::Stop being called
* _and_ all encoded media data been passed to OnDataAvailable handler.
* 3) MediaRecorder::Stop is called by user or the document is going to
* inactive or invisible.
*/
class MediaRecorder::Session: public nsIObserver
{
@ -93,7 +99,7 @@ class MediaRecorder::Session: public nsIObserver
nsRefPtr<MediaRecorder> recorder = mSession->mRecorder;
if (!recorder) {
return NS_OK;
return NS_OK;
}
recorder->SetMimeType(mSession->mMimeType);
if (mSession->IsEncoderError()) {
@ -291,11 +297,7 @@ public:
}
return false;
}
void ForgetMediaRecorder()
{
LOG(PR_LOG_DEBUG, ("Session.ForgetMediaRecorder (%p)", mRecorder));
mRecorder = nullptr;
}
private:
// Pull encoded meida data from MediaEncoder and put into EncodedBufferCache.
@ -340,7 +342,7 @@ private:
mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
// Allocate encoder and bind with the Track Union Stream.
TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(mRecorder->mSessions.LastElement());
TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(this);
mRecorder->mStream->OnTracksAvailable(tracksAvailableCallback);
}
@ -441,8 +443,9 @@ private:
}
private:
// Hold weak a reference to MediaRecoder and can be accessed ONLY on main thread.
MediaRecorder* mRecorder;
// Hold reference to MediaRecoder that ensure MediaRecorder is alive
// if there is an active session. Access ONLY on main thread.
nsRefPtr<MediaRecorder> mRecorder;
// Receive track data from source and dispatch to Encoder.
// Pause/ Resume controller.
@ -473,12 +476,7 @@ NS_IMPL_ISUPPORTS(MediaRecorder::Session, nsIObserver)
MediaRecorder::~MediaRecorder()
{
LOG(PR_LOG_DEBUG, ("~MediaRecorder (%p)", this));
for (uint32_t i = 0; i < mSessions.Length(); i ++) {
if (mSessions[i]) {
mSessions[i]->ForgetMediaRecorder();
mSessions[i]->Stop();
}
}
UnRegisterActivityObserver();
}
MediaRecorder::MediaRecorder(DOMMediaStream& aStream, nsPIDOMWindow* aOwnerWindow)
@ -494,6 +492,33 @@ MediaRecorder::MediaRecorder(DOMMediaStream& aStream, nsPIDOMWindow* aOwnerWindo
gMediaRecorderLog = PR_NewLogModule("MediaRecorder");
}
#endif
RegisterActivityObserver();
}
void
MediaRecorder::RegisterActivityObserver()
{
nsPIDOMWindow* window = GetOwner();
if (window) {
nsIDocument* doc = window->GetExtantDoc();
if (doc) {
doc->RegisterActivityObserver(
NS_ISUPPORTS_CAST(nsIDocumentActivity*, this));
}
}
}
void
MediaRecorder::UnRegisterActivityObserver()
{
nsPIDOMWindow* window = GetOwner();
if (window) {
nsIDocument* doc = window->GetExtantDoc();
if (doc) {
doc->UnregisterActivityObserver(
NS_ISUPPORTS_CAST(nsIDocumentActivity*, this));
}
}
}
void
@ -561,9 +586,8 @@ MediaRecorder::Stop(ErrorResult& aResult)
return;
}
mState = RecordingState::Inactive;
if (mSessions.Length() > 0) {
mSessions.LastElement()->Stop();
}
MOZ_ASSERT(mSessions.Length() > 0);
mSessions.LastElement()->Stop();
}
void
@ -628,7 +652,7 @@ MediaRecorder::RequestData(ErrorResult& aResult)
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
MOZ_ASSERT(mSessions.Length() > 0);
NS_DispatchToMainThread(
new CreateAndDispatchBlobEventRunnable(mSessions.LastElement()->GetEncodedData(),
this));
@ -778,5 +802,22 @@ MediaRecorder::RemoveSession(Session* aSession)
mSessions.RemoveElement(aSession);
}
void
MediaRecorder::NotifyOwnerDocumentActivityChanged()
{
nsPIDOMWindow* window = GetOwner();
NS_ENSURE_TRUE_VOID(window);
nsIDocument* doc = window->GetExtantDoc();
NS_ENSURE_TRUE_VOID(doc);
LOG(PR_LOG_DEBUG, ("MediaRecorder %p document IsActive %d isVisible %d\n",
this, doc->IsActive(), doc->IsVisible()));
if (!doc->IsActive() || !doc->IsVisible()) {
// Stop the session.
ErrorResult result;
Stop(result);
}
}
}
}

View File

@ -9,6 +9,7 @@
#include "mozilla/dom/MediaRecorderBinding.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "nsIDocumentActivity.h"
// Max size for allowing queue encoded data in memory
#define MAX_ALLOW_MEMORY_BUFFER 1024000
@ -35,7 +36,8 @@ namespace dom {
* Also extract the encoded data and create blobs on every timeslice passed from start function or RequestData function called by UA.
*/
class MediaRecorder : public DOMEventTargetHelper
class MediaRecorder : public DOMEventTargetHelper,
public nsIDocumentActivity
{
class Session;
friend class CreateAndDispatchBlobEventRunnable;
@ -85,6 +87,8 @@ public:
IMPL_EVENT_HANDLER(stop)
IMPL_EVENT_HANDLER(warning)
NS_DECL_NSIDOCUMENTACTIVITY
protected:
MediaRecorder& operator = (const MediaRecorder& x) MOZ_DELETE;
// Create dataavailable event with Blob data and it runs in main thread
@ -105,12 +109,18 @@ protected:
nsRefPtr<DOMMediaStream> mStream;
// The current state of the MediaRecorder object.
RecordingState mState;
// Hold the sessions pointer in media recorder and clean in the destructor of recorder.
// Hold the sessions pointer and clean it when the DestroyRunnable for a
// session is running.
nsTArray<Session*> mSessions;
// Thread safe for mMimeType.
Mutex mMutex;
// It specifies the container format as well as the audio and video capture formats.
nsString mMimeType;
private:
// Register MediaRecorder into Document to listen the activity changes.
void RegisterActivityObserver();
void UnRegisterActivityObserver();
};
}