mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
Bug 1403706 - Remove race conditions in temporary blob - part 2 - MediaRecorder should use MutableBlobStorage, r=pehrsons, r=smaug
This commit is contained in:
parent
7c36c77153
commit
970f42c82a
@ -11,6 +11,7 @@
|
|||||||
#include "prio.h"
|
#include "prio.h"
|
||||||
|
|
||||||
class nsIEventTarget;
|
class nsIEventTarget;
|
||||||
|
class nsIRunnable;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#include "EncodedBufferCache.h"
|
|
||||||
#include "prio.h"
|
|
||||||
#include "nsAnonymousTemporaryFile.h"
|
|
||||||
#include "mozilla/Monitor.h"
|
|
||||||
#include "mozilla/dom/ContentChild.h"
|
|
||||||
#include "mozilla/dom/File.h"
|
|
||||||
#include "nsThreadUtils.h"
|
|
||||||
#include "nsXULAppAPI.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
void
|
|
||||||
EncodedBufferCache::AppendBuffer(nsTArray<uint8_t> & aBuf)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
|
||||||
|
|
||||||
MutexAutoLock lock(mMutex);
|
|
||||||
mDataSize += aBuf.Length();
|
|
||||||
|
|
||||||
mEncodedBuffers.AppendElement()->SwapElements(aBuf);
|
|
||||||
|
|
||||||
if (!mTempFileEnabled && mDataSize > mMaxMemoryStorage) {
|
|
||||||
nsresult rv;
|
|
||||||
PRFileDesc* tempFD = nullptr;
|
|
||||||
{
|
|
||||||
// Release the mMutex because of the sync dispatch to the main thread.
|
|
||||||
MutexAutoUnlock unlock(mMutex);
|
|
||||||
if (XRE_IsParentProcess()) {
|
|
||||||
// In case we are in the parent process, do a synchronous I/O here to open a
|
|
||||||
// temporary file.
|
|
||||||
rv = NS_OpenAnonymousTemporaryFile(&tempFD);
|
|
||||||
} else {
|
|
||||||
// In case we are in the child process, we don't have access to open a file
|
|
||||||
// directly due to sandbox restrictions, so we need to ask the parent process
|
|
||||||
// to do that for us. In order to initiate the IPC, we need to first go to
|
|
||||||
// the main thread. This is done by dispatching a runnable to the main thread.
|
|
||||||
// From there, we start an asynchronous IPC, and we block the current thread
|
|
||||||
// using a monitor while this async work is in progress. When we receive the
|
|
||||||
// resulting file descriptor from the parent process, we notify the monitor
|
|
||||||
// and unblock the current thread and continue.
|
|
||||||
typedef dom::ContentChild::AnonymousTemporaryFileCallback
|
|
||||||
AnonymousTemporaryFileCallback;
|
|
||||||
bool done = false;
|
|
||||||
Monitor monitor("EncodeBufferCache::AppendBuffer");
|
|
||||||
RefPtr<dom::ContentChild> cc = dom::ContentChild::GetSingleton();
|
|
||||||
nsCOMPtr<nsIRunnable> runnable =
|
|
||||||
NewRunnableMethod<AnonymousTemporaryFileCallback>(
|
|
||||||
"dom::ContentChild::AsyncOpenAnonymousTemporaryFile",
|
|
||||||
cc,
|
|
||||||
&dom::ContentChild::AsyncOpenAnonymousTemporaryFile,
|
|
||||||
[&](PRFileDesc* aFile) {
|
|
||||||
rv = aFile ? NS_OK : NS_ERROR_FAILURE;
|
|
||||||
tempFD = aFile;
|
|
||||||
MonitorAutoLock lock(monitor);
|
|
||||||
done = true;
|
|
||||||
lock.Notify();
|
|
||||||
});
|
|
||||||
MonitorAutoLock lock(monitor);
|
|
||||||
rv = NS_DispatchToMainThread(runnable);
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
|
||||||
while (!done) {
|
|
||||||
lock.Wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!NS_FAILED(rv)) {
|
|
||||||
// Check the mDataSize again since we release the mMutex before.
|
|
||||||
if (mDataSize > mMaxMemoryStorage) {
|
|
||||||
mFD = tempFD;
|
|
||||||
mTempFileEnabled = true;
|
|
||||||
} else {
|
|
||||||
// Close the tempFD because the data had been taken during the
|
|
||||||
// MutexAutoUnlock.
|
|
||||||
PR_Close(tempFD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mTempFileEnabled) {
|
|
||||||
// has created temporary file, write buffer in it
|
|
||||||
for (uint32_t i = 0; i < mEncodedBuffers.Length(); i++) {
|
|
||||||
int32_t amount = PR_Write(mFD, mEncodedBuffers.ElementAt(i).Elements(), mEncodedBuffers.ElementAt(i).Length());
|
|
||||||
if (amount < 0 || size_t(amount) < mEncodedBuffers.ElementAt(i).Length()) {
|
|
||||||
NS_WARNING("Failed to write media cache block!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mEncodedBuffers.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<dom::Blob>
|
|
||||||
EncodedBufferCache::ExtractBlob(nsISupports* aParent,
|
|
||||||
const nsAString &aContentType)
|
|
||||||
{
|
|
||||||
MutexAutoLock lock(mMutex);
|
|
||||||
RefPtr<dom::Blob> blob;
|
|
||||||
if (mTempFileEnabled) {
|
|
||||||
// generate new temporary file to write
|
|
||||||
blob = dom::Blob::CreateTemporaryBlob(aParent, mFD, 0, mDataSize,
|
|
||||||
aContentType);
|
|
||||||
// fallback to memory blob
|
|
||||||
mTempFileEnabled = false;
|
|
||||||
mDataSize = 0;
|
|
||||||
mFD = nullptr;
|
|
||||||
} else {
|
|
||||||
void* blobData = malloc(mDataSize);
|
|
||||||
NS_ASSERTION(blobData, "out of memory!!");
|
|
||||||
|
|
||||||
if (blobData) {
|
|
||||||
for (uint32_t i = 0, offset = 0; i < mEncodedBuffers.Length(); i++) {
|
|
||||||
memcpy((uint8_t*)blobData + offset, mEncodedBuffers.ElementAt(i).Elements(),
|
|
||||||
mEncodedBuffers.ElementAt(i).Length());
|
|
||||||
offset += mEncodedBuffers.ElementAt(i).Length();
|
|
||||||
}
|
|
||||||
blob = dom::Blob::CreateMemoryBlob(aParent, blobData, mDataSize,
|
|
||||||
aContentType);
|
|
||||||
mEncodedBuffers.Clear();
|
|
||||||
} else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
mDataSize = 0;
|
|
||||||
return blob.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
EncodedBufferCache::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
|
||||||
{
|
|
||||||
MutexAutoLock lock(mMutex);
|
|
||||||
return mEncodedBuffers.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
@ -1,66 +0,0 @@
|
|||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#ifndef EncodedBufferCache_h_
|
|
||||||
#define EncodedBufferCache_h_
|
|
||||||
|
|
||||||
#include "nsCOMPtr.h"
|
|
||||||
#include "nsTArray.h"
|
|
||||||
#include "mozilla/Mutex.h"
|
|
||||||
|
|
||||||
struct PRFileDesc;
|
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
namespace dom {
|
|
||||||
class Blob;
|
|
||||||
} // namespace dom
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data is moved into a temporary file when it grows beyond
|
|
||||||
* the maximal size passed in the Init function.
|
|
||||||
* The AppendBuffer and ExtractBlob methods are thread-safe and can be called on
|
|
||||||
* different threads at the same time.
|
|
||||||
*/
|
|
||||||
class EncodedBufferCache
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit EncodedBufferCache(uint32_t aMaxMemoryStorage)
|
|
||||||
: mFD(nullptr),
|
|
||||||
mMutex("EncodedBufferCache.Data.Mutex"),
|
|
||||||
mDataSize(0),
|
|
||||||
mMaxMemoryStorage(aMaxMemoryStorage),
|
|
||||||
mTempFileEnabled(false) { }
|
|
||||||
~EncodedBufferCache()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
// Append buffers in cache, check if the queue is too large then switch to write buffer to file system
|
|
||||||
// aBuf will append to mEncodedBuffers or temporary File, aBuf also be cleared
|
|
||||||
void AppendBuffer(nsTArray<uint8_t> & aBuf);
|
|
||||||
// Read all buffer from memory or file System, also Remove the temporary file or clean the buffers in memory.
|
|
||||||
already_AddRefed<dom::Blob> ExtractBlob(nsISupports* aParent, const nsAString &aContentType);
|
|
||||||
// Returns the heap size in bytes of our internal buffers.
|
|
||||||
// Note that this intentionally ignores the data in the temp file.
|
|
||||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
|
||||||
|
|
||||||
private:
|
|
||||||
//array for storing the encoded data.
|
|
||||||
nsTArray<nsTArray<uint8_t> > mEncodedBuffers;
|
|
||||||
// File handle for the temporary file
|
|
||||||
PRFileDesc* mFD;
|
|
||||||
// Used to protect the mEncodedBuffer for avoiding AppendBuffer/Consume on different thread at the same time.
|
|
||||||
Mutex mMutex;
|
|
||||||
// the current buffer size can be read
|
|
||||||
uint64_t mDataSize;
|
|
||||||
// The maximal buffer allowed in memory
|
|
||||||
uint32_t mMaxMemoryStorage;
|
|
||||||
// indicate the buffer is stored on temporary file or not
|
|
||||||
bool mTempFileEnabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif
|
|
@ -9,7 +9,6 @@
|
|||||||
#include "AudioNodeEngine.h"
|
#include "AudioNodeEngine.h"
|
||||||
#include "AudioNodeStream.h"
|
#include "AudioNodeStream.h"
|
||||||
#include "DOMMediaStream.h"
|
#include "DOMMediaStream.h"
|
||||||
#include "EncodedBufferCache.h"
|
|
||||||
#include "GeckoProfiler.h"
|
#include "GeckoProfiler.h"
|
||||||
#include "MediaDecoder.h"
|
#include "MediaDecoder.h"
|
||||||
#include "MediaEncoder.h"
|
#include "MediaEncoder.h"
|
||||||
@ -19,6 +18,7 @@
|
|||||||
#include "mozilla/dom/BlobEvent.h"
|
#include "mozilla/dom/BlobEvent.h"
|
||||||
#include "mozilla/dom/File.h"
|
#include "mozilla/dom/File.h"
|
||||||
#include "mozilla/dom/MediaRecorderErrorEvent.h"
|
#include "mozilla/dom/MediaRecorderErrorEvent.h"
|
||||||
|
#include "mozilla/dom/MutableBlobStorage.h"
|
||||||
#include "mozilla/dom/VideoStreamTrack.h"
|
#include "mozilla/dom/VideoStreamTrack.h"
|
||||||
#include "mozilla/media/MediaUtils.h"
|
#include "mozilla/media/MediaUtils.h"
|
||||||
#include "mozilla/MemoryReporting.h"
|
#include "mozilla/MemoryReporting.h"
|
||||||
@ -201,11 +201,17 @@ class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
|
|||||||
// Main thread task.
|
// Main thread task.
|
||||||
// Create a blob event and send back to client.
|
// Create a blob event and send back to client.
|
||||||
class PushBlobRunnable : public Runnable
|
class PushBlobRunnable : public Runnable
|
||||||
|
, public MutableBlobStorageCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit PushBlobRunnable(Session* aSession)
|
NS_DECL_ISUPPORTS_INHERITED
|
||||||
|
|
||||||
|
// aDestroyRunnable can be null. If it's not, it will be dispatched after
|
||||||
|
// the PushBlobRunnable::Run().
|
||||||
|
PushBlobRunnable(Session* aSession, Runnable* aDestroyRunnable)
|
||||||
: Runnable("dom::MediaRecorder::Session::PushBlobRunnable")
|
: Runnable("dom::MediaRecorder::Session::PushBlobRunnable")
|
||||||
, mSession(aSession)
|
, mSession(aSession)
|
||||||
|
, mDestroyRunnable(aDestroyRunnable)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
NS_IMETHOD Run() override
|
NS_IMETHOD Run() override
|
||||||
@ -213,21 +219,72 @@ class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
|
|||||||
LOG(LogLevel::Debug, ("Session.PushBlobRunnable s=(%p)", mSession.get()));
|
LOG(LogLevel::Debug, ("Session.PushBlobRunnable s=(%p)", mSession.get()));
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
mSession->GetBlobWhenReady(this);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BlobStoreCompleted(MutableBlobStorage* aBlobStorage, Blob* aBlob,
|
||||||
|
nsresult aRv) override
|
||||||
|
{
|
||||||
RefPtr<MediaRecorder> recorder = mSession->mRecorder;
|
RefPtr<MediaRecorder> recorder = mSession->mRecorder;
|
||||||
if (!recorder) {
|
if (!recorder) {
|
||||||
return NS_OK;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv = recorder->CreateAndDispatchBlobEvent(mSession->GetEncodedData());
|
if (NS_FAILED(aRv)) {
|
||||||
|
recorder->NotifyError(aRv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult rv = recorder->CreateAndDispatchBlobEvent(aBlob);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
recorder->NotifyError(rv);
|
recorder->NotifyError(aRv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDestroyRunnable &&
|
||||||
|
NS_FAILED(NS_DispatchToMainThread(mDestroyRunnable.forget()))) {
|
||||||
|
MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
~PushBlobRunnable() = default;
|
||||||
|
|
||||||
|
RefPtr<Session> mSession;
|
||||||
|
|
||||||
|
// The generation of the blob is async. In order to avoid dispatching the
|
||||||
|
// DestroyRunnable before pushing the blob event, we store the runnable
|
||||||
|
// here.
|
||||||
|
RefPtr<Runnable> mDestroyRunnable;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StoreEncodedBufferRunnable final : public Runnable
|
||||||
|
{
|
||||||
|
RefPtr<Session> mSession;
|
||||||
|
nsTArray<nsTArray<uint8_t>> mBuffer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StoreEncodedBufferRunnable(Session* aSession,
|
||||||
|
nsTArray<nsTArray<uint8_t>>&& aBuffer)
|
||||||
|
: Runnable("StoreEncodedBufferRunnable")
|
||||||
|
, mSession(aSession)
|
||||||
|
, mBuffer(Move(aBuffer))
|
||||||
|
{}
|
||||||
|
|
||||||
|
NS_IMETHOD
|
||||||
|
Run() override
|
||||||
|
{
|
||||||
|
mSession->MaybeCreateMutableBlobStorage();
|
||||||
|
for (uint32_t i = 0; i < mBuffer.Length(); i++) {
|
||||||
|
if (!mBuffer[i].IsEmpty()) {
|
||||||
|
mSession->mMutableBlobStorage->Append(mBuffer[i].Elements(),
|
||||||
|
mBuffer[i].Length());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
RefPtr<Session> mSession;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Notify encoder error, run in main thread task. (Bug 1095381)
|
// Notify encoder error, run in main thread task. (Bug 1095381)
|
||||||
@ -433,9 +490,8 @@ public:
|
|||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
uint32_t maxMem = Preferences::GetUint("media.recorder.max_memory",
|
mMaxMemory = Preferences::GetUint("media.recorder.max_memory",
|
||||||
MAX_ALLOW_MEMORY_BUFFER);
|
MAX_ALLOW_MEMORY_BUFFER);
|
||||||
mEncodedBufferCache = new EncodedBufferCache(maxMem);
|
|
||||||
mLastBlobTimeStamp = TimeStamp::Now();
|
mLastBlobTimeStamp = TimeStamp::Now();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,7 +607,7 @@ public:
|
|||||||
LOG(LogLevel::Debug, ("Session.RequestData"));
|
LOG(LogLevel::Debug, ("Session.RequestData"));
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
|
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, nullptr)))) {
|
||||||
MOZ_ASSERT(false, "RequestData NS_DispatchToMainThread failed");
|
MOZ_ASSERT(false, "RequestData NS_DispatchToMainThread failed");
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
@ -559,19 +615,35 @@ public:
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<nsIDOMBlob> GetEncodedData()
|
void
|
||||||
|
MaybeCreateMutableBlobStorage()
|
||||||
|
{
|
||||||
|
if (!mMutableBlobStorage) {
|
||||||
|
mMutableBlobStorage =
|
||||||
|
new MutableBlobStorage(MutableBlobStorage::eCouldBeInTemporaryFile,
|
||||||
|
nullptr, mMaxMemory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GetBlobWhenReady(MutableBlobStorageCallback* aCallback)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
return mEncodedBufferCache->ExtractBlob(mRecorder->GetParentObject(),
|
|
||||||
mMimeType);
|
MaybeCreateMutableBlobStorage();
|
||||||
|
mMutableBlobStorage->GetBlobWhenReady(mRecorder->GetParentObject(),
|
||||||
|
NS_ConvertUTF16toUTF8(mMimeType),
|
||||||
|
aCallback);
|
||||||
|
mMutableBlobStorage = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<SizeOfPromise>
|
RefPtr<SizeOfPromise>
|
||||||
SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
size_t encodedBufferSize =
|
size_t encodedBufferSize = mMutableBlobStorage
|
||||||
mEncodedBufferCache->SizeOfExcludingThis(aMallocSizeOf);
|
? mMutableBlobStorage->SizeOfCurrentMemoryBuffer()
|
||||||
|
: 0;
|
||||||
|
|
||||||
if (!mEncoder) {
|
if (!mEncoder) {
|
||||||
return SizeOfPromise::CreateAndResolve(encodedBufferSize, __func__);
|
return SizeOfPromise::CreateAndResolve(encodedBufferSize, __func__);
|
||||||
@ -593,11 +665,11 @@ private:
|
|||||||
MOZ_ASSERT(mShutdownPromise);
|
MOZ_ASSERT(mShutdownPromise);
|
||||||
LOG(LogLevel::Debug, ("Session.~Session (%p)", this));
|
LOG(LogLevel::Debug, ("Session.~Session (%p)", this));
|
||||||
}
|
}
|
||||||
// Pull encoded media data from MediaEncoder and put into EncodedBufferCache.
|
// Pull encoded media data from MediaEncoder and put into MutableBlobStorage.
|
||||||
// Destroy this session object in the end of this function.
|
// Destroy this session object in the end of this function.
|
||||||
// If the bool aForceFlush is true, we will force to dispatch a
|
// If the bool aForceFlush is true, we will force to dispatch a
|
||||||
// PushBlobRunnable to main thread.
|
// PushBlobRunnable to main thread.
|
||||||
void Extract(bool aForceFlush)
|
void Extract(bool aForceFlush, Runnable* aDestroyRunnable)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
|
MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
|
||||||
|
|
||||||
@ -615,11 +687,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append pulled data into cache buffer.
|
// Append pulled data into cache buffer.
|
||||||
for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
|
NS_DispatchToMainThread(new StoreEncodedBufferRunnable(this,
|
||||||
if (!encodedBuf[i].IsEmpty()) {
|
Move(encodedBuf)));
|
||||||
mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Whether push encoded data back to onDataAvailable automatically or we
|
// Whether push encoded data back to onDataAvailable automatically or we
|
||||||
// need a flush.
|
// need a flush.
|
||||||
@ -630,11 +699,15 @@ private:
|
|||||||
pushBlob = true;
|
pushBlob = true;
|
||||||
}
|
}
|
||||||
if (pushBlob) {
|
if (pushBlob) {
|
||||||
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
|
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, aDestroyRunnable)))) {
|
||||||
MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
|
MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
|
||||||
} else {
|
} else {
|
||||||
mLastBlobTimeStamp = TimeStamp::Now();
|
mLastBlobTimeStamp = TimeStamp::Now();
|
||||||
}
|
}
|
||||||
|
} else if (aDestroyRunnable) {
|
||||||
|
if (NS_FAILED(NS_DispatchToMainThread(aDestroyRunnable))) {
|
||||||
|
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -894,15 +967,20 @@ private:
|
|||||||
&MediaRecorder::NotifyError,
|
&MediaRecorder::NotifyError,
|
||||||
rv));
|
rv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefPtr<Runnable> destroyRunnable = new DestroyRunnable(this);
|
||||||
|
|
||||||
if (rv != NS_ERROR_DOM_SECURITY_ERR) {
|
if (rv != NS_ERROR_DOM_SECURITY_ERR) {
|
||||||
// Don't push a blob if there was a security error.
|
// Don't push a blob if there was a security error.
|
||||||
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
|
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, destroyRunnable)))) {
|
||||||
MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
|
MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (NS_FAILED(NS_DispatchToMainThread(destroyRunnable))) {
|
||||||
|
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
|
|
||||||
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
|
||||||
}
|
|
||||||
mNeedSessionEndTask = false;
|
mNeedSessionEndTask = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -919,11 +997,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append pulled data into cache buffer.
|
// Append pulled data into cache buffer.
|
||||||
for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
|
NS_DispatchToMainThread(new StoreEncodedBufferRunnable(this,
|
||||||
if (!encodedBuf[i].IsEmpty()) {
|
Move(encodedBuf)));
|
||||||
mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaEncoderDataAvailable()
|
void MediaEncoderDataAvailable()
|
||||||
@ -936,7 +1011,7 @@ private:
|
|||||||
mIsStartEventFired = true;
|
mIsStartEventFired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Extract(false);
|
Extract(false, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaEncoderError()
|
void MediaEncoderError()
|
||||||
@ -953,14 +1028,11 @@ private:
|
|||||||
MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
|
MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
|
||||||
MOZ_ASSERT(mEncoder->IsShutdown());
|
MOZ_ASSERT(mEncoder->IsShutdown());
|
||||||
|
|
||||||
// Forces the last blob even if it's not time for it yet.
|
// For the stop event. Let's the creation of the blob to dispatch this runnable.
|
||||||
Extract(true);
|
RefPtr<Runnable> destroyRunnable = new DestroyRunnable(this);
|
||||||
|
|
||||||
// For the stop event.
|
// Forces the last blob even if it's not time for it yet.
|
||||||
if (NS_FAILED(NS_DispatchToMainThread(
|
Extract(true, destroyRunnable);
|
||||||
new DestroyRunnable(this)))) {
|
|
||||||
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up.
|
// Clean up.
|
||||||
mEncoderListener->Forget();
|
mEncoderListener->Forget();
|
||||||
@ -1065,15 +1137,17 @@ private:
|
|||||||
// Set in Shutdown() and resolved when shutdown is complete.
|
// Set in Shutdown() and resolved when shutdown is complete.
|
||||||
RefPtr<ShutdownPromise> mShutdownPromise;
|
RefPtr<ShutdownPromise> mShutdownPromise;
|
||||||
// A buffer to cache encoded media data.
|
// A buffer to cache encoded media data.
|
||||||
nsAutoPtr<EncodedBufferCache> mEncodedBufferCache;
|
RefPtr<MutableBlobStorage> mMutableBlobStorage;
|
||||||
|
// Max memory to use for the MutableBlobStorage.
|
||||||
|
uint64_t mMaxMemory;
|
||||||
// Current session mimeType
|
// Current session mimeType
|
||||||
nsString mMimeType;
|
nsString mMimeType;
|
||||||
// Timestamp of the last fired dataavailable event.
|
// Timestamp of the last fired dataavailable event.
|
||||||
TimeStamp mLastBlobTimeStamp;
|
TimeStamp mLastBlobTimeStamp;
|
||||||
// The interval of passing encoded data from EncodedBufferCache to onDataAvailable
|
// The interval of passing encoded data from MutableBlobStorage to
|
||||||
// handler. "mTimeSlice < 0" means Session object does not push encoded data to
|
// onDataAvailable handler. "mTimeSlice < 0" means Session object does not
|
||||||
// onDataAvailable, instead, it passive wait the client side pull encoded data
|
// push encoded data to onDataAvailable, instead, it passive wait the client
|
||||||
// by calling requestData API.
|
// side pull encoded data by calling requestData API.
|
||||||
const int32_t mTimeSlice;
|
const int32_t mTimeSlice;
|
||||||
// Indicate this session's stop has been called.
|
// Indicate this session's stop has been called.
|
||||||
bool mStopIssued;
|
bool mStopIssued;
|
||||||
@ -1085,6 +1159,8 @@ private:
|
|||||||
bool mNeedSessionEndTask;
|
bool mNeedSessionEndTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS_INHERITED0(MediaRecorder::Session::PushBlobRunnable, Runnable)
|
||||||
|
|
||||||
MediaRecorder::~MediaRecorder()
|
MediaRecorder::~MediaRecorder()
|
||||||
{
|
{
|
||||||
LOG(LogLevel::Debug, ("~MediaRecorder (%p)", this));
|
LOG(LogLevel::Debug, ("~MediaRecorder (%p)", this));
|
||||||
@ -1448,16 +1524,14 @@ MediaRecorder::IsTypeSupported(const nsAString& aMIMEType)
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
MediaRecorder::CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob)
|
MediaRecorder::CreateAndDispatchBlobEvent(Blob* aBlob)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
|
MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
|
||||||
|
|
||||||
BlobEventInit init;
|
BlobEventInit init;
|
||||||
init.mBubbles = false;
|
init.mBubbles = false;
|
||||||
init.mCancelable = false;
|
init.mCancelable = false;
|
||||||
|
init.mData = aBlob;
|
||||||
nsCOMPtr<nsIDOMBlob> blob = aBlob;
|
|
||||||
init.mData = static_cast<Blob*>(blob.get());
|
|
||||||
|
|
||||||
RefPtr<BlobEvent> event =
|
RefPtr<BlobEvent> event =
|
||||||
BlobEvent::Constructor(this,
|
BlobEvent::Constructor(this,
|
||||||
|
@ -26,16 +26,17 @@ class GlobalObject;
|
|||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
class AudioNode;
|
class AudioNode;
|
||||||
|
class Blob;
|
||||||
class DOMException;
|
class DOMException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of https://dvcs.w3.org/hg/dap/raw-file/default/media-stream-capture/MediaRecorder.html
|
* Implementation of https://dvcs.w3.org/hg/dap/raw-file/default/media-stream-capture/MediaRecorder.html
|
||||||
* The MediaRecorder accepts a mediaStream as input source passed from UA. When recorder starts,
|
* The MediaRecorder accepts a mediaStream as input source passed from UA. When recorder starts,
|
||||||
* a MediaEncoder will be created and accept the mediaStream as input source.
|
* a MediaEncoder will be created and accept the mediaStream as input source.
|
||||||
* Encoder will get the raw data by track data changes, encode it by selected MIME Type, then store the encoded in EncodedBufferCache object.
|
* Encoder will get the raw data by track data changes, encode it by selected MIME Type, then store the encoded in a MutableBlobStorage object.
|
||||||
* The encoded data will be extracted on every timeslice passed from Start function call or by RequestData function.
|
* The encoded data will be extracted on every timeslice passed from Start function call or by RequestData function.
|
||||||
* Thread model:
|
* Thread model:
|
||||||
* When the recorder starts, it creates a "Media Encoder" thread to read data from MediaEncoder object and store buffer in EncodedBufferCache object.
|
* When the recorder starts, it creates a "Media Encoder" thread to read data from MediaEncoder object and store buffer in MutableBlobStorage object.
|
||||||
* Also extract the encoded data and create blobs on every timeslice passed from start function or RequestData function called by UA.
|
* Also extract the encoded data and create blobs on every timeslice passed from start function or RequestData function called by UA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ public:
|
|||||||
void Pause(ErrorResult& aResult);
|
void Pause(ErrorResult& aResult);
|
||||||
// Resume a paused recording.
|
// Resume a paused recording.
|
||||||
void Resume(ErrorResult& aResult);
|
void Resume(ErrorResult& aResult);
|
||||||
// Extract encoded data Blob from EncodedBufferCache.
|
// Extract encoded data Blob from MutableBlobStorage.
|
||||||
void RequestData(ErrorResult& aResult);
|
void RequestData(ErrorResult& aResult);
|
||||||
// Return the The DOMMediaStream passed from UA.
|
// Return the The DOMMediaStream passed from UA.
|
||||||
DOMMediaStream* Stream() const { return mDOMStream; }
|
DOMMediaStream* Stream() const { return mDOMStream; }
|
||||||
@ -121,7 +122,7 @@ protected:
|
|||||||
|
|
||||||
MediaRecorder& operator = (const MediaRecorder& x) = delete;
|
MediaRecorder& operator = (const MediaRecorder& x) = delete;
|
||||||
// Create dataavailable event with Blob data and it runs in main thread
|
// Create dataavailable event with Blob data and it runs in main thread
|
||||||
nsresult CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob);
|
nsresult CreateAndDispatchBlobEvent(Blob* aBlob);
|
||||||
// Creating a simple event to notify UA simple event.
|
// Creating a simple event to notify UA simple event.
|
||||||
void DispatchSimpleEvent(const nsAString & aStr);
|
void DispatchSimpleEvent(const nsAString & aStr);
|
||||||
// Creating a error event with message.
|
// Creating a error event with message.
|
||||||
|
@ -104,7 +104,6 @@ EXPORTS += [
|
|||||||
'DecoderDoctorDiagnostics.h',
|
'DecoderDoctorDiagnostics.h',
|
||||||
'DecoderTraits.h',
|
'DecoderTraits.h',
|
||||||
'DOMMediaStream.h',
|
'DOMMediaStream.h',
|
||||||
'EncodedBufferCache.h',
|
|
||||||
'FileBlockCache.h',
|
'FileBlockCache.h',
|
||||||
'FrameStatistics.h',
|
'FrameStatistics.h',
|
||||||
'Intervals.h',
|
'Intervals.h',
|
||||||
@ -218,7 +217,6 @@ UNIFIED_SOURCES += [
|
|||||||
'CubebUtils.cpp',
|
'CubebUtils.cpp',
|
||||||
'DecoderDoctorDiagnostics.cpp',
|
'DecoderDoctorDiagnostics.cpp',
|
||||||
'DOMMediaStream.cpp',
|
'DOMMediaStream.cpp',
|
||||||
'EncodedBufferCache.cpp',
|
|
||||||
'FileBlockCache.cpp',
|
'FileBlockCache.cpp',
|
||||||
'FileMediaResource.cpp',
|
'FileMediaResource.cpp',
|
||||||
'GetUserMediaRequest.cpp',
|
'GetUserMediaRequest.cpp',
|
||||||
|
Loading…
Reference in New Issue
Block a user