mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-11 22:41:02 +00:00
Bug 973522 MediaRecorder causes large leak. r=roc, jsmith
This commit is contained in:
parent
926fd8dee8
commit
05878d4b12
@ -22,12 +22,19 @@
|
|||||||
#include "mozilla/dom/AudioStreamTrack.h"
|
#include "mozilla/dom/AudioStreamTrack.h"
|
||||||
#include "mozilla/dom/VideoStreamTrack.h"
|
#include "mozilla/dom/VideoStreamTrack.h"
|
||||||
|
|
||||||
|
#ifdef PR_LOGGING
|
||||||
|
PRLogModuleInfo* gMediaRecorderLog;
|
||||||
|
#define LOG(type, msg) PR_LOG(gMediaRecorderLog, type, msg)
|
||||||
|
#else
|
||||||
|
#define LOG(type, msg)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_INHERITED_2(MediaRecorder, nsDOMEventTargetHelper,
|
NS_IMPL_CYCLE_COLLECTION_INHERITED_1(MediaRecorder, nsDOMEventTargetHelper,
|
||||||
mStream, mSession)
|
mStream)
|
||||||
|
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaRecorder)
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaRecorder)
|
||||||
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
|
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
|
||||||
@ -81,9 +88,14 @@ class MediaRecorder::Session: public nsIObserver
|
|||||||
|
|
||||||
NS_IMETHODIMP Run()
|
NS_IMETHODIMP Run()
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.PushBlobRunnable s=(%p)", mSession.get()));
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
MediaRecorder *recorder = mSession->mRecorder;
|
nsRefPtr<MediaRecorder> recorder = mSession->mRecorder;
|
||||||
|
if (!recorder) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
recorder->SetMimeType(mSession->mMimeType);
|
||||||
if (mSession->IsEncoderError()) {
|
if (mSession->IsEncoderError()) {
|
||||||
recorder->NotifyError(NS_ERROR_UNEXPECTED);
|
recorder->NotifyError(NS_ERROR_UNEXPECTED);
|
||||||
}
|
}
|
||||||
@ -91,6 +103,7 @@ class MediaRecorder::Session: public nsIObserver
|
|||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
recorder->NotifyError(rv);
|
recorder->NotifyError(rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +124,7 @@ class MediaRecorder::Session: public nsIObserver
|
|||||||
MOZ_ASSERT(NS_GetCurrentThread() == mSession->mReadThread);
|
MOZ_ASSERT(NS_GetCurrentThread() == mSession->mReadThread);
|
||||||
|
|
||||||
mSession->Extract();
|
mSession->Extract();
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.ExtractRunnable shutdown = %d", mSession->mEncoder->IsShutdown()));
|
||||||
if (!mSession->mEncoder->IsShutdown()) {
|
if (!mSession->mEncoder->IsShutdown()) {
|
||||||
NS_DispatchToCurrentThread(new ExtractRunnable(mSession));
|
NS_DispatchToCurrentThread(new ExtractRunnable(mSession));
|
||||||
} else {
|
} else {
|
||||||
@ -123,7 +137,7 @@ class MediaRecorder::Session: public nsIObserver
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Session* mSession;
|
nsRefPtr<Session> mSession;
|
||||||
};
|
};
|
||||||
|
|
||||||
// For Ensure recorder has tracks to record.
|
// For Ensure recorder has tracks to record.
|
||||||
@ -149,12 +163,12 @@ class MediaRecorder::Session: public nsIObserver
|
|||||||
trackType |= DOMMediaStream::HINT_CONTENTS_AUDIO;
|
trackType |= DOMMediaStream::HINT_CONTENTS_AUDIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.NotifyTracksAvailable track type = (%d)", trackType));
|
||||||
mSession->AfterTracksAdded(trackType);
|
mSession->AfterTracksAdded(trackType);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
nsRefPtr<Session> mSession;
|
nsRefPtr<Session> mSession;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Main thread task.
|
// Main thread task.
|
||||||
// To delete RecordingSession object.
|
// To delete RecordingSession object.
|
||||||
class DestroyRunnable : public nsRunnable
|
class DestroyRunnable : public nsRunnable
|
||||||
@ -165,9 +179,13 @@ class MediaRecorder::Session: public nsIObserver
|
|||||||
|
|
||||||
NS_IMETHODIMP Run()
|
NS_IMETHODIMP Run()
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.DestroyRunnable session refcnt = (%d) stopIssued %d s=(%p)",
|
||||||
|
(int)mSession->mRefCnt, mSession->mStopIssued, mSession.get()));
|
||||||
MOZ_ASSERT(NS_IsMainThread() && mSession.get());
|
MOZ_ASSERT(NS_IsMainThread() && mSession.get());
|
||||||
MediaRecorder *recorder = mSession->mRecorder;
|
nsRefPtr<MediaRecorder> recorder = mSession->mRecorder;
|
||||||
|
if (!recorder) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
// SourceMediaStream is ended, and send out TRACK_EVENT_END notification.
|
// SourceMediaStream is ended, and send out TRACK_EVENT_END notification.
|
||||||
// Read Thread will be terminate soon.
|
// Read Thread will be terminate soon.
|
||||||
// We need to switch MediaRecorder to "Stop" state first to make sure
|
// We need to switch MediaRecorder to "Stop" state first to make sure
|
||||||
@ -176,16 +194,18 @@ class MediaRecorder::Session: public nsIObserver
|
|||||||
// Also avoid to run if this session already call stop before
|
// Also avoid to run if this session already call stop before
|
||||||
if (!mSession->mStopIssued) {
|
if (!mSession->mStopIssued) {
|
||||||
ErrorResult result;
|
ErrorResult result;
|
||||||
|
mSession->mStopIssued = true;
|
||||||
recorder->Stop(result);
|
recorder->Stop(result);
|
||||||
NS_DispatchToMainThread(new DestroyRunnable(mSession.forget()));
|
NS_DispatchToMainThread(new DestroyRunnable(mSession.forget()));
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch stop event and clear MIME type.
|
// Dispatch stop event and clear MIME type.
|
||||||
recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
|
|
||||||
mSession->mMimeType = NS_LITERAL_STRING("");
|
mSession->mMimeType = NS_LITERAL_STRING("");
|
||||||
recorder->SetMimeType(mSession->mMimeType);
|
recorder->SetMimeType(mSession->mMimeType);
|
||||||
|
recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
|
||||||
|
recorder->RemoveSession(mSession);
|
||||||
|
mSession->mRecorder = nullptr;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,11 +235,13 @@ public:
|
|||||||
// Only DestroyRunnable is allowed to delete Session object.
|
// Only DestroyRunnable is allowed to delete Session object.
|
||||||
virtual ~Session()
|
virtual ~Session()
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.~Session (%p)", this));
|
||||||
CleanupStreams();
|
CleanupStreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.Start %p", this));
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
SetupStreams();
|
SetupStreams();
|
||||||
@ -227,8 +249,8 @@ public:
|
|||||||
|
|
||||||
void Stop()
|
void Stop()
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.Stop %p", this));
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
mStopIssued = true;
|
mStopIssued = true;
|
||||||
CleanupStreams();
|
CleanupStreams();
|
||||||
nsContentUtils::UnregisterShutdownObserver(this);
|
nsContentUtils::UnregisterShutdownObserver(this);
|
||||||
@ -236,6 +258,7 @@ public:
|
|||||||
|
|
||||||
nsresult Pause()
|
nsresult Pause()
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.Pause"));
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
NS_ENSURE_TRUE(mTrackUnionStream, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(mTrackUnionStream, NS_ERROR_FAILURE);
|
||||||
@ -246,6 +269,7 @@ public:
|
|||||||
|
|
||||||
nsresult Resume()
|
nsresult Resume()
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.Resume"));
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
NS_ENSURE_TRUE(mTrackUnionStream, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(mTrackUnionStream, NS_ERROR_FAILURE);
|
||||||
@ -256,6 +280,7 @@ public:
|
|||||||
|
|
||||||
already_AddRefed<nsIDOMBlob> GetEncodedData()
|
already_AddRefed<nsIDOMBlob> GetEncodedData()
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
return mEncodedBufferCache->ExtractBlob(mMimeType);
|
return mEncodedBufferCache->ExtractBlob(mMimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,6 +291,11 @@ public:
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
void ForgetMediaRecorder()
|
||||||
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.ForgetMediaRecorder (%p)", mRecorder));
|
||||||
|
mRecorder = nullptr;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Pull encoded meida data from MediaEncoder and put into EncodedBufferCache.
|
// Pull encoded meida data from MediaEncoder and put into EncodedBufferCache.
|
||||||
@ -273,14 +303,13 @@ private:
|
|||||||
void Extract()
|
void Extract()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_GetCurrentThread() == mReadThread);
|
MOZ_ASSERT(NS_GetCurrentThread() == mReadThread);
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.Extract %p", this));
|
||||||
// Whether push encoded data back to onDataAvailable automatically.
|
// Whether push encoded data back to onDataAvailable automatically.
|
||||||
const bool pushBlob = (mTimeSlice > 0) ? true : false;
|
const bool pushBlob = (mTimeSlice > 0) ? true : false;
|
||||||
|
|
||||||
// Pull encoded media data from MediaEncoder
|
// Pull encoded media data from MediaEncoder
|
||||||
nsTArray<nsTArray<uint8_t> > encodedBuf;
|
nsTArray<nsTArray<uint8_t> > encodedBuf;
|
||||||
mEncoder->GetEncodedData(&encodedBuf, mMimeType);
|
mEncoder->GetEncodedData(&encodedBuf, mMimeType);
|
||||||
mRecorder->SetMimeType(mMimeType);
|
|
||||||
|
|
||||||
// Append pulled data into cache buffer.
|
// Append pulled data into cache buffer.
|
||||||
for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
|
for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
|
||||||
@ -311,12 +340,13 @@ private:
|
|||||||
mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
|
mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
|
||||||
|
|
||||||
// Allocate encoder and bind with the Track Union Stream.
|
// Allocate encoder and bind with the Track Union Stream.
|
||||||
TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(mRecorder->mSession);
|
TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(mRecorder->mSessions.LastElement());
|
||||||
mRecorder->mStream->OnTracksAvailable(tracksAvailableCallback);
|
mRecorder->mStream->OnTracksAvailable(tracksAvailableCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AfterTracksAdded(uint8_t aTrackTypes)
|
void AfterTracksAdded(uint8_t aTrackTypes)
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.AfterTracksAdded %p", this));
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
// Allocate encoder and bind with union stream.
|
// Allocate encoder and bind with union stream.
|
||||||
@ -367,10 +397,10 @@ private:
|
|||||||
void DoSessionEndTask(nsresult rv)
|
void DoSessionEndTask(nsresult rv)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
mRecorder->NotifyError(rv);
|
mRecorder->NotifyError(rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
CleanupStreams();
|
CleanupStreams();
|
||||||
// Destroy this session object in main thread.
|
// Destroy this session object in main thread.
|
||||||
NS_DispatchToMainThread(new PushBlobRunnable(this));
|
NS_DispatchToMainThread(new PushBlobRunnable(this));
|
||||||
@ -392,9 +422,14 @@ private:
|
|||||||
NS_IMETHODIMP Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
|
NS_IMETHODIMP Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
LOG(PR_LOG_DEBUG, ("Session.Observe XPCOM_SHUTDOWN %p", this));
|
||||||
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
|
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
|
||||||
// Force stop Session to terminate Read Thread.
|
// Force stop Session to terminate Read Thread.
|
||||||
|
mEncoder->Cancel();
|
||||||
|
if (mRecorder) {
|
||||||
|
mRecorder->RemoveSession(this);
|
||||||
|
mRecorder = nullptr;
|
||||||
|
}
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,9 +437,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Hold a reference to MediaRecoder to make sure MediaRecoder be
|
// Hold weak a reference to MediaRecoder and can be accessed ONLY on main thread.
|
||||||
// destroyed after all session object dead.
|
MediaRecorder* mRecorder;
|
||||||
nsRefPtr<MediaRecorder> mRecorder;
|
|
||||||
|
|
||||||
// Receive track data from source and dispatch to Encoder.
|
// Receive track data from source and dispatch to Encoder.
|
||||||
// Pause/ Resume controller.
|
// Pause/ Resume controller.
|
||||||
@ -434,18 +468,28 @@ NS_IMPL_ISUPPORTS1(MediaRecorder::Session, nsIObserver)
|
|||||||
|
|
||||||
MediaRecorder::~MediaRecorder()
|
MediaRecorder::~MediaRecorder()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mSession == nullptr);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaRecorder::MediaRecorder(DOMMediaStream& aStream, nsPIDOMWindow* aOwnerWindow)
|
MediaRecorder::MediaRecorder(DOMMediaStream& aStream, nsPIDOMWindow* aOwnerWindow)
|
||||||
: nsDOMEventTargetHelper(aOwnerWindow),
|
: nsDOMEventTargetHelper(aOwnerWindow),
|
||||||
mState(RecordingState::Inactive),
|
mState(RecordingState::Inactive),
|
||||||
mSession(nullptr),
|
|
||||||
mMutex("Session.Data.Mutex")
|
mMutex("Session.Data.Mutex")
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aOwnerWindow);
|
MOZ_ASSERT(aOwnerWindow);
|
||||||
MOZ_ASSERT(aOwnerWindow->IsInnerWindow());
|
MOZ_ASSERT(aOwnerWindow->IsInnerWindow());
|
||||||
mStream = &aStream;
|
mStream = &aStream;
|
||||||
|
#ifdef PR_LOGGING
|
||||||
|
if (!gMediaRecorderLog) {
|
||||||
|
gMediaRecorderLog = PR_NewLogModule("MediaRecorder");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -465,6 +509,7 @@ MediaRecorder::GetMimeType(nsString &aMimeType)
|
|||||||
void
|
void
|
||||||
MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
|
MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("MediaRecorder.Start %p", this));
|
||||||
if (mState != RecordingState::Inactive) {
|
if (mState != RecordingState::Inactive) {
|
||||||
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
return;
|
return;
|
||||||
@ -497,59 +542,60 @@ MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
|
|||||||
|
|
||||||
mState = RecordingState::Recording;
|
mState = RecordingState::Recording;
|
||||||
// Start a session
|
// Start a session
|
||||||
mSession = new Session(this, timeSlice);
|
|
||||||
mSession->Start();
|
mSessions.AppendElement();
|
||||||
|
mSessions.LastElement() = new Session(this, timeSlice);
|
||||||
|
mSessions.LastElement()->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaRecorder::Stop(ErrorResult& aResult)
|
MediaRecorder::Stop(ErrorResult& aResult)
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("MediaRecorder.Stop %p", this));
|
||||||
if (mState == RecordingState::Inactive) {
|
if (mState == RecordingState::Inactive) {
|
||||||
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mState = RecordingState::Inactive;
|
mState = RecordingState::Inactive;
|
||||||
|
if (mSessions.Length() > 0) {
|
||||||
mSession->Stop();
|
mSessions.LastElement()->Stop();
|
||||||
mSession = nullptr;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaRecorder::Pause(ErrorResult& aResult)
|
MediaRecorder::Pause(ErrorResult& aResult)
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("MediaRecorder.Pause"));
|
||||||
if (mState != RecordingState::Recording) {
|
if (mState != RecordingState::Recording) {
|
||||||
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(mSession != nullptr);
|
MOZ_ASSERT(mSessions.Length() > 0);
|
||||||
if (mSession) {
|
nsresult rv = mSessions.LastElement()->Pause();
|
||||||
nsresult rv = mSession->Pause();
|
if (NS_FAILED(rv)) {
|
||||||
if (NS_FAILED(rv)) {
|
NotifyError(rv);
|
||||||
NotifyError(rv);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
mState = RecordingState::Paused;
|
|
||||||
}
|
}
|
||||||
|
mState = RecordingState::Paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaRecorder::Resume(ErrorResult& aResult)
|
MediaRecorder::Resume(ErrorResult& aResult)
|
||||||
{
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("MediaRecorder.Resume"));
|
||||||
if (mState != RecordingState::Paused) {
|
if (mState != RecordingState::Paused) {
|
||||||
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(mSession != nullptr);
|
MOZ_ASSERT(mSessions.Length() > 0);
|
||||||
if (mSession) {
|
nsresult rv = mSessions.LastElement()->Resume();
|
||||||
nsresult rv = mSession->Resume();
|
if (NS_FAILED(rv)) {
|
||||||
if (NS_FAILED(rv)) {
|
NotifyError(rv);
|
||||||
NotifyError(rv);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
mState = RecordingState::Recording;
|
|
||||||
}
|
}
|
||||||
|
mState = RecordingState::Recording;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateAndDispatchBlobEventRunnable : public nsRunnable {
|
class CreateAndDispatchBlobEventRunnable : public nsRunnable {
|
||||||
@ -580,8 +626,9 @@ MediaRecorder::RequestData(ErrorResult& aResult)
|
|||||||
}
|
}
|
||||||
|
|
||||||
NS_DispatchToMainThread(
|
NS_DispatchToMainThread(
|
||||||
new CreateAndDispatchBlobEventRunnable(mSession->GetEncodedData(), this),
|
new CreateAndDispatchBlobEventRunnable(mSessions.LastElement()->GetEncodedData(),
|
||||||
NS_DISPATCH_NORMAL);
|
this),
|
||||||
|
NS_DISPATCH_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
@ -617,12 +664,11 @@ nsresult
|
|||||||
MediaRecorder::CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob)
|
MediaRecorder::CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob)
|
||||||
{
|
{
|
||||||
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
|
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
|
||||||
|
|
||||||
if (!CheckPrincipal()) {
|
if (!CheckPrincipal()) {
|
||||||
// Media is not same-origin, don't allow the data out.
|
// Media is not same-origin, don't allow the data out.
|
||||||
|
nsRefPtr<nsIDOMBlob> blob = aBlob;
|
||||||
return NS_ERROR_DOM_SECURITY_ERR;
|
return NS_ERROR_DOM_SECURITY_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlobEventInit init;
|
BlobEventInit init;
|
||||||
init.mBubbles = false;
|
init.mBubbles = false;
|
||||||
init.mCancelable = false;
|
init.mCancelable = false;
|
||||||
@ -704,6 +750,10 @@ MediaRecorder::NotifyError(nsresult aRv)
|
|||||||
|
|
||||||
bool MediaRecorder::CheckPrincipal()
|
bool MediaRecorder::CheckPrincipal()
|
||||||
{
|
{
|
||||||
|
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
|
||||||
|
if (!mStream) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
nsCOMPtr<nsIPrincipal> principal = mStream->GetPrincipal();
|
nsCOMPtr<nsIPrincipal> principal = mStream->GetPrincipal();
|
||||||
if (!GetOwner())
|
if (!GetOwner())
|
||||||
return false;
|
return false;
|
||||||
@ -718,5 +768,12 @@ bool MediaRecorder::CheckPrincipal()
|
|||||||
return subsumes;
|
return subsumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MediaRecorder::RemoveSession(Session* aSession)
|
||||||
|
{
|
||||||
|
LOG(PR_LOG_DEBUG, ("MediaRecorder.RemoveSession (%p)", aSession));
|
||||||
|
mSessions.RemoveElement(aSession);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,13 +100,14 @@ protected:
|
|||||||
void SetMimeType(const nsString &aMimeType);
|
void SetMimeType(const nsString &aMimeType);
|
||||||
|
|
||||||
MediaRecorder(const MediaRecorder& x) MOZ_DELETE; // prevent bad usage
|
MediaRecorder(const MediaRecorder& x) MOZ_DELETE; // prevent bad usage
|
||||||
|
// Remove session pointer.
|
||||||
|
void RemoveSession(Session* aSession);
|
||||||
// MediaStream passed from js context
|
// MediaStream passed from js context
|
||||||
nsRefPtr<DOMMediaStream> mStream;
|
nsRefPtr<DOMMediaStream> mStream;
|
||||||
// The current state of the MediaRecorder object.
|
// The current state of the MediaRecorder object.
|
||||||
RecordingState mState;
|
RecordingState mState;
|
||||||
// Current recording session.
|
// Hold the sessions pointer in media recorder and clean in the destructor of recorder.
|
||||||
nsRefPtr<Session> mSession;
|
nsTArray<Session*> mSessions;
|
||||||
// Thread safe for mMimeType.
|
// Thread safe for mMimeType.
|
||||||
Mutex mMutex;
|
Mutex mMutex;
|
||||||
// It specifies the container format as well as the audio and video capture formats.
|
// It specifies the container format as well as the audio and video capture formats.
|
||||||
|
@ -403,6 +403,7 @@ skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
|
|||||||
[test_mediarecorder_avoid_recursion.html]
|
[test_mediarecorder_avoid_recursion.html]
|
||||||
[test_mediarecorder_record_timeslice.html]
|
[test_mediarecorder_record_timeslice.html]
|
||||||
[test_mediarecorder_record_audiocontext.html]
|
[test_mediarecorder_record_audiocontext.html]
|
||||||
|
[test_mediarecorder_record_audiocontext_mlk.html]
|
||||||
[test_mediarecorder_record_4ch_audiocontext.html]
|
[test_mediarecorder_record_4ch_audiocontext.html]
|
||||||
skip-if = (toolkit == 'gonk' && !debug)
|
skip-if = (toolkit == 'gonk' && !debug)
|
||||||
[test_mediarecorder_record_stopms.html]
|
[test_mediarecorder_record_stopms.html]
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>capture for possible memory leak when record AudioContext</title>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=973765">Mozill
|
||||||
|
a Bug 973765</a>
|
||||||
|
<pre id="test">
|
||||||
|
<script class="testbody" type="text/javascript">
|
||||||
|
// This test case want to capture the memory leak if exit the browser after running those script.
|
||||||
|
var ac = new window.AudioContext();
|
||||||
|
var destStream = ac.createMediaStreamDestination().stream;
|
||||||
|
var recorder = new MediaRecorder(destStream);
|
||||||
|
recorder.start(1000);
|
||||||
|
is(recorder.state, 'recording', 'Media recorder should be recording');
|
||||||
|
is(recorder.stream, destStream,
|
||||||
|
'Media recorder stream = element stream at the start of recording');
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user