Bug 1403706 - Remove race conditions in temporary blob - part 2 - MediaRecorder should use MutableBlobStorage, r=pehrsons, r=smaug

This commit is contained in:
Andrea Marchesini 2017-10-05 07:41:41 +02:00
parent 7c36c77153
commit 970f42c82a
6 changed files with 131 additions and 263 deletions

View File

@ -11,6 +11,7 @@
#include "prio.h" #include "prio.h"
class nsIEventTarget; class nsIEventTarget;
class nsIRunnable;
namespace mozilla { namespace mozilla {

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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.

View File

@ -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',