mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1146086: use promise to Init() in PlatformDecoderModule. r=jya,r=cpearce (aed679865) - partial of Bug 1128380: Add IsHardwareAccelerated implementation for AVCC and mac decoder. r=cpearce (8b376df05) - Bug 1192675: P1. Ensure VDA/VT APIs are only ever accessed from the same thread. r=cpearce (fa9c8de6a) - Bug 1178098 - Report why DXVA initialization failed to about:support. r=cpearce (0b06a28e9) - Bug 1167690 - Part 1: Hook up NPPVpluginIsPlayingAudio to the plugin process; r=josh (30df04ca2) - Bug 1167690 - Add NPAPI:AudioControl enums to npapi.h. r=josh (5369f6fa9) - Bug 1167690 - Part 2: Integrate plugins which support the NPAPI audio extensions with the Audio Channel Service; r=BenWa (145cecdc4) - Bug 1167690 - Part 3: Hook up NPNVmuteAudioBool to the plugin process; r=josh (36558b729) - Bug 1167690 - Part 4: Add support for testing plugin audio channel integration to the test plugin; r=josh (04af51882)
This commit is contained in:
@@ -2254,7 +2254,7 @@ nsDOMWindowUtils::GetLayerManagerRemote(bool* retval)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetSupportsHardwareH264Decoding(bool* retval)
|
||||
nsDOMWindowUtils::GetSupportsHardwareH264Decoding(nsAString& aRetval)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
|
||||
@@ -2267,9 +2267,15 @@ nsDOMWindowUtils::GetSupportsHardwareH264Decoding(bool* retval)
|
||||
if (!mgr)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
*retval = MP4Decoder::IsVideoAccelerated(mgr->GetCompositorBackendType());
|
||||
nsCString failureReason;
|
||||
if (MP4Decoder::IsVideoAccelerated(mgr->GetCompositorBackendType(), failureReason)) {
|
||||
aRetval.AssignLiteral("Yes");
|
||||
} else {
|
||||
aRetval.AssignLiteral("No; ");
|
||||
AppendUTF8toUTF16(failureReason, aRetval);
|
||||
}
|
||||
#else
|
||||
*retval = false;
|
||||
aRetval.AssignLiteral("No; Compiled without MP4 support.");
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ interface nsIJSRAIIHelper;
|
||||
interface nsIContentPermissionRequest;
|
||||
interface nsIObserver;
|
||||
|
||||
[scriptable, uuid(e6d3ced6-fbce-4ea5-902a-c2055680d250)]
|
||||
[scriptable, uuid(47fa312b-2ad1-4b80-8a0a-c9822e2d1ec9)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@@ -1335,7 +1335,7 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
* test video, does not mean that all h264 video decoding will be done
|
||||
* in hardware.
|
||||
*/
|
||||
readonly attribute boolean supportsHardwareH264Decoding;
|
||||
readonly attribute AString supportsHardwareH264Decoding;
|
||||
|
||||
/**
|
||||
* Record (and return) frame-intervals for frames which were presented
|
||||
|
||||
+118
-42
@@ -92,6 +92,7 @@ MediaFormatReader::Shutdown()
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
mDemuxerInitRequest.DisconnectIfExists();
|
||||
mDecodersInitRequest.DisconnectIfExists();
|
||||
mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
mSeekPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
|
||||
mSkipRequest.DisconnectIfExists();
|
||||
@@ -224,19 +225,19 @@ MediaFormatReader::AsyncReadMetadata()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
nsRefPtr<MetadataPromise> p = mMetadataPromise.Ensure(__func__);
|
||||
|
||||
if (mInitDone) {
|
||||
// We are returning from dormant.
|
||||
if (!EnsureDecodersSetup()) {
|
||||
return MetadataPromise::CreateAndReject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
if (!EnsureDecodersCreated()) {
|
||||
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
return p;
|
||||
}
|
||||
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
|
||||
metadata->mInfo = mInfo;
|
||||
metadata->mTags = nullptr;
|
||||
return MetadataPromise::CreateAndResolve(metadata, __func__);
|
||||
MOZ_ASSERT(!mDecodersInitRequest.Exists());
|
||||
EnsureDecodersInitialized();
|
||||
return p;
|
||||
}
|
||||
|
||||
nsRefPtr<MetadataPromise> p = mMetadataPromise.Ensure(__func__);
|
||||
|
||||
mDemuxerInitRequest.Begin(mDemuxer->Init()
|
||||
->Then(OwnerThread(), __func__, this,
|
||||
&MediaFormatReader::OnDemuxerInitDone,
|
||||
@@ -319,16 +320,24 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
|
||||
MOZ_ASSERT(mAudioTrackDemuxer);
|
||||
}
|
||||
|
||||
mInitDone = true;
|
||||
|
||||
if (!IsWaitingOnCDMResource() && !EnsureDecodersSetup()) {
|
||||
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
} else {
|
||||
if (IsWaitingOnCDMResource()) {
|
||||
// Decoder can't be allocated before CDM resource is ready, so resolving the
|
||||
// mMetadataPromise here to wait for CDM on MDSM.
|
||||
mInitDone = true;
|
||||
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
|
||||
metadata->mInfo = mInfo;
|
||||
metadata->mTags = nullptr;
|
||||
mMetadataPromise.Resolve(metadata, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EnsureDecodersCreated()) {
|
||||
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mDecodersInitRequest.Exists());
|
||||
EnsureDecodersInitialized();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -343,10 +352,9 @@ MediaFormatReader::OnDemuxerInitFailed(DemuxerFailureReason aFailure)
|
||||
}
|
||||
|
||||
bool
|
||||
MediaFormatReader::EnsureDecodersSetup()
|
||||
MediaFormatReader::EnsureDecodersCreated()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(mInitDone);
|
||||
|
||||
if (!mPlatform) {
|
||||
if (IsEncrypted()) {
|
||||
@@ -364,6 +372,7 @@ MediaFormatReader::EnsureDecodersSetup()
|
||||
NS_ENSURE_TRUE(IsSupportedAudioMimeType(mInfo.mAudio.mMimeType),
|
||||
false);
|
||||
|
||||
mAudio.mDecoderInitialized = false;
|
||||
mAudio.mDecoder =
|
||||
mPlatform->CreateDecoder(mAudio.mInfo ?
|
||||
*mAudio.mInfo->GetAsAudioInfo() :
|
||||
@@ -371,14 +380,13 @@ MediaFormatReader::EnsureDecodersSetup()
|
||||
mAudio.mTaskQueue,
|
||||
mAudio.mCallback);
|
||||
NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, false);
|
||||
nsresult rv = mAudio.mDecoder->Init();
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
}
|
||||
|
||||
if (HasVideo() && !mVideo.mDecoder) {
|
||||
NS_ENSURE_TRUE(IsSupportedVideoMimeType(mInfo.mVideo.mMimeType),
|
||||
false);
|
||||
|
||||
mVideo.mDecoderInitialized = false;
|
||||
if (mSharedDecoderManager &&
|
||||
mPlatform->SupportsSharedDecoders(mInfo.mVideo)) {
|
||||
mVideo.mDecoder =
|
||||
@@ -401,13 +409,86 @@ MediaFormatReader::EnsureDecodersSetup()
|
||||
mDecoder->GetImageContainer());
|
||||
}
|
||||
NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, false);
|
||||
nsresult rv = mVideo.mDecoder->Init();
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaFormatReader::EnsureDecodersInitialized()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(mVideo.mDecoder || mAudio.mDecoder);
|
||||
|
||||
// DecodeDemuxedSample() could call this function before mDecodersInitRequest
|
||||
// is completed. And it is ok to return false here because DecodeDemuxedSample
|
||||
// will call ScheduleUpdate() again.
|
||||
// It also avoids calling decoder->Init() multiple times.
|
||||
if (mDecodersInitRequest.Exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<nsRefPtr<MediaDataDecoder::InitPromise>> promises;
|
||||
|
||||
if (mVideo.mDecoder && !mVideo.mDecoderInitialized) {
|
||||
promises.AppendElement(mVideo.mDecoder->Init());
|
||||
}
|
||||
|
||||
if (mAudio.mDecoder && !mAudio.mDecoderInitialized) {
|
||||
promises.AppendElement(mAudio.mDecoder->Init());
|
||||
}
|
||||
|
||||
if (promises.Length()) {
|
||||
mDecodersInitRequest.Begin(MediaDataDecoder::InitPromise::All(OwnerThread(), promises)
|
||||
->Then(OwnerThread(), __func__, this,
|
||||
&MediaFormatReader::OnDecoderInitDone,
|
||||
&MediaFormatReader::OnDecoderInitFailed));
|
||||
}
|
||||
|
||||
LOG("Init decoders: audio: %p, audio init: %d, video: %p, video init: %d",
|
||||
mAudio.mDecoder.get(), mAudio.mDecoderInitialized,
|
||||
mVideo.mDecoder.get(), mVideo.mDecoderInitialized);
|
||||
|
||||
// Return false if any decoder is under initialization.
|
||||
return !promises.Length();
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::OnDecoderInitDone(const nsTArray<TrackType>& aTrackTypes)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
mDecodersInitRequest.Complete();
|
||||
|
||||
for (const auto& track : aTrackTypes) {
|
||||
auto& decoder = GetDecoderData(track);
|
||||
decoder.mDecoderInitialized = true;
|
||||
|
||||
ScheduleUpdate(track);
|
||||
}
|
||||
|
||||
if (!mMetadataPromise.IsEmpty()) {
|
||||
mInitDone = true;
|
||||
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
|
||||
metadata->mInfo = mInfo;
|
||||
metadata->mTags = nullptr;
|
||||
mMetadataPromise.Resolve(metadata, __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
mDecodersInitRequest.Complete();
|
||||
|
||||
NS_WARNING("Failed to init decoder");
|
||||
|
||||
mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__);
|
||||
|
||||
NotifyError(TrackType::kAudioTrack);
|
||||
NotifyError(TrackType::kVideoTrack);
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::ReadUpdatedMetadata(MediaInfo* aInfo)
|
||||
{
|
||||
@@ -434,7 +515,7 @@ MediaFormatReader::DisableHardwareAcceleration()
|
||||
Flush(TrackInfo::kVideoTrack);
|
||||
mVideo.mDecoder->Shutdown();
|
||||
mVideo.mDecoder = nullptr;
|
||||
if (!EnsureDecodersSetup()) {
|
||||
if (!EnsureDecodersCreated()) {
|
||||
LOG("Unable to re-create decoder, aborting");
|
||||
NotifyError(TrackInfo::kVideoTrack);
|
||||
return;
|
||||
@@ -484,13 +565,6 @@ MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
return VideoDataPromise::CreateAndReject(CANCELED, __func__);
|
||||
}
|
||||
|
||||
if (!EnsureDecodersSetup()) {
|
||||
NS_WARNING("Error constructing decoders");
|
||||
return VideoDataPromise::CreateAndReject(DECODE_ERROR, __func__);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder);
|
||||
|
||||
mVideo.mForceDecodeAhead = aForceDecodeAhead;
|
||||
media::TimeUnit timeThreshold{media::TimeUnit::FromMicroseconds(aTimeThreshold)};
|
||||
if (ShouldSkip(aSkipToNextKeyframe, timeThreshold)) {
|
||||
@@ -582,13 +656,9 @@ MediaFormatReader::RequestAudioData()
|
||||
return AudioDataPromise::CreateAndReject(CANCELED, __func__);
|
||||
}
|
||||
|
||||
if (!EnsureDecodersSetup()) {
|
||||
NS_WARNING("Error constructing decoders");
|
||||
return AudioDataPromise::CreateAndReject(DECODE_ERROR, __func__);
|
||||
}
|
||||
|
||||
nsRefPtr<AudioDataPromise> p = mAudio.mPromise.Ensure(__func__);
|
||||
ScheduleUpdate(TrackInfo::kAudioTrack);
|
||||
ScheduleUpdate(TrackType::kAudioTrack);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -801,6 +871,17 @@ MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EnsureDecodersCreated()) {
|
||||
NS_WARNING("Error constructing decoders");
|
||||
NotifyError(aTrack);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EnsureDecodersInitialized()) {
|
||||
ScheduleUpdate(aTrack);
|
||||
return;
|
||||
}
|
||||
|
||||
// Decode all our demuxed frames.
|
||||
bool samplesPending = false;
|
||||
while (decoder.mQueuedSamples.Length()) {
|
||||
@@ -834,15 +915,9 @@ MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
|
||||
Flush(aTrack);
|
||||
decoder.mDecoder->Shutdown();
|
||||
decoder.mDecoder = nullptr;
|
||||
if (!EnsureDecodersSetup()) {
|
||||
LOG("Unable to re-create decoder, aborting");
|
||||
NotifyError(aTrack);
|
||||
return;
|
||||
}
|
||||
LOGV("%s decoder:%p created for sid:%u",
|
||||
TrackTypeToStr(aTrack), decoder.mDecoder.get(), info->GetID());
|
||||
if (sample->mKeyframe) {
|
||||
decoder.mQueuedSamples.MoveElementsFrom(samples);
|
||||
ScheduleUpdate(aTrack);
|
||||
} else {
|
||||
MOZ_ASSERT(decoder.mTimeThreshold.isNothing());
|
||||
LOG("Stream change occurred on a non-keyframe. Seeking to:%lld",
|
||||
@@ -875,8 +950,8 @@ MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
|
||||
}
|
||||
decoder.mTimeThreshold.reset();
|
||||
}));
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
LOGV("Input:%lld (dts:%lld kf:%d)",
|
||||
@@ -967,8 +1042,9 @@ MediaFormatReader::Update(TrackType aTrack)
|
||||
if (!decoder.mOutput.IsEmpty()) {
|
||||
// We have a decoded sample ready to be returned.
|
||||
if (aTrack == TrackType::kVideoTrack) {
|
||||
nsCString error;
|
||||
mVideo.mIsHardwareAccelerated =
|
||||
mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated();
|
||||
mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error);
|
||||
}
|
||||
while (decoder.mOutput.Length()) {
|
||||
nsRefPtr<MediaData> output = decoder.mOutput[0];
|
||||
|
||||
@@ -106,7 +106,10 @@ private:
|
||||
void NotifyDemuxer(uint32_t aLength, int64_t aOffset);
|
||||
void ReturnOutput(MediaData* aData, TrackType aTrack);
|
||||
|
||||
bool EnsureDecodersSetup();
|
||||
bool EnsureDecodersCreated();
|
||||
// It returns true when all decoders are initialized. False when there is pending
|
||||
// initialization.
|
||||
bool EnsureDecodersInitialized();
|
||||
|
||||
// Enqueues a task to call Update(aTrack) on the decoder task queue.
|
||||
// Lock for corresponding track must be held.
|
||||
@@ -191,6 +194,7 @@ private:
|
||||
, mWaitingForData(false)
|
||||
, mReceivedNewData(false)
|
||||
, mDiscontinuity(true)
|
||||
, mDecoderInitialized(false)
|
||||
, mOutputRequested(false)
|
||||
, mInputExhausted(false)
|
||||
, mError(false)
|
||||
@@ -239,6 +243,8 @@ private:
|
||||
}
|
||||
|
||||
// MediaDataDecoder handler's variables.
|
||||
// False when decoder is created. True when decoder Init() promise is resolved.
|
||||
bool mDecoderInitialized;
|
||||
bool mOutputRequested;
|
||||
bool mInputExhausted;
|
||||
bool mError;
|
||||
@@ -333,6 +339,9 @@ private:
|
||||
|
||||
DecoderData& GetDecoderData(TrackType aTrack);
|
||||
|
||||
void OnDecoderInitDone(const nsTArray<TrackType>& aTrackTypes);
|
||||
void OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason);
|
||||
|
||||
// Demuxer objects.
|
||||
void OnDemuxerInitDone(nsresult);
|
||||
void OnDemuxerInitFailed(DemuxerFailureReason aFailure);
|
||||
@@ -407,6 +416,9 @@ private:
|
||||
Maybe<media::TimeUnit> mPendingSeekTime;
|
||||
MozPromiseHolder<SeekPromise> mSeekPromise;
|
||||
|
||||
// Pending decoders initialization.
|
||||
MozPromiseRequestHolder<MediaDataDecoder::InitPromise::AllPromiseType> mDecodersInitRequest;
|
||||
|
||||
nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
|
||||
|
||||
// Main thread objects
|
||||
|
||||
@@ -287,22 +287,20 @@ CreateTestH264Decoder(layers::LayersBackend aBackend,
|
||||
if (!decoder) {
|
||||
return nullptr;
|
||||
}
|
||||
nsresult rv = decoder->Init();
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
MP4Decoder::IsVideoAccelerated(layers::LayersBackend aBackend)
|
||||
MP4Decoder::IsVideoAccelerated(layers::LayersBackend aBackend, nsACString& aFailureReason)
|
||||
{
|
||||
VideoInfo config;
|
||||
nsRefPtr<MediaDataDecoder> decoder(CreateTestH264Decoder(aBackend, config));
|
||||
if (!decoder) {
|
||||
aFailureReason.AssignLiteral("Failed to create H264 decoder");
|
||||
return false;
|
||||
}
|
||||
bool result = decoder->IsHardwareAccelerated();
|
||||
decoder->Shutdown();
|
||||
bool result = decoder->IsHardwareAccelerated(aFailureReason);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -340,8 +338,6 @@ CreateTestAACDecoder(AudioInfo& aConfig)
|
||||
if (!decoder) {
|
||||
return nullptr;
|
||||
}
|
||||
nsresult rv = decoder->Init();
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
return decoder.forget();
|
||||
}
|
||||
@@ -377,7 +373,6 @@ MP4Decoder::CanCreateAACDecoder()
|
||||
MOZ_ARRAY_LENGTH(sTestAACExtraData));
|
||||
nsRefPtr<MediaDataDecoder> decoder(CreateTestAACDecoder(config));
|
||||
if (decoder) {
|
||||
decoder->Shutdown();
|
||||
result = true;
|
||||
}
|
||||
haveCachedResult = true;
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
// Returns true if the MP4 backend is preffed on.
|
||||
static bool IsEnabled();
|
||||
|
||||
static bool IsVideoAccelerated(layers::LayersBackend aBackend);
|
||||
static bool IsVideoAccelerated(layers::LayersBackend aBackend, nsACString& aReason);
|
||||
static bool CanCreateAACDecoder();
|
||||
static bool CanCreateH264Decoder();
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#define PlatformDecoderModule_h_
|
||||
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
@@ -201,16 +202,24 @@ protected:
|
||||
virtual ~MediaDataDecoder() {};
|
||||
|
||||
public:
|
||||
enum DecoderFailureReason {
|
||||
INIT_ERROR,
|
||||
CANCELED
|
||||
};
|
||||
|
||||
typedef TrackInfo::TrackType TrackType;
|
||||
typedef MozPromise<TrackType, DecoderFailureReason, /* IsExclusive = */ true> InitPromise;
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDataDecoder)
|
||||
|
||||
// Initialize the decoder. The decoder should be ready to decode after
|
||||
// this returns. The decoder should do any initialization here, rather
|
||||
// Initialize the decoder. The decoder should be ready to decode once
|
||||
// promise resolves. The decoder should do any initialization here, rather
|
||||
// than in its constructor or PlatformDecoderModule::Create*Decoder(),
|
||||
// so that if the MP4Reader needs to shutdown during initialization,
|
||||
// it can call Shutdown() to cancel this operation. Any initialization
|
||||
// that requires blocking the calling thread in this function *must*
|
||||
// be done here so that it can be canceled by calling Shutdown()!
|
||||
virtual nsresult Init() = 0;
|
||||
virtual nsRefPtr<InitPromise> Init() = 0;
|
||||
|
||||
// Inserts a sample into the decoder's decode pipeline.
|
||||
virtual nsresult Input(MediaRawData* aSample) = 0;
|
||||
@@ -246,7 +255,9 @@ public:
|
||||
virtual nsresult Shutdown() = 0;
|
||||
|
||||
// Called from the state machine task queue or main thread.
|
||||
virtual bool IsHardwareAccelerated() const { return false; }
|
||||
// Decoder needs to decide whether or not hardware accelearation is supported
|
||||
// after creating. It doesn't need to call Init() before calling this function.
|
||||
virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const { return false; }
|
||||
|
||||
// ConfigurationChanged will be called to inform the video or audio decoder
|
||||
// that the format of the next input sample is about to change.
|
||||
|
||||
@@ -54,6 +54,7 @@ SharedDecoderManager::SharedDecoderManager()
|
||||
: mTaskQueue(new FlushableTaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER)))
|
||||
, mActiveProxy(nullptr)
|
||||
, mActiveCallback(nullptr)
|
||||
, mInit(false)
|
||||
, mWaitForInternalDrain(false)
|
||||
, mMonitor("SharedDecoderManager")
|
||||
, mDecoderReleasedResources(false)
|
||||
@@ -92,11 +93,7 @@ SharedDecoderManager::CreateVideoDecoder(
|
||||
mPDM = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
nsresult rv = mDecoder->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
mDecoder = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mPDM = aPDM;
|
||||
}
|
||||
|
||||
@@ -124,8 +121,8 @@ SharedDecoderManager::Recreate(const VideoInfo& aConfig)
|
||||
if (!mDecoder) {
|
||||
return false;
|
||||
}
|
||||
nsresult rv = mDecoder->Init();
|
||||
return rv == NS_OK;
|
||||
mInit = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -162,6 +159,35 @@ SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy)
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
SharedDecoderManager::InitDecoder()
|
||||
{
|
||||
if (!mInit && mDecoder) {
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
|
||||
nsRefPtr<SharedDecoderManager> self = this;
|
||||
nsRefPtr<MediaDataDecoder::InitPromise> p = mDecoderInitPromise.Ensure(__func__);
|
||||
|
||||
// The mTaskQueue is flushable which can't be used in MediaPromise. So we get
|
||||
// the current AbstractThread instead of it. The MOZ_ASSERT above ensures
|
||||
// we are running in AbstractThread so we won't get a nullptr.
|
||||
mDecoderInitPromiseRequest.Begin(
|
||||
mDecoder->Init()->Then(AbstractThread::GetCurrent(), __func__,
|
||||
[self] (TrackInfo::TrackType aType) -> void {
|
||||
self->mDecoderInitPromiseRequest.Complete();
|
||||
self->mInit = true;
|
||||
self->mDecoderInitPromise.ResolveIfExists(aType, __func__);
|
||||
},
|
||||
[self] (MediaDataDecoder::DecoderFailureReason aReason) -> void {
|
||||
self->mDecoderInitPromiseRequest.Complete();
|
||||
self->mDecoderInitPromise.RejectIfExists(aReason, __func__);
|
||||
}));
|
||||
return p;
|
||||
}
|
||||
|
||||
return MediaDataDecoder::InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
SharedDecoderManager::DrainComplete()
|
||||
{
|
||||
@@ -189,6 +215,7 @@ SharedDecoderManager::Shutdown()
|
||||
mTaskQueue->AwaitShutdownAndIdle();
|
||||
mTaskQueue = nullptr;
|
||||
}
|
||||
mDecoderInitPromiseRequest.DisconnectIfExists();
|
||||
}
|
||||
|
||||
SharedDecoderProxy::SharedDecoderProxy(SharedDecoderManager* aManager,
|
||||
@@ -203,10 +230,14 @@ SharedDecoderProxy::~SharedDecoderProxy()
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
SharedDecoderProxy::Init()
|
||||
{
|
||||
return NS_OK;
|
||||
if (mManager->mActiveProxy != this) {
|
||||
mManager->Select(this);
|
||||
}
|
||||
|
||||
return mManager->InitDecoder();
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -246,9 +277,9 @@ SharedDecoderProxy::Shutdown()
|
||||
}
|
||||
|
||||
bool
|
||||
SharedDecoderProxy::IsHardwareAccelerated() const
|
||||
SharedDecoderProxy::IsHardwareAccelerated(nsACString& aFailureReason) const
|
||||
{
|
||||
return mManager->mDecoder->IsHardwareAccelerated();
|
||||
return mManager->mDecoder->IsHardwareAccelerated(aFailureReason);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -48,6 +48,8 @@ private:
|
||||
virtual ~SharedDecoderManager();
|
||||
void DrainComplete();
|
||||
|
||||
nsRefPtr<MediaDataDecoder::InitPromise> InitDecoder();
|
||||
|
||||
nsRefPtr<PlatformDecoderModule> mPDM;
|
||||
nsRefPtr<MediaDataDecoder> mDecoder;
|
||||
layers::LayersBackend mLayersBackend;
|
||||
@@ -56,6 +58,9 @@ private:
|
||||
SharedDecoderProxy* mActiveProxy;
|
||||
MediaDataDecoderCallback* mActiveCallback;
|
||||
nsAutoPtr<MediaDataDecoderCallback> mCallback;
|
||||
MozPromiseHolder<MediaDataDecoder::InitPromise> mDecoderInitPromise;
|
||||
MozPromiseRequestHolder<MediaDataDecoder::InitPromise> mDecoderInitPromiseRequest;
|
||||
bool mInit;
|
||||
// access protected by mMonitor
|
||||
bool mWaitForInternalDrain;
|
||||
Monitor mMonitor;
|
||||
@@ -69,12 +74,12 @@ public:
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
virtual ~SharedDecoderProxy();
|
||||
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<MediaDataDecoder::InitPromise> Init() override;
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual nsresult Drain() override;
|
||||
virtual nsresult Shutdown() override;
|
||||
virtual bool IsHardwareAccelerated() const override;
|
||||
virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
|
||||
|
||||
friend class SharedDecoderManager;
|
||||
|
||||
|
||||
@@ -25,15 +25,17 @@ public:
|
||||
|
||||
BlankMediaDataDecoder(BlankMediaDataCreator* aCreator,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
TrackInfo::TrackType aType)
|
||||
: mCreator(aCreator)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mCallback(aCallback)
|
||||
, mType(aType)
|
||||
{
|
||||
}
|
||||
|
||||
virtual nsresult Init() override {
|
||||
return NS_OK;
|
||||
virtual nsRefPtr<InitPromise> Init() override {
|
||||
return InitPromise::CreateAndResolve(mType, __func__);
|
||||
}
|
||||
|
||||
virtual nsresult Shutdown() override {
|
||||
@@ -89,6 +91,7 @@ private:
|
||||
nsAutoPtr<BlankMediaDataCreator> mCreator;
|
||||
RefPtr<FlushableTaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
TrackInfo::TrackType mType;
|
||||
};
|
||||
|
||||
class BlankVideoDataCreator {
|
||||
@@ -221,7 +224,8 @@ public:
|
||||
nsRefPtr<MediaDataDecoder> decoder =
|
||||
new BlankMediaDataDecoder<BlankVideoDataCreator>(creator,
|
||||
aVideoTaskQueue,
|
||||
aCallback);
|
||||
aCallback,
|
||||
TrackInfo::kVideoTrack);
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
@@ -236,7 +240,8 @@ public:
|
||||
nsRefPtr<MediaDataDecoder> decoder =
|
||||
new BlankMediaDataDecoder<BlankAudioDataCreator>(creator,
|
||||
aAudioTaskQueue,
|
||||
aCallback);
|
||||
aCallback,
|
||||
TrackInfo::kAudioTrack);
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
|
||||
@@ -47,19 +47,19 @@ OpusDataDecoder::Shutdown()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
OpusDataDecoder::Init()
|
||||
{
|
||||
size_t length = mInfo.mCodecSpecificConfig->Length();
|
||||
uint8_t *p = mInfo.mCodecSpecificConfig->Elements();
|
||||
if (length < sizeof(uint64_t)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
int64_t codecDelay = BigEndian::readUint64(p);
|
||||
length -= sizeof(uint64_t);
|
||||
p += sizeof(uint64_t);
|
||||
if (NS_FAILED(DecodeHeader(p, length))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
int r;
|
||||
@@ -75,7 +75,7 @@ OpusDataDecoder::Init()
|
||||
if (codecDelay != FramesToUsecs(mOpusParser->mPreSkip,
|
||||
mOpusParser->mRate).value()) {
|
||||
NS_WARNING("Invalid Opus header: CodecDelay and pre-skip do not match!");
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
if (mInfo.mRate != (uint32_t)mOpusParser->mRate) {
|
||||
@@ -85,7 +85,8 @@ OpusDataDecoder::Init()
|
||||
NS_WARNING("Invalid Opus header: container and codec channels do not match!");
|
||||
}
|
||||
|
||||
return r == OPUS_OK ? NS_OK : NS_ERROR_FAILURE;
|
||||
return r == OPUS_OK ? InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__)
|
||||
: InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
~OpusDataDecoder();
|
||||
|
||||
nsresult Init() override;
|
||||
nsRefPtr<InitPromise> Init() override;
|
||||
nsresult Input(MediaRawData* aSample) override;
|
||||
nsresult Flush() override;
|
||||
nsresult Drain() override;
|
||||
|
||||
@@ -55,7 +55,7 @@ VPXDecoder::Shutdown()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
VPXDecoder::Init()
|
||||
{
|
||||
vpx_codec_iface_t* dx = nullptr;
|
||||
@@ -65,9 +65,9 @@ VPXDecoder::Init()
|
||||
dx = vpx_codec_vp9_dx();
|
||||
}
|
||||
if (!dx || vpx_codec_dec_init(&mVPX, dx, nullptr, 0)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
return NS_OK;
|
||||
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
|
||||
~VPXDecoder();
|
||||
|
||||
nsresult Init() override;
|
||||
nsRefPtr<InitPromise> Init() override;
|
||||
nsresult Input(MediaRawData* aSample) override;
|
||||
nsresult Flush() override;
|
||||
nsresult Drain() override;
|
||||
|
||||
@@ -63,7 +63,7 @@ VorbisDataDecoder::Shutdown()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
VorbisDataDecoder::Init()
|
||||
{
|
||||
vorbis_info_init(&mVorbisInfo);
|
||||
@@ -75,17 +75,17 @@ VorbisDataDecoder::Init()
|
||||
uint8_t *p = mInfo.mCodecSpecificConfig->Elements();
|
||||
for(int i = 0; i < 3; i++) {
|
||||
if (available < 2) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
available -= 2;
|
||||
size_t length = BigEndian::readUint16(p);
|
||||
p += 2;
|
||||
if (available < length) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
available -= length;
|
||||
if (NS_FAILED(DecodeHeader((const unsigned char*)p, length))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
p += length;
|
||||
}
|
||||
@@ -94,12 +94,12 @@ VorbisDataDecoder::Init()
|
||||
|
||||
int r = vorbis_synthesis_init(&mVorbisDsp, &mVorbisInfo);
|
||||
if (r) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
r = vorbis_block_init(&mVorbisDsp, &mVorbisBlock);
|
||||
if (r) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
if (mInfo.mRate != (uint32_t)mVorbisDsp.vi->rate) {
|
||||
@@ -111,7 +111,7 @@ VorbisDataDecoder::Init()
|
||||
("Invalid Vorbis header: container and codec channels do not match!"));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
~VorbisDataDecoder();
|
||||
|
||||
nsresult Init() override;
|
||||
nsRefPtr<InitPromise> Init() override;
|
||||
nsresult Input(MediaRawData* aSample) override;
|
||||
nsresult Flush() override;
|
||||
nsresult Drain() override;
|
||||
|
||||
@@ -167,7 +167,7 @@ GMPAudioDecoder::GMPInitDone(GMPAudioDecoderProxy* aGMP)
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
GMPAudioDecoder::Init()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
@@ -188,7 +188,8 @@ GMPAudioDecoder::Init()
|
||||
NS_ProcessNextEvent(gmpThread, true);
|
||||
}
|
||||
|
||||
return mGMP ? NS_OK : NS_ERROR_FAILURE;
|
||||
return mGMP ? InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__)
|
||||
: InitPromise::CreateAndReject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<InitPromise> Init() override;
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual nsresult Drain() override;
|
||||
|
||||
@@ -211,7 +211,7 @@ GMPVideoDecoder::GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
GMPVideoDecoder::Init()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
@@ -232,7 +232,8 @@ GMPVideoDecoder::Init()
|
||||
NS_ProcessNextEvent(gmpThread, true);
|
||||
}
|
||||
|
||||
return mGMP ? NS_OK : NS_ERROR_FAILURE;
|
||||
return mGMP ? InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__)
|
||||
: InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<InitPromise> Init() override;
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual nsresult Drain() override;
|
||||
|
||||
@@ -21,16 +21,21 @@ MediaDataDecoderCallbackProxy::FlushComplete()
|
||||
mProxyDecoder->FlushComplete();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
MediaDataDecoderProxy::InternalInit()
|
||||
{
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
return mProxyDecoder->Init();
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
MediaDataDecoderProxy::Init()
|
||||
{
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
nsRefPtr<InitTask> task(new InitTask(mProxyDecoder));
|
||||
nsresult rv = mProxyThread->Dispatch(task, NS_DISPATCH_SYNC);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(task->Result(), task->Result());
|
||||
|
||||
return NS_OK;
|
||||
return ProxyMediaCall(mProxyThreadWrapper, this, __func__,
|
||||
&MediaDataDecoderProxy::InternalInit);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -32,30 +32,6 @@ private:
|
||||
nsRefPtr<MediaRawData> mSample;
|
||||
};
|
||||
|
||||
class InitTask : public nsRunnable {
|
||||
public:
|
||||
explicit InitTask(MediaDataDecoder* aDecoder)
|
||||
: mDecoder(aDecoder)
|
||||
, mResultValid(false)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mResult = mDecoder->Init();
|
||||
mResultValid = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Result() {
|
||||
MOZ_ASSERT(mResultValid);
|
||||
return mResult;
|
||||
}
|
||||
|
||||
private:
|
||||
MediaDataDecoder* mDecoder;
|
||||
nsresult mResult;
|
||||
bool mResultValid;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Condition {
|
||||
public:
|
||||
@@ -127,6 +103,7 @@ public:
|
||||
, mIsShutdown(false)
|
||||
#endif
|
||||
{
|
||||
mProxyThreadWrapper = CreateXPCOMAbstractThreadWrapper(aProxyThread, false);
|
||||
}
|
||||
|
||||
// Ideally, this would return a regular MediaDataDecoderCallback pointer
|
||||
@@ -150,7 +127,7 @@ public:
|
||||
// Init and Shutdown run synchronously on the proxy thread, all others are
|
||||
// asynchronously and responded to via the MediaDataDecoderCallback.
|
||||
// Note: the nsresults returned by the proxied decoder are lost.
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<InitPromise> Init() override;
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual nsresult Drain() override;
|
||||
@@ -160,6 +137,8 @@ public:
|
||||
void FlushComplete();
|
||||
|
||||
private:
|
||||
nsRefPtr<InitPromise> InternalInit();
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsOnProxyThread() {
|
||||
return NS_GetCurrentThread() == mProxyThread;
|
||||
@@ -171,6 +150,7 @@ private:
|
||||
|
||||
nsRefPtr<MediaDataDecoder> mProxyDecoder;
|
||||
nsCOMPtr<nsIThread> mProxyThread;
|
||||
nsRefPtr<AbstractThread> mProxyThreadWrapper;
|
||||
|
||||
MediaDataDecoderCallbackProxy mProxyCallback;
|
||||
|
||||
|
||||
@@ -47,14 +47,18 @@ public:
|
||||
|
||||
}
|
||||
|
||||
nsresult Init() override {
|
||||
nsRefPtr<InitPromise> Init() override {
|
||||
mSurfaceTexture = AndroidSurfaceTexture::Create();
|
||||
if (!mSurfaceTexture) {
|
||||
NS_WARNING("Failed to create SurfaceTexture for video decode\n");
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
return InitDecoder(mSurfaceTexture->JavaSurface());
|
||||
if (NS_FAILED(InitDecoder(mSurfaceTexture->JavaSurface()))) {
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
|
||||
}
|
||||
|
||||
void Cleanup() override {
|
||||
@@ -336,9 +340,17 @@ MediaCodecDataDecoder::~MediaCodecDataDecoder()
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
nsresult MediaCodecDataDecoder::Init()
|
||||
nsRefPtr<MediaDataDecoder::InitPromise> MediaCodecDataDecoder::Init()
|
||||
{
|
||||
return InitDecoder(nullptr);
|
||||
nsresult rv = InitDecoder(nullptr);
|
||||
|
||||
TrackInfo::TrackType type =
|
||||
(mType == MediaData::AUDIO_DATA ? TrackInfo::TrackType::kAudioTrack
|
||||
: TrackInfo::TrackType::kVideoTrack);
|
||||
|
||||
return NS_SUCCEEDED(rv) ?
|
||||
InitPromise::CreateAndResolve(type, __func__) :
|
||||
InitPromise::CreateAndReject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
nsresult MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface)
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
|
||||
virtual ~MediaCodecDataDecoder();
|
||||
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<MediaDataDecoder::InitPromise> Init() override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual nsresult Drain() override;
|
||||
virtual nsresult Shutdown() override;
|
||||
|
||||
@@ -50,14 +50,15 @@ AppleATDecoder::~AppleATDecoder()
|
||||
MOZ_ASSERT(!mConverter);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
AppleATDecoder::Init()
|
||||
{
|
||||
if (!mFormatID) {
|
||||
NS_ERROR("Non recognised format");
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
return NS_OK;
|
||||
|
||||
return InitPromise::CreateAndResolve(TrackType::kAudioTrack, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
virtual ~AppleATDecoder();
|
||||
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<InitPromise> Init() override;
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual nsresult Drain() override;
|
||||
|
||||
@@ -43,6 +43,56 @@ private:
|
||||
T mRef;
|
||||
};
|
||||
|
||||
// CFRefPtr: A CoreFoundation smart pointer.
|
||||
template <class T>
|
||||
class CFRefPtr {
|
||||
public:
|
||||
explicit CFRefPtr(T aRef)
|
||||
: mRef(aRef)
|
||||
{
|
||||
if (mRef) {
|
||||
CFRetain(mRef);
|
||||
}
|
||||
}
|
||||
// Copy constructor.
|
||||
CFRefPtr(const CFRefPtr<T>& aCFRefPtr)
|
||||
: mRef(aCFRefPtr.mRef)
|
||||
{
|
||||
if (mRef) {
|
||||
CFRetain(mRef);
|
||||
}
|
||||
}
|
||||
// Copy operator
|
||||
CFRefPtr<T>& operator=(const CFRefPtr<T>& aCFRefPtr)
|
||||
{
|
||||
if (mRef == aCFRefPtr.mRef) {
|
||||
return;
|
||||
}
|
||||
if (mRef) {
|
||||
CFRelease(mRef);
|
||||
}
|
||||
mRef = aCFRefPtr.mRef;
|
||||
if (mRef) {
|
||||
CFRetain(mRef);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
~CFRefPtr()
|
||||
{
|
||||
if (mRef) {
|
||||
CFRelease(mRef);
|
||||
}
|
||||
}
|
||||
// Return the wrapped ref so it can be used as an in parameter.
|
||||
operator T()
|
||||
{
|
||||
return mRef;
|
||||
}
|
||||
|
||||
private:
|
||||
T mRef;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_AppleUtils_h
|
||||
|
||||
@@ -40,8 +40,12 @@ AppleVDADecoder::AppleVDADecoder(const VideoInfo& aConfig,
|
||||
, mPictureHeight(aConfig.mImage.height)
|
||||
, mDisplayWidth(aConfig.mDisplay.width)
|
||||
, mDisplayHeight(aConfig.mDisplay.height)
|
||||
, mInputIncoming(0)
|
||||
, mIsShutDown(false)
|
||||
, mUseSoftwareImages(false)
|
||||
, mIs106(!nsCocoaFeatures::OnLionOrLater())
|
||||
, mMonitor("AppleVideoDecoder")
|
||||
, mIsFlushing(false)
|
||||
, mDecoder(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AppleVDADecoder);
|
||||
@@ -73,35 +77,42 @@ AppleVDADecoder::~AppleVDADecoder()
|
||||
MOZ_COUNT_DTOR(AppleVDADecoder);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
AppleVDADecoder::Init()
|
||||
{
|
||||
if (!gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding()) {
|
||||
// This GPU is blacklisted for hardware decoding.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mDecoder) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsresult rv = InitializeSession();
|
||||
return rv;
|
||||
return InitPromise::CreateAndResolve(TrackType::kVideoTrack, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVDADecoder::Shutdown()
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown);
|
||||
mIsShutDown = true;
|
||||
if (mTaskQueue) {
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &AppleVDADecoder::ProcessShutdown);
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
} else {
|
||||
ProcessShutdown();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
AppleVDADecoder::ProcessShutdown()
|
||||
{
|
||||
if (mDecoder) {
|
||||
LOG("%s: cleaning up decoder %p", __func__, mDecoder);
|
||||
VDADecoderDestroy(mDecoder);
|
||||
mDecoder = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVDADecoder::Input(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
|
||||
LOG("mp4 input sample %p pts %lld duration %lld us%s %d bytes",
|
||||
aSample,
|
||||
aSample->mTime,
|
||||
@@ -109,6 +120,8 @@ AppleVDADecoder::Input(MediaRawData* aSample)
|
||||
aSample->mKeyframe ? " keyframe" : "",
|
||||
aSample->Size());
|
||||
|
||||
mInputIncoming++;
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
|
||||
this,
|
||||
@@ -121,21 +134,51 @@ AppleVDADecoder::Input(MediaRawData* aSample)
|
||||
nsresult
|
||||
AppleVDADecoder::Flush()
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
mIsFlushing = true;
|
||||
mTaskQueue->Flush();
|
||||
OSStatus rv = VDADecoderFlush(mDecoder, 0 /*dont emit*/);
|
||||
if (rv != noErr) {
|
||||
LOG("AppleVDADecoder::Flush failed waiting for platform decoder "
|
||||
"with error:%d.", rv);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &AppleVDADecoder::ProcessFlush);
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
while (mIsFlushing) {
|
||||
mon.Wait();
|
||||
}
|
||||
ClearReorderedFrames();
|
||||
|
||||
mInputIncoming = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVDADecoder::Drain()
|
||||
{
|
||||
mTaskQueue->AwaitIdle();
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &AppleVDADecoder::ProcessDrain);
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
AppleVDADecoder::ProcessFlush()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
|
||||
OSStatus rv = VDADecoderFlush(mDecoder, 0 /*dont emit*/);
|
||||
if (rv != noErr) {
|
||||
LOG("AppleVDADecoder::Flush failed waiting for platform decoder "
|
||||
"with error:%d.", rv);
|
||||
}
|
||||
ClearReorderedFrames();
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mIsFlushing = false;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
AppleVDADecoder::ProcessDrain()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
|
||||
OSStatus rv = VDADecoderFlush(mDecoder, kVDADecoderFlush_EmitFrames);
|
||||
if (rv != noErr) {
|
||||
LOG("AppleVDADecoder::Drain failed waiting for platform decoder "
|
||||
@@ -143,7 +186,6 @@ AppleVDADecoder::Drain()
|
||||
}
|
||||
DrainReorderedFrames();
|
||||
mCallback->DrainComplete();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -208,17 +250,19 @@ PlatformCallback(void* decompressionOutputRefCon,
|
||||
CFNumberGetValue(boref, kCFNumberSInt64Type, &byte_offset);
|
||||
CFNumberGetValue(kfref, kCFNumberSInt8Type, &is_sync_point);
|
||||
|
||||
nsAutoPtr<AppleVDADecoder::AppleFrameRef> frameRef(
|
||||
new AppleVDADecoder::AppleFrameRef(
|
||||
AppleVDADecoder::AppleFrameRef frameRef(
|
||||
media::TimeUnit::FromMicroseconds(dts),
|
||||
media::TimeUnit::FromMicroseconds(pts),
|
||||
media::TimeUnit::FromMicroseconds(duration),
|
||||
byte_offset,
|
||||
is_sync_point == 1));
|
||||
is_sync_point == 1);
|
||||
|
||||
// Forward the data back to an object method which can access
|
||||
// the correct MP4Reader callback.
|
||||
decoder->OutputFrame(image, frameRef);
|
||||
// the correct reader's callback.
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethodWithArgs<CFRefPtr<CVPixelBufferRef>, AppleVDADecoder::AppleFrameRef>(
|
||||
decoder, &AppleVDADecoder::OutputFrame, image, frameRef);
|
||||
decoder->DispatchOutputTask(task.forget());
|
||||
}
|
||||
|
||||
AppleVDADecoder::AppleFrameRef*
|
||||
@@ -246,15 +290,22 @@ AppleVDADecoder::ClearReorderedFrames()
|
||||
|
||||
// Copy and return a decoded frame.
|
||||
nsresult
|
||||
AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
|
||||
nsAutoPtr<AppleVDADecoder::AppleFrameRef> aFrameRef)
|
||||
AppleVDADecoder::OutputFrame(CFRefPtr<CVPixelBufferRef> aImage,
|
||||
AppleVDADecoder::AppleFrameRef aFrameRef)
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
|
||||
if (mIsFlushing) {
|
||||
// We are in the process of flushing; ignore frame.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
LOG("mp4 output frame %lld dts %lld pts %lld duration %lld us%s",
|
||||
aFrameRef->byte_offset,
|
||||
aFrameRef->decode_timestamp.ToMicroseconds(),
|
||||
aFrameRef->composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef->duration.ToMicroseconds(),
|
||||
aFrameRef->is_sync_point ? " keyframe" : ""
|
||||
aFrameRef.byte_offset,
|
||||
aFrameRef.decode_timestamp.ToMicroseconds(),
|
||||
aFrameRef.composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef.duration.ToMicroseconds(),
|
||||
aFrameRef.is_sync_point ? " keyframe" : ""
|
||||
);
|
||||
|
||||
// Where our resulting image will end up.
|
||||
@@ -312,12 +363,12 @@ AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
|
||||
VideoData::Create(info,
|
||||
mImageContainer,
|
||||
nullptr,
|
||||
aFrameRef->byte_offset,
|
||||
aFrameRef->composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef->duration.ToMicroseconds(),
|
||||
aFrameRef.byte_offset,
|
||||
aFrameRef.composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef.duration.ToMicroseconds(),
|
||||
buffer,
|
||||
aFrameRef->is_sync_point,
|
||||
aFrameRef->decode_timestamp.ToMicroseconds(),
|
||||
aFrameRef.is_sync_point,
|
||||
aFrameRef.decode_timestamp.ToMicroseconds(),
|
||||
visible);
|
||||
// Unlock the returned image data.
|
||||
CVPixelBufferUnlockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
|
||||
@@ -336,12 +387,12 @@ AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
|
||||
data =
|
||||
VideoData::CreateFromImage(info,
|
||||
mImageContainer,
|
||||
aFrameRef->byte_offset,
|
||||
aFrameRef->composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef->duration.ToMicroseconds(),
|
||||
aFrameRef.byte_offset,
|
||||
aFrameRef.composition_timestamp.ToMicroseconds(),
|
||||
aFrameRef.duration.ToMicroseconds(),
|
||||
image.forget(),
|
||||
aFrameRef->is_sync_point,
|
||||
aFrameRef->decode_timestamp.ToMicroseconds(),
|
||||
aFrameRef.is_sync_point,
|
||||
aFrameRef.decode_timestamp.ToMicroseconds(),
|
||||
visible);
|
||||
}
|
||||
|
||||
@@ -366,6 +417,10 @@ AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
|
||||
nsresult
|
||||
AppleVDADecoder::SubmitFrame(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
|
||||
mInputIncoming--;
|
||||
|
||||
AutoCFRelease<CFDataRef> block =
|
||||
CFDataCreate(kCFAllocatorDefault, aSample->Data(), aSample->Size());
|
||||
if (!block) {
|
||||
@@ -438,7 +493,7 @@ AppleVDADecoder::SubmitFrame(MediaRawData* aSample)
|
||||
}
|
||||
|
||||
// Ask for more data.
|
||||
if (mTaskQueue->IsEmpty()) {
|
||||
if (!mInputIncoming) {
|
||||
LOG("AppleVDADecoder task queue empty; requesting more data");
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
@@ -579,11 +634,18 @@ AppleVDADecoder::CreateVDADecoder(
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
layers::ImageContainer* aImageContainer)
|
||||
{
|
||||
nsRefPtr<AppleVDADecoder> decoder =
|
||||
new AppleVDADecoder(aConfig, aVideoTaskQueue, aCallback, aImageContainer);
|
||||
if (NS_FAILED(decoder->Init())) {
|
||||
if (!gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding()) {
|
||||
// This GPU is blacklisted for hardware decoding.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<AppleVDADecoder> decoder =
|
||||
new AppleVDADecoder(aConfig, aVideoTaskQueue, aCallback, aImageContainer);
|
||||
|
||||
if (NS_FAILED(decoder->InitializeSession())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#define mozilla_AppleVDADecoder_h
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "MP4Decoder.h"
|
||||
#include "nsIThread.h"
|
||||
@@ -70,16 +71,34 @@ public:
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
layers::ImageContainer* aImageContainer);
|
||||
virtual ~AppleVDADecoder();
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<InitPromise> Init() override;
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual nsresult Drain() override;
|
||||
virtual nsresult Shutdown() override;
|
||||
virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult OutputFrame(CVPixelBufferRef aImage,
|
||||
nsAutoPtr<AppleFrameRef> aFrameRef);
|
||||
void DispatchOutputTask(already_AddRefed<nsIRunnable> aTask)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> task = aTask;
|
||||
if (mIsShutDown || mIsFlushing) {
|
||||
return;
|
||||
}
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
}
|
||||
|
||||
nsresult OutputFrame(CFRefPtr<CVPixelBufferRef> aImage,
|
||||
AppleFrameRef aFrameRef);
|
||||
|
||||
protected:
|
||||
// Flush and Drain operation, always run
|
||||
virtual void ProcessFlush();
|
||||
virtual void ProcessDrain();
|
||||
virtual void ProcessShutdown();
|
||||
|
||||
protected:
|
||||
AppleFrameRef* CreateAppleFrameRef(const MediaRawData* aSample);
|
||||
void DrainReorderedFrames();
|
||||
void ClearReorderedFrames();
|
||||
@@ -95,16 +114,29 @@ public:
|
||||
uint32_t mDisplayWidth;
|
||||
uint32_t mDisplayHeight;
|
||||
uint32_t mMaxRefFrames;
|
||||
// Increased when Input is called, and decreased when ProcessFrame runs.
|
||||
// Reaching 0 indicates that there's no pending Input.
|
||||
Atomic<uint32_t> mInputIncoming;
|
||||
Atomic<bool> mIsShutDown;
|
||||
|
||||
bool mUseSoftwareImages;
|
||||
bool mIs106;
|
||||
|
||||
// For wait on mIsFlushing during Shutdown() process.
|
||||
Monitor mMonitor;
|
||||
// Set on reader/decode thread calling Flush() to indicate that output is
|
||||
// not required and so input samples on mTaskQueue need not be processed.
|
||||
// Cleared on mTaskQueue in ProcessDrain().
|
||||
Atomic<bool> mIsFlushing;
|
||||
|
||||
private:
|
||||
VDADecoder mDecoder;
|
||||
|
||||
// Method to pass a frame to VideoToolbox for decoding.
|
||||
nsresult SubmitFrame(MediaRawData* aSample);
|
||||
// Method to set up the decompression session.
|
||||
nsresult InitializeSession();
|
||||
|
||||
// Method to pass a frame to VideoToolbox for decoding.
|
||||
nsresult SubmitFrame(MediaRawData* aSample);
|
||||
CFDictionaryRef CreateDecoderSpecification();
|
||||
};
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ AppleVTDecoder::AppleVTDecoder(const VideoInfo& aConfig,
|
||||
: AppleVDADecoder(aConfig, aVideoTaskQueue, aCallback, aImageContainer)
|
||||
, mFormat(nullptr)
|
||||
, mSession(nullptr)
|
||||
, mIsHardwareAccelerated(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AppleVTDecoder);
|
||||
// TODO: Verify aConfig.mime_type.
|
||||
@@ -50,15 +51,20 @@ AppleVTDecoder::~AppleVTDecoder()
|
||||
MOZ_COUNT_DTOR(AppleVTDecoder);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
AppleVTDecoder::Init()
|
||||
{
|
||||
nsresult rv = InitializeSession();
|
||||
return rv;
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return InitPromise::CreateAndResolve(TrackType::kVideoTrack, __func__);
|
||||
}
|
||||
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVTDecoder::Shutdown()
|
||||
void
|
||||
AppleVTDecoder::ProcessShutdown()
|
||||
{
|
||||
if (mSession) {
|
||||
LOG("%s: cleaning up session %p", __func__, mSession);
|
||||
@@ -71,12 +77,13 @@ AppleVTDecoder::Shutdown()
|
||||
CFRelease(mFormat);
|
||||
mFormat = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVTDecoder::Input(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
|
||||
LOG("mp4 input sample %p pts %lld duration %lld us%s %d bytes",
|
||||
aSample,
|
||||
aSample->mTime,
|
||||
@@ -96,33 +103,34 @@ AppleVTDecoder::Input(MediaRawData* aSample)
|
||||
LOG(" sha1 %s", digest.get());
|
||||
#endif // LOG_MEDIA_SHA1
|
||||
|
||||
mInputIncoming++;
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
|
||||
this,
|
||||
&AppleVTDecoder::SubmitFrame,
|
||||
nsRefPtr<MediaRawData>(aSample));
|
||||
this, &AppleVTDecoder::SubmitFrame, aSample);
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVTDecoder::Flush()
|
||||
void
|
||||
AppleVTDecoder::ProcessFlush()
|
||||
{
|
||||
mTaskQueue->Flush();
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
nsresult rv = WaitForAsynchronousFrames();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("AppleVTDecoder::Flush failed waiting for platform decoder "
|
||||
"with error:%d.", rv);
|
||||
}
|
||||
ClearReorderedFrames();
|
||||
|
||||
return rv;
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mIsFlushing = false;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleVTDecoder::Drain()
|
||||
void
|
||||
AppleVTDecoder::ProcessDrain()
|
||||
{
|
||||
mTaskQueue->AwaitIdle();
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
nsresult rv = WaitForAsynchronousFrames();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("AppleVTDecoder::Drain failed waiting for platform decoder "
|
||||
@@ -130,7 +138,6 @@ AppleVTDecoder::Drain()
|
||||
}
|
||||
DrainReorderedFrames();
|
||||
mCallback->DrainComplete();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -168,9 +175,10 @@ PlatformCallback(void* decompressionOutputRefCon,
|
||||
MOZ_ASSERT(CFGetTypeID(image) == CVPixelBufferGetTypeID(),
|
||||
"VideoToolbox returned an unexpected image type");
|
||||
|
||||
// Forward the data back to an object method which can access
|
||||
// the correct MP4Reader callback.
|
||||
decoder->OutputFrame(image, frameRef);
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethodWithArgs<CFRefPtr<CVPixelBufferRef>, AppleVTDecoder::AppleFrameRef>(
|
||||
decoder, &AppleVTDecoder::OutputFrame, image, *frameRef);
|
||||
decoder->DispatchOutputTask(task.forget());
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -202,6 +210,8 @@ TimingInfoFromSample(MediaRawData* aSample)
|
||||
nsresult
|
||||
AppleVTDecoder::SubmitFrame(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
mInputIncoming--;
|
||||
// For some reason this gives me a double-free error with stagefright.
|
||||
AutoCFRelease<CMBlockBufferRef> block = nullptr;
|
||||
AutoCFRelease<CMSampleBufferRef> sample = nullptr;
|
||||
@@ -245,7 +255,7 @@ AppleVTDecoder::SubmitFrame(MediaRawData* aSample)
|
||||
}
|
||||
|
||||
// Ask for more data.
|
||||
if (mTaskQueue->IsEmpty()) {
|
||||
if (!mInputIncoming) {
|
||||
LOG("AppleVTDecoder task queue empty; requesting more data");
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
@@ -313,8 +323,9 @@ AppleVTDecoder::InitializeSession()
|
||||
if (rv != noErr) {
|
||||
LOG("AppleVTDecoder: system doesn't support hardware acceleration");
|
||||
}
|
||||
mIsHardwareAccelerated = rv == noErr && isUsingHW == kCFBooleanTrue;
|
||||
LOG("AppleVTDecoder: %s hardware accelerated decoding",
|
||||
(rv == noErr && isUsingHW == kCFBooleanTrue) ? "using" : "not using");
|
||||
mIsHardwareAccelerated ? "using" : "not using");
|
||||
} else {
|
||||
LOG("AppleVTDecoder: couldn't determine hardware acceleration status.");
|
||||
}
|
||||
|
||||
@@ -20,11 +20,17 @@ public:
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
layers::ImageContainer* aImageContainer);
|
||||
virtual ~AppleVTDecoder();
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<InitPromise> Init() override;
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual nsresult Drain() override;
|
||||
virtual nsresult Shutdown() override;
|
||||
virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const override
|
||||
{
|
||||
return mIsHardwareAccelerated;
|
||||
}
|
||||
|
||||
protected:
|
||||
void ProcessFlush() override;
|
||||
void ProcessDrain() override;
|
||||
void ProcessShutdown() override;
|
||||
|
||||
private:
|
||||
CMVideoFormatDescriptionRef mFormat;
|
||||
@@ -37,6 +43,7 @@ private:
|
||||
nsresult WaitForAsynchronousFrames();
|
||||
CFDictionaryRef CreateDecoderSpecification();
|
||||
CFDictionaryRef CreateDecoderExtensions();
|
||||
bool mIsHardwareAccelerated;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -31,16 +31,18 @@ FFmpegAudioDecoder<LIBAV_VER>::FFmpegAudioDecoder(
|
||||
mExtraData->AppendElements(*aConfig.mCodecSpecificConfig);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
FFmpegAudioDecoder<LIBAV_VER>::Init()
|
||||
{
|
||||
nsresult rv = FFmpegDataDecoder::Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsresult rv = InitDecoder();
|
||||
|
||||
avcodec_decode_audio4 = (decltype(avcodec_decode_audio4))FFmpegRuntimeLinker::avc_ptr[_decode_audio4];
|
||||
av_init_packet1 = (decltype(av_init_packet1))FFmpegRuntimeLinker::avc_ptr[_init_packet];
|
||||
if(rv == NS_OK) {
|
||||
avcodec_decode_audio4 = (decltype(avcodec_decode_audio4))FFmpegRuntimeLinker::avc_ptr[_decode_audio4];
|
||||
av_init_packet1 = (decltype(av_init_packet1))FFmpegRuntimeLinker::avc_ptr[_init_packet];
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return rv == NS_OK ? InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__)
|
||||
: InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
const AudioInfo& aConfig);
|
||||
virtual ~FFmpegAudioDecoder();
|
||||
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<InitPromise> Init() override;
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
virtual nsresult Drain() override;
|
||||
void InitCodecContext() override;
|
||||
|
||||
@@ -40,7 +40,7 @@ FFmpegDataDecoder<LIBAV_VER>::~FFmpegDataDecoder()
|
||||
}
|
||||
|
||||
nsresult
|
||||
FFmpegDataDecoder<LIBAV_VER>::Init()
|
||||
FFmpegDataDecoder<LIBAV_VER>::InitDecoder()
|
||||
{
|
||||
FFMPEG_LOG("Initialising FFmpeg decoder.");
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
|
||||
static bool Link();
|
||||
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<InitPromise> Init() override = 0;
|
||||
virtual nsresult Input(MediaRawData* aSample) override = 0;
|
||||
virtual nsresult Flush() override;
|
||||
virtual nsresult Drain() override = 0;
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
protected:
|
||||
virtual void InitCodecContext() {}
|
||||
AVFrame* PrepareFrame();
|
||||
nsresult InitDecoder();
|
||||
|
||||
FlushableTaskQueue* mTaskQueue;
|
||||
AVCodecContext* mCodecContext;
|
||||
|
||||
@@ -128,14 +128,17 @@ FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
|
||||
mExtraData->AppendElements(*aConfig.mExtraData);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
FFmpegH264Decoder<LIBAV_VER>::Init()
|
||||
{
|
||||
nsresult rv = FFmpegDataDecoder::Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_FAILED(InitDecoder())) {
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
avcodec_decode_video2 = (decltype(avcodec_decode_video2))FFmpegRuntimeLinker::avc_ptr[_decode_video2];
|
||||
av_init_packet = (decltype(av_init_packet))FFmpegRuntimeLinker::avc_ptr[_init_packet];
|
||||
return NS_OK;
|
||||
|
||||
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
ImageContainer* aImageContainer);
|
||||
virtual ~FFmpegH264Decoder();
|
||||
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<InitPromise> Init() override;
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
virtual nsresult Drain() override;
|
||||
virtual nsresult Flush() override;
|
||||
|
||||
@@ -37,6 +37,8 @@ public:
|
||||
|
||||
virtual bool HasQueuedSample() override;
|
||||
|
||||
virtual TrackType GetTrackType() override { return TrackType::kAudioTrack; }
|
||||
|
||||
private:
|
||||
nsresult CreateAudioData(int64_t aStreamOffset,
|
||||
AudioData** aOutData);
|
||||
|
||||
@@ -37,7 +37,7 @@ GonkMediaDataDecoder::~GonkMediaDataDecoder()
|
||||
MOZ_COUNT_DTOR(GonkMediaDataDecoder);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
GonkMediaDataDecoder::Init()
|
||||
{
|
||||
sp<MediaCodecProxy> decoder;
|
||||
@@ -45,7 +45,7 @@ GonkMediaDataDecoder::Init()
|
||||
mDecoder = decoder;
|
||||
mDrainComplete = false;
|
||||
|
||||
return NS_OK;
|
||||
return InitPromise::CreateAndResolve(mManager->GetTrackType(), __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -18,6 +18,8 @@ class MediaRawData;
|
||||
// Manage the data flow from inputting encoded data and outputting decode data.
|
||||
class GonkDecoderManager {
|
||||
public:
|
||||
typedef TrackInfo::TrackType TrackType;
|
||||
|
||||
virtual ~GonkDecoderManager() {}
|
||||
|
||||
// Creates and initializs the GonkDecoder.
|
||||
@@ -39,6 +41,8 @@ public:
|
||||
// Flush the queued sample.
|
||||
virtual nsresult Flush() = 0;
|
||||
|
||||
virtual TrackType GetTrackType() = 0;
|
||||
|
||||
void ClearQueuedSample() {
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
mQueueSample.Clear();
|
||||
@@ -60,9 +64,9 @@ public:
|
||||
|
||||
~GonkMediaDataDecoder();
|
||||
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<InitPromise> Init() override;
|
||||
|
||||
virtual nsresult Input(MediaRawData* aSample);
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
|
||||
virtual nsresult Flush() override;
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ public:
|
||||
|
||||
virtual bool HasQueuedSample() override;
|
||||
|
||||
virtual TrackType GetTrackType() override { return TrackType::kVideoTrack; }
|
||||
|
||||
static void RecycleCallback(TextureClient* aClient, void* aClosure);
|
||||
|
||||
private:
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "mfapi.h"
|
||||
#include "MFTDecoder.h"
|
||||
#include "DriverCrashGuard.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
const CLSID CLSID_VideoProcessorMFT =
|
||||
{
|
||||
@@ -48,7 +49,7 @@ public:
|
||||
D3D9DXVA2Manager();
|
||||
virtual ~D3D9DXVA2Manager();
|
||||
|
||||
HRESULT Init();
|
||||
HRESULT Init(nsACString& aFailureReason);
|
||||
|
||||
IUnknown* GetDXVADeviceManager() override;
|
||||
|
||||
@@ -87,13 +88,14 @@ D3D9DXVA2Manager::GetDXVADeviceManager()
|
||||
}
|
||||
|
||||
HRESULT
|
||||
D3D9DXVA2Manager::Init()
|
||||
D3D9DXVA2Manager::Init(nsACString& aFailureReason)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
gfx::D3D9VideoCrashGuard crashGuard;
|
||||
if (crashGuard.Crashed()) {
|
||||
NS_WARNING("DXVA2D3D9 crash detected");
|
||||
aFailureReason.AssignLiteral("DXVA2D3D9 crashes detected in the past");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
@@ -106,6 +108,7 @@ D3D9DXVA2Manager::Init()
|
||||
HRESULT hr = d3d9Create(D3D_SDK_VERSION, getter_AddRefs(d3d9Ex));
|
||||
if (!d3d9Ex) {
|
||||
NS_WARNING("Direct3DCreate9 failed");
|
||||
aFailureReason.AssignLiteral("Direct3DCreate9 failed");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
@@ -115,7 +118,10 @@ D3D9DXVA2Manager::Init()
|
||||
D3DDEVTYPE_HAL,
|
||||
(D3DFORMAT)MAKEFOURCC('N','V','1','2'),
|
||||
D3DFMT_X8R8G8B8);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
aFailureReason = nsPrintfCString("CheckDeviceFormatConversion failed with error %X", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Create D3D9DeviceEx.
|
||||
D3DPRESENT_PARAMETERS params = {0};
|
||||
@@ -138,7 +144,10 @@ D3D9DXVA2Manager::Init()
|
||||
¶ms,
|
||||
nullptr,
|
||||
getter_AddRefs(device));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
aFailureReason = nsPrintfCString("CreateDeviceEx failed with error %X", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Ensure we can create queries to synchronize operations between devices.
|
||||
// Without this, when we make a copy of the frame in order to share it with
|
||||
@@ -147,7 +156,10 @@ D3D9DXVA2Manager::Init()
|
||||
nsRefPtr<IDirect3DQuery9> query;
|
||||
|
||||
hr = device->CreateQuery(D3DQUERYTYPE_EVENT, getter_AddRefs(query));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
aFailureReason = nsPrintfCString("CreateQuery failed with error %X", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Create and initialize IDirect3DDeviceManager9.
|
||||
UINT resetToken = 0;
|
||||
@@ -155,9 +167,15 @@ D3D9DXVA2Manager::Init()
|
||||
|
||||
hr = wmf::DXVA2CreateDirect3DDeviceManager9(&resetToken,
|
||||
getter_AddRefs(deviceManager));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
aFailureReason = nsPrintfCString("DXVA2CreateDirect3DDeviceManager9 failed with error %X", hr);
|
||||
return hr;
|
||||
}
|
||||
hr = deviceManager->ResetDevice(device, resetToken);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
aFailureReason = nsPrintfCString("IDirect3DDeviceManager9::ResetDevice failed with error %X", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
mResetToken = resetToken;
|
||||
mD3D9 = d3d9Ex;
|
||||
@@ -203,7 +221,7 @@ static uint32_t sDXVAVideosCount = 0;
|
||||
|
||||
/* static */
|
||||
DXVA2Manager*
|
||||
DXVA2Manager::CreateD3D9DXVA()
|
||||
DXVA2Manager::CreateD3D9DXVA(nsACString& aFailureReason)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
HRESULT hr;
|
||||
@@ -213,11 +231,12 @@ DXVA2Manager::CreateD3D9DXVA()
|
||||
const uint32_t dxvaLimit =
|
||||
Preferences::GetInt("media.windows-media-foundation.max-dxva-videos", 8);
|
||||
if (sDXVAVideosCount == dxvaLimit) {
|
||||
aFailureReason.AssignLiteral("Too many DXVA videos playing");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoPtr<D3D9DXVA2Manager> d3d9Manager(new D3D9DXVA2Manager());
|
||||
hr = d3d9Manager->Init();
|
||||
hr = d3d9Manager->Init(aFailureReason);
|
||||
if (SUCCEEDED(hr)) {
|
||||
return d3d9Manager.forget();
|
||||
}
|
||||
@@ -232,7 +251,7 @@ public:
|
||||
D3D11DXVA2Manager();
|
||||
virtual ~D3D11DXVA2Manager();
|
||||
|
||||
HRESULT Init();
|
||||
HRESULT Init(nsACString& aFailureReason);
|
||||
|
||||
IUnknown* GetDXVADeviceManager() override;
|
||||
|
||||
@@ -281,28 +300,46 @@ D3D11DXVA2Manager::GetDXVADeviceManager()
|
||||
}
|
||||
|
||||
HRESULT
|
||||
D3D11DXVA2Manager::Init()
|
||||
D3D11DXVA2Manager::Init(nsACString& aFailureReason)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
mDevice = gfxWindowsPlatform::GetPlatform()->CreateD3D11DecoderDevice();
|
||||
NS_ENSURE_TRUE(mDevice, E_FAIL);
|
||||
if (!mDevice) {
|
||||
aFailureReason.AssignLiteral("Failed to create D3D11 device for decoder");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
mDevice->GetImmediateContext(byRef(mContext));
|
||||
NS_ENSURE_TRUE(mContext, E_FAIL);
|
||||
if (!mContext) {
|
||||
aFailureReason.AssignLiteral("Failed to get immediate context for d3d11 device");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
hr = wmf::MFCreateDXGIDeviceManager(&mDeviceManagerToken, byRef(mDXGIDeviceManager));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr),hr);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
aFailureReason = nsPrintfCString("MFCreateDXGIDeviceManager failed with code %X", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = mDXGIDeviceManager->ResetDevice(mDevice, mDeviceManagerToken);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr),hr);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
aFailureReason = nsPrintfCString("IMFDXGIDeviceManager::ResetDevice failed with code %X", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
mTransform = new MFTDecoder();
|
||||
hr = mTransform->Create(CLSID_VideoProcessorMFT);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
aFailureReason = nsPrintfCString("MFTDecoder::Create(CLSID_VideoProcessorMFT) failed with code %X", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = mTransform->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER, ULONG_PTR(mDXGIDeviceManager.get()));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
aFailureReason = nsPrintfCString("MFTDecoder::SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER) failed with code %X", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -458,18 +495,19 @@ D3D11DXVA2Manager::ConfigureForSize(uint32_t aWidth, uint32_t aHeight)
|
||||
|
||||
/* static */
|
||||
DXVA2Manager*
|
||||
DXVA2Manager::CreateD3D11DXVA()
|
||||
DXVA2Manager::CreateD3D11DXVA(nsACString& aFailureReason)
|
||||
{
|
||||
// DXVA processing takes up a lot of GPU resources, so limit the number of
|
||||
// videos we use DXVA with at any one time.
|
||||
const uint32_t dxvaLimit =
|
||||
Preferences::GetInt("media.windows-media-foundation.max-dxva-videos", 8);
|
||||
if (sDXVAVideosCount == dxvaLimit) {
|
||||
aFailureReason.AssignLiteral("Too many DXVA videos playing");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoPtr<D3D11DXVA2Manager> manager(new D3D11DXVA2Manager());
|
||||
HRESULT hr = manager->Init();
|
||||
HRESULT hr = manager->Init(aFailureReason);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
|
||||
|
||||
return manager.forget();
|
||||
|
||||
@@ -23,8 +23,8 @@ public:
|
||||
|
||||
// Creates and initializes a DXVA2Manager. We can use DXVA2 via either
|
||||
// D3D9Ex or D3D11.
|
||||
static DXVA2Manager* CreateD3D9DXVA();
|
||||
static DXVA2Manager* CreateD3D11DXVA();
|
||||
static DXVA2Manager* CreateD3D9DXVA(nsACString& aFailureReason);
|
||||
static DXVA2Manager* CreateD3D11DXVA(nsACString& aFailureReason);
|
||||
|
||||
// Returns a pointer to the D3D device manager responsible for managing the
|
||||
// device we're using for hardware accelerated video decoding. If we're using
|
||||
|
||||
@@ -31,6 +31,10 @@ public:
|
||||
|
||||
virtual void Shutdown() override;
|
||||
|
||||
virtual TrackInfo::TrackType GetType() override {
|
||||
return TrackInfo::kAudioTrack;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
HRESULT UpdateOutputType();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "WMFDecoderModule.h"
|
||||
#include "WMFVideoMFTManager.h"
|
||||
#include "WMFAudioMFTManager.h"
|
||||
#include "MFTDecoder.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Services.h"
|
||||
@@ -98,13 +99,21 @@ WMFDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
|
||||
FlushableTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
nsAutoPtr<WMFVideoMFTManager> manager =
|
||||
new WMFVideoMFTManager(aConfig,
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
sDXVAEnabled && ShouldUseDXVA(aConfig));
|
||||
|
||||
nsRefPtr<MFTDecoder> mft = manager->Init();
|
||||
|
||||
if (!mft) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoder> decoder =
|
||||
new WMFMediaDataDecoder(new WMFVideoMFTManager(aConfig,
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
sDXVAEnabled && ShouldUseDXVA(aConfig)),
|
||||
aVideoTaskQueue,
|
||||
aCallback);
|
||||
new WMFMediaDataDecoder(manager.forget(), mft, aVideoTaskQueue, aCallback);
|
||||
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
@@ -113,10 +122,15 @@ WMFDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
|
||||
FlushableTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
nsAutoPtr<WMFAudioMFTManager> manager = new WMFAudioMFTManager(aConfig);
|
||||
nsRefPtr<MFTDecoder> mft = manager->Init();
|
||||
|
||||
if (!mft) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoder> decoder =
|
||||
new WMFMediaDataDecoder(new WMFAudioMFTManager(aConfig),
|
||||
aAudioTaskQueue,
|
||||
aCallback);
|
||||
new WMFMediaDataDecoder(manager.forget(), mft, aAudioTaskQueue, aCallback);
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,12 @@ PRLogModuleInfo* GetDemuxerLog();
|
||||
namespace mozilla {
|
||||
|
||||
WMFMediaDataDecoder::WMFMediaDataDecoder(MFTManager* aMFTManager,
|
||||
MFTDecoder* aDecoder,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
: mTaskQueue(aTaskQueue)
|
||||
, mCallback(aCallback)
|
||||
, mDecoder(aDecoder)
|
||||
, mMFTManager(aMFTManager)
|
||||
, mMonitor("WMFMediaDataDecoder")
|
||||
, mIsFlushing(false)
|
||||
@@ -32,16 +34,14 @@ WMFMediaDataDecoder::~WMFMediaDataDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
WMFMediaDataDecoder::Init()
|
||||
{
|
||||
MOZ_ASSERT(!mDecoder);
|
||||
MOZ_ASSERT(!mIsShutDown);
|
||||
|
||||
mDecoder = mMFTManager->Init();
|
||||
NS_ENSURE_TRUE(mDecoder, NS_ERROR_FAILURE);
|
||||
|
||||
return NS_OK;
|
||||
return mDecoder ?
|
||||
InitPromise::CreateAndResolve(mMFTManager->GetType(), __func__) :
|
||||
InitPromise::CreateAndReject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -188,10 +188,10 @@ WMFMediaDataDecoder::Drain()
|
||||
}
|
||||
|
||||
bool
|
||||
WMFMediaDataDecoder::IsHardwareAccelerated() const {
|
||||
WMFMediaDataDecoder::IsHardwareAccelerated(nsACString& aFailureReason) const {
|
||||
MOZ_ASSERT(!mIsShutDown);
|
||||
|
||||
return mMFTManager && mMFTManager->IsHardwareAccelerated();
|
||||
return mMFTManager && mMFTManager->IsHardwareAccelerated(aFailureReason);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -43,7 +43,9 @@ public:
|
||||
// Destroys all resources.
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual bool IsHardwareAccelerated() const { return false; }
|
||||
virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const { return false; }
|
||||
|
||||
virtual TrackInfo::TrackType GetType() = 0;
|
||||
|
||||
};
|
||||
|
||||
@@ -55,11 +57,12 @@ public:
|
||||
class WMFMediaDataDecoder : public MediaDataDecoder {
|
||||
public:
|
||||
WMFMediaDataDecoder(MFTManager* aOutputSource,
|
||||
MFTDecoder* aDecoder,
|
||||
FlushableTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
~WMFMediaDataDecoder();
|
||||
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<MediaDataDecoder::InitPromise> Init() override;
|
||||
|
||||
virtual nsresult Input(MediaRawData* aSample);
|
||||
|
||||
@@ -69,7 +72,7 @@ public:
|
||||
|
||||
virtual nsresult Shutdown() override;
|
||||
|
||||
virtual bool IsHardwareAccelerated() const override;
|
||||
virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "IMFYCbCrImage.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
@@ -125,8 +126,9 @@ WMFVideoMFTManager::GetMediaSubtypeGUID()
|
||||
|
||||
class CreateDXVAManagerEvent : public nsRunnable {
|
||||
public:
|
||||
CreateDXVAManagerEvent(LayersBackend aBackend)
|
||||
CreateDXVAManagerEvent(LayersBackend aBackend, nsCString& aFailureReason)
|
||||
: mBackend(aBackend)
|
||||
, mFailureReason(aFailureReason)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
@@ -134,14 +136,15 @@ public:
|
||||
if (mBackend == LayersBackend::LAYERS_D3D11 &&
|
||||
Preferences::GetBool("media.windows-media-foundation.allow-d3d11-dxva", false) &&
|
||||
IsWin8OrLater()) {
|
||||
mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA();
|
||||
mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA(mFailureReason);
|
||||
} else {
|
||||
mDXVA2Manager = DXVA2Manager::CreateD3D9DXVA();
|
||||
mDXVA2Manager = DXVA2Manager::CreateD3D9DXVA(mFailureReason);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
nsAutoPtr<DXVA2Manager> mDXVA2Manager;
|
||||
LayersBackend mBackend;
|
||||
nsACString& mFailureReason;
|
||||
};
|
||||
|
||||
bool
|
||||
@@ -152,15 +155,19 @@ WMFVideoMFTManager::InitializeDXVA(bool aForceD3D9)
|
||||
// If we use DXVA but aren't running with a D3D layer manager then the
|
||||
// readback of decoded video frames from GPU to CPU memory grinds painting
|
||||
// to a halt, and makes playback performance *worse*.
|
||||
if (!mDXVAEnabled ||
|
||||
(mLayersBackend != LayersBackend::LAYERS_D3D9 &&
|
||||
mLayersBackend != LayersBackend::LAYERS_D3D11)) {
|
||||
if (!mDXVAEnabled) {
|
||||
mDXVAFailureReason.AssignLiteral("Hardware video decoding disabled or blacklisted");
|
||||
return false;
|
||||
}
|
||||
if (mLayersBackend != LayersBackend::LAYERS_D3D9 &&
|
||||
mLayersBackend != LayersBackend::LAYERS_D3D11) {
|
||||
mDXVAFailureReason.AssignLiteral("Unsupported layers backend");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DXVA manager must be created on the main thread.
|
||||
nsRefPtr<CreateDXVAManagerEvent> event =
|
||||
new CreateDXVAManagerEvent(aForceD3D9 ? LayersBackend::LAYERS_D3D9 : mLayersBackend);
|
||||
new CreateDXVAManagerEvent(aForceD3D9 ? LayersBackend::LAYERS_D3D9 : mLayersBackend, mDXVAFailureReason);
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
event->Run();
|
||||
@@ -181,7 +188,10 @@ WMFVideoMFTManager::Init()
|
||||
// to d3d9.
|
||||
if (!decoder && mDXVA2Manager && mDXVA2Manager->IsD3D11()) {
|
||||
mDXVA2Manager = nullptr;
|
||||
nsCString d3d11Failure = mDXVAFailureReason;
|
||||
decoder = InitInternal(true);
|
||||
mDXVAFailureReason.Append(NS_LITERAL_CSTRING("; "));
|
||||
mDXVAFailureReason.Append(d3d11Failure);
|
||||
}
|
||||
|
||||
return decoder.forget();
|
||||
@@ -216,7 +226,11 @@ WMFVideoMFTManager::InitInternal(bool aForceD3D9)
|
||||
hr = decoder->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER, manager);
|
||||
if (SUCCEEDED(hr)) {
|
||||
mUseHwAccel = true;
|
||||
} else {
|
||||
mDXVAFailureReason = nsPrintfCString("MFT_MESSAGE_SET_D3D_MANAGER failed with code %X", hr);
|
||||
}
|
||||
} else {
|
||||
mDXVAFailureReason.AssignLiteral("Decoder returned false for MF_SA_D3D_AWARE");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,8 +554,9 @@ WMFVideoMFTManager::Shutdown()
|
||||
}
|
||||
|
||||
bool
|
||||
WMFVideoMFTManager::IsHardwareAccelerated() const
|
||||
WMFVideoMFTManager::IsHardwareAccelerated(nsACString& aFailureReason) const
|
||||
{
|
||||
aFailureReason = mDXVAFailureReason;
|
||||
return mDecoder && mUseHwAccel;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,11 @@ public:
|
||||
|
||||
virtual void Shutdown() override;
|
||||
|
||||
virtual bool IsHardwareAccelerated() const override;
|
||||
virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
|
||||
|
||||
virtual TrackInfo::TrackType GetType() override {
|
||||
return TrackInfo::kVideoTrack;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -67,6 +71,8 @@ private:
|
||||
const layers::LayersBackend mLayersBackend;
|
||||
bool mUseHwAccel;
|
||||
|
||||
nsCString mDXVAFailureReason;
|
||||
|
||||
enum StreamType {
|
||||
Unknown,
|
||||
H264,
|
||||
|
||||
@@ -29,6 +29,7 @@ H264Converter::H264Converter(PlatformDecoderModule* aPDM,
|
||||
, mCallback(aCallback)
|
||||
, mDecoder(nullptr)
|
||||
, mNeedAVCC(aPDM->DecoderNeedsConversion(aConfig) == PlatformDecoderModule::kNeedAVCC)
|
||||
, mDecoderInitializing(false)
|
||||
, mLastError(NS_OK)
|
||||
{
|
||||
CreateDecoder();
|
||||
@@ -38,13 +39,15 @@ H264Converter::~H264Converter()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
H264Converter::Init()
|
||||
{
|
||||
if (mDecoder) {
|
||||
return mDecoder->Init();
|
||||
}
|
||||
return mLastError;
|
||||
|
||||
return MediaDataDecoder::InitPromise::CreateAndReject(
|
||||
MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -59,6 +62,12 @@ H264Converter::Input(MediaRawData* aSample)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (mDecoderInitializing) {
|
||||
mMediaRawSamples.AppendElement(aSample);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (!mDecoder) {
|
||||
// It is not possible to create an AVCC H264 decoder without SPS.
|
||||
@@ -104,6 +113,7 @@ H264Converter::Shutdown()
|
||||
{
|
||||
if (mDecoder) {
|
||||
nsresult rv = mDecoder->Shutdown();
|
||||
mInitPromiseRequest.DisconnectIfExists();
|
||||
mDecoder = nullptr;
|
||||
return rv;
|
||||
}
|
||||
@@ -111,12 +121,12 @@ H264Converter::Shutdown()
|
||||
}
|
||||
|
||||
bool
|
||||
H264Converter::IsHardwareAccelerated() const
|
||||
H264Converter::IsHardwareAccelerated(nsACString& aFailureReason) const
|
||||
{
|
||||
if (mDecoder) {
|
||||
return mDecoder->IsHardwareAccelerated();
|
||||
return mDecoder->IsHardwareAccelerated(aFailureReason);
|
||||
}
|
||||
return MediaDataDecoder::IsHardwareAccelerated();
|
||||
return MediaDataDecoder::IsHardwareAccelerated(aFailureReason);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -151,8 +161,40 @@ H264Converter::CreateDecoderAndInit(MediaRawData* aSample)
|
||||
UpdateConfigFromExtraData(extra_data);
|
||||
|
||||
nsresult rv = CreateDecoder();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return Init();
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mDecoderInitializing = true;
|
||||
nsRefPtr<H264Converter> self = this;
|
||||
|
||||
// The mVideoTaskQueue is flushable which can't be used in MediaPromise. So
|
||||
// we get the current AbstractThread instead of it. The MOZ_ASSERT above
|
||||
// ensures we are running in AbstractThread so we won't get a nullptr.
|
||||
mInitPromiseRequest.Begin(mDecoder->Init()
|
||||
->Then(AbstractThread::GetCurrent(), __func__, this,
|
||||
&H264Converter::OnDecoderInitDone,
|
||||
&H264Converter::OnDecoderInitFailed));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
H264Converter::OnDecoderInitDone(const TrackType aTrackType)
|
||||
{
|
||||
mInitPromiseRequest.Complete();
|
||||
for (uint32_t i = 0 ; i < mMediaRawSamples.Length(); i++) {
|
||||
if (NS_FAILED(mDecoder->Input(mMediaRawSamples[i]))) {
|
||||
mCallback->Error();
|
||||
}
|
||||
}
|
||||
mMediaRawSamples.Clear();
|
||||
mDecoderInitializing = false;
|
||||
}
|
||||
|
||||
void
|
||||
H264Converter::OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason)
|
||||
{
|
||||
mInitPromiseRequest.Complete();
|
||||
mCallback->Error();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -29,12 +29,12 @@ public:
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
virtual ~H264Converter();
|
||||
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<InitPromise> Init() override;
|
||||
virtual nsresult Input(MediaRawData* aSample) override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual nsresult Drain() override;
|
||||
virtual nsresult Shutdown() override;
|
||||
virtual bool IsHardwareAccelerated() const override;
|
||||
virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
|
||||
|
||||
// Return true if mimetype is H.264.
|
||||
static bool IsH264(const TrackInfo& aConfig);
|
||||
@@ -49,14 +49,20 @@ private:
|
||||
nsresult CheckForSPSChange(MediaRawData* aSample);
|
||||
void UpdateConfigFromExtraData(MediaByteBuffer* aExtraData);
|
||||
|
||||
void OnDecoderInitDone(const TrackType aTrackType);
|
||||
void OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason);
|
||||
|
||||
nsRefPtr<PlatformDecoderModule> mPDM;
|
||||
VideoInfo mCurrentConfig;
|
||||
layers::LayersBackend mLayersBackend;
|
||||
nsRefPtr<layers::ImageContainer> mImageContainer;
|
||||
nsRefPtr<FlushableTaskQueue> mVideoTaskQueue;
|
||||
nsTArray<nsRefPtr<MediaRawData>> mMediaRawSamples;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
nsRefPtr<MediaDataDecoder> mDecoder;
|
||||
MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
|
||||
bool mNeedAVCC;
|
||||
bool mDecoderInitializing;
|
||||
nsresult mLastError;
|
||||
};
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ ogg_packet InitOggPacket(const unsigned char* aData, size_t aLength,
|
||||
class VorbisDecoder : public WebMAudioDecoder
|
||||
{
|
||||
public:
|
||||
nsresult Init();
|
||||
nsRefPtr<InitPromise> Init() override;
|
||||
void Shutdown();
|
||||
nsresult ResetDecode();
|
||||
nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
|
||||
@@ -94,14 +94,14 @@ VorbisDecoder::Shutdown()
|
||||
mReader = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<InitPromise>
|
||||
VorbisDecoder::Init()
|
||||
{
|
||||
vorbis_info_init(&mVorbisInfo);
|
||||
vorbis_comment_init(&mVorbisComment);
|
||||
PodZero(&mVorbisDsp);
|
||||
PodZero(&mVorbisBlock);
|
||||
return NS_OK;
|
||||
return InitPromise::CreateAndResolve(TrackType::kAudioTrack, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -229,7 +229,7 @@ VorbisDecoder::Decode(const unsigned char* aData, size_t aLength,
|
||||
class OpusDecoder : public WebMAudioDecoder
|
||||
{
|
||||
public:
|
||||
nsresult Init();
|
||||
nsRefPtr<InitPromise> Init() override;
|
||||
void Shutdown();
|
||||
nsresult ResetDecode();
|
||||
nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
|
||||
@@ -277,10 +277,10 @@ OpusDecoder::Shutdown()
|
||||
mReader = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<InitPromise>
|
||||
OpusDecoder::Init()
|
||||
{
|
||||
return NS_OK;
|
||||
return InitPromise::CreateAndResolve(TrackType::kAudioTrack, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -106,12 +106,12 @@ IntelWebMVideoDecoder::IsSupportedVideoMimeType(const nsACString& aMimeType)
|
||||
mPlatform->SupportsMimeType(aMimeType);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<InitPromise>
|
||||
IntelWebMVideoDecoder::Init(unsigned int aWidth, unsigned int aHeight)
|
||||
{
|
||||
mPlatform = PlatformDecoderModule::Create();
|
||||
if (!mPlatform) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
mDecoderConfig = new VideoInfo();
|
||||
@@ -127,12 +127,12 @@ IntelWebMVideoDecoder::Init(unsigned int aWidth, unsigned int aHeight)
|
||||
mDecoderConfig->mMimeType = "video/webm; codecs=vp9";
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
const VideoInfo& video = *mDecoderConfig;
|
||||
if (!IsSupportedVideoMimeType(video.mMimeType)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
mMediaDataDecoder =
|
||||
mPlatform->CreateDecoder(video,
|
||||
@@ -141,11 +141,10 @@ IntelWebMVideoDecoder::Init(unsigned int aWidth, unsigned int aHeight)
|
||||
mReader->GetLayersBackendType(),
|
||||
mReader->GetDecoder()->GetImageContainer());
|
||||
if (!mMediaDataDecoder) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
nsresult rv = mMediaDataDecoder->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
|
||||
return mMediaDataDecoder->Init();
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -28,7 +28,8 @@ class IntelWebMVideoDecoder : public WebMVideoDecoder, public MediaDataDecoderCa
|
||||
{
|
||||
public:
|
||||
static WebMVideoDecoder* Create(WebMReader* aReader);
|
||||
virtual nsresult Init(unsigned int aWidth, unsigned int aHeight) override;
|
||||
virtual nsRefPtr<InitPromise> Init(unsigned int aWidth = 0,
|
||||
unsigned int aHeight = 0) override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual void Shutdown() override;
|
||||
|
||||
|
||||
@@ -54,11 +54,23 @@ SoftwareWebMVideoDecoder::Create(WebMReader* aReader)
|
||||
return new SoftwareWebMVideoDecoder(aReader);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRefPtr<InitPromise>
|
||||
SoftwareWebMVideoDecoder::Init(unsigned int aWidth, unsigned int aHeight)
|
||||
{
|
||||
nsresult rv = InitDecoder(aWidth, aHeight);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return InitPromise::CreateAndResolve(TrackType::kVideoTrack, __func__);
|
||||
}
|
||||
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SoftwareWebMVideoDecoder::InitDecoder(unsigned int aWidth, unsigned int aHeight)
|
||||
{
|
||||
int decode_threads = 2; //Default to 2 threads for small sizes or VP8
|
||||
|
||||
|
||||
vpx_codec_iface_t* dx = nullptr;
|
||||
switch(mReader->GetVideoCodec()) {
|
||||
case NESTEGG_CODEC_VP8:
|
||||
|
||||
@@ -17,7 +17,8 @@ class SoftwareWebMVideoDecoder : public WebMVideoDecoder
|
||||
public:
|
||||
static WebMVideoDecoder* Create(WebMReader* aReader);
|
||||
|
||||
virtual nsresult Init(unsigned int aWidth, unsigned int aHeight) override;
|
||||
virtual nsRefPtr<InitPromise> Init(unsigned int aWidth = 0,
|
||||
unsigned int aHeight = 0) override;
|
||||
|
||||
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold) override;
|
||||
@@ -28,6 +29,7 @@ public:
|
||||
~SoftwareWebMVideoDecoder();
|
||||
|
||||
private:
|
||||
nsresult InitDecoder(unsigned int aWidth, unsigned int aHeight);
|
||||
nsRefPtr<WebMReader> mReader;
|
||||
|
||||
// VPx decoder state
|
||||
|
||||
@@ -276,8 +276,22 @@ void WebMReader::Cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
nsRefPtr<MediaDecoderReader::MetadataPromise>
|
||||
WebMReader::AsyncReadMetadata()
|
||||
{
|
||||
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
|
||||
|
||||
if (NS_FAILED(RetrieveWebMMetadata(&metadata->mInfo)) ||
|
||||
!metadata->mInfo.HasValidMedia()) {
|
||||
return MetadataPromise::CreateAndReject(ReadMetadataFailureReason::METADATA_ERROR,
|
||||
__func__);
|
||||
}
|
||||
|
||||
return MetadataPromise::CreateAndResolve(metadata, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
WebMReader::RetrieveWebMMetadata(MediaInfo* aInfo)
|
||||
{
|
||||
// We can't use OnTaskQueue() here because of the wacky initialization task
|
||||
// queue that TrackBuffer uses. We should be able to fix this when we do
|
||||
@@ -330,20 +344,17 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
||||
#if defined(MOZ_PDM_VPX)
|
||||
if (sIsIntelDecoderEnabled) {
|
||||
mVideoDecoder = IntelWebMVideoDecoder::Create(this);
|
||||
if (mVideoDecoder &&
|
||||
NS_FAILED(mVideoDecoder->Init(params.display_width, params.display_height))) {
|
||||
mVideoDecoder = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// If there's no decoder yet (e.g. HW decoder not available), use the software decoder.
|
||||
if (!mVideoDecoder) {
|
||||
mVideoDecoder = SoftwareWebMVideoDecoder::Create(this);
|
||||
if (mVideoDecoder &&
|
||||
NS_FAILED(mVideoDecoder->Init(params.display_width, params.display_height))) {
|
||||
mVideoDecoder = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (mVideoDecoder) {
|
||||
mInitPromises.AppendElement(mVideoDecoder->Init(params.display_width,
|
||||
params.display_height));
|
||||
}
|
||||
|
||||
if (!mVideoDecoder) {
|
||||
@@ -426,7 +437,10 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
||||
Cleanup();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (NS_FAILED(mAudioDecoder->Init())) {
|
||||
|
||||
if (mAudioDecoder) {
|
||||
mInitPromises.AppendElement(mAudioDecoder->Init());
|
||||
} else {
|
||||
Cleanup();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -461,8 +475,6 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
||||
|
||||
*aInfo = mInfo;
|
||||
|
||||
*aTags = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "nsAutoRef.h"
|
||||
#include "nestegg/nestegg.h"
|
||||
|
||||
@@ -23,6 +24,10 @@ namespace mozilla {
|
||||
static const unsigned NS_PER_USEC = 1000;
|
||||
static const double NS_PER_S = 1e9;
|
||||
|
||||
typedef MediaDataDecoder::InitPromise InitPromise;
|
||||
typedef TrackInfo::TrackType TrackType;
|
||||
typedef MediaDataDecoder::DecoderFailureReason DecoderFailureReason;
|
||||
|
||||
class WebMBufferedState;
|
||||
class WebMPacketQueue;
|
||||
|
||||
@@ -32,7 +37,7 @@ class WebMReader;
|
||||
class WebMVideoDecoder
|
||||
{
|
||||
public:
|
||||
virtual nsresult Init(unsigned int aWidth = 0, unsigned int aHeight = 0) = 0;
|
||||
virtual nsRefPtr<InitPromise> Init(unsigned int aWidth = 0, unsigned int aHeight = 0) = 0;
|
||||
virtual nsresult Flush() { return NS_OK; }
|
||||
virtual void Shutdown() = 0;
|
||||
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
@@ -45,7 +50,7 @@ public:
|
||||
class WebMAudioDecoder
|
||||
{
|
||||
public:
|
||||
virtual nsresult Init() = 0;
|
||||
virtual nsRefPtr<InitPromise> Init() = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual nsresult ResetDecode() = 0;
|
||||
virtual nsresult DecodeHeader(const unsigned char* aData, size_t aLength) = 0;
|
||||
@@ -85,8 +90,8 @@ public:
|
||||
return mHasVideo;
|
||||
}
|
||||
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) override;
|
||||
virtual nsRefPtr<MetadataPromise> AsyncReadMetadata() override;
|
||||
|
||||
virtual nsRefPtr<SeekPromise>
|
||||
Seek(int64_t aTime, int64_t aEndTime) override;
|
||||
|
||||
@@ -120,7 +125,6 @@ public:
|
||||
uint64_t GetCodecDelay() { return mCodecDelay; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
|
||||
|
||||
// Decode a nestegg packet of audio data. Push the audio data on the
|
||||
@@ -143,6 +147,8 @@ protected:
|
||||
bool ShouldSkipVideoFrame(int64_t aTimeThreshold);
|
||||
|
||||
private:
|
||||
nsresult RetrieveWebMMetadata(MediaInfo* aInfo);
|
||||
|
||||
// Get the timestamp of keyframe greater than aTimeThreshold.
|
||||
int64_t GetNextKeyframeTime(int64_t aTimeThreshold);
|
||||
// Push the packets into aOutput which's timestamp is less than aEndTime.
|
||||
@@ -160,6 +166,8 @@ private:
|
||||
nsAutoPtr<WebMAudioDecoder> mAudioDecoder;
|
||||
nsAutoPtr<WebMVideoDecoder> mVideoDecoder;
|
||||
|
||||
nsTArray<nsRefPtr<InitPromise>> mInitPromises;
|
||||
|
||||
// Queue of video and audio packets that have been read but not decoded. These
|
||||
// must only be accessed from the decode thread.
|
||||
WebMPacketQueue mVideoPackets;
|
||||
|
||||
@@ -351,6 +351,8 @@ typedef enum {
|
||||
/* In the NPDrawingModelCoreAnimation drawing model, the browser asks the plug-in for a Core Animation layer. */
|
||||
, NPPVpluginCoreAnimationLayer = 1003
|
||||
#endif
|
||||
/* Notification that the plugin just started or stopped playing audio */
|
||||
, NPPVpluginIsPlayingAudio = 4000
|
||||
|
||||
} NPPVariable;
|
||||
|
||||
@@ -409,6 +411,9 @@ typedef enum {
|
||||
, NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */
|
||||
, NPNVsupportsUpdatedCocoaTextInputBool = 3002 /* TRUE if the browser supports the updated
|
||||
Cocoa text input specification. */
|
||||
#endif
|
||||
, NPNVmuteAudioBool = 4000 /* Request that the browser wants to mute or unmute the plugin */
|
||||
#if defined(XP_MACOSX)
|
||||
, NPNVsupportsCompositingCoreAnimationPluginsBool = 74656 /* TRUE if the browser supports
|
||||
CA model compositing */
|
||||
#endif
|
||||
|
||||
@@ -106,6 +106,8 @@ using mozilla::plugins::PluginModuleContentParent;
|
||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
|
||||
#endif
|
||||
|
||||
#include "nsIAudioChannelAgent.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::plugins::parent;
|
||||
|
||||
@@ -2411,6 +2413,46 @@ _setvalue(NPP npp, NPPVariable variable, void *result)
|
||||
return inst->SetUsesDOMForCursor(useDOMForCursor);
|
||||
}
|
||||
|
||||
case NPPVpluginIsPlayingAudio: {
|
||||
bool isMuted = !result;
|
||||
|
||||
nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*) npp->ndata;
|
||||
MOZ_ASSERT(inst);
|
||||
|
||||
if (isMuted && !inst->HasAudioChannelAgent()) {
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAudioChannelAgent> agent;
|
||||
nsresult rv = inst->GetOrCreateAudioChannelAgent(getter_AddRefs(agent));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(agent);
|
||||
|
||||
if (isMuted) {
|
||||
rv = agent->NotifyStoppedPlaying();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
} else {
|
||||
float volume = 0.0;
|
||||
bool muted = true;
|
||||
rv = agent->NotifyStartedPlaying(&volume, &muted);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
rv = inst->WindowVolumeChanged(volume, muted);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
|
||||
#ifndef MOZ_WIDGET_ANDROID
|
||||
// On android, their 'drawing model' uses the same constant!
|
||||
case NPPVpluginDrawingModel: {
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsILoadContext.h"
|
||||
#include "mozilla/dom/HTMLObjectElementBinding.h"
|
||||
#include "AudioChannelService.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@@ -168,7 +169,7 @@ using namespace mozilla::layers;
|
||||
|
||||
static NS_DEFINE_IID(kIOutputStreamIID, NS_IOUTPUTSTREAM_IID);
|
||||
|
||||
NS_IMPL_ISUPPORTS0(nsNPAPIPluginInstance)
|
||||
NS_IMPL_ISUPPORTS(nsNPAPIPluginInstance, nsIAudioChannelAgentCallback)
|
||||
|
||||
nsNPAPIPluginInstance::nsNPAPIPluginInstance()
|
||||
: mDrawingModel(kDefaultDrawingModel)
|
||||
@@ -252,6 +253,7 @@ nsNPAPIPluginInstance::Destroy()
|
||||
{
|
||||
Stop();
|
||||
mPlugin = nullptr;
|
||||
mAudioChannelAgent = nullptr;
|
||||
|
||||
#if MOZ_WIDGET_ANDROID
|
||||
if (mContentSurface)
|
||||
@@ -1787,3 +1789,71 @@ nsNPAPIPluginInstance::GetRunID(uint32_t* aRunID)
|
||||
|
||||
return library->GetRunID(aRunID);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsNPAPIPluginInstance::GetOrCreateAudioChannelAgent(nsIAudioChannelAgent** aAgent)
|
||||
{
|
||||
if (!mAudioChannelAgent) {
|
||||
nsresult rv;
|
||||
mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
|
||||
if (NS_WARN_IF(!mAudioChannelAgent)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetDOMWindow();
|
||||
if (NS_WARN_IF(!window)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = mAudioChannelAgent->Init(window->GetCurrentInnerWindow(),
|
||||
(int32_t)AudioChannelService::GetDefaultAudioChannel(),
|
||||
this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAudioChannelAgent> agent = mAudioChannelAgent;
|
||||
agent.forget(aAgent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNPAPIPluginInstance::WindowVolumeChanged(float aVolume, bool aMuted)
|
||||
{
|
||||
// We just support mute/unmute
|
||||
nsresult rv = SetMuted(aMuted);
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNPAPIPluginInstance::WindowAudioCaptureChanged()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsNPAPIPluginInstance::SetMuted(bool aIsMuted)
|
||||
{
|
||||
if (RUNNING != mRunning)
|
||||
return NS_OK;
|
||||
|
||||
PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance informing plugin of mute state change this=%p\n",this));
|
||||
|
||||
if (!mPlugin || !mPlugin->GetLibrary())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
|
||||
|
||||
if (!pluginFunctions->setvalue)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PluginDestructionGuard guard(this);
|
||||
|
||||
NPError error;
|
||||
NPBool value = static_cast<NPBool>(aIsMuted);
|
||||
NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setvalue)(&mNPP, NPNVmuteAudioBool, &value), this,
|
||||
NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
|
||||
return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "nsHashKeys.h"
|
||||
#include <prinrval.h>
|
||||
#include "js/TypeDecls.h"
|
||||
#include "nsIAudioChannelAgent.h"
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIRunnable.h"
|
||||
@@ -75,13 +76,14 @@ public:
|
||||
bool needUnschedule;
|
||||
};
|
||||
|
||||
class nsNPAPIPluginInstance : public nsISupports
|
||||
class nsNPAPIPluginInstance final : public nsIAudioChannelAgentCallback
|
||||
{
|
||||
private:
|
||||
typedef mozilla::PluginLibrary PluginLibrary;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
|
||||
|
||||
nsresult Initialize(nsNPAPIPlugin *aPlugin, nsPluginInstanceOwner* aOwner, const nsACString& aMIMEType);
|
||||
nsresult Start();
|
||||
@@ -118,6 +120,15 @@ public:
|
||||
nsPluginInstanceOwner* GetOwner();
|
||||
void SetOwner(nsPluginInstanceOwner *aOwner);
|
||||
|
||||
bool HasAudioChannelAgent() const
|
||||
{
|
||||
return !!mAudioChannelAgent;
|
||||
}
|
||||
|
||||
nsresult GetOrCreateAudioChannelAgent(nsIAudioChannelAgent** aAgent);
|
||||
|
||||
nsresult SetMuted(bool aIsMuted);
|
||||
|
||||
nsNPAPIPlugin* GetPlugin();
|
||||
|
||||
nsresult GetNPP(NPP * aNPP);
|
||||
@@ -405,6 +416,8 @@ private:
|
||||
uint32_t mCachedParamLength;
|
||||
char **mCachedParamNames;
|
||||
char **mCachedParamValues;
|
||||
|
||||
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
|
||||
};
|
||||
|
||||
// On Android, we need to guard against plugin code leaking entries in the local
|
||||
|
||||
@@ -87,6 +87,8 @@ child:
|
||||
intr NPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId()
|
||||
returns (nsCString plug_id, NPError result);
|
||||
|
||||
intr NPP_SetValue_NPNVmuteAudioBool(bool muted) returns (NPError result);
|
||||
|
||||
intr NPP_HandleEvent(NPRemoteEvent event)
|
||||
returns (int16_t handled);
|
||||
// special cases where we need to a shared memory buffer
|
||||
@@ -150,6 +152,8 @@ parent:
|
||||
returns (NPError result);
|
||||
intr NPN_SetValue_NPPVpluginEventModel(int eventModel)
|
||||
returns (NPError result);
|
||||
intr NPN_SetValue_NPPVpluginIsPlayingAudio(bool isAudioPlaying)
|
||||
returns (NPError result);
|
||||
|
||||
intr NPN_GetURL(nsCString url, nsCString target)
|
||||
returns (NPError result);
|
||||
|
||||
@@ -624,6 +624,14 @@ PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue)
|
||||
}
|
||||
#endif
|
||||
|
||||
case NPPVpluginIsPlayingAudio: {
|
||||
NPError rv = NPERR_GENERIC_ERROR;
|
||||
if (!CallNPN_SetValue_NPPVpluginIsPlayingAudio((NPBool)(intptr_t)aValue, &rv)) {
|
||||
return NPERR_GENERIC_ERROR;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_LOG(GetPluginLog(), LogLevel::Warning,
|
||||
("In PluginInstanceChild::NPN_SetValue: Unhandled NPPVariable %i (%s)",
|
||||
@@ -763,6 +771,20 @@ PluginInstanceChild::AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginInstanceChild::AnswerNPP_SetValue_NPNVmuteAudioBool(const bool& value,
|
||||
NPError* result)
|
||||
{
|
||||
if (!mPluginIface->setvalue) {
|
||||
*result = NPERR_GENERIC_ERROR;
|
||||
return true;
|
||||
}
|
||||
|
||||
NPBool v = value;
|
||||
*result = mPluginIface->setvalue(GetNPP(), NPNVmuteAudioBool, &v);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
|
||||
int16_t* handled)
|
||||
|
||||
@@ -80,6 +80,8 @@ protected:
|
||||
NPError* aResult) override;
|
||||
virtual bool
|
||||
AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value, NPError* result) override;
|
||||
virtual bool
|
||||
AnswerNPP_SetValue_NPNVmuteAudioBool(const bool& value, NPError* result) override;
|
||||
|
||||
virtual bool
|
||||
AnswerNPP_HandleEvent(const NPRemoteEvent& event, int16_t* handled) override;
|
||||
|
||||
@@ -440,6 +440,15 @@ PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginEventModel(
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginIsPlayingAudio(
|
||||
const bool& isAudioPlaying, NPError* result)
|
||||
{
|
||||
*result = mNPNIface->setvalue(mNPP, NPPVpluginIsPlayingAudio,
|
||||
(void*)(intptr_t)isAudioPlaying);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginInstanceParent::AnswerNPN_GetURL(const nsCString& url,
|
||||
const nsCString& target,
|
||||
@@ -1142,15 +1151,22 @@ PluginInstanceParent::NPP_GetValue(NPPVariable aVariable,
|
||||
NPError
|
||||
PluginInstanceParent::NPP_SetValue(NPNVariable variable, void* value)
|
||||
{
|
||||
NPError result;
|
||||
switch (variable) {
|
||||
case NPNVprivateModeBool:
|
||||
NPError result;
|
||||
if (!CallNPP_SetValue_NPNVprivateModeBool(*static_cast<NPBool*>(value),
|
||||
&result))
|
||||
return NPERR_GENERIC_ERROR;
|
||||
|
||||
return result;
|
||||
|
||||
case NPNVmuteAudioBool:
|
||||
if (!CallNPP_SetValue_NPNVmuteAudioBool(*static_cast<NPBool*>(value),
|
||||
&result))
|
||||
return NPERR_GENERIC_ERROR;
|
||||
|
||||
return result;
|
||||
|
||||
default:
|
||||
NS_ERROR("Unhandled NPNVariable in NPP_SetValue");
|
||||
MOZ_LOG(GetPluginLog(), LogLevel::Warning,
|
||||
|
||||
@@ -128,6 +128,9 @@ public:
|
||||
virtual bool
|
||||
AnswerNPN_SetValue_NPPVpluginEventModel(const int& eventModel,
|
||||
NPError* result) override;
|
||||
virtual bool
|
||||
AnswerNPN_SetValue_NPPVpluginIsPlayingAudio(const bool& isAudioPlaying,
|
||||
NPError* result) override;
|
||||
|
||||
virtual bool
|
||||
AnswerNPN_GetURL(const nsCString& url, const nsCString& target,
|
||||
|
||||
@@ -419,3 +419,14 @@ x86-only on some OSes:
|
||||
Returns the contents scale factor. On platforms without support for this query
|
||||
always returns 1.0 (a double value). Likewise on hardware without HiDPI mode
|
||||
support.
|
||||
|
||||
== Plugin audio channel support ==
|
||||
|
||||
* startAudioPlayback()
|
||||
Simulates the plugin starting to play back audio.
|
||||
|
||||
* stopAudioPlayback()
|
||||
Simulates the plugin stopping to play back audio.
|
||||
|
||||
* audioMuted()
|
||||
Returns the last value set by NPP_SetValue(NPNVmuteAudioBool).
|
||||
|
||||
@@ -168,6 +168,9 @@ static bool getNPNVdocumentOrigin(NPObject* npobj, const NPVariant* args, uint32
|
||||
static bool getMouseUpEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
static bool queryContentsScaleFactor(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
static bool echoString(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
static bool startAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
static bool stopAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
static bool getAudioMuted(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
|
||||
static const NPUTF8* sPluginMethodIdentifierNames[] = {
|
||||
"npnEvaluateTest",
|
||||
@@ -234,6 +237,9 @@ static const NPUTF8* sPluginMethodIdentifierNames[] = {
|
||||
"getMouseUpEventCount",
|
||||
"queryContentsScaleFactor",
|
||||
"echoString",
|
||||
"startAudioPlayback",
|
||||
"stopAudioPlayback",
|
||||
"audioMuted",
|
||||
};
|
||||
static NPIdentifier sPluginMethodIdentifiers[ARRAY_LENGTH(sPluginMethodIdentifierNames)];
|
||||
static const ScriptableFunction sPluginMethodFunctions[] = {
|
||||
@@ -301,6 +307,9 @@ static const ScriptableFunction sPluginMethodFunctions[] = {
|
||||
getMouseUpEventCount,
|
||||
queryContentsScaleFactor,
|
||||
echoString,
|
||||
startAudioPlayback,
|
||||
stopAudioPlayback,
|
||||
getAudioMuted,
|
||||
};
|
||||
|
||||
static_assert(ARRAY_LENGTH(sPluginMethodIdentifierNames) ==
|
||||
@@ -784,6 +793,8 @@ NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char*
|
||||
instanceData->npnNewStream = false;
|
||||
instanceData->invalidateDuringPaint = false;
|
||||
instanceData->slowPaint = false;
|
||||
instanceData->playingAudio = false;
|
||||
instanceData->audioMuted = false;
|
||||
instanceData->writeCount = 0;
|
||||
instanceData->writeReadyCount = 0;
|
||||
memset(&instanceData->window, 0, sizeof(instanceData->window));
|
||||
@@ -1440,6 +1451,11 @@ NPP_SetValue(NPP instance, NPNVariable variable, void* value)
|
||||
instanceData->lastReportedPrivateModeState = bool(*static_cast<NPBool*>(value));
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
if (variable == NPNVmuteAudioBool) {
|
||||
InstanceData* instanceData = (InstanceData*)(instance->pdata);
|
||||
instanceData->audioMuted = bool(*static_cast<NPBool*>(value));
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
return NPERR_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
@@ -3707,3 +3723,45 @@ bool echoString(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVar
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
toggleAudioPlayback(NPObject* npobj, uint32_t argCount, bool playingAudio, NPVariant* result)
|
||||
{
|
||||
if (argCount != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NPP npp = static_cast<TestNPObject*>(npobj)->npp;
|
||||
InstanceData* id = static_cast<InstanceData*>(npp->pdata);
|
||||
id->playingAudio = playingAudio;
|
||||
|
||||
NPN_SetValue(npp, NPPVpluginIsPlayingAudio, (void*)playingAudio);
|
||||
|
||||
VOID_TO_NPVARIANT(*result);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
startAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
|
||||
{
|
||||
return toggleAudioPlayback(npobj, argCount, true, result);
|
||||
}
|
||||
|
||||
static bool
|
||||
stopAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
|
||||
{
|
||||
return toggleAudioPlayback(npobj, argCount, false, result);
|
||||
}
|
||||
|
||||
static bool
|
||||
getAudioMuted(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
|
||||
{
|
||||
if (argCount != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NPP npp = static_cast<TestNPObject*>(npobj)->npp;
|
||||
InstanceData* id = static_cast<InstanceData*>(npp->pdata);
|
||||
BOOLEAN_TO_NPVARIANT(id->audioMuted, *result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,8 @@ typedef struct InstanceData {
|
||||
bool asyncCallbackResult;
|
||||
bool invalidateDuringPaint;
|
||||
bool slowPaint;
|
||||
bool playingAudio;
|
||||
bool audioMuted;
|
||||
int32_t winX;
|
||||
int32_t winY;
|
||||
int32_t lastMouseX;
|
||||
|
||||
@@ -207,7 +207,7 @@ const SNAPSHOT_SCHEMA = {
|
||||
type: "boolean",
|
||||
},
|
||||
supportsHardwareH264: {
|
||||
type: "boolean",
|
||||
type: "string",
|
||||
},
|
||||
numAcceleratedWindowsMessage: {
|
||||
type: "array",
|
||||
|
||||
@@ -141,4 +141,11 @@ AbstractThread::DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable)
|
||||
GetCurrent()->TailDispatcher().AddDirectTask(Move(aRunnable));
|
||||
}
|
||||
|
||||
already_AddRefed<AbstractThread>
|
||||
CreateXPCOMAbstractThreadWrapper(nsIThread* aThread, bool aRequireTailDispatch)
|
||||
{
|
||||
nsRefPtr<XPCOMThreadWrapper> wrapper = new XPCOMThreadWrapper(aThread, aRequireTailDispatch);
|
||||
return wrapper.forget();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -92,6 +92,9 @@ protected:
|
||||
const bool mSupportsTailDispatch;
|
||||
};
|
||||
|
||||
already_AddRefed<AbstractThread> CreateXPCOMAbstractThreadWrapper(nsIThread* aThread,
|
||||
bool aRequireTailDispatch);
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user