--- a/dom/media/Benchmark.cpp
+++ b/dom/media/Benchmark.cpp
@@ -209,29 +209,25 @@ BenchmarkPlayback::DemuxNextSample()
}
void
BenchmarkPlayback::InitDecoder(TrackInfo&& aInfo)
{
MOZ_ASSERT(OnThread());
RefPtr<PDMFactory> platform = new PDMFactory();
- mDecoder = platform->CreateDecoder({ aInfo, mDecoderTaskQueue });
- if (!mDecoder) {
- MainThreadShutdown();
- return;
- }
RefPtr<Benchmark> ref(mMainThreadState);
- mDecoder->Init()->Then(
- Thread(), __func__,
- [this, ref](TrackInfo::TrackType aTrackType) {
- InputExhausted();
- },
- [this, ref](const MediaResult& aError) {
- MainThreadShutdown();
+ platform->CreateDecoder({ aInfo, mDecoderTaskQueue })
+ ->Then(Thread(), __func__,
+ [this, ref] (RefPtr<MediaDataDecoder> aDecoder) {
+ mDecoder = aDecoder.forget();
+ InputExhausted();
+ },
+ [this, ref] () {
+ MainThreadShutdown();
});
}
void
BenchmarkPlayback::MainThreadShutdown()
{
MOZ_ASSERT(OnThread());
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -42,16 +42,18 @@ mozilla::LazyLogModule gMediaDemuxerLog(
#define LOG(arg, ...) MOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Debug, ("MediaFormatReader(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
#define LOGV(arg, ...) MOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Verbose, ("MediaFormatReader(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
#define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
namespace mozilla {
+using CreateDecoderPromise = PlatformDecoderModule::CreateDecoderPromise;
+
/**
* This is a singleton which controls the number of decoders that can be
* created concurrently. Before calling PDMFactory::CreateDecoder(), Alloc()
* must be called to get a token object as a permission to create a decoder.
* The token should stay alive until Shutdown() is called on the decoder.
* The destructor of the token will restore the decoder count so it is available
* for next calls of Alloc().
*/
@@ -477,54 +479,54 @@ public:
// pristine state so CreateDecoder() is ready to be called again immediately.
void ShutdownDecoder(TrackType aTrack)
{
MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack
|| aTrack == TrackInfo::kVideoTrack);
auto& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo;
data.mPolicy->Cancel();
data.mTokenRequest.DisconnectIfExists();
- data.mInitRequest.DisconnectIfExists();
+ data.mCreateDecoderRequest.DisconnectIfExists();
if (data.mDecoder) {
mOwner->mShutdownPromisePool->ShutdownDecoder(data.mDecoder.forget());
}
data.mStage = Stage::None;
+ data.mToken = nullptr;
MOZ_ASSERT(!data.mToken);
}
private:
class Wrapper;
enum class Stage : int8_t
{
None,
WaitForToken,
CreateDecoder,
- WaitForInit
+ WaitForDecoder
};
struct Data
{
Data(DecoderData& aOwnerData, TrackType aTrack, TaskQueue* aThread)
: mOwnerData(aOwnerData)
, mTrack(aTrack)
, mPolicy(new LocalAllocPolicy(aTrack, aThread)) { }
DecoderData& mOwnerData;
const TrackType mTrack;
RefPtr<LocalAllocPolicy> mPolicy;
Stage mStage = Stage::None;
RefPtr<Token> mToken;
RefPtr<MediaDataDecoder> mDecoder;
MozPromiseRequestHolder<TokenPromise> mTokenRequest;
- MozPromiseRequestHolder<InitPromise> mInitRequest;
+ MozPromiseRequestHolder<CreateDecoderPromise> mCreateDecoderRequest;
} mAudio, mVideo;
void RunStage(Data& aData);
- MediaResult DoCreateDecoder(Data& aData);
- void DoInitDecoder(Data& aData);
+ void DoCreateDecoder(Data& aData);
// guaranteed to be valid by the owner.
const NotNull<MediaFormatReader*> mOwner;
};
void
MediaFormatReader::DecoderFactory::CreateDecoder(TrackType aTrack)
{
@@ -610,78 +612,68 @@ MediaFormatReader::DecoderFactory::RunSt
MOZ_ASSERT(!aData.mToken);
MOZ_ASSERT(aData.mTokenRequest.Exists());
break;
}
case Stage::CreateDecoder: {
MOZ_ASSERT(aData.mToken);
MOZ_ASSERT(!aData.mDecoder);
- MOZ_ASSERT(!aData.mInitRequest.Exists());
-
- MediaResult rv = DoCreateDecoder(aData);
- if (NS_FAILED(rv)) {
- NS_WARNING("Error constructing decoders");
- aData.mToken = nullptr;
- aData.mStage = Stage::None;
- mOwner->NotifyError(aData.mTrack, rv);
- return;
- }
-
- aData.mDecoder = new Wrapper(aData.mDecoder.forget(), aData.mToken.forget());
- DoInitDecoder(aData);
- aData.mStage = Stage::WaitForInit;
+ MOZ_ASSERT(!aData.mCreateDecoderRequest.Exists());
+ DoCreateDecoder(aData);
+ aData.mStage = Stage::WaitForDecoder;
break;
}
- case Stage::WaitForInit: {
- MOZ_ASSERT(aData.mDecoder);
- MOZ_ASSERT(aData.mInitRequest.Exists());
+ case Stage::WaitForDecoder: {
+ MOZ_ASSERT(!aData.mDecoder);
+ MOZ_ASSERT(aData.mCreateDecoderRequest.Exists());
break;
}
}
}
-MediaResult
+void
MediaFormatReader::DecoderFactory::DoCreateDecoder(Data& aData)
{
auto& ownerData = aData.mOwnerData;
auto decoderCreatingError = "error creating audio decoder";
MediaResult result =
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, decoderCreatingError);
if (!mOwner->mPlatform) {
mOwner->mPlatform = new PDMFactory();
if (mOwner->IsEncrypted()) {
MOZ_ASSERT(mOwner->mCDMProxy);
mOwner->mPlatform->SetCDMProxy(mOwner->mCDMProxy);
}
}
+ RefPtr<CreateDecoderPromise> createDecoderPromise;
switch (aData.mTrack) {
case TrackInfo::kAudioTrack: {
- aData.mDecoder = mOwner->mPlatform->CreateDecoder({
+ createDecoderPromise = mOwner->mPlatform->CreateDecoder({
ownerData.mInfo
? *ownerData.mInfo->GetAsAudioInfo()
: *ownerData.mOriginalInfo->GetAsAudioInfo(),
ownerData.mTaskQueue,
mOwner->mCrashHelper,
ownerData.mIsBlankDecode,
&result,
TrackInfo::kAudioTrack,
&mOwner->OnTrackWaitingForKeyProducer()
});
break;
}
case TrackType::kVideoTrack: {
// Decoders use the layers backend to decide if they can use hardware decoding,
// so specify LAYERS_NONE if we want to forcibly disable it.
- aData.mDecoder = mOwner->mPlatform->CreateDecoder({
+ createDecoderPromise = mOwner->mPlatform->CreateDecoder({
ownerData.mInfo
? *ownerData.mInfo->GetAsVideoInfo()
: *ownerData.mOriginalInfo->GetAsVideoInfo(),
ownerData.mTaskQueue,
mOwner->mKnowsCompositor,
mOwner->GetImageContainer(),
mOwner->mCrashHelper,
ownerData.mIsBlankDecode,
@@ -691,49 +683,41 @@ MediaFormatReader::DecoderFactory::DoCre
});
break;
}
default:
break;
}
- if (aData.mDecoder) {
- return NS_OK;
- }
-
- ownerData.mDescription = decoderCreatingError;
- return result;
-}
-
-void
-MediaFormatReader::DecoderFactory::DoInitDecoder(Data& aData)
-{
- auto& ownerData = aData.mOwnerData;
-
- aData.mDecoder->Init()
- ->Then(mOwner->OwnerThread(), __func__,
- [this, &aData, &ownerData](TrackType aTrack) {
- aData.mInitRequest.Complete();
- aData.mStage = Stage::None;
- MutexAutoLock lock(ownerData.mMutex);
- ownerData.mDecoder = aData.mDecoder.forget();
- ownerData.mDescription = ownerData.mDecoder->GetDescriptionName();
- mOwner->SetVideoDecodeThreshold();
- mOwner->ScheduleUpdate(aTrack);
- },
- [this, &aData, &ownerData](const MediaResult& aError) {
- aData.mInitRequest.Complete();
- MOZ_RELEASE_ASSERT(!ownerData.mDecoder,
- "Can't have a decoder already set");
- aData.mStage = Stage::None;
- mOwner->mShutdownPromisePool->ShutdownDecoder(aData.mDecoder.forget());
- mOwner->NotifyError(aData.mTrack, aError);
- })
- ->Track(aData.mInitRequest);
+ createDecoderPromise->Then(mOwner->OwnerThread(), __func__,
+ [this, &aData, &ownerData] (RefPtr<MediaDataDecoder> aDecoder) {
+ aData.mCreateDecoderRequest.Complete();
+ aData.mStage = Stage::None;
+
+ MutexAutoLock lock(ownerData.mMutex);
+ ownerData.mDecoder = new Wrapper(aDecoder.forget(), aData.mToken.forget());
+ ownerData.mDescription = ownerData.mDecoder->GetDescriptionName();
+
+ mOwner->SetVideoDecodeThreshold();
+ mOwner->ScheduleUpdate(aData.mTrack);
+ },
+ [this, &aData, &ownerData, &decoderCreatingError] () {
+ aData.mCreateDecoderRequest.Complete();
+ aData.mStage = Stage::None;
+ aData.mToken = nullptr;
+ NS_WARNING("Error constructing decoders");
+
+ MOZ_RELEASE_ASSERT(!ownerData.mDecoder,
+ "Can't have a decoder already set");
+ MutexAutoLock lock(ownerData.mMutex);
+ ownerData.mDescription = decoderCreatingError;
+
+ mOwner->NotifyError(aData.mTrack, NS_ERROR_DOM_MEDIA_FATAL_ERR);
+ })->Track(aData.mCreateDecoderRequest);
}
// DemuxerProxy ensures that the original main demuxer is only ever accessed
// via its own dedicated task queue.
// This ensure that the reader's taskqueue will never blocked while a demuxer
// is itself blocked attempting to access the MediaCache or the MediaResource.
class MediaFormatReader::DemuxerProxy
{
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -208,34 +208,32 @@ MP4Decoder::IsEnabled()
// attempt to use hardware decoding for small videos.
static const uint8_t sTestH264ExtraData[] = {
0x01, 0x42, 0xc0, 0x1e, 0xff, 0xe1, 0x00, 0x17, 0x67, 0x42,
0xc0, 0x1e, 0xbb, 0x40, 0x50, 0x17, 0xfc, 0xb8, 0x08, 0x80,
0x00, 0x00, 0x32, 0x00, 0x00, 0x0b, 0xb5, 0x07, 0x8b, 0x17,
0x50, 0x01, 0x00, 0x04, 0x68, 0xce, 0x32, 0xc8
};
-static already_AddRefed<MediaDataDecoder>
+static RefPtr<PlatformDecoderModule::CreateDecoderPromise>
CreateTestH264Decoder(layers::KnowsCompositor* aKnowsCompositor,
VideoInfo& aConfig,
TaskQueue* aTaskQueue)
{
aConfig.mMimeType = "video/avc";
aConfig.mId = 1;
aConfig.mDuration = 40000;
aConfig.mMediaTime = 0;
aConfig.mImage = aConfig.mDisplay = nsIntSize(640, 360);
aConfig.mExtraData = new MediaByteBuffer();
aConfig.mExtraData->AppendElements(sTestH264ExtraData,
MOZ_ARRAY_LENGTH(sTestH264ExtraData));
RefPtr<PDMFactory> platform = new PDMFactory();
- RefPtr<MediaDataDecoder> decoder(platform->CreateDecoder({ aConfig, aTaskQueue, aKnowsCompositor }));
-
- return decoder.forget();
+ return platform->CreateDecoder({ aConfig, aTaskQueue, aKnowsCompositor });
}
/* static */ already_AddRefed<dom::Promise>
MP4Decoder::IsVideoAccelerated(layers::KnowsCompositor* aKnowsCompositor, nsIGlobalObject* aParent)
{
MOZ_ASSERT(NS_IsMainThread());
ErrorResult rv;
@@ -244,51 +242,41 @@ MP4Decoder::IsVideoAccelerated(layers::K
if (rv.Failed()) {
rv.SuppressException();
return nullptr;
}
RefPtr<TaskQueue> taskQueue =
new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
VideoInfo config;
- RefPtr<MediaDataDecoder> decoder(CreateTestH264Decoder(aKnowsCompositor, config, taskQueue));
- if (!decoder) {
- taskQueue->BeginShutdown();
- taskQueue->AwaitShutdownAndIdle();
- promise->MaybeResolve(NS_LITERAL_STRING("No; Failed to create H264 decoder"));
- return promise.forget();
- }
- decoder->Init()
- ->Then(aParent->AbstractMainThreadFor(TaskCategory::Other),
- __func__,
- [promise, decoder, taskQueue] (TrackInfo::TrackType aTrack) {
- nsCString failureReason;
- bool ok = decoder->IsHardwareAccelerated(failureReason);
- nsAutoString result;
- if (ok) {
- result.AssignLiteral("Yes");
- } else {
- result.AssignLiteral("No");
- }
- if (failureReason.Length()) {
- result.AppendLiteral("; ");
- AppendUTF8toUTF16(failureReason, result);
- }
- decoder->Shutdown();
- taskQueue->BeginShutdown();
- taskQueue->AwaitShutdownAndIdle();
- promise->MaybeResolve(result);
- },
- [promise, decoder, taskQueue] (MediaResult aError) {
- decoder->Shutdown();
- taskQueue->BeginShutdown();
- taskQueue->AwaitShutdownAndIdle();
- promise->MaybeResolve(NS_LITERAL_STRING("No; Failed to initialize H264 decoder"));
- });
+ CreateTestH264Decoder(aKnowsCompositor, config, taskQueue)
+ ->Then(aParent->AbstractMainThreadFor(TaskCategory::Other), __func__,
+ [promise, taskQueue] (RefPtr<MediaDataDecoder> aDecoder) {
+ nsCString failureReason;
+ bool ok = aDecoder->IsHardwareAccelerated(failureReason);
+ nsAutoString result;
+ if (ok) {
+ result.AssignLiteral("Yes");
+ } else {
+ result.AssignLiteral("No");
+ }
+ if (failureReason.Length()) {
+ result.AppendLiteral("; ");
+ AppendUTF8toUTF16(failureReason, result);
+ }
+ aDecoder->Shutdown();
+ taskQueue->BeginShutdown();
+ taskQueue->AwaitShutdownAndIdle();
+ promise->MaybeResolve(result);
+ }, [promise, taskQueue] () {
+ taskQueue->BeginShutdown();
+ taskQueue->AwaitShutdownAndIdle();
+ promise->MaybeResolve(NS_LITERAL_STRING("No; Failed to create H264 decoder"));
+ });
return promise.forget();
}
void
MP4Decoder::GetMozDebugReaderData(nsACString& aString)
{
if (mReader) {
--- a/dom/media/ipc/PVideoDecoder.ipdl
+++ b/dom/media/ipc/PVideoDecoder.ipdl
@@ -56,16 +56,20 @@ parent:
async SetSeekThreshold(int64_t time);
async __delete__();
child:
async InitComplete(bool hardware, nsCString hardwareReason, uint32_t conversion);
async InitFailed(nsresult reason);
+ async CreateDecoderCompleted(bool hardware,
+ nsCString hardwareReason,
+ uint32_t conversion);
+ async CreateDecoderFailed();
async FlushComplete();
// Each output includes a SurfaceDescriptorGPUVideo that represents the decoded
// frame. This SurfaceDescriptor can be used on the Layers IPDL protocol, but
// must be released explicitly using DeallocateSurfaceDescriptorGPUVideo
// on the manager protocol.
async Output(VideoDataIPDL data);
--- a/dom/media/ipc/PVideoDecoderManager.ipdl
+++ b/dom/media/ipc/PVideoDecoderManager.ipdl
@@ -11,17 +11,17 @@ using struct mozilla::layers::TextureFac
namespace mozilla {
namespace dom {
sync protocol PVideoDecoderManager
{
manages PVideoDecoder;
parent:
- sync PVideoDecoder(VideoInfo info, TextureFactoryIdentifier identifier) returns (bool success);
+ sync PVideoDecoder(VideoInfo info, TextureFactoryIdentifier identifier);
sync Readback(SurfaceDescriptorGPUVideo sd) returns (SurfaceDescriptor aResult);
async DeallocateSurfaceDescriptorGPUVideo(SurfaceDescriptorGPUVideo sd);
};
} // namespace dom
} // namespace mozilla
--- a/dom/media/ipc/RemoteVideoDecoder.cpp
+++ b/dom/media/ipc/RemoteVideoDecoder.cpp
@@ -11,16 +11,18 @@
#include "MediaInfo.h"
#include "MediaPrefs.h"
#include "ImageContainer.h"
#include "mozilla/layers/SynchronousTask.h"
namespace mozilla {
namespace dom {
+using CreateDecoderPromise = PlatformDecoderModule::CreateDecoderPromise;
+
using base::Thread;
using namespace ipc;
using namespace layers;
using namespace gfx;
RemoteVideoDecoder::RemoteVideoDecoder()
: mActor(new VideoDecoderChild())
{
@@ -42,16 +44,43 @@ RemoteVideoDecoder::~RemoteVideoDecoder(
// Drop out references to the actor so that the last ref
// always gets released on the manager thread.
actor = nullptr;
mActor = nullptr;
VideoDecoderManagerChild::GetManagerThread()->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
}
+RefPtr<CreateDecoderPromise>
+RemoteVideoDecoder::CreateRemoteDecoder(const CreateDecoderParams& aParams)
+{
+ MOZ_ASSERT(mCreateDecoderPromise.IsEmpty());
+
+ RefPtr<CreateDecoderPromise> promise;
+ SynchronousTask task("InitIPDL");
+ VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([&]() {
+ AutoCompleteTask complete(&task);
+ promise = mActor->InitIPDL(aParams.VideoConfig(),
+ aParams.mKnowsCompositor->GetTextureFactoryIdentifier());
+ }), NS_DISPATCH_NORMAL);
+ task.Wait();
+
+ RefPtr<RemoteVideoDecoder> self = this;
+ promise
+ ->Then(VideoDecoderManagerChild::GetManagerAbstractThread(), __func__,
+ [self] () {
+ self->mCreateDecoderPromise.Resolve(self, __func__);
+ },
+ [self] () {
+ self->mCreateDecoderPromise.Reject(false, __func__);
+ });
+
+ return mCreateDecoderPromise.Ensure(__func__);
+}
+
RefPtr<MediaDataDecoder::InitPromise>
RemoteVideoDecoder::Init()
{
RefPtr<RemoteVideoDecoder> self = this;
return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__, [self, this]() { return mActor->Init(); });
}
@@ -133,37 +162,29 @@ RemoteDecoderModule::SupportsMimeType(co
bool
RemoteDecoderModule::Supports(const TrackInfo& aTrackInfo,
DecoderDoctorDiagnostics* aDiagnostics) const
{
return mWrapped->Supports(aTrackInfo, aDiagnostics);
}
-already_AddRefed<MediaDataDecoder>
+RefPtr<CreateDecoderPromise>
RemoteDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
{
if (!MediaPrefs::PDMUseGPUDecoder() ||
!aParams.mKnowsCompositor ||
aParams.mKnowsCompositor->GetTextureFactoryIdentifier().mParentProcessType != GeckoProcessType_GPU) {
return mWrapped->CreateVideoDecoder(aParams);
}
- RefPtr<RemoteVideoDecoder> object = new RemoteVideoDecoder();
+ RefPtr<RemoteVideoDecoder> remoteDecoderHolder = new RemoteVideoDecoder();
+ return remoteDecoderHolder->CreateRemoteDecoder(aParams);
+}
- SynchronousTask task("InitIPDL");
- bool success;
- VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([&]() {
- AutoCompleteTask complete(&task);
- success = object->mActor->InitIPDL(aParams.VideoConfig(),
- aParams.mKnowsCompositor->GetTextureFactoryIdentifier());
- }), NS_DISPATCH_NORMAL);
- task.Wait();
-
- if (!success) {
- return nullptr;
- }
-
- return object.forget();
+RefPtr<CreateDecoderPromise>
+RemoteDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
+{
+ return CreateDecoderPromise::CreateAndReject(false, __func__);
}
} // namespace dom
} // namespace mozilla
--- a/dom/media/ipc/RemoteVideoDecoder.h
+++ b/dom/media/ipc/RemoteVideoDecoder.h
@@ -21,16 +21,19 @@ class RemoteDecoderModule;
// to a 'real' decoder in the GPU process.
// All requests get forwarded to a VideoDecoderChild instance that
// operates solely on the VideoDecoderManagerChild thread.
class RemoteVideoDecoder : public MediaDataDecoder
{
public:
friend class RemoteDecoderModule;
+ RefPtr<PlatformDecoderModule::CreateDecoderPromise>
+ CreateRemoteDecoder(const CreateDecoderParams& aParams);
+
// MediaDataDecoder
RefPtr<InitPromise> Init() override;
RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
RefPtr<DecodePromise> Drain() override;
RefPtr<FlushPromise> Flush() override;
RefPtr<ShutdownPromise> Shutdown() override;
bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
void SetSeekThreshold(const media::TimeUnit& aTime) override;
@@ -41,16 +44,17 @@ public:
private:
RemoteVideoDecoder();
~RemoteVideoDecoder();
// Only ever written to from the reader task queue (during the constructor and
// destructor when we can guarantee no other threads are accessing it). Only
// read from the manager thread.
RefPtr<VideoDecoderChild> mActor;
+ MozPromiseHolder<PlatformDecoderModule::CreateDecoderPromise> mCreateDecoderPromise;
};
// A PDM implementation that creates RemoteVideoDecoders.
// We currently require a 'wrapped' PDM in order to be able to answer SupportsMimeType
// and DecoderNeedsConversion. Ideally we'd check these over IPDL using the manager
// protocol
class RemoteDecoderModule : public PlatformDecoderModule
{
@@ -61,24 +65,21 @@ public:
nsresult Startup() override;
bool SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const override;
bool Supports(const TrackInfo& aTrackInfo,
DecoderDoctorDiagnostics* aDiagnostics) const override;
- already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
- const CreateDecoderParams& aParams) override;
+ RefPtr<PlatformDecoderModule::CreateDecoderPromise>
+ CreateVideoDecoder(const CreateDecoderParams& aParams) override;
- already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
- const CreateDecoderParams& aParams) override
- {
- return nullptr;
- }
+ RefPtr<PlatformDecoderModule::CreateDecoderPromise>
+ CreateAudioDecoder(const CreateDecoderParams& aParams) override;
private:
RefPtr<PlatformDecoderModule> mWrapped;
};
} // namespace dom
} // namespace mozilla
--- a/dom/media/ipc/VideoDecoderChild.cpp
+++ b/dom/media/ipc/VideoDecoderChild.cpp
@@ -9,35 +9,39 @@
#include "base/thread.h"
#include "MediaInfo.h"
#include "ImageContainer.h"
#include "GPUVideoImage.h"
namespace mozilla {
namespace dom {
+using CreateDecoderPromise = PlatformDecoderModule::CreateDecoderPromise;
+
using base::Thread;
using namespace ipc;
using namespace layers;
using namespace gfx;
VideoDecoderChild::VideoDecoderChild()
: mThread(VideoDecoderManagerChild::GetManagerThread())
, mCanSend(false)
, mInitialized(false)
+ , mHasDecoder(false)
, mIsHardwareAccelerated(false)
, mConversion(MediaDataDecoder::ConversionRequired::kNeedNone)
, mNeedNewDecoder(false)
{
}
VideoDecoderChild::~VideoDecoderChild()
{
AssertOnManagerThread();
mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+ mCreateDecoderPromise.RejectIfExists(false, __func__);
}
mozilla::ipc::IPCResult
VideoDecoderChild::RecvOutput(const VideoDataIPDL& aData)
{
AssertOnManagerThread();
VideoInfo info(aData.display().width, aData.display().height);
@@ -105,73 +109,93 @@ mozilla::ipc::IPCResult
VideoDecoderChild::RecvInitFailed(const nsresult& aReason)
{
AssertOnManagerThread();
mInitPromise.RejectIfExists(aReason, __func__);
return IPC_OK();
}
mozilla::ipc::IPCResult
+VideoDecoderChild::RecvCreateDecoderCompleted(const bool& aHardware,
+ const nsCString& aHardwareReason,
+ const uint32_t& aConversion)
+{
+ AssertOnManagerThread();
+ mCreateDecoderPromise.ResolveIfExists(nullptr, __func__);
+ mHasDecoder = true;
+ mIsHardwareAccelerated = aHardware;
+ mHardwareAcceleratedReason = aHardwareReason;
+ mConversion = static_cast<MediaDataDecoder::ConversionRequired>(aConversion);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+VideoDecoderChild::RecvCreateDecoderFailed()
+{
+ AssertOnManagerThread();
+ mCreateDecoderPromise.RejectIfExists(false, __func__);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
VideoDecoderChild::RecvFlushComplete()
{
AssertOnManagerThread();
mFlushPromise.ResolveIfExists(true, __func__);
return IPC_OK();
}
void
VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy)
{
if (aWhy == AbnormalShutdown) {
// Defer reporting an error until we've recreated the manager so that
// it'll be safe for MediaFormatReader to recreate decoders
RefPtr<VideoDecoderChild> ref = this;
GetManager()->RunWhenRecreated(NS_NewRunnableFunction([=]() {
- if (ref->mInitialized) {
+ if (ref->mHasDecoder) {
mDecodedData.Clear();
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER,
__func__);
mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER,
__func__);
mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER,
__func__);
// Make sure the next request will be rejected accordingly if ever
// called.
mNeedNewDecoder = true;
} else {
- ref->mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER,
- __func__);
+ ref->mCreateDecoderPromise.RejectIfExists(false, __func__);
}
}));
}
mCanSend = false;
}
-bool
+RefPtr<CreateDecoderPromise>
VideoDecoderChild::InitIPDL(const VideoInfo& aVideoInfo,
const layers::TextureFactoryIdentifier& aIdentifier)
{
RefPtr<VideoDecoderManagerChild> manager =
VideoDecoderManagerChild::GetSingleton();
// If the manager isn't available, then don't initialize mIPDLSelfRef and
// leave us in an error state. We'll then immediately reject the promise when
// Init() is called and the caller can try again. Hopefully by then the new
// manager is ready, or we've notified the caller of it being no longer
// available. If not, then the cycle repeats until we're ready.
if (!manager || !manager->CanSend()) {
- return true;
+ // TODO : async continue to create PVideoDecoder.
+ return CreateDecoderPromise::CreateAndReject(false, __func__);
}
mIPDLSelfRef = this;
- bool success = false;
- if (manager->SendPVideoDecoderConstructor(this, aVideoInfo, aIdentifier,
- &success)) {
+ if (manager->SendPVideoDecoderConstructor(this, aVideoInfo, aIdentifier)) {
mCanSend = true;
}
- return success;
+ return mCreateDecoderPromise.Ensure(__func__);
}
void
VideoDecoderChild::DestroyIPDL()
{
if (mCanSend) {
PVideoDecoderChild::Send__delete__(this);
}
@@ -270,20 +294,22 @@ VideoDecoderChild::Drain()
return mDrainPromise.Ensure(__func__);
}
void
VideoDecoderChild::Shutdown()
{
AssertOnManagerThread();
mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+ mCreateDecoderPromise.RejectIfExists(false, __func__);
if (mCanSend) {
SendShutdown();
}
mInitialized = false;
+ mHasDecoder = false;
}
bool
VideoDecoderChild::IsHardwareAccelerated(nsACString& aFailureReason) const
{
aFailureReason = mHardwareAcceleratedReason;
return mIsHardwareAccelerated;
}
--- a/dom/media/ipc/VideoDecoderChild.h
+++ b/dom/media/ipc/VideoDecoderChild.h
@@ -28,32 +28,38 @@ public:
mozilla::ipc::IPCResult RecvOutput(const VideoDataIPDL& aData) override;
mozilla::ipc::IPCResult RecvInputExhausted() override;
mozilla::ipc::IPCResult RecvDrainComplete() override;
mozilla::ipc::IPCResult RecvError(const nsresult& aError) override;
mozilla::ipc::IPCResult RecvInitComplete(const bool& aHardware,
const nsCString& aHardwareReason,
const uint32_t& aConversion) override;
mozilla::ipc::IPCResult RecvInitFailed(const nsresult& aReason) override;
+ mozilla::ipc::IPCResult RecvCreateDecoderCompleted(const bool& aHardware,
+ const nsCString& aHardwareReason,
+ const uint32_t& aConversion) override;
+ mozilla::ipc::IPCResult RecvCreateDecoderFailed() override;
mozilla::ipc::IPCResult RecvFlushComplete() override;
void ActorDestroy(ActorDestroyReason aWhy) override;
RefPtr<MediaDataDecoder::InitPromise> Init();
RefPtr<MediaDataDecoder::DecodePromise> Decode(MediaRawData* aSample);
RefPtr<MediaDataDecoder::DecodePromise> Drain();
RefPtr<MediaDataDecoder::FlushPromise> Flush();
void Shutdown();
bool IsHardwareAccelerated(nsACString& aFailureReason) const;
void SetSeekThreshold(const media::TimeUnit& aTime);
MediaDataDecoder::ConversionRequired NeedsConversion() const;
MOZ_IS_CLASS_INIT
- bool InitIPDL(const VideoInfo& aVideoInfo,
- const layers::TextureFactoryIdentifier& aIdentifier);
+ RefPtr<PlatformDecoderModule::CreateDecoderPromise>
+ InitIPDL(const VideoInfo& aVideoInfo,
+ const layers::TextureFactoryIdentifier& aIdentifier);
+
void DestroyIPDL();
// Called from IPDL when our actor has been destroyed
void IPDLActorDestroyed();
VideoDecoderManagerChild* GetManager();
private:
@@ -63,20 +69,23 @@ private:
RefPtr<VideoDecoderChild> mIPDLSelfRef;
RefPtr<nsIThread> mThread;
MozPromiseHolder<MediaDataDecoder::InitPromise> mInitPromise;
MozPromiseHolder<MediaDataDecoder::DecodePromise> mDecodePromise;
MozPromiseHolder<MediaDataDecoder::DecodePromise> mDrainPromise;
MozPromiseHolder<MediaDataDecoder::FlushPromise> mFlushPromise;
+ MozPromiseHolder<PlatformDecoderModule::CreateDecoderPromise>
+ mCreateDecoderPromise;
nsCString mHardwareAcceleratedReason;
bool mCanSend;
bool mInitialized;
+ bool mHasDecoder;
Atomic<bool> mIsHardwareAccelerated;
Atomic<MediaDataDecoder::ConversionRequired> mConversion;
// Set to true if the actor got destroyed and we haven't yet notified the
// caller.
bool mNeedNewDecoder;
MediaDataDecoder::DecodedData mDecodedData;
};
--- a/dom/media/ipc/VideoDecoderManagerChild.cpp
+++ b/dom/media/ipc/VideoDecoderManagerChild.cpp
@@ -107,18 +107,17 @@ VideoDecoderManagerChild::GetManagerThre
/* static */ AbstractThread*
VideoDecoderManagerChild::GetManagerAbstractThread()
{
return sVideoDecoderChildAbstractThread;
}
PVideoDecoderChild*
VideoDecoderManagerChild::AllocPVideoDecoderChild(const VideoInfo& aVideoInfo,
- const layers::TextureFactoryIdentifier& aIdentifier,
- bool* aSuccess)
+ const layers::TextureFactoryIdentifier& aIdentifier)
{
return new VideoDecoderChild();
}
bool
VideoDecoderManagerChild::DeallocPVideoDecoderChild(PVideoDecoderChild* actor)
{
VideoDecoderChild* child = static_cast<VideoDecoderChild*>(actor);
--- a/dom/media/ipc/VideoDecoderManagerChild.h
+++ b/dom/media/ipc/VideoDecoderManagerChild.h
@@ -66,18 +66,17 @@ protected:
void InitIPDL();
void ActorDestroy(ActorDestroyReason aWhy) override;
void DeallocPVideoDecoderManagerChild() override;
void HandleFatalError(const char* aName, const char* aMsg) const override;
PVideoDecoderChild* AllocPVideoDecoderChild(const VideoInfo& aVideoInfo,
- const layers::TextureFactoryIdentifier& aIdentifier,
- bool* aSuccess) override;
+ const layers::TextureFactoryIdentifier& aIdentifier) override;
bool DeallocPVideoDecoderChild(PVideoDecoderChild* actor) override;
private:
// Main thread only
static void InitializeThread();
VideoDecoderManagerChild()
: mCanSend(false)
--- a/dom/media/ipc/VideoDecoderManagerParent.cpp
+++ b/dom/media/ipc/VideoDecoderManagerParent.cpp
@@ -151,22 +151,20 @@ VideoDecoderManagerParent::VideoDecoderM
VideoDecoderManagerParent::~VideoDecoderManagerParent()
{
MOZ_COUNT_DTOR(VideoDecoderManagerParent);
}
PVideoDecoderParent*
VideoDecoderManagerParent::AllocPVideoDecoderParent(const VideoInfo& aVideoInfo,
- const layers::TextureFactoryIdentifier& aIdentifier,
- bool* aSuccess)
+ const layers::TextureFactoryIdentifier& aIdentifier)
{
return new VideoDecoderParent(this, aVideoInfo, aIdentifier, sManagerTaskQueue,
- new TaskQueue(SharedThreadPool::Get(NS_LITERAL_CSTRING("VideoDecoderParent"), 4)),
- aSuccess);
+ new TaskQueue(SharedThreadPool::Get(NS_LITERAL_CSTRING("VideoDecoderParent"), 4)));
}
bool
VideoDecoderManagerParent::DeallocPVideoDecoderParent(PVideoDecoderParent* actor)
{
VideoDecoderParent* parent = static_cast<VideoDecoderParent*>(actor);
parent->Destroy();
return true;
--- a/dom/media/ipc/VideoDecoderManagerParent.h
+++ b/dom/media/ipc/VideoDecoderManagerParent.h
@@ -24,17 +24,17 @@ public:
static void StartupThreads();
static void ShutdownThreads();
static void ShutdownVideoBridge();
bool OnManagerThread();
protected:
- PVideoDecoderParent* AllocPVideoDecoderParent(const VideoInfo& aVideoInfo, const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess) override;
+ PVideoDecoderParent* AllocPVideoDecoderParent(const VideoInfo& aVideoInfo, const layers::TextureFactoryIdentifier& aIdentifier) override;
bool DeallocPVideoDecoderParent(PVideoDecoderParent* actor) override;
mozilla::ipc::IPCResult RecvReadback(const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) override;
mozilla::ipc::IPCResult RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) override;
void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override {}
void DeallocPVideoDecoderManagerParent() override;
--- a/dom/media/ipc/VideoDecoderParent.cpp
+++ b/dom/media/ipc/VideoDecoderParent.cpp
@@ -40,18 +40,17 @@ public:
private:
virtual ~KnowsCompositorVideo() = default;
};
VideoDecoderParent::VideoDecoderParent(VideoDecoderManagerParent* aParent,
const VideoInfo& aVideoInfo,
const layers::TextureFactoryIdentifier& aIdentifier,
TaskQueue* aManagerTaskQueue,
- TaskQueue* aDecodeTaskQueue,
- bool* aSuccess)
+ TaskQueue* aDecodeTaskQueue)
: mParent(aParent)
, mManagerTaskQueue(aManagerTaskQueue)
, mDecodeTaskQueue(aDecodeTaskQueue)
, mKnowsCompositor(new KnowsCompositorVideo)
, mDestroyed(false)
{
MOZ_COUNT_CTOR(VideoDecoderParent);
MOZ_ASSERT(OnManagerThread());
@@ -70,23 +69,40 @@ VideoDecoderParent::VideoDecoderParent(V
RefPtr<WMFDecoderModule> pdm(new WMFDecoderModule());
pdm->Startup();
CreateDecoderParams params(aVideoInfo);
params.mTaskQueue = mDecodeTaskQueue;
params.mKnowsCompositor = mKnowsCompositor;
params.mImageContainer = new layers::ImageContainer();
- mDecoder = pdm->CreateVideoDecoder(params);
+ RefPtr<VideoDecoderParent> self = this;
+ pdm->CreateVideoDecoder(params)->Then(mManagerTaskQueue, __func__,
+ [self] (RefPtr<MediaDataDecoder> aDecoder) {
+ self->mDecoder = aDecoder.forget();
+ MOZ_ASSERT(self->mDecoder);
+
+ nsCString hardwareReason;
+ bool hardwareAccelerated =
+ self->mDecoder->IsHardwareAccelerated(hardwareReason);
+ uint32_t conversion =
+ static_cast<uint32_t>(self->mDecoder->NeedsConversion());
+ Unused << self->SendCreateDecoderCompleted(hardwareAccelerated,
+ hardwareReason,
+ conversion);
+ },
+ [self] () {
+ if (!self->mDestroyed) {
+ Unused << self->SendCreateDecoderFailed();
+ }
+ });
#else
MOZ_ASSERT(false,
"Can't use RemoteVideoDecoder on non-Windows platforms yet");
#endif
-
- *aSuccess = !!mDecoder;
}
VideoDecoderParent::~VideoDecoderParent()
{
MOZ_COUNT_DTOR(VideoDecoderParent);
}
void
--- a/dom/media/ipc/VideoDecoderParent.h
+++ b/dom/media/ipc/VideoDecoderParent.h
@@ -25,18 +25,17 @@ public:
// We refcount this class since the task queue can have runnables
// that reference us.
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderParent)
VideoDecoderParent(VideoDecoderManagerParent* aParent,
const VideoInfo& aVideoInfo,
const layers::TextureFactoryIdentifier& aIdentifier,
TaskQueue* aManagerTaskQueue,
- TaskQueue* aDecodeTaskQueue,
- bool* aSuccess);
+ TaskQueue* aDecodeTaskQueue);
void Destroy();
// PVideoDecoderParent
mozilla::ipc::IPCResult RecvInit() override;
mozilla::ipc::IPCResult RecvInput(const MediaRawDataIPDL& aData) override;
mozilla::ipc::IPCResult RecvFlush() override;
mozilla::ipc::IPCResult RecvDrain() override;
--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -46,16 +46,18 @@
#include "mozilla/dom/RemoteVideoDecoder.h"
#include "mp4_demuxer/H264.h"
#include <functional>
namespace mozilla {
+using CreateDecoderPromise = PlatformDecoderModule::CreateDecoderPromise;
+
extern already_AddRefed<PlatformDecoderModule> CreateAgnosticDecoderModule();
extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
class PDMFactoryImpl final
{
public:
PDMFactoryImpl()
{
@@ -195,17 +197,17 @@ PDMFactory::EnsureInit() const
if (!sInstance) {
sInstance = new PDMFactoryImpl();
ClearOnShutdown(&sInstance);
}
});
SyncRunnable::DispatchToThread(mainThread, runnable);
}
-already_AddRefed<MediaDataDecoder>
+RefPtr<CreateDecoderPromise>
PDMFactory::CreateDecoder(const CreateDecoderParams& aParams)
{
if (aParams.mUseBlankDecoder) {
MOZ_ASSERT(mBlankPDM);
return CreateDecoderWithPDM(mBlankPDM, aParams);
}
const TrackInfo& config = aParams.mConfig;
@@ -226,37 +228,55 @@ PDMFactory::CreateDecoder(const CreateDe
diagnostics->SetFFmpegFailedToLoad();
}
if (mGMPPDMFailedToStartup) {
diagnostics->SetGMPPDMFailedToStartup();
}
}
for (auto& current : mCurrentPDMs) {
- if (!current->SupportsMimeType(config.mMimeType, diagnostics)) {
+ if (!current->SupportsMimeType(config.mMimeType, diagnostics) ||
+ !CheckMediaFormat(aParams)) {
continue;
}
- RefPtr<MediaDataDecoder> m = CreateDecoderWithPDM(current, aParams);
- if (m) {
- return m.forget();
- }
+ return CreateDecoderWithPDM(current, aParams);
}
NS_WARNING("Unable to create a decoder, no platform found.");
- return nullptr;
+
+ return CreateDecoderPromise::CreateAndReject(false, __func__);
}
-already_AddRefed<MediaDataDecoder>
+RefPtr<CreateDecoderPromise>
PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
const CreateDecoderParams& aParams)
{
MOZ_ASSERT(aPDM);
- RefPtr<MediaDataDecoder> m;
+
+ if (aParams.mConfig.IsAudio()) {
+ return aPDM->CreateAudioDecoder(aParams);
+ } else if (aParams.mConfig.IsVideo()) {
+ if (MP4Decoder::IsH264(aParams.mConfig.mMimeType) &&
+ !aParams.mUseBlankDecoder) {
+ RefPtr<H264Converter> h = new H264Converter(aPDM, aParams);
+ return h->CreateVideoDecoder();
+ }
+ return aPDM->CreateVideoDecoder(aParams);
+ }
+
+ *aParams.mError = MediaResult(
+ NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("Decoder configuration error, expected audio or video."));
+ return CreateDecoderPromise::CreateAndReject(false, __func__);
+}
+
+bool
+PDMFactory::CheckMediaFormat(const CreateDecoderParams& aParams) const
+{
+ SupportChecker supportChecker;
MediaResult* result = aParams.mError;
-
- SupportChecker supportChecker;
const TrackInfo& config = aParams.mConfig;
supportChecker.AddMediaFormatChecker(config);
auto checkResult = supportChecker.Check();
if (checkResult.mReason != SupportChecker::Reason::kSupported) {
DecoderDoctorDiagnostics* diagnostics = aParams.mDiagnostics;
if (checkResult.mReason
== SupportChecker::Reason::kVideoFormatNotSupported) {
@@ -270,45 +290,19 @@ PDMFactory::CreateDecoderWithPDM(Platfor
== SupportChecker::Reason::kAudioFormatNotSupported) {
if (diagnostics) {
diagnostics->SetAudioNotSupported();
}
if (result) {
*result = checkResult.mMediaResult;
}
}
- return nullptr;
- }
-
- if (config.IsAudio()) {
- m = aPDM->CreateAudioDecoder(aParams);
- return m.forget();
- }
-
- if (!config.IsVideo()) {
- *result = MediaResult(
- NS_ERROR_DOM_MEDIA_FATAL_ERR,
- RESULT_DETAIL("Decoder configuration error, expected audio or video."));
- return nullptr;
+ return false;
}
-
- if (MP4Decoder::IsH264(config.mMimeType) && !aParams.mUseBlankDecoder) {
- RefPtr<H264Converter> h = new H264Converter(aPDM, aParams);
- const nsresult rv = h->GetLastError();
- if (NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_INITIALIZED) {
- // The H264Converter either successfully created the wrapped decoder,
- // or there wasn't enough AVCC data to do so. Otherwise, there was some
- // problem, for example WMF DLLs were missing.
- m = h.forget();
- }
- } else {
- m = aPDM->CreateVideoDecoder(aParams);
- }
-
- return m.forget();
+ return true;
}
bool
PDMFactory::SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const
{
UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aMimeType);
if (!trackInfo) {
--- a/dom/media/platforms/PDMFactory.h
+++ b/dom/media/platforms/PDMFactory.h
@@ -25,17 +25,17 @@ public:
PDMFactory();
// Factory method that creates the appropriate PlatformDecoderModule for
// the platform we're running on. Caller is responsible for deleting this
// instance. It's expected that there will be multiple
// PlatformDecoderModules alive at the same time.
// This is called on the decode task queue.
- already_AddRefed<MediaDataDecoder>
+ RefPtr<PlatformDecoderModule::CreateDecoderPromise>
CreateDecoder(const CreateDecoderParams& aParams);
bool SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const;
bool Supports(const TrackInfo& aTrackInfo,
DecoderDoctorDiagnostics* aDiagnostics) const;
// Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or
@@ -56,20 +56,22 @@ private:
void CreateBlankPDM();
// Startup the provided PDM and add it to our list if successful.
bool StartupPDM(PlatformDecoderModule* aPDM);
// Returns the first PDM in our list supporting the mimetype.
already_AddRefed<PlatformDecoderModule>
GetDecoder(const TrackInfo& aTrackInfo,
DecoderDoctorDiagnostics* aDiagnostics) const;
- already_AddRefed<MediaDataDecoder>
+ RefPtr<PlatformDecoderModule::CreateDecoderPromise>
CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
const CreateDecoderParams& aParams);
+ bool CheckMediaFormat(const CreateDecoderParams& aParams) const;
+
nsTArray<RefPtr<PlatformDecoderModule>> mCurrentPDMs;
RefPtr<PlatformDecoderModule> mEMEPDM;
RefPtr<PlatformDecoderModule> mBlankPDM;
bool mWMFFailedToLoad = false;
bool mFFmpegFailedToLoad = false;
bool mGMPPDMFailedToStartup = false;
--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -6,96 +6,118 @@
#include "mozilla/TaskQueue.h"
#include "H264Converter.h"
#include "ImageContainer.h"
#include "MediaInfo.h"
#include "MediaPrefs.h"
#include "mp4_demuxer/AnnexB.h"
-#include "mp4_demuxer/H264.h"
namespace mozilla
{
+using CreateDecoderPromise = PlatformDecoderModule::CreateDecoderPromise;
+
H264Converter::H264Converter(PlatformDecoderModule* aPDM,
const CreateDecoderParams& aParams)
: mPDM(aPDM)
, mOriginalConfig(aParams.VideoConfig())
, mCurrentConfig(aParams.VideoConfig())
, mKnowsCompositor(aParams.mKnowsCompositor)
, mImageContainer(aParams.mImageContainer)
, mTaskQueue(aParams.mTaskQueue)
, mDecoder(nullptr)
, mGMPCrashHelper(aParams.mCrashHelper)
- , mLastError(NS_OK)
, mType(aParams.mType)
, mOnWaitingForKeyEvent(aParams.mOnWaitingForKeyEvent)
, mDecoderOptions(aParams.mOptions)
-{
- CreateDecoder(aParams.mDiagnostics);
-}
+ , mDiagnostics(aParams.mDiagnostics)
+{}
H264Converter::~H264Converter()
{
}
+RefPtr<CreateDecoderPromise>
+H264Converter::CreateVideoDecoder()
+{
+ RefPtr<CreateDecoderPromise> promise;
+ if (!CanCreateDecoder(mCurrentConfig.mExtraData)) {
+ promise = CreateDecoderPromise::CreateAndReject(false, __func__);
+ return promise.forget();
+ }
+
+ PromiseHolder* rawHolder = new PromiseHolder();
+ promise = rawHolder->Ensure(__func__);
+
+ RefPtr<H264Converter> self = this;
+ mPDM->CreateVideoDecoder({
+ mUseOriginalConfig ? mOriginalConfig : mCurrentConfig,
+ mTaskQueue,
+ mDiagnostics,
+ mImageContainer,
+ mKnowsCompositor,
+ mGMPCrashHelper,
+ mType,
+ mOnWaitingForKeyEvent,
+ mDecoderOptions
+ })->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__,
+ [self, rawHolder] (RefPtr<MediaDataDecoder> aDecoder) {
+ self->mUseOriginalConfig = false;
+ self->mNeedKeyframe = true;
+ UniquePtr<PromiseHolder> holder(rawHolder);
+ holder->Resolve(aDecoder, __func__);
+ },
+ [rawHolder] () {
+ UniquePtr<PromiseHolder> holder(rawHolder);
+ holder->Reject(false, __func__);
+ });
+
+ return promise.forget();
+}
+
RefPtr<MediaDataDecoder::InitPromise>
H264Converter::Init()
{
if (mDecoder) {
return mDecoder->Init();
}
// We haven't been able to initialize a decoder due to a missing SPS/PPS.
return MediaDataDecoder::InitPromise::CreateAndResolve(
TrackType::kVideoTrack, __func__);
}
RefPtr<MediaDataDecoder::DecodePromise>
H264Converter::Decode(MediaRawData* aSample)
{
- MOZ_RELEASE_ASSERT(!mDecodePromiseRequest.Exists()
- && !mInitPromiseRequest.Exists(),
+ MOZ_RELEASE_ASSERT(!mDecodePromiseRequest.Exists() &&
+ !mCreateDecoderRequest.Exists(),
"Can't request a new decode until previous one completed");
if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) {
// We need AVCC content to be able to later parse the SPS.
// This is a no-op if the data is already AVCC.
return DecodePromise::CreateAndReject(
MediaResult(NS_ERROR_OUT_OF_MEMORY, RESULT_DETAIL("ConvertSampleToAVCC")),
__func__);
}
- nsresult rv;
if (!mDecoder) {
// It is not possible to create an AVCC H264 decoder without SPS.
// As such, creation will fail if the extra_data just extracted doesn't
// contain a SPS.
- rv = CreateDecoderAndInit(aSample);
- if (rv == NS_ERROR_NOT_INITIALIZED) {
- // We are missing the required SPS to create the decoder.
- // Ignore for the time being, the MediaRawData will be dropped.
- return DecodePromise::CreateAndResolve(DecodedData(), __func__);
- }
- } else {
- rv = CheckForSPSChange(aSample);
- }
-
- if (rv == NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER) {
- // The decoder is pending initialization.
RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
+ CreateDecoderAndDecodeFirstSample(aSample);
return p;
}
- if (NS_FAILED(rv)) {
- return DecodePromise::CreateAndReject(
- MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
- RESULT_DETAIL("Unable to create H264 decoder")),
- __func__);
+ if (IsSPSChanged(aSample)) {
+ return RecycleOrRecreateDecoder(aSample);
}
if (mNeedKeyframe && !aSample->mKeyframe) {
return DecodePromise::CreateAndResolve(DecodedData(), __func__);
}
if (!mNeedAVCC) {
mNeedAVCC =
@@ -135,21 +157,20 @@ H264Converter::Drain()
return mDecoder->Drain();
}
return DecodePromise::CreateAndResolve(DecodedData(), __func__);
}
RefPtr<ShutdownPromise>
H264Converter::Shutdown()
{
- mInitPromiseRequest.DisconnectIfExists();
+ mCreateDecoderRequest.DisconnectIfExists();
mDecodePromiseRequest.DisconnectIfExists();
mFlushRequest.DisconnectIfExists();
mShutdownRequest.DisconnectIfExists();
- mPendingSample = nullptr;
if (mShutdownPromise) {
// We have a shutdown in progress, return that promise instead as we can't
// shutdown a decoder twice.
return mShutdownPromise.forget();
}
if (mDecoder) {
RefPtr<MediaDataDecoder> decoder = mDecoder.forget();
return decoder->Shutdown();
@@ -171,108 +192,54 @@ H264Converter::SetSeekThreshold(const me
{
if (mDecoder) {
mDecoder->SetSeekThreshold(aTime);
} else {
MediaDataDecoder::SetSeekThreshold(aTime);
}
}
-nsresult
-H264Converter::CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics)
+bool
+H264Converter::CanCreateDecoder(MediaByteBuffer* aExtraData)
{
- if (!mp4_demuxer::AnnexB::HasSPS(mCurrentConfig.mExtraData)) {
+ if (!mp4_demuxer::AnnexB::HasSPS(aExtraData)) {
// nothing found yet, will try again later
- return NS_ERROR_NOT_INITIALIZED;
- }
- UpdateConfigFromExtraData(mCurrentConfig.mExtraData);
-
- mp4_demuxer::SPSData spsdata;
- if (mp4_demuxer::H264::DecodeSPSFromExtraData(mCurrentConfig.mExtraData, spsdata)) {
- // Do some format check here.
- // WMF H.264 Video Decoder and Apple ATDecoder do not support YUV444 format.
- if (spsdata.profile_idc == 244 /* Hi444PP */
- || spsdata.chroma_format_idc == PDMFactory::kYUV444) {
- mLastError = NS_ERROR_FAILURE;
- if (aDiagnostics) {
- aDiagnostics->SetVideoNotSupported();
- }
- return NS_ERROR_FAILURE;
- }
- } else {
- // SPS was invalid.
- mLastError = NS_ERROR_FAILURE;
- return NS_ERROR_FAILURE;
+ return true;
}
- mDecoder = mPDM->CreateVideoDecoder({
- mUseOriginalConfig ? mOriginalConfig : mCurrentConfig,
- mTaskQueue,
- aDiagnostics,
- mImageContainer,
- mKnowsCompositor,
- mGMPCrashHelper,
- mType,
- mOnWaitingForKeyEvent,
- mDecoderOptions
- });
-
- if (!mDecoder) {
- mLastError = NS_ERROR_FAILURE;
- return NS_ERROR_FAILURE;
+ mp4_demuxer::SPSData spsdata;
+ if (!mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata)) {
+ // SPS was invalid.
+ return false;
}
- mUseOriginalConfig = false;
- mNeedKeyframe = true;
-
- return NS_OK;
-}
-
-nsresult
-H264Converter::CreateDecoderAndInit(MediaRawData* aSample)
-{
- RefPtr<MediaByteBuffer> extra_data =
- mp4_demuxer::AnnexB::ExtractExtraData(aSample);
- if (!mp4_demuxer::AnnexB::HasSPS(extra_data)) {
- return NS_ERROR_NOT_INITIALIZED;
- }
- UpdateConfigFromExtraData(extra_data);
-
- nsresult rv = CreateDecoder(/* DecoderDoctorDiagnostics* */ nullptr);
-
- if (NS_SUCCEEDED(rv)) {
- // Queue the incoming sample.
- mPendingSample = aSample;
-
- mDecoder->Init()
- ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__, this,
- &H264Converter::OnDecoderInitDone,
- &H264Converter::OnDecoderInitFailed)
- ->Track(mInitPromiseRequest);
- return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER;
- }
- return rv;
+ return UpdateConfigFromExtraData(aExtraData);
}
void
-H264Converter::OnDecoderInitDone(const TrackType aTrackType)
+H264Converter::CreateDecoderAndDecodeFirstSample(MediaRawData* aSample)
{
- mInitPromiseRequest.Complete();
- RefPtr<MediaRawData> sample = mPendingSample.forget();
- DecodeFirstSample(sample);
-}
+ MOZ_ASSERT(!mDecodePromise.IsEmpty());
-void
-H264Converter::OnDecoderInitFailed(const MediaResult& aError)
-{
- mInitPromiseRequest.Complete();
- mDecodePromise.Reject(
- MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
- RESULT_DETAIL("Unable to initialize H264 decoder")),
- __func__);
+ RefPtr<H264Converter> self = this;
+ RefPtr<MediaRawData> sample = aSample;
+ CreateVideoDecoder()
+ ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__,
+ [self, sample] (RefPtr<MediaDataDecoder> aDecoder) {
+ self->mDecoder = aDecoder;
+ self->mCreateDecoderRequest.Complete();
+ self->DecodeFirstSample(sample);
+ }, [self] () {
+ self->mCreateDecoderRequest.Complete();
+ self->mDecodePromise.Reject(
+ MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("Unable to create H264 decoder")),
+ __func__);
+ })
+ ->Track(mCreateDecoderRequest);
}
void
H264Converter::DecodeFirstSample(MediaRawData* aSample)
{
if (mNeedKeyframe && !aSample->mKeyframe) {
mDecodePromise.Resolve(DecodedData(), __func__);
return;
@@ -302,102 +269,101 @@ H264Converter::DecodeFirstSample(MediaRa
},
[self, this](const MediaResult& aError) {
mDecodePromiseRequest.Complete();
mDecodePromise.Reject(aError, __func__);
})
->Track(mDecodePromiseRequest);
}
-nsresult
-H264Converter::CheckForSPSChange(MediaRawData* aSample)
+
+bool
+H264Converter::IsSPSChanged(MediaRawData* aSample) const
{
RefPtr<MediaByteBuffer> extra_data =
mp4_demuxer::AnnexB::ExtractExtraData(aSample);
- if (!mp4_demuxer::AnnexB::HasSPS(extra_data)
- || mp4_demuxer::AnnexB::CompareExtraData(extra_data,
- mCurrentConfig.mExtraData)) {
- return NS_OK;
- }
-
- RefPtr<MediaRawData> sample = aSample;
+ return (mp4_demuxer::AnnexB::HasSPS(extra_data) &&
+ !mp4_demuxer::AnnexB::CompareExtraData(extra_data,
+ mCurrentConfig.mExtraData));
+}
- if (CanRecycleDecoder()) {
- // Do not recreate the decoder, reuse it.
- UpdateConfigFromExtraData(extra_data);
- // Ideally we would want to drain the decoder instead of flushing it.
- // However the draining operation requires calling Drain and looping several
- // times which isn't possible from within the H264Converter. So instead we
- // flush the decoder. In practice, this is a no-op as SPS change will only
- // be used with MSE. And with MSE, the MediaFormatReader would have drained
- // the decoder already.
- RefPtr<H264Converter> self = this;
- mDecoder->Flush()
- ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
- __func__,
- [self, sample, this]() {
- mFlushRequest.Complete();
- DecodeFirstSample(sample);
- },
- [self, this](const MediaResult& aError) {
- mFlushRequest.Complete();
- mDecodePromise.Reject(aError, __func__);
- })
- ->Track(mFlushRequest);
- mNeedKeyframe = true;
- // This is not really initializing the decoder, but it will do as it
- // indicates an operation is pending.
- return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER;
+RefPtr<MediaDataDecoder::DecodePromise>
+H264Converter::RecycleOrRecreateDecoder(MediaRawData* aSample)
+{
+ RefPtr<MediaRawData> sample = aSample;
+ RefPtr<MediaDataDecoder::DecodePromise> promise =
+ mDecodePromise.Ensure(__func__);
+
+ RefPtr<MediaByteBuffer> extra_data =
+ mp4_demuxer::AnnexB::ExtractExtraData(aSample);
+
+ // Check whether the decoder supports new sps.
+ if (!UpdateConfigFromExtraData(extra_data)) {
+ return DecodePromise::CreateAndReject(
+ MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ RESULT_DETAIL("Unable to create or recycle H264 decoder")),
+ __func__);
}
- // The SPS has changed, signal to flush the current decoder and create a
- // new one.
+ // The SPS has changed, signal to flush the current decoder and then create a
+ // new one or recycle the old decoder.
RefPtr<H264Converter> self = this;
mDecoder->Flush()
- ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
- __func__,
- [self, sample, this]() {
- mFlushRequest.Complete();
- mShutdownPromise = Shutdown();
- mShutdownPromise
- ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
- __func__,
- [self, sample, this]() {
- mShutdownRequest.Complete();
- mShutdownPromise = nullptr;
- mNeedAVCC.reset();
- nsresult rv = CreateDecoderAndInit(sample);
- if (rv == NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER) {
- // All good so far, will continue later.
- return;
- }
- MOZ_ASSERT(NS_FAILED(rv));
- mDecodePromise.Reject(rv, __func__);
- return;
- },
- [] { MOZ_CRASH("Can't reach here'"); })
- ->Track(mShutdownRequest);
- },
- [self, this](const MediaResult& aError) {
- mFlushRequest.Complete();
- mDecodePromise.Reject(aError, __func__);
- })
- ->Track(mFlushRequest);
- return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER;
+ ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__,
+ [self, sample, this]() {
+ mFlushRequest.Complete();
+ if (CanRecycleDecoder()) {
+ DecodeFirstSample(sample);
+ } else {
+ mShutdownPromise = Shutdown();
+ mShutdownPromise
+ ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__,
+ [self, sample, this]() {
+ mShutdownRequest.Complete();
+ mShutdownPromise = nullptr;
+ mNeedAVCC.reset();
+ CreateDecoderAndDecodeFirstSample(sample);
+ },
+ [] { MOZ_CRASH("Can't reach here'"); })
+ ->Track(mShutdownRequest);
+ }
+ },
+ [self, this](const MediaResult& aError) {
+ mFlushRequest.Complete();
+ mDecodePromise.Reject(aError, __func__);
+ })
+ ->Track(mFlushRequest);
+
+ return promise.forget();
}
-void
+bool
+H264Converter::IsUnSupportedSPSFormat(mp4_demuxer::SPSData aSPS) const
+{
+ // WMF H.264 Video Decoder and Apple ATDecoder do not support YUV444 format.
+ return (aSPS.profile_idc == 244 /* Hi444PP */ ||
+ aSPS.chroma_format_idc == PDMFactory::kYUV444);
+}
+
+bool
H264Converter::UpdateConfigFromExtraData(MediaByteBuffer* aExtraData)
{
mp4_demuxer::SPSData spsdata;
if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata)
&& spsdata.pic_width > 0
&& spsdata.pic_height > 0) {
mp4_demuxer::H264::EnsureSPSIsSane(spsdata);
+ if (IsUnSupportedSPSFormat(spsdata)) {
+ if (mDiagnostics) {
+ mDiagnostics->SetVideoNotSupported();
+ }
+ return false;
+ }
mCurrentConfig.mImage.width = spsdata.pic_width;
mCurrentConfig.mImage.height = spsdata.pic_height;
mCurrentConfig.mDisplay.width = spsdata.display_width;
mCurrentConfig.mDisplay.height = spsdata.display_height;
}
mCurrentConfig.mExtraData = aExtraData;
+ return true;
}
} // namespace mozilla
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -4,16 +4,17 @@
* 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 mozilla_H264Converter_h
#define mozilla_H264Converter_h
#include "PlatformDecoderModule.h"
#include "mozilla/Maybe.h"
+#include "mp4_demuxer/H264.h"
namespace mozilla {
// H264Converter is a MediaDataDecoder wrapper used to ensure that
// only AVCC or AnnexB is fed to the underlying MediaDataDecoder.
// The H264Converter allows playback of content where the SPS NAL may not be
// provided in the init segment (e.g. AVC3 or Annex B)
// H264Converter will monitor the input data, and will delay creation of the
@@ -22,16 +23,19 @@ namespace mozilla {
class H264Converter : public MediaDataDecoder
{
public:
H264Converter(PlatformDecoderModule* aPDM,
const CreateDecoderParams& aParams);
virtual ~H264Converter();
+ RefPtr<PlatformDecoderModule::CreateDecoderPromise>
+ CreateVideoDecoder();
+
RefPtr<InitPromise> Init() override;
RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
RefPtr<DecodePromise> Drain() override;
RefPtr<FlushPromise> Flush() override;
RefPtr<ShutdownPromise> Shutdown() override;
bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
const char* GetDescriptionName() const override
{
@@ -57,60 +61,56 @@ public:
ConversionRequired NeedsConversion() const override
{
if (mDecoder) {
return mDecoder->NeedsConversion();
}
// Default so no conversion is performed.
return ConversionRequired::kNeedAVCC;
}
- nsresult GetLastError() const { return mLastError; }
private:
- // Will create the required MediaDataDecoder if need AVCC and we have a SPS NAL.
- // Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and
- // will set mError accordingly.
- nsresult CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics);
- nsresult CreateDecoderAndInit(MediaRawData* aSample);
- nsresult CheckForSPSChange(MediaRawData* aSample);
- void UpdateConfigFromExtraData(MediaByteBuffer* aExtraData);
+ bool CanCreateDecoder(mozilla::MediaByteBuffer* aExtraData);
+ void CreateDecoderAndDecodeFirstSample(MediaRawData* aSample);
+ RefPtr<DecodePromise> RecycleOrRecreateDecoder(MediaRawData* aSample);
+ void DecodeFirstSample(MediaRawData* aSample);
- void OnDecoderInitDone(const TrackType aTrackType);
- void OnDecoderInitFailed(const MediaResult& aError);
+ bool UpdateConfigFromExtraData(mozilla::MediaByteBuffer* aExtraData);
bool CanRecycleDecoder() const
{
MOZ_ASSERT(mDecoder);
return MediaPrefs::MediaDecoderCheckRecycling()
&& mDecoder->SupportDecoderRecycling();
}
- void DecodeFirstSample(MediaRawData* aSample);
+ bool IsSPSChanged(MediaRawData* aSample) const;
+ bool IsUnSupportedSPSFormat(mp4_demuxer::SPSData aSPS) const;
RefPtr<PlatformDecoderModule> mPDM;
const VideoInfo mOriginalConfig;
VideoInfo mCurrentConfig;
RefPtr<layers::KnowsCompositor> mKnowsCompositor;
RefPtr<layers::ImageContainer> mImageContainer;
const RefPtr<TaskQueue> mTaskQueue;
- RefPtr<MediaRawData> mPendingSample;
RefPtr<MediaDataDecoder> mDecoder;
- MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
MozPromiseRequestHolder<DecodePromise> mDecodePromiseRequest;
MozPromiseHolder<DecodePromise> mDecodePromise;
MozPromiseRequestHolder<FlushPromise> mFlushRequest;
MozPromiseRequestHolder<ShutdownPromise> mShutdownRequest;
RefPtr<ShutdownPromise> mShutdownPromise;
+ MozPromiseRequestHolder<PlatformDecoderModule::CreateDecoderPromise> mCreateDecoderRequest;
+
RefPtr<GMPCrashHelper> mGMPCrashHelper;
Maybe<bool> mNeedAVCC;
- nsresult mLastError;
bool mNeedKeyframe = true;
// Set to true once a decoder has been created.
bool mUseOriginalConfig = true;
const TrackInfo::TrackType mType;
MediaEventProducer<TrackInfo::TrackType>* const mOnWaitingForKeyEvent;
const CreateDecoderParams::OptionSet mDecoderOptions;
+ DecoderDoctorDiagnostics* mDiagnostics;
};
} // namespace mozilla
#endif // mozilla_H264Converter_h