mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 17:25:36 +00:00
Bug 1014393 - Remove MediaRecorder::Session::PushBlobRunnable. r=bryce
This moves the impl of PushBlobRunnable from a runnable to MozPromise, which let's us more easily modularize it's parts (gather the blob, fire dataavailable) to make individual code paths more explicit. Differential Revision: https://phabricator.services.mozilla.com/D17813 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
e22277134b
commit
e8685eab05
@ -210,63 +210,6 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
|
|||||||
public DOMMediaStream::TrackListener {
|
public DOMMediaStream::TrackListener {
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Session)
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Session)
|
||||||
|
|
||||||
// Main thread task.
|
|
||||||
// Create a blob event and send back to client.
|
|
||||||
class PushBlobRunnable : public Runnable, public MutableBlobStorageCallback {
|
|
||||||
public:
|
|
||||||
// We need to always declare refcounting because
|
|
||||||
// MutableBlobStorageCallback has pure-virtual refcounting.
|
|
||||||
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"),
|
|
||||||
mSession(aSession),
|
|
||||||
mDestroyRunnable(aDestroyRunnable) {}
|
|
||||||
|
|
||||||
NS_IMETHOD Run() override {
|
|
||||||
LOG(LogLevel::Debug, ("Session.PushBlobRunnable s=(%p)", mSession.get()));
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
mSession->GetBlobWhenReady(this);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, Blob* aBlob,
|
|
||||||
nsresult aRv) override {
|
|
||||||
RefPtr<MediaRecorder> recorder = mSession->mRecorder;
|
|
||||||
if (!recorder) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NS_FAILED(aRv)) {
|
|
||||||
mSession->DoSessionEndTask(aRv);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult rv = recorder->CreateAndDispatchBlobEvent(aBlob);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
mSession->DoSessionEndTask(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 {
|
class StoreEncodedBufferRunnable final : public Runnable {
|
||||||
RefPtr<Session> mSession;
|
RefPtr<Session> mSession;
|
||||||
nsTArray<nsTArray<uint8_t>> mBuffer;
|
nsTArray<nsTArray<uint8_t>> mBuffer;
|
||||||
@ -463,13 +406,13 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
|
|||||||
};
|
};
|
||||||
|
|
||||||
friend class EncoderErrorNotifierRunnable;
|
friend class EncoderErrorNotifierRunnable;
|
||||||
friend class PushBlobRunnable;
|
|
||||||
friend class DestroyRunnable;
|
friend class DestroyRunnable;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Session(MediaRecorder* aRecorder, uint32_t aTimeSlice)
|
Session(MediaRecorder* aRecorder, uint32_t aTimeSlice)
|
||||||
: mRecorder(aRecorder),
|
: mRecorder(aRecorder),
|
||||||
mMediaStreamReady(false),
|
mMediaStreamReady(false),
|
||||||
|
mMainThread(mRecorder->GetOwner()->EventTargetFor(TaskCategory::Other)),
|
||||||
mTimeSlice(aTimeSlice),
|
mTimeSlice(aTimeSlice),
|
||||||
mRunningState(RunningState::Idling) {
|
mRunningState(RunningState::Idling) {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
@ -626,17 +569,31 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult RequestData() {
|
void RequestData() {
|
||||||
LOG(LogLevel::Debug, ("Session.RequestData"));
|
LOG(LogLevel::Debug, ("Session.RequestData"));
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
if (NS_FAILED(
|
GatherBlob()->Then(
|
||||||
NS_DispatchToMainThread(new PushBlobRunnable(this, nullptr)))) {
|
mMainThread, __func__,
|
||||||
MOZ_ASSERT(false, "RequestData NS_DispatchToMainThread failed");
|
[this, self = RefPtr<Session>(this)](
|
||||||
return NS_ERROR_FAILURE;
|
const BlobPromise::ResolveOrRejectValue& aResult) {
|
||||||
}
|
RefPtr<MediaRecorder> recorder = mRecorder;
|
||||||
|
if (!recorder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
if (aResult.IsReject()) {
|
||||||
|
LOG(LogLevel::Warning, ("GatherBlob failed for RequestData()"));
|
||||||
|
DoSessionEndTask(aResult.RejectValue());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult rv =
|
||||||
|
recorder->CreateAndDispatchBlobEvent(aResult.ResolveValue());
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
DoSessionEndTask(NS_OK);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaybeCreateMutableBlobStorage() {
|
void MaybeCreateMutableBlobStorage() {
|
||||||
@ -646,14 +603,46 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetBlobWhenReady(MutableBlobStorageCallback* aCallback) {
|
static const bool IsExclusive = true;
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
using BlobPromise =
|
||||||
|
MozPromise<nsMainThreadPtrHandle<Blob>, nsresult, IsExclusive>;
|
||||||
|
class BlobStorer : public MutableBlobStorageCallback {
|
||||||
|
MozPromiseHolder<BlobPromise> mHolder;
|
||||||
|
|
||||||
|
virtual ~BlobStorer() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BlobStorer() = default;
|
||||||
|
|
||||||
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlobStorer, override)
|
||||||
|
|
||||||
|
void BlobStoreCompleted(MutableBlobStorage*, Blob* aBlob,
|
||||||
|
nsresult aRv) override {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
if (NS_FAILED(aRv)) {
|
||||||
|
mHolder.Reject(aRv, __func__);
|
||||||
|
} else {
|
||||||
|
mHolder.Resolve(nsMainThreadPtrHandle<Blob>(
|
||||||
|
MakeAndAddRef<nsMainThreadPtrHolder<Blob>>(
|
||||||
|
"BlobStorer::ResolveBlob", aBlob)),
|
||||||
|
__func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<BlobPromise> Promise() { return mHolder.Ensure(__func__); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stops gathering data into the current blob and resolves when the current
|
||||||
|
// blob is available. Future data will be stored in a new blob.
|
||||||
|
RefPtr<BlobPromise> GatherBlob() {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
RefPtr<BlobStorer> storer = MakeAndAddRef<BlobStorer>();
|
||||||
MaybeCreateMutableBlobStorage();
|
MaybeCreateMutableBlobStorage();
|
||||||
mMutableBlobStorage->GetBlobWhenReady(mRecorder->GetParentObject(),
|
mMutableBlobStorage->GetBlobWhenReady(
|
||||||
NS_ConvertUTF16toUTF8(mMimeType),
|
mRecorder->GetParentObject(), NS_ConvertUTF16toUTF8(mMimeType), storer);
|
||||||
aCallback);
|
|
||||||
mMutableBlobStorage = nullptr;
|
mMutableBlobStorage = nullptr;
|
||||||
|
|
||||||
|
return storer->Promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<SizeOfPromise> SizeOfExcludingThis(
|
RefPtr<SizeOfPromise> SizeOfExcludingThis(
|
||||||
@ -684,10 +673,11 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
|
|||||||
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 MutableBlobStorage.
|
// 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 a dispatch of a blob to
|
||||||
// PushBlobRunnable to main thread.
|
// main thread.
|
||||||
void Extract(bool aForceFlush, Runnable* aDestroyRunnable) {
|
void Extract(bool aForceFlush, Runnable* aDestroyRunnable) {
|
||||||
MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
|
MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
|
||||||
|
|
||||||
@ -716,12 +706,29 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
|
|||||||
pushBlob = true;
|
pushBlob = true;
|
||||||
}
|
}
|
||||||
if (pushBlob) {
|
if (pushBlob) {
|
||||||
if (NS_FAILED(NS_DispatchToMainThread(
|
mLastBlobTimeStamp = TimeStamp::Now();
|
||||||
new PushBlobRunnable(this, aDestroyRunnable)))) {
|
InvokeAsync(mMainThread, this, __func__, &Session::GatherBlob)
|
||||||
MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
|
->Then(mMainThread, __func__,
|
||||||
} else {
|
[this, self = RefPtr<Session>(this)](
|
||||||
mLastBlobTimeStamp = TimeStamp::Now();
|
const BlobPromise::ResolveOrRejectValue& aResult) {
|
||||||
}
|
RefPtr<MediaRecorder> recorder = mRecorder;
|
||||||
|
if (!recorder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aResult.IsReject()) {
|
||||||
|
LOG(LogLevel::Warning,
|
||||||
|
("GatherBlob failed for pushing blob"));
|
||||||
|
DoSessionEndTask(aResult.RejectValue());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult rv = recorder->CreateAndDispatchBlobEvent(
|
||||||
|
aResult.ResolveValue());
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
DoSessionEndTask(NS_OK);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (aDestroyRunnable) {
|
} else if (aDestroyRunnable) {
|
||||||
if (NS_FAILED(NS_DispatchToMainThread(aDestroyRunnable))) {
|
if (NS_FAILED(NS_DispatchToMainThread(aDestroyRunnable))) {
|
||||||
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
||||||
@ -1023,16 +1030,17 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
|
|||||||
|
|
||||||
RefPtr<Runnable> destroyRunnable = new DestroyRunnable(this);
|
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(
|
DebugOnly<nsresult> rv = NS_DispatchToMainThread(destroyRunnable);
|
||||||
new PushBlobRunnable(this, destroyRunnable)))) {
|
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv),
|
||||||
MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
|
"NS_DispatchToMainThread DestroyRunnable failed");
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (NS_FAILED(NS_DispatchToMainThread(destroyRunnable))) {
|
DebugOnly<nsresult> rv = mEncoderThread->Dispatch(
|
||||||
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
NewRunnableMethod<bool, StoreRefPtrPassByPtr<Runnable>>(
|
||||||
}
|
"mozilla::MediaRecorder::Session::Extract", this,
|
||||||
|
&Session::Extract, true, std::move(destroyRunnable)));
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1193,6 +1201,8 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
|
|||||||
// set.
|
// set.
|
||||||
nsTArray<RefPtr<MediaStreamTrack>> mMediaStreamTracks;
|
nsTArray<RefPtr<MediaStreamTrack>> mMediaStreamTracks;
|
||||||
|
|
||||||
|
// Main thread used for MozPromise operations.
|
||||||
|
const RefPtr<nsISerialEventTarget> mMainThread;
|
||||||
// Runnable thread for reading data from MediaEncoder.
|
// Runnable thread for reading data from MediaEncoder.
|
||||||
RefPtr<TaskQueue> mEncoderThread;
|
RefPtr<TaskQueue> mEncoderThread;
|
||||||
// MediaEncoder pipeline.
|
// MediaEncoder pipeline.
|
||||||
@ -1218,8 +1228,6 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
|
|||||||
Result<RunningState, nsresult> mRunningState;
|
Result<RunningState, nsresult> mRunningState;
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS_INHERITED0(MediaRecorder::Session::PushBlobRunnable, Runnable)
|
|
||||||
|
|
||||||
MediaRecorder::~MediaRecorder() {
|
MediaRecorder::~MediaRecorder() {
|
||||||
LOG(LogLevel::Debug, ("~MediaRecorder (%p)", this));
|
LOG(LogLevel::Debug, ("~MediaRecorder (%p)", this));
|
||||||
UnRegisterActivityObserver();
|
UnRegisterActivityObserver();
|
||||||
@ -1377,10 +1385,7 @@ void MediaRecorder::RequestData(ErrorResult& aResult) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(mSessions.Length() > 0);
|
MOZ_ASSERT(mSessions.Length() > 0);
|
||||||
nsresult rv = mSessions.LastElement()->RequestData();
|
mSessions.LastElement()->RequestData();
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
NotifyError(rv);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject* MediaRecorder::WrapObject(JSContext* aCx,
|
JSObject* MediaRecorder::WrapObject(JSContext* aCx,
|
||||||
|
Loading…
Reference in New Issue
Block a user