mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 22:28:36 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1206977: P1. Remove unused PDM function members. r=cpearce (3ff104135b) - Bug 1193670: P1. Remove use of SharedDecoderManager. r=cpearce (adb5606e3e) - Bug 1193670: P2. Remove use of SharedDecoderManager from MediaDecoderReader. r=cpearce (61b9170c51) - Bug 1194612: P1. Dont reject init promise when initialising H264Converter. r=alfredo (d7d9e81361) - Bug 1194612: P2. Don't drop first sample with SPS/PPS NALs. r=alfredo (ea42652155) - Bug 1194612: P3. Remove redundant member. r=alfredo (2d30f940c9) - Bug 1195625 - Use correct TaskQueue in SharedDecoderManager and H264Converter promise. r=jya (87e52cd322) - Bug 1194518 - Part 1: Passthrough decoder wrapper, useful to spy on MediaFormatReader-decoder calls. r=jya (8e6f7df905) - Bug 1194518 - Part 2: Using passthrough wrapper if pref 'media.fuzz.vdeo-decode-passthrough' is true. r=jya (2a50be71f0) - Bug 1194518 - Part 3: Delaying decoder wrapper, ensures a decoder appears consistently slow. r=jya (9d461352e5) - Bug 1194518 - Part 4: Using delaying wrapper according to pref 'media.fuzz.video-decode-minimum-frame-interval-ms'. r=jya (9ec9684024) - Bug 1194518 - Part 5: Using std::deque instead of nsTArray to store delayed frames. r=jya (6f3f26578c) - Bug 1197145 - Added BaseTimeDuration::IsZero(), BaseTimeDuration::operator bool(), TimeStamp::operator bool(). r=nfroyd (1ba008c9fa) - Bug 1202556 - Detect underflow in TimeStamp addition/subtraction operators; r=froydnj (864a288877) - Bug 1176731 - Don't mark static inline functions as MFBT_API in TimeStamp.h. r=Waldo (6c94439c61) - bug 1170586 - Make TimeStamp::FromSystemTime available on iOS. r=froydnj (f9ae7bd8b8) - Bug 1193670: P3. Remove no longer needed SharedDecoderManager class. r=cpearce (c56ef98e6d) - Bug 1206977: P2. Wrap PDM creation in a new PDMFactory class. r=cpearce (6425ab58ca) - Bug 1206977: P3. Allow PDM fallback. r=cpearce (6cce420063) - Bug 1206977: P4. Add AgnosticDecoderModule object. r=cpearce (1d4581f74c) - Bug 1206977: P5. Update PlatformDecoderModule documentation. r=cpearce (af14651d29) - Bug 1206977: P6. Make PlatformDecoderModule::SupportsMimeType pure virtual. r=cpearce (f328eb0a48) - Bug 1146086: Properly marking overridden member with override keyword. a=bustage (c809b2311c) - Bug 1204776: P1. Have the PlatformDecoderModules use their own log. r=cpearce (b8565d268b) - Bug 1204776: P2. Make Apple PDM use PlatformDecoderModule log. r=cpearce (74b9985af8) - Bug 1204776: P3. Have FFmpeg PDM use PlatformDecoderModule log. r=cpearce (caa498a139) - Bug 1204776: P4. Have VPX/Opus/Vorbis decoder use PlatformDecoderModule log. r=cpearce (f2fce6c79a) - fix (215c918930) - Bug 1206977: [webm] P7. Remove IntelWebMVideoDecoder. r=kinetik (c455347fe1) - Bug 1206977: P8. Have PDMFactory directly manage the EMEDecoderModule. r=cpearce (d830cbe570) - Bug 1212164: Prevent use of demuxer before initialization completes. r=cpearce (99c268d31d) - Bug 1152652: Part1. Use mStandardMozillaStyle for crypto classes. r=edwin (af58e4e822) - Bug 1206977: P9. Ensure PDMs are only ever created through the PDMFactory. r=cpearce (4d7375ea3d) - Bug 1206977: P10. Remove redundant code. r=cpearce (8abd18ffe1) - Bug 1206977: P11. Don't rely on SupportsMimeType to determine if a track can be played. r=cpearce (b569a8f5e1) - Bug 1212246. Part 1 - remove the aBorrowedTaskQueue parameter from the MediaDecoderReader constructor. r=jya. (0f481aa22b) - Bug 1212246. Part 2 - remove mTaskQueueIsBorrowed and unnecessary checks for mTaskQueue is never null. r=jya. (27ec74d634) - Bug 1212723. Part 1 - don't share mBufferedState per bug 1212723 comment 6. r=jya. (59e1b89bee) - Bug 1212723. Part 2 - remove unused argument aCloneDonor from MediaDecoderReader::Init(). r=jya. (45bc778f41)
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
#include "ADTSDemuxer.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "MediaFormatReader.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "PDMFactory.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -32,9 +32,8 @@ ADTSDecoder::CreateStateMachine()
|
||||
/* static */ bool
|
||||
ADTSDecoder::IsEnabled()
|
||||
{
|
||||
PlatformDecoderModule::Init();
|
||||
|
||||
nsRefPtr<PlatformDecoderModule> platform = PlatformDecoderModule::Create();
|
||||
PDMFactory::Init();
|
||||
nsRefPtr<PDMFactory> platform = new PDMFactory();
|
||||
return (platform && platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm")));
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "MediaFormatReader.h"
|
||||
#include "MP3Demuxer.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "PDMFactory.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -32,18 +32,11 @@ MP3Decoder::CreateStateMachine() {
|
||||
static already_AddRefed<MediaDataDecoder>
|
||||
CreateTestMP3Decoder(AudioInfo& aConfig)
|
||||
{
|
||||
PlatformDecoderModule::Init();
|
||||
|
||||
nsRefPtr<PlatformDecoderModule> platform = PlatformDecoderModule::Create();
|
||||
if (!platform || !platform->SupportsMimeType(aConfig.mMimeType)) {
|
||||
return nullptr;
|
||||
}
|
||||
PDMFactory::Init();
|
||||
|
||||
nsRefPtr<PDMFactory> platform = new PDMFactory();
|
||||
nsRefPtr<MediaDataDecoder> decoder(
|
||||
platform->CreateDecoder(aConfig, nullptr, nullptr));
|
||||
if (!decoder) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
@@ -317,20 +317,20 @@ protected:
|
||||
class CryptoTrack
|
||||
{
|
||||
public:
|
||||
CryptoTrack() : valid(false) {}
|
||||
bool valid;
|
||||
int32_t mode;
|
||||
int32_t iv_size;
|
||||
nsTArray<uint8_t> key;
|
||||
CryptoTrack() : mValid(false) {}
|
||||
bool mValid;
|
||||
int32_t mMode;
|
||||
int32_t mIVSize;
|
||||
nsTArray<uint8_t> mKeyId;
|
||||
};
|
||||
|
||||
class CryptoSample : public CryptoTrack
|
||||
{
|
||||
public:
|
||||
nsTArray<uint16_t> plain_sizes;
|
||||
nsTArray<uint32_t> encrypted_sizes;
|
||||
nsTArray<uint8_t> iv;
|
||||
nsTArray<nsCString> session_ids;
|
||||
nsTArray<uint16_t> mPlainSizes;
|
||||
nsTArray<uint32_t> mEncryptedSizes;
|
||||
nsTArray<uint8_t> mIV;
|
||||
nsTArray<nsCString> mSessionIds;
|
||||
};
|
||||
|
||||
// MediaRawData is a MediaData container used to store demuxed, still compressed
|
||||
|
||||
@@ -62,13 +62,11 @@ public:
|
||||
size_t mSize;
|
||||
};
|
||||
|
||||
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder,
|
||||
TaskQueue* aBorrowedTaskQueue)
|
||||
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
||||
: mAudioCompactor(mAudioQueue)
|
||||
, mDecoder(aDecoder)
|
||||
, mTaskQueue(aBorrowedTaskQueue ? aBorrowedTaskQueue
|
||||
: new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
|
||||
/* aSupportsTailDispatch = */ true))
|
||||
, mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
|
||||
/* aSupportsTailDispatch = */ true))
|
||||
, mWatchManager(this, mTaskQueue)
|
||||
, mTimer(new MediaTimer())
|
||||
, mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
|
||||
@@ -78,7 +76,6 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder,
|
||||
, mIgnoreAudioOutputFormat(false)
|
||||
, mHitAudioDecodeError(false)
|
||||
, mShutdown(false)
|
||||
, mTaskQueueIsBorrowed(!!aBorrowedTaskQueue)
|
||||
, mAudioDiscontinuity(false)
|
||||
, mVideoDiscontinuity(false)
|
||||
{
|
||||
@@ -370,9 +367,11 @@ MediaDecoderReader::RequestAudioData()
|
||||
AudioQueue().Finish();
|
||||
break;
|
||||
}
|
||||
// AudioQueue size is still zero, post a task to try again.
|
||||
// (|mVideoSinkBufferCount| > 0)
|
||||
if (AudioQueue().GetSize() == 0 && mTaskQueue) {
|
||||
// AudioQueue size is still zero, post a task to try again. Don't spin
|
||||
// waiting in this while loop since it somehow prevents audio EOS from
|
||||
// coming in gstreamer 1.x when there is still video buffer waiting to be
|
||||
// consumed. (|mVideoSinkBufferCount| > 0)
|
||||
if (AudioQueue().GetSize() == 0) {
|
||||
RefPtr<nsIRunnable> task(new ReRequestAudioTask(this));
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
return p;
|
||||
@@ -423,22 +422,10 @@ MediaDecoderReader::Shutdown()
|
||||
|
||||
nsRefPtr<ShutdownPromise> p;
|
||||
|
||||
// Spin down the task queue if necessary. We wait until BreakCycles to null
|
||||
// out mTaskQueue, since otherwise any remaining tasks could crash when they
|
||||
// invoke OnTaskQueue().
|
||||
if (mTaskQueue && !mTaskQueueIsBorrowed) {
|
||||
// If we own our task queue, shutdown ends when the task queue is done.
|
||||
p = mTaskQueue->BeginShutdown();
|
||||
} else {
|
||||
// If we don't own our task queue, we resolve immediately (though
|
||||
// asynchronously).
|
||||
p = ShutdownPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
|
||||
mTimer = nullptr;
|
||||
mDecoder = nullptr;
|
||||
|
||||
return p;
|
||||
return mTaskQueue->BeginShutdown();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
namespace mozilla {
|
||||
|
||||
class MediaDecoderReader;
|
||||
class SharedDecoderManager;
|
||||
|
||||
struct WaitForDataRejectValue
|
||||
{
|
||||
@@ -82,7 +81,7 @@ public:
|
||||
|
||||
// The caller must ensure that Shutdown() is called before aDecoder is
|
||||
// destroyed.
|
||||
explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder, TaskQueue* aBorrowedTaskQueue = nullptr);
|
||||
explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder);
|
||||
|
||||
// Does any spinup that needs to happen on this task queue. This runs on a
|
||||
// different thread than Init, and there should not be ordering dependencies
|
||||
@@ -92,7 +91,7 @@ public:
|
||||
|
||||
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
|
||||
// on failure.
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor) = 0;
|
||||
virtual nsresult Init() { return NS_OK; }
|
||||
|
||||
// True if this reader is waiting for a Content Decryption Module to become
|
||||
// available.
|
||||
@@ -100,7 +99,6 @@ public:
|
||||
// Release media resources they should be released in dormant state
|
||||
// The reader can be made usable again by calling ReadMetadata().
|
||||
virtual void ReleaseMediaResources() {};
|
||||
virtual void SetSharedDecoderManager(SharedDecoderManager* aManager) {}
|
||||
// Breaks reference-counted cycles. Called during shutdown.
|
||||
// WARNING: If you override this, you must call the base implementation
|
||||
// in your override.
|
||||
@@ -428,8 +426,6 @@ private:
|
||||
MozPromiseHolder<AudioDataPromise> mBaseAudioPromise;
|
||||
MozPromiseHolder<VideoDataPromise> mBaseVideoPromise;
|
||||
|
||||
bool mTaskQueueIsBorrowed;
|
||||
|
||||
// Flags whether a the next audio/video sample comes after a "gap" or
|
||||
// "discontinuity" in the stream. For example after a seek.
|
||||
bool mAudioDiscontinuity;
|
||||
|
||||
@@ -1019,13 +1019,7 @@ bool MediaDecoderStateMachine::IsPlaying() const
|
||||
nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MediaDecoderReader* cloneReader = nullptr;
|
||||
if (aCloneDonor) {
|
||||
cloneReader = aCloneDonor->mReader;
|
||||
}
|
||||
|
||||
nsresult rv = mReader->Init(cloneReader);
|
||||
nsresult rv = mReader->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
ScheduleStateMachineCrossThread();
|
||||
return NS_OK;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "MediaInfo.h"
|
||||
#include "MediaFormatReader.h"
|
||||
#include "MediaResource.h"
|
||||
#include "SharedDecoderManager.h"
|
||||
#include "mozilla/SharedThreadPool.h"
|
||||
#include "VideoUtils.h"
|
||||
|
||||
@@ -64,12 +63,12 @@ TrackTypeToStr(TrackInfo::TrackType aTrack)
|
||||
#endif
|
||||
|
||||
MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
|
||||
MediaDataDemuxer* aDemuxer,
|
||||
TaskQueue* aBorrowedTaskQueue)
|
||||
: MediaDecoderReader(aDecoder, aBorrowedTaskQueue)
|
||||
, mDemuxer(aDemuxer)
|
||||
MediaDataDemuxer* aDemuxer)
|
||||
: MediaDecoderReader(aDecoder)
|
||||
, mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2))
|
||||
, mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2))
|
||||
, mDemuxer(aDemuxer)
|
||||
, mDemuxerInitDone(false)
|
||||
, mLastReportedNumDecodedFrames(0)
|
||||
, mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
|
||||
, mInitDone(false)
|
||||
@@ -170,10 +169,10 @@ MediaFormatReader::InitLayersBackendType()
|
||||
static bool sIsEMEEnabled = false;
|
||||
|
||||
nsresult
|
||||
MediaFormatReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
MediaFormatReader::Init()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
PlatformDecoderModule::Init();
|
||||
PDMFactory::Init();
|
||||
|
||||
InitLayersBackendType();
|
||||
|
||||
@@ -196,20 +195,6 @@ bool MediaFormatReader::IsWaitingOnCDMResource() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaFormatReader::IsSupportedAudioMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return mPlatform && (mPlatform->SupportsMimeType(aMimeType) ||
|
||||
PlatformDecoderModule::AgnosticMimeType(aMimeType));
|
||||
}
|
||||
|
||||
bool
|
||||
MediaFormatReader::IsSupportedVideoMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return mPlatform && (mPlatform->SupportsMimeType(aMimeType) ||
|
||||
PlatformDecoderModule::AgnosticMimeType(aMimeType));
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::MetadataPromise>
|
||||
MediaFormatReader::AsyncReadMetadata()
|
||||
{
|
||||
@@ -241,6 +226,8 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
mDemuxerInitRequest.Complete();
|
||||
|
||||
mDemuxerInitDone = true;
|
||||
|
||||
// To decode, we need valid video and a place to put it.
|
||||
bool videoActive = !!mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack) &&
|
||||
mDecoder->GetImageContainer();
|
||||
@@ -321,21 +308,15 @@ MediaFormatReader::EnsureDecodersCreated()
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
if (!mPlatform) {
|
||||
mPlatform = new PDMFactory();
|
||||
NS_ENSURE_TRUE(mPlatform, false);
|
||||
if (IsEncrypted()) {
|
||||
// EME not supported.
|
||||
return false;
|
||||
} else {
|
||||
mPlatform = PlatformDecoderModule::Create();
|
||||
NS_ENSURE_TRUE(mPlatform, false);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mPlatform);
|
||||
|
||||
if (HasAudio() && !mAudio.mDecoder) {
|
||||
NS_ENSURE_TRUE(IsSupportedAudioMimeType(mInfo.mAudio.mMimeType),
|
||||
false);
|
||||
|
||||
mAudio.mDecoderInitialized = false;
|
||||
mAudio.mDecoder =
|
||||
mPlatform->CreateDecoder(mAudio.mInfo ?
|
||||
@@ -347,37 +328,18 @@ MediaFormatReader::EnsureDecodersCreated()
|
||||
}
|
||||
|
||||
if (HasVideo() && !mVideo.mDecoder) {
|
||||
NS_ENSURE_TRUE(IsSupportedVideoMimeType(mInfo.mVideo.mMimeType),
|
||||
false);
|
||||
|
||||
mVideo.mDecoderInitialized = false;
|
||||
// If we've disabled hardware acceleration for this reader, then we can't use
|
||||
// the shared decoder.
|
||||
if (mSharedDecoderManager &&
|
||||
mPlatform->SupportsSharedDecoders(mInfo.mVideo) &&
|
||||
!mHardwareAccelerationDisabled) {
|
||||
mVideo.mDecoder =
|
||||
mSharedDecoderManager->CreateVideoDecoder(mPlatform,
|
||||
mVideo.mInfo ?
|
||||
*mVideo.mInfo->GetAsVideoInfo() :
|
||||
mInfo.mVideo,
|
||||
mLayersBackendType,
|
||||
mDecoder->GetImageContainer(),
|
||||
mVideo.mTaskQueue,
|
||||
mVideo.mCallback);
|
||||
} else {
|
||||
// Decoders use the layers backend to decide if they can use hardware decoding,
|
||||
// so specify LAYERS_NONE if we want to forcibly disable it.
|
||||
mVideo.mDecoder =
|
||||
mPlatform->CreateDecoder(mVideo.mInfo ?
|
||||
*mVideo.mInfo->GetAsVideoInfo() :
|
||||
mInfo.mVideo,
|
||||
mVideo.mTaskQueue,
|
||||
mVideo.mCallback,
|
||||
mHardwareAccelerationDisabled ? LayersBackend::LAYERS_NONE :
|
||||
mLayersBackendType,
|
||||
mDecoder->GetImageContainer());
|
||||
}
|
||||
// Decoders use the layers backend to decide if they can use hardware decoding,
|
||||
// so specify LAYERS_NONE if we want to forcibly disable it.
|
||||
mVideo.mDecoder =
|
||||
mPlatform->CreateDecoder(mVideo.mInfo ?
|
||||
*mVideo.mInfo->GetAsVideoInfo() :
|
||||
mInfo.mVideo,
|
||||
mVideo.mTaskQueue,
|
||||
mVideo.mCallback,
|
||||
mHardwareAccelerationDisabled ? LayersBackend::LAYERS_NONE :
|
||||
mLayersBackendType,
|
||||
mDecoder->GetImageContainer());
|
||||
NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, false);
|
||||
}
|
||||
|
||||
@@ -1465,22 +1427,6 @@ void MediaFormatReader::ReleaseMediaResources()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::SetIdle()
|
||||
{
|
||||
if (mSharedDecoderManager && mVideo.mDecoder) {
|
||||
mSharedDecoderManager->SetIdle(mVideo.mDecoder);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::SetSharedDecoderManager(SharedDecoderManager* aManager)
|
||||
{
|
||||
#if !defined(MOZ_WIDGET_ANDROID)
|
||||
mSharedDecoderManager = aManager;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
MediaFormatReader::VideoIsHardwareAccelerated() const
|
||||
{
|
||||
@@ -1493,7 +1439,8 @@ MediaFormatReader::NotifyDemuxer(uint32_t aLength, int64_t aOffset)
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
LOGV("aLength=%u, aOffset=%lld", aLength, aOffset);
|
||||
if (mShutdown || !mDemuxer) {
|
||||
if (mShutdown || !mDemuxer ||
|
||||
(!mDemuxerInitDone && !mDemuxerInitRequest.Exists())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#include "MediaDataDemuxer.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "PDMFactory.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -23,13 +23,11 @@ class MediaFormatReader final : public MediaDecoderReader
|
||||
typedef media::Interval<int64_t> ByteInterval;
|
||||
|
||||
public:
|
||||
explicit MediaFormatReader(AbstractMediaDecoder* aDecoder,
|
||||
MediaDataDemuxer* aDemuxer,
|
||||
TaskQueue* aBorrowedTaskQueue = nullptr);
|
||||
MediaFormatReader(AbstractMediaDecoder* aDecoder, MediaDataDemuxer* aDemuxer);
|
||||
|
||||
virtual ~MediaFormatReader();
|
||||
|
||||
nsresult Init(MediaDecoderReader* aCloneDonor) override;
|
||||
nsresult Init() override;
|
||||
|
||||
size_t SizeOfVideoQueueInFrames() override;
|
||||
size_t SizeOfAudioQueueInFrames() override;
|
||||
@@ -71,10 +69,7 @@ public:
|
||||
bool ForceZeroStartTime() const override;
|
||||
|
||||
// For Media Resource Management
|
||||
void SetIdle() override;
|
||||
void ReleaseMediaResources() override;
|
||||
void SetSharedDecoderManager(SharedDecoderManager* aManager)
|
||||
override;
|
||||
|
||||
nsresult ResetDecode() override;
|
||||
|
||||
@@ -143,15 +138,12 @@ private:
|
||||
void Error(TrackType aTrack);
|
||||
void Flush(TrackType aTrack);
|
||||
void DrainComplete(TrackType aTrack);
|
||||
bool IsSupportedAudioMimeType(const nsACString& aMimeType);
|
||||
bool IsSupportedVideoMimeType(const nsACString& aMimeType);
|
||||
|
||||
bool ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold);
|
||||
|
||||
size_t SizeOfQueue(TrackType aTrack);
|
||||
|
||||
nsRefPtr<MediaDataDemuxer> mDemuxer;
|
||||
nsRefPtr<PlatformDecoderModule> mPlatform;
|
||||
nsRefPtr<PDMFactory> mPlatform;
|
||||
|
||||
class DecoderCallback : public MediaDataDecoderCallback {
|
||||
public:
|
||||
@@ -178,6 +170,7 @@ private:
|
||||
bool OnReaderTaskQueue() override {
|
||||
return mReader->OnTaskQueue();
|
||||
}
|
||||
|
||||
private:
|
||||
MediaFormatReader* mReader;
|
||||
TrackType mType;
|
||||
@@ -342,6 +335,8 @@ private:
|
||||
void OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason);
|
||||
|
||||
// Demuxer objects.
|
||||
nsRefPtr<MediaDataDemuxer> mDemuxer;
|
||||
bool mDemuxerInitDone;
|
||||
void OnDemuxerInitDone(nsresult);
|
||||
void OnDemuxerInitFailed(DemuxerFailureReason aFailure);
|
||||
MozPromiseRequestHolder<MediaDataDemuxer::InitPromise> mDemuxerInitRequest;
|
||||
@@ -420,8 +415,6 @@ private:
|
||||
// Pending decoders initialization.
|
||||
MozPromiseRequestHolder<MediaDataDecoder::InitPromise::AllPromiseType> mDecodersInitRequest;
|
||||
|
||||
nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
|
||||
|
||||
#if defined(READER_DORMANT_HEURISTIC)
|
||||
const bool mDormantEnabled;
|
||||
#endif
|
||||
|
||||
@@ -36,11 +36,6 @@ AndroidMediaReader::AndroidMediaReader(AbstractMediaDecoder *aDecoder,
|
||||
{
|
||||
}
|
||||
|
||||
nsresult AndroidMediaReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult AndroidMediaReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "ImageContainer.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/layers/SharedRGBImage.h"
|
||||
|
||||
|
||||
#include "MPAPI.h"
|
||||
|
||||
class nsACString;
|
||||
@@ -42,7 +42,6 @@ public:
|
||||
AndroidMediaReader(AbstractMediaDecoder* aDecoder,
|
||||
const nsACString& aContentType);
|
||||
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor);
|
||||
virtual nsresult ResetDecode();
|
||||
|
||||
virtual bool DecodeAudioData();
|
||||
|
||||
@@ -103,7 +103,7 @@ AppleMP3Reader::Read(uint32_t *aNumBytes, char *aData)
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleMP3Reader::Init(MediaDecoderReader* aCloneDonor)
|
||||
AppleMP3Reader::Init()
|
||||
{
|
||||
AudioFileTypeID fileType = kAudioFileMP3Type;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
explicit AppleMP3Reader(AbstractMediaDecoder *aDecoder);
|
||||
virtual ~AppleMP3Reader() override;
|
||||
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor) override;
|
||||
virtual nsresult Init() override;
|
||||
|
||||
nsresult PushDataToDemuxer();
|
||||
|
||||
|
||||
@@ -56,13 +56,6 @@ DirectShowReader::~DirectShowReader()
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult
|
||||
DirectShowReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Try to parse the MP3 stream to make sure this is indeed an MP3, get the
|
||||
// estimated duration of the stream, and find the offset of the actual MP3
|
||||
// frames in the stream, as DirectShow doesn't like large ID3 sections.
|
||||
|
||||
@@ -43,8 +43,6 @@ public:
|
||||
|
||||
virtual ~DirectShowReader();
|
||||
|
||||
nsresult Init(MediaDecoderReader* aCloneDonor) override;
|
||||
|
||||
bool DecodeAudioData() override;
|
||||
bool DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold) override;
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
#ifdef MOZ_APPLEMEDIA
|
||||
#include "AppleDecoderModule.h"
|
||||
#endif
|
||||
#ifdef MOZ_FFMPEG
|
||||
#include "FFmpegRuntimeLinker.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
|
||||
#include "PDMFactory.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) || defined(MOZ_APPLEMEDIA) || defined(MOZ_FFMPEG)
|
||||
@@ -151,11 +151,8 @@ MP4Decoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs,
|
||||
}
|
||||
|
||||
// Verify that we have a PDM that supports the whitelisted types.
|
||||
PlatformDecoderModule::Init();
|
||||
nsRefPtr<PlatformDecoderModule> platform = PlatformDecoderModule::Create();
|
||||
if (!platform) {
|
||||
return false;
|
||||
}
|
||||
PDMFactory::Init();
|
||||
nsRefPtr<PDMFactory> platform = new PDMFactory();
|
||||
for (const nsCString& codecMime : codecMimes) {
|
||||
if (!platform->SupportsMimeType(codecMime)) {
|
||||
return false;
|
||||
@@ -180,78 +177,11 @@ MP4Decoder::CanHandleMediaType(const nsAString& aContentType)
|
||||
return CanHandleMediaType(NS_ConvertUTF16toUTF8(mimeType), codecs);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsFFmpegAvailable()
|
||||
{
|
||||
#ifndef MOZ_FFMPEG
|
||||
return false;
|
||||
#else
|
||||
if (!Preferences::GetBool("media.ffmpeg.enabled", false)) {
|
||||
return false;
|
||||
}
|
||||
PlatformDecoderModule::Init();
|
||||
nsRefPtr<PlatformDecoderModule> m = FFmpegRuntimeLinker::CreateDecoderModule();
|
||||
return !!m;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
IsAppleAvailable()
|
||||
{
|
||||
#ifndef MOZ_APPLEMEDIA
|
||||
// Not the right platform.
|
||||
return false;
|
||||
#else
|
||||
return Preferences::GetBool("media.apple.mp4.enabled", false);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
IsAndroidAvailable()
|
||||
{
|
||||
#ifndef MOZ_WIDGET_ANDROID
|
||||
return false;
|
||||
#else
|
||||
// We need android.media.MediaCodec which exists in API level 16 and higher.
|
||||
return AndroidBridge::Bridge() && (AndroidBridge::Bridge()->GetAPIVersion() >= 16);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
IsGonkMP4DecoderAvailable()
|
||||
{
|
||||
return Preferences::GetBool("media.gonk.enabled", false);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsGMPDecoderAvailable()
|
||||
{
|
||||
return Preferences::GetBool("media.gmp.decoder.enabled", false);
|
||||
}
|
||||
|
||||
static bool
|
||||
HavePlatformMPEGDecoders()
|
||||
{
|
||||
return Preferences::GetBool("media.use-blank-decoder") ||
|
||||
#ifdef XP_WIN
|
||||
// We have H.264/AAC platform decoders on Windows Vista and up.
|
||||
IsFFmpegAvailable() ||
|
||||
IsVistaOrLater() ||
|
||||
#endif
|
||||
IsAndroidAvailable() ||
|
||||
IsAppleAvailable() ||
|
||||
IsGonkMP4DecoderAvailable() ||
|
||||
IsGMPDecoderAvailable() ||
|
||||
// TODO: Other platforms...
|
||||
false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
MP4Decoder::IsEnabled()
|
||||
{
|
||||
return Preferences::GetBool("media.fragmented-mp4.enabled") &&
|
||||
HavePlatformMPEGDecoders();
|
||||
return Preferences::GetBool("media.mp4.enabled");
|
||||
}
|
||||
|
||||
static const uint8_t sTestH264ExtraData[] = {
|
||||
@@ -275,18 +205,11 @@ CreateTestH264Decoder(layers::LayersBackend aBackend,
|
||||
aConfig.mExtraData->AppendElements(sTestH264ExtraData,
|
||||
MOZ_ARRAY_LENGTH(sTestH264ExtraData));
|
||||
|
||||
PlatformDecoderModule::Init();
|
||||
|
||||
nsRefPtr<PlatformDecoderModule> platform = PlatformDecoderModule::Create();
|
||||
if (!platform) {
|
||||
return nullptr;
|
||||
}
|
||||
PDMFactory::Init();
|
||||
|
||||
nsRefPtr<PDMFactory> platform = new PDMFactory();
|
||||
nsRefPtr<MediaDataDecoder> decoder(
|
||||
platform->CreateDecoder(aConfig, nullptr, nullptr, aBackend, nullptr));
|
||||
if (!decoder) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return decoder.forget();
|
||||
}
|
||||
@@ -304,79 +227,4 @@ MP4Decoder::IsVideoAccelerated(layers::LayersBackend aBackend, nsACString& aFail
|
||||
return result;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
MP4Decoder::CanCreateH264Decoder()
|
||||
{
|
||||
static bool haveCachedResult = false;
|
||||
static bool result = false;
|
||||
if (haveCachedResult) {
|
||||
return result;
|
||||
}
|
||||
VideoInfo config;
|
||||
nsRefPtr<MediaDataDecoder> decoder(
|
||||
CreateTestH264Decoder(layers::LayersBackend::LAYERS_BASIC, config));
|
||||
if (decoder) {
|
||||
decoder->Shutdown();
|
||||
result = true;
|
||||
}
|
||||
haveCachedResult = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
static already_AddRefed<MediaDataDecoder>
|
||||
CreateTestAACDecoder(AudioInfo& aConfig)
|
||||
{
|
||||
PlatformDecoderModule::Init();
|
||||
|
||||
nsRefPtr<PlatformDecoderModule> platform = PlatformDecoderModule::Create();
|
||||
if (!platform) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoder> decoder(
|
||||
platform->CreateDecoder(aConfig, nullptr, nullptr));
|
||||
if (!decoder) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
// bipbop.mp4's extradata/config...
|
||||
static const uint8_t sTestAACExtraData[] = {
|
||||
0x03, 0x80, 0x80, 0x80, 0x22, 0x00, 0x02, 0x00, 0x04, 0x80,
|
||||
0x80, 0x80, 0x14, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x51, 0x00, 0x00, 0x11, 0x51, 0x05, 0x80, 0x80, 0x80,
|
||||
0x02, 0x13, 0x90, 0x06, 0x80, 0x80, 0x80, 0x01, 0x02
|
||||
};
|
||||
|
||||
static const uint8_t sTestAACConfig[] = { 0x13, 0x90 };
|
||||
|
||||
/* static */ bool
|
||||
MP4Decoder::CanCreateAACDecoder()
|
||||
{
|
||||
static bool haveCachedResult = false;
|
||||
static bool result = false;
|
||||
if (haveCachedResult) {
|
||||
return result;
|
||||
}
|
||||
AudioInfo config;
|
||||
config.mMimeType = "audio/mp4a-latm";
|
||||
config.mRate = 22050;
|
||||
config.mChannels = 2;
|
||||
config.mBitDepth = 16;
|
||||
config.mProfile = 2;
|
||||
config.mExtendedProfile = 2;
|
||||
config.mCodecSpecificConfig->AppendElements(sTestAACConfig,
|
||||
MOZ_ARRAY_LENGTH(sTestAACConfig));
|
||||
config.mExtraData->AppendElements(sTestAACExtraData,
|
||||
MOZ_ARRAY_LENGTH(sTestAACExtraData));
|
||||
nsRefPtr<MediaDataDecoder> decoder(CreateTestAACDecoder(config));
|
||||
if (decoder) {
|
||||
result = true;
|
||||
}
|
||||
haveCachedResult = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -297,11 +297,11 @@ MP4TrackDemuxer::UpdateSamples(nsTArray<nsRefPtr<MediaRawData>>& aSamples)
|
||||
{
|
||||
for (size_t i = 0; i < aSamples.Length(); i++) {
|
||||
MediaRawData* sample = aSamples[i];
|
||||
if (sample->mCrypto.valid) {
|
||||
if (sample->mCrypto.mValid) {
|
||||
nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
|
||||
writer->mCrypto.mode = mInfo->mCrypto.mode;
|
||||
writer->mCrypto.iv_size = mInfo->mCrypto.iv_size;
|
||||
writer->mCrypto.key.AppendElements(mInfo->mCrypto.key);
|
||||
writer->mCrypto.mMode = mInfo->mCrypto.mMode;
|
||||
writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
|
||||
writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
|
||||
}
|
||||
if (mInfo->GetAsVideoInfo()) {
|
||||
sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData;
|
||||
|
||||
@@ -41,7 +41,7 @@ GMPAudioSamplesImpl::GMPAudioSamplesImpl(MediaRawData* aSample,
|
||||
, mRate(aRate)
|
||||
{
|
||||
mBuffer.AppendElements(aSample->Data(), aSample->Size());
|
||||
if (aSample->mCrypto.valid) {
|
||||
if (aSample->mCrypto.mValid) {
|
||||
mCrypto = new GMPEncryptedBufferDataImpl(aSample->mCrypto);
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ GMPAudioSamplesImpl::GetDecryptionData() const
|
||||
void
|
||||
GMPAudioSamplesImpl::InitCrypto(const CryptoSample& aCrypto)
|
||||
{
|
||||
if (!aCrypto.valid) {
|
||||
if (!aCrypto.mValid) {
|
||||
return;
|
||||
}
|
||||
mCrypto = new GMPEncryptedBufferDataImpl(aCrypto);
|
||||
|
||||
@@ -142,13 +142,13 @@ GMPDecryptorParent::Decrypt(uint32_t aId,
|
||||
}
|
||||
|
||||
// Caller should ensure parameters passed in are valid.
|
||||
MOZ_ASSERT(!aBuffer.IsEmpty() && aCrypto.valid);
|
||||
MOZ_ASSERT(!aBuffer.IsEmpty() && aCrypto.mValid);
|
||||
|
||||
GMPDecryptionData data(aCrypto.key,
|
||||
aCrypto.iv,
|
||||
aCrypto.plain_sizes,
|
||||
aCrypto.encrypted_sizes,
|
||||
aCrypto.session_ids);
|
||||
GMPDecryptionData data(aCrypto.mKeyId,
|
||||
aCrypto.mIV,
|
||||
aCrypto.mPlainSizes,
|
||||
aCrypto.mEncryptedSizes,
|
||||
aCrypto.mSessionIds);
|
||||
|
||||
unused << SendDecrypt(aId, aBuffer, data);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
GMPEncryptedBufferDataImpl::GMPEncryptedBufferDataImpl(const CryptoSample& aCrypto)
|
||||
: mKeyId(aCrypto.key)
|
||||
, mIV(aCrypto.iv)
|
||||
, mClearBytes(aCrypto.plain_sizes)
|
||||
, mCipherBytes(aCrypto.encrypted_sizes)
|
||||
, mSessionIdList(aCrypto.session_ids)
|
||||
: mKeyId(aCrypto.mKeyId)
|
||||
, mIV(aCrypto.mIV)
|
||||
, mClearBytes(aCrypto.mPlainSizes)
|
||||
, mCipherBytes(aCrypto.mEncryptedSizes)
|
||||
, mSessionIdList(aCrypto.mSessionIds)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ GStreamerReader::~GStreamerReader()
|
||||
NS_ASSERTION(!mPlayBin, "No Shutdown() after Init()");
|
||||
}
|
||||
|
||||
nsresult GStreamerReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
nsresult GStreamerReader::Init()
|
||||
{
|
||||
GStreamerFormatHelper::Instance();
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
explicit GStreamerReader(AbstractMediaDecoder* aDecoder);
|
||||
virtual ~GStreamerReader();
|
||||
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor) override;
|
||||
virtual nsresult Init() override;
|
||||
virtual nsRefPtr<ShutdownPromise> Shutdown() override;
|
||||
virtual nsresult ResetDecode() override;
|
||||
virtual bool DecodeAudioData() override;
|
||||
|
||||
@@ -177,19 +177,19 @@ static nsCString
|
||||
ToCryptoString(const CryptoSample& aCrypto)
|
||||
{
|
||||
nsCString res;
|
||||
if (aCrypto.valid) {
|
||||
res.AppendPrintf("%d %d ", aCrypto.mode, aCrypto.iv_size);
|
||||
for (size_t i = 0; i < aCrypto.key.Length(); i++) {
|
||||
res.AppendPrintf("%02x", aCrypto.key[i]);
|
||||
if (aCrypto.mValid) {
|
||||
res.AppendPrintf("%d %d ", aCrypto.mMode, aCrypto.mIVSize);
|
||||
for (size_t i = 0; i < aCrypto.mKeyId.Length(); i++) {
|
||||
res.AppendPrintf("%02x", aCrypto.mKeyId[i]);
|
||||
}
|
||||
res.Append(" ");
|
||||
for (size_t i = 0; i < aCrypto.iv.Length(); i++) {
|
||||
res.AppendPrintf("%02x", aCrypto.iv[i]);
|
||||
for (size_t i = 0; i < aCrypto.mIV.Length(); i++) {
|
||||
res.AppendPrintf("%02x", aCrypto.mIV[i]);
|
||||
}
|
||||
EXPECT_EQ(aCrypto.plain_sizes.Length(), aCrypto.encrypted_sizes.Length());
|
||||
for (size_t i = 0; i < aCrypto.plain_sizes.Length(); i++) {
|
||||
res.AppendPrintf(" %d,%d", aCrypto.plain_sizes[i],
|
||||
aCrypto.encrypted_sizes[i]);
|
||||
EXPECT_EQ(aCrypto.mPlainSizes.Length(), aCrypto.mEncryptedSizes.Length());
|
||||
for (size_t i = 0; i < aCrypto.mPlainSizes.Length(); i++) {
|
||||
res.AppendPrintf(" %d,%d", aCrypto.mPlainSizes[i],
|
||||
aCrypto.mEncryptedSizes[i]);
|
||||
}
|
||||
} else {
|
||||
res.Append("no crypto");
|
||||
|
||||
@@ -152,7 +152,7 @@ OggReader::~OggReader()
|
||||
MOZ_COUNT_DTOR(OggReader);
|
||||
}
|
||||
|
||||
nsresult OggReader::Init(MediaDecoderReader* aCloneDonor) {
|
||||
nsresult OggReader::Init() {
|
||||
int ret = ogg_sync_init(&mOggState);
|
||||
NS_ENSURE_TRUE(ret == 0, NS_ERROR_FAILURE);
|
||||
return NS_OK;
|
||||
|
||||
@@ -50,7 +50,7 @@ protected:
|
||||
~OggReader();
|
||||
|
||||
public:
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor) override;
|
||||
virtual nsresult Init() override;
|
||||
virtual nsresult ResetDecode() override;
|
||||
virtual bool DecodeAudioData() override;
|
||||
|
||||
|
||||
@@ -283,12 +283,6 @@ MediaCodecReader::~MediaCodecReader()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaCodecReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::ReleaseMediaResources()
|
||||
{
|
||||
|
||||
@@ -60,10 +60,6 @@ public:
|
||||
MediaCodecReader(AbstractMediaDecoder* aDecoder);
|
||||
virtual ~MediaCodecReader();
|
||||
|
||||
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
|
||||
// on failure.
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor);
|
||||
|
||||
// Release media resources they should be released in dormant state
|
||||
virtual void ReleaseMediaResources();
|
||||
|
||||
|
||||
@@ -139,11 +139,6 @@ MediaOmxReader::~MediaOmxReader()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult MediaOmxReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<AbstractMediaDecoder>
|
||||
MediaOmxReader::SafeGetDecoder() {
|
||||
nsRefPtr<AbstractMediaDecoder> decoder;
|
||||
|
||||
@@ -70,8 +70,6 @@ public:
|
||||
MediaOmxReader(AbstractMediaDecoder* aDecoder);
|
||||
~MediaOmxReader();
|
||||
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor);
|
||||
|
||||
protected:
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
|
||||
public:
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PDMFactory.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "WMFDecoderModule.h"
|
||||
#endif
|
||||
#ifdef MOZ_FFMPEG
|
||||
#include "FFmpegRuntimeLinker.h"
|
||||
#endif
|
||||
#ifdef MOZ_APPLEMEDIA
|
||||
#include "AppleDecoderModule.h"
|
||||
#endif
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
#include "GonkDecoderModule.h"
|
||||
#endif
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#include "AndroidDecoderModule.h"
|
||||
#endif
|
||||
#include "GMPDecoderModule.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
|
||||
#include "mozilla/SharedThreadPool.h"
|
||||
|
||||
#include "MediaInfo.h"
|
||||
#include "FuzzingWrapper.h"
|
||||
#include "H264Converter.h"
|
||||
|
||||
#include "AgnosticDecoderModule.h"
|
||||
|
||||
#ifdef MOZ_EME
|
||||
#include "EMEDecoderModule.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
extern already_AddRefed<PlatformDecoderModule> CreateAgnosticDecoderModule();
|
||||
extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
|
||||
|
||||
bool PDMFactory::sUseBlankDecoder = false;
|
||||
bool PDMFactory::sGonkDecoderEnabled = false;
|
||||
bool PDMFactory::sAndroidMCDecoderEnabled = false;
|
||||
bool PDMFactory::sAndroidMCDecoderPreferred = false;
|
||||
bool PDMFactory::sGMPDecoderEnabled = false;
|
||||
bool PDMFactory::sEnableFuzzingWrapper = false;
|
||||
uint32_t PDMFactory::sVideoOutputMinimumInterval_ms = 0;
|
||||
bool PDMFactory::sDontDelayInputExhausted = false;
|
||||
|
||||
/* static */
|
||||
void
|
||||
PDMFactory::Init()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
static bool alreadyInitialized = false;
|
||||
if (alreadyInitialized) {
|
||||
return;
|
||||
}
|
||||
alreadyInitialized = true;
|
||||
|
||||
Preferences::AddBoolVarCache(&sUseBlankDecoder,
|
||||
"media.fragmented-mp4.use-blank-decoder");
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
Preferences::AddBoolVarCache(&sGonkDecoderEnabled,
|
||||
"media.fragmented-mp4.gonk.enabled", false);
|
||||
#endif
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Preferences::AddBoolVarCache(&sAndroidMCDecoderEnabled,
|
||||
"media.fragmented-mp4.android-media-codec.enabled", false);
|
||||
Preferences::AddBoolVarCache(&sAndroidMCDecoderPreferred,
|
||||
"media.fragmented-mp4.android-media-codec.preferred", false);
|
||||
#endif
|
||||
|
||||
Preferences::AddBoolVarCache(&sGMPDecoderEnabled,
|
||||
"media.fragmented-mp4.gmp.enabled", false);
|
||||
|
||||
Preferences::AddBoolVarCache(&sEnableFuzzingWrapper,
|
||||
"media.decoder.fuzzing.enabled", false);
|
||||
Preferences::AddUintVarCache(&sVideoOutputMinimumInterval_ms,
|
||||
"media.decoder.fuzzing.video-output-minimum-interval-ms", 0);
|
||||
Preferences::AddBoolVarCache(&sDontDelayInputExhausted,
|
||||
"media.decoder.fuzzing.dont-delay-inputexhausted", false);
|
||||
|
||||
#ifdef XP_WIN
|
||||
WMFDecoderModule::Init();
|
||||
#endif
|
||||
#ifdef MOZ_APPLEMEDIA
|
||||
AppleDecoderModule::Init();
|
||||
#endif
|
||||
#ifdef MOZ_FFMPEG
|
||||
FFmpegRuntimeLinker::Link();
|
||||
#endif
|
||||
}
|
||||
|
||||
PDMFactory::PDMFactory()
|
||||
{
|
||||
CreatePDMs();
|
||||
}
|
||||
|
||||
PDMFactory::~PDMFactory()
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
PDMFactory::CreateDecoder(const TrackInfo& aConfig,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer)
|
||||
{
|
||||
nsRefPtr<PlatformDecoderModule> current = (mEMEPDM && aConfig.mCrypto.mValid)
|
||||
? mEMEPDM : GetDecoder(aConfig.mMimeType);
|
||||
|
||||
if (!current) {
|
||||
return nullptr;
|
||||
}
|
||||
nsRefPtr<MediaDataDecoder> m;
|
||||
|
||||
if (aConfig.GetAsAudioInfo()) {
|
||||
m = current->CreateAudioDecoder(*aConfig.GetAsAudioInfo(),
|
||||
aTaskQueue,
|
||||
aCallback);
|
||||
return m.forget();
|
||||
}
|
||||
|
||||
if (!aConfig.GetAsVideoInfo()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MediaDataDecoderCallback* callback = aCallback;
|
||||
nsRefPtr<DecoderCallbackFuzzingWrapper> callbackWrapper;
|
||||
if (sEnableFuzzingWrapper) {
|
||||
callbackWrapper = new DecoderCallbackFuzzingWrapper(aCallback);
|
||||
callbackWrapper->SetVideoOutputMinimumInterval(
|
||||
TimeDuration::FromMilliseconds(sVideoOutputMinimumInterval_ms));
|
||||
callbackWrapper->SetDontDelayInputExhausted(sDontDelayInputExhausted);
|
||||
callback = callbackWrapper.get();
|
||||
}
|
||||
|
||||
if (H264Converter::IsH264(aConfig)) {
|
||||
nsRefPtr<H264Converter> h
|
||||
= new H264Converter(current,
|
||||
*aConfig.GetAsVideoInfo(),
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
aTaskQueue,
|
||||
callback);
|
||||
const nsresult rv = h->GetLastError();
|
||||
if (NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_INITIALIZED) {
|
||||
// The H264Converter either successfully created the wrapped decoder,
|
||||
// or there wasn't enough AVCC data to do so. Otherwise, there was some
|
||||
// problem, for example WMF DLLs were missing.
|
||||
m = h.forget();
|
||||
}
|
||||
} else {
|
||||
m = current->CreateVideoDecoder(*aConfig.GetAsVideoInfo(),
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
aTaskQueue,
|
||||
callback);
|
||||
}
|
||||
|
||||
if (callbackWrapper && m) {
|
||||
m = new DecoderFuzzingWrapper(m.forget(), callbackWrapper.forget());
|
||||
}
|
||||
|
||||
return m.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
PDMFactory::SupportsMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
if (mEMEPDM) {
|
||||
return mEMEPDM->SupportsMimeType(aMimeType);
|
||||
}
|
||||
nsRefPtr<PlatformDecoderModule> current = GetDecoder(aMimeType);
|
||||
return !!current;
|
||||
}
|
||||
|
||||
void
|
||||
PDMFactory::CreatePDMs()
|
||||
{
|
||||
nsRefPtr<PlatformDecoderModule> m;
|
||||
|
||||
if (sGMPDecoderEnabled) {
|
||||
m = new GMPDecoderModule();
|
||||
StartupPDM(m);
|
||||
}
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if(sAndroidMCDecoderPreferred && sAndroidMCDecoderEnabled) {
|
||||
m = new AndroidDecoderModule();
|
||||
StartupPDM(m);
|
||||
}
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
m = new WMFDecoderModule();
|
||||
StartupPDM(m);
|
||||
#endif
|
||||
#ifdef MOZ_FFMPEG
|
||||
m = FFmpegRuntimeLinker::CreateDecoderModule();
|
||||
StartupPDM(m);
|
||||
#endif
|
||||
#ifdef MOZ_APPLEMEDIA
|
||||
m = new AppleDecoderModule();
|
||||
StartupPDM(m);
|
||||
#endif
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
if (sGonkDecoderEnabled) {
|
||||
m = new GonkDecoderModule();
|
||||
StartupPDM(m);
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if(sAndroidMCDecoderEnabled){
|
||||
m = new AndroidDecoderModule();
|
||||
StartupPDM(m);
|
||||
}
|
||||
#endif
|
||||
|
||||
m = new AgnosticDecoderModule();
|
||||
StartupPDM(m);
|
||||
|
||||
if (sUseBlankDecoder) {
|
||||
m = CreateBlankDecoderModule();
|
||||
StartupPDM(m);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PDMFactory::StartupPDM(PlatformDecoderModule* aPDM)
|
||||
{
|
||||
if (aPDM && NS_SUCCEEDED(aPDM->Startup())) {
|
||||
mCurrentPDMs.AppendElement(aPDM);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
already_AddRefed<PlatformDecoderModule>
|
||||
PDMFactory::GetDecoder(const nsACString& aMimeType)
|
||||
{
|
||||
nsRefPtr<PlatformDecoderModule> pdm;
|
||||
for (auto& current : mCurrentPDMs) {
|
||||
if (current->SupportsMimeType(aMimeType)) {
|
||||
pdm = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pdm.forget();
|
||||
}
|
||||
|
||||
#ifdef MOZ_EME
|
||||
void
|
||||
PDMFactory::SetCDMProxy(CDMProxy* aProxy)
|
||||
{
|
||||
bool cdmDecodesAudio;
|
||||
bool cdmDecodesVideo;
|
||||
{
|
||||
CDMCaps::AutoLock caps(aProxy->Capabilites());
|
||||
cdmDecodesAudio = caps.CanDecryptAndDecodeAudio();
|
||||
cdmDecodesVideo = caps.CanDecryptAndDecodeVideo();
|
||||
}
|
||||
|
||||
nsRefPtr<PDMFactory> m = new PDMFactory();
|
||||
mEMEPDM = new EMEDecoderModule(aProxy, m, cdmDecodesAudio, cdmDecodesVideo);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,73 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(PDMFactory_h_)
|
||||
#define PDMFactory_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
class CDMProxy;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class PDMFactory final {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PDMFactory)
|
||||
|
||||
PDMFactory();
|
||||
|
||||
// Call on the main thread to initialize the static state
|
||||
// needed by Create().
|
||||
static void Init();
|
||||
|
||||
// Factory method that creates the appropriate PlatformDecoderModule for
|
||||
// the platform we're running on. Caller is responsible for deleting this
|
||||
// instance. It's expected that there will be multiple
|
||||
// PlatformDecoderModules alive at the same time.
|
||||
// This is called on the decode task queue.
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
CreateDecoder(const TrackInfo& aConfig,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
layers::LayersBackend aLayersBackend = layers::LayersBackend::LAYERS_NONE,
|
||||
layers::ImageContainer* aImageContainer = nullptr);
|
||||
|
||||
bool SupportsMimeType(const nsACString& aMimeType);
|
||||
|
||||
#ifdef MOZ_EME
|
||||
// Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or
|
||||
// decrypt-and-decode EME encrypted content. If the CDM only decrypts and
|
||||
// does not decode, we create a PDM and use that to create MediaDataDecoders
|
||||
// that we use on on aTaskQueue to decode the decrypted stream.
|
||||
// This is called on the decode task queue.
|
||||
void SetCDMProxy(CDMProxy* aProxy);
|
||||
#endif
|
||||
|
||||
private:
|
||||
virtual ~PDMFactory();
|
||||
void CreatePDMs();
|
||||
// Startup the provided PDM and add it to our list if successful.
|
||||
bool StartupPDM(PlatformDecoderModule* aPDM);
|
||||
// Returns the first PDM in our list supporting the mimetype.
|
||||
already_AddRefed<PlatformDecoderModule> GetDecoder(const nsACString& aMimeType);
|
||||
|
||||
// Caches pref media.fragmented-mp4.use-blank-decoder
|
||||
static bool sUseBlankDecoder;
|
||||
static bool sGonkDecoderEnabled;
|
||||
static bool sAndroidMCDecoderPreferred;
|
||||
static bool sAndroidMCDecoderEnabled;
|
||||
static bool sGMPDecoderEnabled;
|
||||
static bool sEnableFuzzingWrapper;
|
||||
static uint32_t sVideoOutputMinimumInterval_ms;
|
||||
static bool sDontDelayInputExhausted;
|
||||
|
||||
nsTArray<nsRefPtr<PlatformDecoderModule>> mCurrentPDMs;
|
||||
nsRefPtr<PlatformDecoderModule> mEMEPDM;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* PDMFactory_h_ */
|
||||
@@ -6,233 +6,10 @@
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "WMFDecoderModule.h"
|
||||
#endif
|
||||
#ifdef MOZ_FFMPEG
|
||||
#include "FFmpegRuntimeLinker.h"
|
||||
#endif
|
||||
#ifdef MOZ_APPLEMEDIA
|
||||
#include "AppleDecoderModule.h"
|
||||
#endif
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
#include "GonkDecoderModule.h"
|
||||
#endif
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#include "AndroidDecoderModule.h"
|
||||
#endif
|
||||
#include "GMPDecoderModule.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#include "mozilla/SharedThreadPool.h"
|
||||
|
||||
#include "MediaInfo.h"
|
||||
#include "H264Converter.h"
|
||||
|
||||
#include "OpusDecoder.h"
|
||||
#include "VorbisDecoder.h"
|
||||
#include "VPXDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
extern already_AddRefed<PlatformDecoderModule> CreateAgnosticDecoderModule();
|
||||
extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
|
||||
|
||||
bool PlatformDecoderModule::sUseBlankDecoder = false;
|
||||
#ifdef MOZ_FFMPEG
|
||||
bool PlatformDecoderModule::sFFmpegDecoderEnabled = false;
|
||||
#endif
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
bool PlatformDecoderModule::sGonkDecoderEnabled = false;
|
||||
#endif
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
bool PlatformDecoderModule::sAndroidMCDecoderEnabled = false;
|
||||
bool PlatformDecoderModule::sAndroidMCDecoderPreferred = false;
|
||||
#endif
|
||||
bool PlatformDecoderModule::sGMPDecoderEnabled = false;
|
||||
|
||||
/* static */
|
||||
void
|
||||
PlatformDecoderModule::Init()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
static bool alreadyInitialized = false;
|
||||
if (alreadyInitialized) {
|
||||
return;
|
||||
PRLogModuleInfo* GetPDMLog() {
|
||||
static PRLogModuleInfo* log = nullptr;
|
||||
if (!log) {
|
||||
log = PR_NewLogModule("PlatformDecoderModule");
|
||||
}
|
||||
alreadyInitialized = true;
|
||||
|
||||
Preferences::AddBoolVarCache(&sUseBlankDecoder,
|
||||
"media.use-blank-decoder");
|
||||
#ifdef MOZ_FFMPEG
|
||||
Preferences::AddBoolVarCache(&sFFmpegDecoderEnabled,
|
||||
"media.ffmpeg.enabled", false);
|
||||
#endif
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
Preferences::AddBoolVarCache(&sGonkDecoderEnabled,
|
||||
"media.gonk.enabled", false);
|
||||
#endif
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Preferences::AddBoolVarCache(&sAndroidMCDecoderEnabled,
|
||||
"media.android-media-codec.enabled", false);
|
||||
Preferences::AddBoolVarCache(&sAndroidMCDecoderPreferred,
|
||||
"media.android-media-codec.preferred", false);
|
||||
#endif
|
||||
|
||||
Preferences::AddBoolVarCache(&sGMPDecoderEnabled,
|
||||
"media.gmp.decoder.enabled", false);
|
||||
|
||||
#ifdef XP_WIN
|
||||
WMFDecoderModule::Init();
|
||||
#endif
|
||||
#ifdef MOZ_APPLEMEDIA
|
||||
AppleDecoderModule::Init();
|
||||
#endif
|
||||
return log;
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<PlatformDecoderModule>
|
||||
PlatformDecoderModule::Create()
|
||||
{
|
||||
// Note: This (usually) runs on the decode thread.
|
||||
|
||||
nsRefPtr<PlatformDecoderModule> m(CreatePDM());
|
||||
|
||||
if (m && NS_SUCCEEDED(m->Startup())) {
|
||||
return m.forget();
|
||||
}
|
||||
return CreateAgnosticDecoderModule();
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<PlatformDecoderModule>
|
||||
PlatformDecoderModule::CreatePDM()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if(sAndroidMCDecoderPreferred && sAndroidMCDecoderEnabled){
|
||||
nsRefPtr<PlatformDecoderModule> m(new AndroidDecoderModule());
|
||||
return m.forget();
|
||||
}
|
||||
#endif
|
||||
if (sUseBlankDecoder) {
|
||||
return CreateBlankDecoderModule();
|
||||
}
|
||||
#ifdef XP_WIN
|
||||
if(IsVistaOrLater()&&!Preferences::GetBool("media.ffmpeg.enabled", false)){
|
||||
nsRefPtr<PlatformDecoderModule> m(new WMFDecoderModule());
|
||||
return m.forget();
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_FFMPEG
|
||||
nsRefPtr<PlatformDecoderModule> mffmpeg = FFmpegRuntimeLinker::CreateDecoderModule();
|
||||
if (mffmpeg) {
|
||||
return mffmpeg.forget();
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_APPLEMEDIA
|
||||
nsRefPtr<PlatformDecoderModule> m(new AppleDecoderModule());
|
||||
return m.forget();
|
||||
#endif
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
if (sGonkDecoderEnabled) {
|
||||
nsRefPtr<PlatformDecoderModule> m(new GonkDecoderModule());
|
||||
return m.forget();
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if(sAndroidMCDecoderEnabled){
|
||||
nsRefPtr<PlatformDecoderModule> m(new AndroidDecoderModule());
|
||||
return m.forget();
|
||||
}
|
||||
#endif
|
||||
if (sGMPDecoderEnabled) {
|
||||
nsRefPtr<PlatformDecoderModule> m(new GMPDecoderModule());
|
||||
return m.forget();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
PlatformDecoderModule::CreateDecoder(const TrackInfo& aConfig,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer)
|
||||
{
|
||||
nsRefPtr<MediaDataDecoder> m;
|
||||
|
||||
bool hasPlatformDecoder = SupportsMimeType(aConfig.mMimeType);
|
||||
|
||||
if (aConfig.GetAsAudioInfo()) {
|
||||
if (!hasPlatformDecoder && VorbisDataDecoder::IsVorbis(aConfig.mMimeType)) {
|
||||
m = new VorbisDataDecoder(*aConfig.GetAsAudioInfo(),
|
||||
aTaskQueue,
|
||||
aCallback);
|
||||
} else if (!hasPlatformDecoder && OpusDataDecoder::IsOpus(aConfig.mMimeType)) {
|
||||
m = new OpusDataDecoder(*aConfig.GetAsAudioInfo(),
|
||||
aTaskQueue,
|
||||
aCallback);
|
||||
} else {
|
||||
m = CreateAudioDecoder(*aConfig.GetAsAudioInfo(),
|
||||
aTaskQueue,
|
||||
aCallback);
|
||||
}
|
||||
return m.forget();
|
||||
}
|
||||
|
||||
if (!aConfig.GetAsVideoInfo()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (H264Converter::IsH264(aConfig)) {
|
||||
nsRefPtr<H264Converter> h
|
||||
= new H264Converter(this,
|
||||
*aConfig.GetAsVideoInfo(),
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
aTaskQueue,
|
||||
aCallback);
|
||||
const nsresult rv = h->GetLastError();
|
||||
if (NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_INITIALIZED) {
|
||||
// The H264Converter either successfully created the wrapped decoder,
|
||||
// or there wasn't enough AVCC data to do so. Otherwise, there was some
|
||||
// problem, for example WMF DLLs were missing.
|
||||
m = h.forget();
|
||||
}
|
||||
} else if (!hasPlatformDecoder && VPXDecoder::IsVPX(aConfig.mMimeType)) {
|
||||
m = new VPXDecoder(*aConfig.GetAsVideoInfo(),
|
||||
aImageContainer,
|
||||
aTaskQueue,
|
||||
aCallback);
|
||||
} else {
|
||||
m = CreateVideoDecoder(*aConfig.GetAsVideoInfo(),
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
aTaskQueue,
|
||||
aCallback);
|
||||
}
|
||||
return m.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
PlatformDecoderModule::SupportsMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return aMimeType.EqualsLiteral("audio/mp4a-latm") ||
|
||||
aMimeType.EqualsLiteral("video/mp4") ||
|
||||
aMimeType.EqualsLiteral("video/avc");
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
PlatformDecoderModule::AgnosticMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return VPXDecoder::IsVPX(aMimeType) ||
|
||||
OpusDataDecoder::IsOpus(aMimeType) ||
|
||||
VorbisDataDecoder::IsVorbis(aMimeType);
|
||||
}
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -30,64 +30,30 @@ class MediaDataDecoderCallback;
|
||||
class FlushableTaskQueue;
|
||||
class CDMProxy;
|
||||
|
||||
// The PlatformDecoderModule interface is used by the MP4Reader to abstract
|
||||
// access to the H264 and Audio (AAC/MP3) decoders provided by various platforms.
|
||||
// It may be extended to support other codecs in future. Each platform (Windows,
|
||||
// MacOSX, Linux, B2G etc) must implement a PlatformDecoderModule to provide
|
||||
// access to its decoders in order to get decompressed H.264/AAC from the
|
||||
// MP4Reader.
|
||||
// The PlatformDecoderModule interface is used by the MediaFormatReader to
|
||||
// abstract access to decoders provided by various
|
||||
// platforms.
|
||||
// Each platform (Windows, MacOSX, Linux, B2G etc) must implement a
|
||||
// PlatformDecoderModule to provide access to its decoders in order to get
|
||||
// decompressed H.264/AAC from the MediaFormatReader.
|
||||
//
|
||||
// Video decoding is asynchronous, and should be performed on the task queue
|
||||
// Decoding is asynchronous, and should be performed on the task queue
|
||||
// provided if the underlying platform isn't already exposing an async API.
|
||||
//
|
||||
// Platforms that don't have a corresponding PlatformDecoderModule won't be
|
||||
// able to play the H.264/AAC data output by the MP4Reader. In practice this
|
||||
// means that we won't have fragmented MP4 supported in Media Source
|
||||
// Extensions.
|
||||
//
|
||||
// A cross-platform decoder module that discards input and produces "blank"
|
||||
// output samples exists for testing, and is created when the pref
|
||||
// "media.use-blank-decoder" is true.
|
||||
|
||||
class PlatformDecoderModule {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PlatformDecoderModule)
|
||||
|
||||
// Call on the main thread to initialize the static state
|
||||
// needed by Create().
|
||||
static void Init();
|
||||
|
||||
// Factory method that creates the appropriate PlatformDecoderModule for
|
||||
// the platform we're running on. Caller is responsible for deleting this
|
||||
// instance. It's expected that there will be multiple
|
||||
// PlatformDecoderModules alive at the same time. There is one
|
||||
// PlatformDecoderModule created per MP4Reader.
|
||||
// This is called on the decode task queue.
|
||||
static already_AddRefed<PlatformDecoderModule> Create();
|
||||
// As Create() but do not initialize the created PlatformDecoderModule.
|
||||
static already_AddRefed<PlatformDecoderModule> CreatePDM();
|
||||
|
||||
// Perform any per-instance initialization.
|
||||
// This is called on the decode task queue.
|
||||
virtual nsresult Startup() { return NS_OK; };
|
||||
|
||||
// Creates a decoder.
|
||||
// See CreateVideoDecoder and CreateAudioDecoder for implementation details.
|
||||
virtual already_AddRefed<MediaDataDecoder>
|
||||
CreateDecoder(const TrackInfo& aConfig,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
layers::LayersBackend aLayersBackend = layers::LayersBackend::LAYERS_NONE,
|
||||
layers::ImageContainer* aImageContainer = nullptr);
|
||||
|
||||
// An audio decoder module must support AAC by default.
|
||||
// A video decoder must support H264 by default.
|
||||
// If more codecs are to be supported, SupportsMimeType will have
|
||||
// to be extended
|
||||
virtual bool SupportsMimeType(const nsACString& aMimeType);
|
||||
|
||||
// MimeType can be decoded with shipped decoders if no platform decoders exist
|
||||
static bool AgnosticMimeType(const nsACString& aMimeType);
|
||||
|
||||
// Indicates if the PlatformDecoderModule supports decoding of aMimeType.
|
||||
virtual bool SupportsMimeType(const nsACString& aMimeType) = 0;
|
||||
|
||||
enum ConversionRequired {
|
||||
kNeedNone,
|
||||
@@ -100,15 +66,13 @@ public:
|
||||
// feeding it to MediaDataDecoder::Input.
|
||||
virtual ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const = 0;
|
||||
|
||||
virtual bool SupportsSharedDecoders(const VideoInfo& aConfig) const {
|
||||
return !AgnosticMimeType(aConfig.mMimeType);
|
||||
}
|
||||
|
||||
protected:
|
||||
PlatformDecoderModule() {}
|
||||
virtual ~PlatformDecoderModule() {}
|
||||
|
||||
friend class H264Converter;
|
||||
friend class PDMFactory;
|
||||
|
||||
// Creates a Video decoder. The layers backend is passed in so that
|
||||
// decoders can determine whether hardware accelerated decoding can be used.
|
||||
// Asynchronous decoding of video should be done in runnables dispatched
|
||||
@@ -141,24 +105,11 @@ protected:
|
||||
CreateAudioDecoder(const AudioInfo& aConfig,
|
||||
FlushableTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) = 0;
|
||||
|
||||
// PDM pref caches...
|
||||
static bool sUseBlankDecoder;
|
||||
#ifdef MOZ_FFMPEG
|
||||
static bool sFFmpegDecoderEnabled;
|
||||
#endif
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
static bool sGonkDecoderEnabled;
|
||||
#endif
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
static bool sAndroidMCDecoderPreferred;
|
||||
static bool sAndroidMCDecoderEnabled;
|
||||
#endif
|
||||
static bool sGMPDecoderEnabled;
|
||||
};
|
||||
|
||||
// A callback used by MediaDataDecoder to return output/errors to the
|
||||
// MP4Reader. Implementation is threadsafe, and can be called on any thread.
|
||||
// MediaFormatReader.
|
||||
// Implementation is threadsafe, and can be called on any thread.
|
||||
class MediaDataDecoderCallback {
|
||||
public:
|
||||
virtual ~MediaDataDecoderCallback() {}
|
||||
@@ -188,7 +139,7 @@ public:
|
||||
//
|
||||
// Unless otherwise noted, all functions are only called on the decode task
|
||||
// queue. An exception is the MediaDataDecoder in
|
||||
// MP4Reader::IsVideoAccelerated() for which all calls (Init(),
|
||||
// MediaFormatReader::IsVideoAccelerated() for which all calls (Init(),
|
||||
// IsHardwareAccelerated(), and Shutdown()) are from the main thread.
|
||||
//
|
||||
// Don't block inside these functions, unless it's explicitly noted that you
|
||||
@@ -216,7 +167,7 @@ public:
|
||||
// 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,
|
||||
// so that if the MediaFormatReader 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()!
|
||||
@@ -231,7 +182,7 @@ public:
|
||||
// decoding resumes after the seek.
|
||||
// While the reader calls Flush(), it ignores all output sent to it;
|
||||
// it is safe (but pointless) to send output while Flush is called.
|
||||
// The MP4Reader will not call Input() while it's calling Flush().
|
||||
// The MediaFormatReader will not call Input() while it's calling Flush().
|
||||
virtual nsresult Flush() = 0;
|
||||
|
||||
|
||||
@@ -240,7 +191,7 @@ public:
|
||||
// it drops the input samples. The decoder may be holding onto samples
|
||||
// that are required to decode samples that it expects to get in future.
|
||||
// This is called when the demuxer reaches end of stream.
|
||||
// The MP4Reader will not call Input() while it's calling Drain().
|
||||
// The MediaFormatReader will not call Input() while it's calling Drain().
|
||||
// This function is asynchronous. The MediaDataDecoder must call
|
||||
// MediaDataDecoderCallback::DrainComplete() once all remaining
|
||||
// samples have been output.
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SharedDecoderManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class SharedDecoderCallback : public MediaDataDecoderCallback
|
||||
{
|
||||
public:
|
||||
explicit SharedDecoderCallback(SharedDecoderManager* aManager)
|
||||
: mManager(aManager)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Output(MediaData* aData) override
|
||||
{
|
||||
if (mManager->mActiveCallback) {
|
||||
mManager->mActiveCallback->Output(aData);
|
||||
}
|
||||
}
|
||||
virtual void Error() override
|
||||
{
|
||||
if (mManager->mActiveCallback) {
|
||||
mManager->mActiveCallback->Error();
|
||||
}
|
||||
}
|
||||
virtual void InputExhausted() override
|
||||
{
|
||||
if (mManager->mActiveCallback) {
|
||||
mManager->mActiveCallback->InputExhausted();
|
||||
}
|
||||
}
|
||||
virtual void DrainComplete() override
|
||||
{
|
||||
if (mManager->mActiveCallback) {
|
||||
mManager->DrainComplete();
|
||||
}
|
||||
}
|
||||
virtual void ReleaseMediaResources() override
|
||||
{
|
||||
if (mManager->mActiveCallback) {
|
||||
mManager->mActiveCallback->ReleaseMediaResources();
|
||||
}
|
||||
}
|
||||
virtual bool OnReaderTaskQueue() override
|
||||
{
|
||||
MOZ_ASSERT(mManager->mActiveCallback);
|
||||
return mManager->mActiveCallback->OnReaderTaskQueue();
|
||||
}
|
||||
|
||||
SharedDecoderManager* mManager;
|
||||
};
|
||||
|
||||
SharedDecoderManager::SharedDecoderManager()
|
||||
: mTaskQueue(new FlushableTaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER)))
|
||||
, mActiveProxy(nullptr)
|
||||
, mActiveCallback(nullptr)
|
||||
, mInit(false)
|
||||
, mWaitForInternalDrain(false)
|
||||
, mMonitor("SharedDecoderManager")
|
||||
, mDecoderReleasedResources(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread()); // taskqueue must be created on main thread.
|
||||
mCallback = new SharedDecoderCallback(this);
|
||||
}
|
||||
|
||||
SharedDecoderManager::~SharedDecoderManager()
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
SharedDecoderManager::CreateVideoDecoder(
|
||||
PlatformDecoderModule* aPDM,
|
||||
const VideoInfo& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
FlushableTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
if (!mDecoder) {
|
||||
mLayersBackend = aLayersBackend;
|
||||
mImageContainer = aImageContainer;
|
||||
// We use the manager's task queue for the decoder, rather than the one
|
||||
// passed in, so that none of the objects sharing the decoder can shutdown
|
||||
// the task queue while we're potentially still using it for a *different*
|
||||
// object also sharing the decoder.
|
||||
mDecoder =
|
||||
aPDM->CreateDecoder(aConfig,
|
||||
mTaskQueue,
|
||||
mCallback,
|
||||
mLayersBackend,
|
||||
mImageContainer);
|
||||
if (!mDecoder) {
|
||||
mPDM = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mPDM = aPDM;
|
||||
}
|
||||
|
||||
nsRefPtr<SharedDecoderProxy> proxy(new SharedDecoderProxy(this, aCallback));
|
||||
return proxy.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
SharedDecoderManager::Recreate(const VideoInfo& aConfig)
|
||||
{
|
||||
mDecoder->Flush();
|
||||
mDecoder->Shutdown();
|
||||
mDecoder = mPDM->CreateDecoder(aConfig,
|
||||
mTaskQueue,
|
||||
mCallback,
|
||||
mLayersBackend,
|
||||
mImageContainer);
|
||||
if (!mDecoder) {
|
||||
return false;
|
||||
}
|
||||
mInit = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SharedDecoderManager::Select(SharedDecoderProxy* aProxy)
|
||||
{
|
||||
if (mActiveProxy == aProxy) {
|
||||
return;
|
||||
}
|
||||
SetIdle(mActiveProxy);
|
||||
|
||||
mActiveProxy = aProxy;
|
||||
mActiveCallback = aProxy->mCallback;
|
||||
}
|
||||
|
||||
void
|
||||
SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy)
|
||||
{
|
||||
if (aProxy && mActiveProxy == aProxy) {
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mWaitForInternalDrain = true;
|
||||
// We don't want to hold the lock while calling Drain() as some
|
||||
// platform implementations call DrainComplete() immediately.
|
||||
}
|
||||
nsresult rv = mActiveProxy->Drain();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
while (mWaitForInternalDrain) {
|
||||
mon.Wait();
|
||||
}
|
||||
}
|
||||
mActiveProxy->Flush();
|
||||
mActiveProxy = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
if (mWaitForInternalDrain) {
|
||||
mWaitForInternalDrain = false;
|
||||
mon.NotifyAll();
|
||||
return;
|
||||
}
|
||||
}
|
||||
mActiveCallback->DrainComplete();
|
||||
}
|
||||
|
||||
void
|
||||
SharedDecoderManager::Shutdown()
|
||||
{
|
||||
if (mDecoder) {
|
||||
mDecoder->Shutdown();
|
||||
mDecoder = nullptr;
|
||||
}
|
||||
mPDM = nullptr;
|
||||
if (mTaskQueue) {
|
||||
mTaskQueue->BeginShutdown();
|
||||
mTaskQueue->AwaitShutdownAndIdle();
|
||||
mTaskQueue = nullptr;
|
||||
}
|
||||
mDecoderInitPromiseRequest.DisconnectIfExists();
|
||||
}
|
||||
|
||||
SharedDecoderProxy::SharedDecoderProxy(SharedDecoderManager* aManager,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
: mManager(aManager)
|
||||
, mCallback(aCallback)
|
||||
{
|
||||
}
|
||||
|
||||
SharedDecoderProxy::~SharedDecoderProxy()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
SharedDecoderProxy::Init()
|
||||
{
|
||||
if (mManager->mActiveProxy != this) {
|
||||
mManager->Select(this);
|
||||
}
|
||||
|
||||
return mManager->InitDecoder();
|
||||
}
|
||||
|
||||
nsresult
|
||||
SharedDecoderProxy::Input(MediaRawData* aSample)
|
||||
{
|
||||
if (mManager->mActiveProxy != this) {
|
||||
mManager->Select(this);
|
||||
}
|
||||
return mManager->mDecoder->Input(aSample);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SharedDecoderProxy::Flush()
|
||||
{
|
||||
if (mManager->mActiveProxy == this) {
|
||||
return mManager->mDecoder->Flush();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SharedDecoderProxy::Drain()
|
||||
{
|
||||
if (mManager->mActiveProxy == this) {
|
||||
return mManager->mDecoder->Drain();
|
||||
} else {
|
||||
mCallback->DrainComplete();
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
SharedDecoderProxy::Shutdown()
|
||||
{
|
||||
mManager->SetIdle(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
SharedDecoderProxy::IsHardwareAccelerated(nsACString& aFailureReason) const
|
||||
{
|
||||
return mManager->mDecoder->IsHardwareAccelerated(aFailureReason);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
@@ -1,92 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef SHARED_DECODER_MANAGER_H_
|
||||
#define SHARED_DECODER_MANAGER_H_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
|
||||
class MediaDataDecoder;
|
||||
class SharedDecoderProxy;
|
||||
class SharedDecoderCallback;
|
||||
|
||||
class SharedDecoderManager
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedDecoderManager)
|
||||
|
||||
SharedDecoderManager();
|
||||
|
||||
already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
|
||||
PlatformDecoderModule* aPDM,
|
||||
const VideoInfo& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
FlushableTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
|
||||
void SetReader(MediaDecoderReader* aReader);
|
||||
void Select(SharedDecoderProxy* aProxy);
|
||||
void SetIdle(MediaDataDecoder* aProxy);
|
||||
void ReleaseMediaResources();
|
||||
void Shutdown();
|
||||
|
||||
friend class SharedDecoderProxy;
|
||||
friend class SharedDecoderCallback;
|
||||
|
||||
bool Recreate(const VideoInfo& aConfig);
|
||||
|
||||
private:
|
||||
virtual ~SharedDecoderManager();
|
||||
void DrainComplete();
|
||||
|
||||
nsRefPtr<MediaDataDecoder::InitPromise> InitDecoder();
|
||||
|
||||
nsRefPtr<PlatformDecoderModule> mPDM;
|
||||
nsRefPtr<MediaDataDecoder> mDecoder;
|
||||
layers::LayersBackend mLayersBackend;
|
||||
nsRefPtr<layers::ImageContainer> mImageContainer;
|
||||
nsRefPtr<FlushableTaskQueue> mTaskQueue;
|
||||
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;
|
||||
bool mDecoderReleasedResources;
|
||||
};
|
||||
|
||||
class SharedDecoderProxy : public MediaDataDecoder
|
||||
{
|
||||
public:
|
||||
SharedDecoderProxy(SharedDecoderManager* aManager,
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
virtual ~SharedDecoderProxy();
|
||||
|
||||
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(nsACString& aFailureReason) const override;
|
||||
|
||||
friend class SharedDecoderManager;
|
||||
|
||||
private:
|
||||
nsRefPtr<SharedDecoderManager> mManager;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AgnosticDecoderModule.h"
|
||||
#include "OpusDecoder.h"
|
||||
#include "VorbisDecoder.h"
|
||||
#include "VPXDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
bool
|
||||
AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return VPXDecoder::IsVPX(aMimeType) ||
|
||||
OpusDataDecoder::IsOpus(aMimeType) ||
|
||||
VorbisDataDecoder::IsVorbis(aMimeType);
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
AgnosticDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
FlushableTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
nsRefPtr<MediaDataDecoder> m;
|
||||
|
||||
if (VPXDecoder::IsVPX(aConfig.mMimeType)) {
|
||||
m = new VPXDecoder(*aConfig.GetAsVideoInfo(),
|
||||
aImageContainer,
|
||||
aVideoTaskQueue,
|
||||
aCallback);
|
||||
}
|
||||
|
||||
return m.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
AgnosticDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
|
||||
FlushableTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
nsRefPtr<MediaDataDecoder> m;
|
||||
|
||||
if (VorbisDataDecoder::IsVorbis(aConfig.mMimeType)) {
|
||||
m = new VorbisDataDecoder(*aConfig.GetAsAudioInfo(),
|
||||
aAudioTaskQueue,
|
||||
aCallback);
|
||||
} else if (OpusDataDecoder::IsOpus(aConfig.mMimeType)) {
|
||||
m = new OpusDataDecoder(*aConfig.GetAsAudioInfo(),
|
||||
aAudioTaskQueue,
|
||||
aCallback);
|
||||
}
|
||||
|
||||
return m.forget();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,39 @@
|
||||
#if !defined(AgnosticDecoderModule_h_)
|
||||
#define AgnosticDecoderModule_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AgnosticDecoderModule : public PlatformDecoderModule {
|
||||
public:
|
||||
AgnosticDecoderModule() = default;
|
||||
virtual ~AgnosticDecoderModule() = default;
|
||||
|
||||
bool SupportsMimeType(const nsACString& aMimeType) override;
|
||||
|
||||
ConversionRequired
|
||||
DecoderNeedsConversion(const TrackInfo& aConfig) const override
|
||||
{
|
||||
return ConversionRequired::kNeedNone;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Decode thread.
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
CreateVideoDecoder(const VideoInfo& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
FlushableTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) override;
|
||||
|
||||
// Decode thread.
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
CreateAudioDecoder(const AudioInfo& aConfig,
|
||||
FlushableTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) override;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* AgnosticDecoderModule_h_ */
|
||||
@@ -251,11 +251,6 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SupportsSharedDecoders(const VideoInfo& aConfig) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
ConversionRequired
|
||||
DecoderNeedsConversion(const TrackInfo& aConfig) const override
|
||||
{
|
||||
@@ -264,27 +259,10 @@ public:
|
||||
|
||||
};
|
||||
|
||||
class AgnosticDecoderModule : public BlankDecoderModule {
|
||||
public:
|
||||
|
||||
bool SupportsMimeType(const nsACString& aMimeType) override
|
||||
{
|
||||
// This module does not support any decoders itself,
|
||||
// agnostic decoders are created in PlatformDecoderModule::CreateDecoder
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule()
|
||||
{
|
||||
nsRefPtr<PlatformDecoderModule> pdm = new BlankDecoderModule();
|
||||
return pdm.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<PlatformDecoderModule> CreateAgnosticDecoderModule()
|
||||
{
|
||||
nsRefPtr<PlatformDecoderModule> adm = new AgnosticDecoderModule();
|
||||
return adm.forget();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -12,13 +12,12 @@
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h> // For PRId64
|
||||
|
||||
#define OPUS_DEBUG(arg, ...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, \
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define OPUS_DEBUG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, \
|
||||
("OpusDataDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
extern PRLogModuleInfo* gMediaDecoderLog;
|
||||
|
||||
OpusDataDecoder::OpusDataDecoder(const AudioInfo& aConfig,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
|
||||
@@ -13,15 +13,14 @@
|
||||
#include <algorithm>
|
||||
|
||||
#undef LOG
|
||||
#define LOG(arg, ...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, ("VPXDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("VPXDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace gfx;
|
||||
using namespace layers;
|
||||
|
||||
extern PRLogModuleInfo* gMediaDecoderLog;
|
||||
|
||||
VPXDecoder::VPXDecoder(const VideoInfo& aConfig,
|
||||
ImageContainer* aImageContainer,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
|
||||
@@ -11,12 +11,11 @@
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
#undef LOG
|
||||
#define LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(type, msg) MOZ_LOG(GetPDMLog(), type, msg)
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
extern PRLogModuleInfo* gMediaDecoderLog;
|
||||
|
||||
ogg_packet InitVorbisPacket(const unsigned char* aData, size_t aLength,
|
||||
bool aBOS, bool aEOS,
|
||||
int64_t aGranulepos, int64_t aPacketNo)
|
||||
|
||||
@@ -33,6 +33,14 @@ public:
|
||||
|
||||
ConversionRequired
|
||||
DecoderNeedsConversion(const TrackInfo& aConfig) const override;
|
||||
|
||||
bool
|
||||
SupportsMimeType(const nsACString& aMimeType) override
|
||||
{
|
||||
// TODO properly.
|
||||
return aMimeType.EqualsLiteral("audio/mp4a-latm") ||
|
||||
aMimeType.EqualsLiteral("video/avc");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
#include "AppleATDecoder.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
PRLogModuleInfo* GetAppleMediaLog();
|
||||
#define LOG(...) MOZ_LOG(GetAppleMediaLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
#define FourCC2Str(n) ((char[5]){(char)(n >> 24), (char)(n >> 16), (char)(n >> 8), (char)(n), 0})
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
#include "nsCocoaFeatures.h"
|
||||
#include "nsDebug.h"
|
||||
|
||||
PRLogModuleInfo* GetAppleMediaLog();
|
||||
#define LOG(...) MOZ_LOG(GetAppleMediaLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
||||
@@ -15,14 +15,6 @@
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
PRLogModuleInfo* GetAppleMediaLog() {
|
||||
static PRLogModuleInfo* log = nullptr;
|
||||
if (!log) {
|
||||
log = PR_NewLogModule("AppleMedia");
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
bool AppleDecoderModule::sInitialized = false;
|
||||
@@ -119,7 +111,9 @@ bool
|
||||
AppleDecoderModule::SupportsMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return aMimeType.EqualsLiteral("audio/mpeg") ||
|
||||
PlatformDecoderModule::SupportsMimeType(aMimeType);
|
||||
aMimeType.EqualsLiteral("audio/mp4a-latm") ||
|
||||
aMimeType.EqualsLiteral("video/mp4") ||
|
||||
aMimeType.EqualsLiteral("video/avc");
|
||||
}
|
||||
|
||||
PlatformDecoderModule::ConversionRequired
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#include <algorithm>
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
PRLogModuleInfo* GetAppleMediaLog();
|
||||
#define LOG(...) MOZ_LOG(GetAppleMediaLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
//#define LOG_MEDIA_SHA1
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include "MainThreadUtils.h"
|
||||
#include "nsDebug.h"
|
||||
|
||||
PRLogModuleInfo* GetAppleMediaLog();
|
||||
#define LOG(...) MOZ_LOG(GetAppleMediaLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
#include "VideoUtils.h"
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
PRLogModuleInfo* GetAppleMediaLog();
|
||||
#define LOG(...) MOZ_LOG(GetAppleMediaLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
//#define LOG_MEDIA_SHA1
|
||||
|
||||
#ifdef LOG_MEDIA_SHA1
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "nsDebug.h"
|
||||
|
||||
PRLogModuleInfo* GetAppleMediaLog();
|
||||
#define LOG(...) MOZ_LOG(GetAppleMediaLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "FFmpegAudioDecoder.h"
|
||||
#include "FFmpegH264Decoder.h"
|
||||
#include "FFmpegRuntimeLinker.h"
|
||||
|
||||
namespace mozilla
|
||||
{
|
||||
@@ -22,6 +23,7 @@ public:
|
||||
Create()
|
||||
{
|
||||
nsRefPtr<PlatformDecoderModule> pdm = new FFmpegDecoderModule();
|
||||
|
||||
return pdm.forget();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
PRLogModuleInfo* GetFFmpegDecoderLog()
|
||||
{
|
||||
static PRLogModuleInfo* sFFmpegDecoderLog = nullptr;
|
||||
if (!sFFmpegDecoderLog) {
|
||||
sFFmpegDecoderLog = PR_NewLogModule("FFmpegDecoderModule");
|
||||
}
|
||||
return sFFmpegDecoderLog;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#ifndef __FFmpegLog_h__
|
||||
#define __FFmpegLog_h__
|
||||
|
||||
extern PRLogModuleInfo* GetFFmpegDecoderLog();
|
||||
#define FFMPEG_LOG(...) MOZ_LOG(GetFFmpegDecoderLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define FFMPEG_LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
#endif // __FFmpegLog_h__
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
#include <android/log.h>
|
||||
#define GADM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkAudioDecoderManager", __VA_ARGS__)
|
||||
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
#define READ_OUTPUT_BUFFER_TIMEOUT_US 3000
|
||||
|
||||
using namespace android;
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
#include <android/log.h>
|
||||
#define GMDD_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkMediaDataDecoder", __VA_ARGS__)
|
||||
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
using namespace android;
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
#include <android/log.h>
|
||||
#define GVDM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkVideoDecoderManager", __VA_ARGS__)
|
||||
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
using namespace mozilla::layers;
|
||||
using namespace android;
|
||||
typedef android::MediaCodecProxy MediaCodecProxy;
|
||||
|
||||
@@ -5,21 +5,25 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS += [
|
||||
'agnostic/AgnosticDecoderModule.h',
|
||||
'agnostic/OpusDecoder.h',
|
||||
'agnostic/VorbisDecoder.h',
|
||||
'agnostic/VPXDecoder.h',
|
||||
'PDMFactory.h',
|
||||
'PlatformDecoderModule.h',
|
||||
'SharedDecoderManager.h',
|
||||
'wrappers/FuzzingWrapper.h',
|
||||
'wrappers/H264Converter.h'
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'agnostic/AgnosticDecoderModule.cpp',
|
||||
'agnostic/BlankDecoderModule.cpp',
|
||||
'agnostic/OpusDecoder.cpp',
|
||||
'agnostic/VorbisDecoder.cpp',
|
||||
'agnostic/VPXDecoder.cpp',
|
||||
'PDMFactory.cpp',
|
||||
'PlatformDecoderModule.cpp',
|
||||
'SharedDecoderManager.cpp',
|
||||
'wrappers/FuzzingWrapper.cpp',
|
||||
'wrappers/H264Converter.cpp'
|
||||
]
|
||||
|
||||
@@ -33,7 +37,6 @@ if CONFIG['MOZ_FFMPEG']:
|
||||
'ffmpeg/FFmpegRuntimeLinker.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'ffmpeg/FFmpegLog.cpp',
|
||||
'ffmpeg/FFmpegRuntimeLinker.cpp',
|
||||
]
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
#include "WMFUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
using mozilla::layers::Image;
|
||||
using mozilla::layers::IMFYCbCrImage;
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "FuzzingWrapper.h"
|
||||
|
||||
PRLogModuleInfo* GetFuzzingWrapperLog() {
|
||||
static PRLogModuleInfo* log = nullptr;
|
||||
if (!log) {
|
||||
log = PR_NewLogModule("MediaFuzzingWrapper");
|
||||
}
|
||||
return log;
|
||||
}
|
||||
#define DFW_LOGD(arg, ...) MOZ_LOG(GetFuzzingWrapperLog(), mozilla::LogLevel::Debug, ("DecoderFuzzingWrapper(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
#define DFW_LOGV(arg, ...) MOZ_LOG(GetFuzzingWrapperLog(), mozilla::LogLevel::Verbose, ("DecoderFuzzingWrapper(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
#define CFW_LOGD(arg, ...) MOZ_LOG(GetFuzzingWrapperLog(), mozilla::LogLevel::Debug, ("DecoderCallbackFuzzingWrapper(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
#define CFW_LOGV(arg, ...) MOZ_LOG(GetFuzzingWrapperLog(), mozilla::LogLevel::Verbose, ("DecoderCallbackFuzzingWrapper(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
DecoderFuzzingWrapper::DecoderFuzzingWrapper(
|
||||
already_AddRefed<MediaDataDecoder> aDecoder,
|
||||
already_AddRefed<DecoderCallbackFuzzingWrapper> aCallbackWrapper)
|
||||
: mDecoder(aDecoder)
|
||||
, mCallbackWrapper(aCallbackWrapper)
|
||||
{
|
||||
DFW_LOGV("aDecoder=%p aCallbackWrapper=%p", mDecoder.get(), mCallbackWrapper.get());
|
||||
}
|
||||
|
||||
DecoderFuzzingWrapper::~DecoderFuzzingWrapper()
|
||||
{
|
||||
DFW_LOGV("");
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
DecoderFuzzingWrapper::Init()
|
||||
{
|
||||
DFW_LOGV("");
|
||||
MOZ_ASSERT(mDecoder);
|
||||
return mDecoder->Init();
|
||||
}
|
||||
|
||||
nsresult
|
||||
DecoderFuzzingWrapper::Input(MediaRawData* aData)
|
||||
{
|
||||
DFW_LOGV("aData.mTime=%lld", aData->mTime);
|
||||
MOZ_ASSERT(mDecoder);
|
||||
return mDecoder->Input(aData);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DecoderFuzzingWrapper::Flush()
|
||||
{
|
||||
DFW_LOGV("");
|
||||
MOZ_ASSERT(mDecoder);
|
||||
// Flush may output some frames (though unlikely).
|
||||
// Flush may block a bit, it's ok if we output some frames in the meantime.
|
||||
nsresult result = mDecoder->Flush();
|
||||
// Clear any delayed output we may have.
|
||||
mCallbackWrapper->ClearDelayedOutput();
|
||||
return result;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DecoderFuzzingWrapper::Drain()
|
||||
{
|
||||
DFW_LOGV("");
|
||||
MOZ_ASSERT(mDecoder);
|
||||
// Note: The decoder should callback DrainComplete(), we'll drain the
|
||||
// delayed output (if any) then.
|
||||
return mDecoder->Drain();
|
||||
}
|
||||
|
||||
nsresult
|
||||
DecoderFuzzingWrapper::Shutdown()
|
||||
{
|
||||
DFW_LOGV("");
|
||||
MOZ_ASSERT(mDecoder);
|
||||
// Both shutdowns below may block a bit.
|
||||
nsresult result = mDecoder->Shutdown();
|
||||
mCallbackWrapper->Shutdown();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
DecoderFuzzingWrapper::IsHardwareAccelerated(nsACString& aFailureReason) const
|
||||
{
|
||||
DFW_LOGV("");
|
||||
MOZ_ASSERT(mDecoder);
|
||||
return mDecoder->IsHardwareAccelerated(aFailureReason);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DecoderFuzzingWrapper::ConfigurationChanged(const TrackInfo& aConfig)
|
||||
{
|
||||
DFW_LOGV("");
|
||||
MOZ_ASSERT(mDecoder);
|
||||
return mDecoder->ConfigurationChanged(aConfig);
|
||||
}
|
||||
|
||||
|
||||
DecoderCallbackFuzzingWrapper::DecoderCallbackFuzzingWrapper(MediaDataDecoderCallback* aCallback)
|
||||
: mCallback(aCallback)
|
||||
, mDontDelayInputExhausted(false)
|
||||
, mDraining(false)
|
||||
, mTaskQueue(new TaskQueue(SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaFuzzingWrapper"), 1)))
|
||||
{
|
||||
CFW_LOGV("aCallback=%p", aCallback);
|
||||
}
|
||||
|
||||
DecoderCallbackFuzzingWrapper::~DecoderCallbackFuzzingWrapper()
|
||||
{
|
||||
CFW_LOGV("");
|
||||
}
|
||||
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::SetVideoOutputMinimumInterval(
|
||||
TimeDuration aFrameOutputMinimumInterval)
|
||||
{
|
||||
CFW_LOGD("aFrameOutputMinimumInterval=%fms",
|
||||
aFrameOutputMinimumInterval.ToMilliseconds());
|
||||
mFrameOutputMinimumInterval = aFrameOutputMinimumInterval;
|
||||
}
|
||||
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::SetDontDelayInputExhausted(
|
||||
bool aDontDelayInputExhausted)
|
||||
{
|
||||
CFW_LOGD("aDontDelayInputExhausted=%d",
|
||||
aDontDelayInputExhausted);
|
||||
mDontDelayInputExhausted = aDontDelayInputExhausted;
|
||||
}
|
||||
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::Output(MediaData* aData)
|
||||
{
|
||||
if (!mTaskQueue->IsCurrentThreadIn()) {
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethodWithArg<StorensRefPtrPassByPtr<MediaData>>(
|
||||
this, &DecoderCallbackFuzzingWrapper::Output, aData);
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
return;
|
||||
}
|
||||
CFW_LOGV("aData.mTime=%lld", aData->mTime);
|
||||
MOZ_ASSERT(mCallback);
|
||||
if (mFrameOutputMinimumInterval) {
|
||||
if (!mPreviousOutput.IsNull()) {
|
||||
if (!mDelayedOutput.empty()) {
|
||||
// We already have some delayed frames, just add this one to the queue.
|
||||
mDelayedOutput.push_back(MakePair<nsRefPtr<MediaData>, bool>(aData, false));
|
||||
CFW_LOGD("delaying output of sample@%lld, total queued:%d",
|
||||
aData->mTime, int(mDelayedOutput.size()));
|
||||
return;
|
||||
}
|
||||
if (TimeStamp::Now() < mPreviousOutput + mFrameOutputMinimumInterval) {
|
||||
// Frame arriving too soon after the previous one, start queuing.
|
||||
mDelayedOutput.push_back(MakePair<nsRefPtr<MediaData>, bool>(aData, false));
|
||||
CFW_LOGD("delaying output of sample@%lld, first queued", aData->mTime);
|
||||
if (!mDelayedOutputTimer) {
|
||||
mDelayedOutputTimer = new MediaTimer();
|
||||
}
|
||||
ScheduleOutputDelayedFrame();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If we're here, we're going to actually output a frame -> Record time.
|
||||
mPreviousOutput = TimeStamp::Now();
|
||||
}
|
||||
|
||||
// Passing the data straight through, no need to dispatch to another queue,
|
||||
// callback should deal with that.
|
||||
mCallback->Output(aData);
|
||||
}
|
||||
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::Error()
|
||||
{
|
||||
if (!mTaskQueue->IsCurrentThreadIn()) {
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::Error);
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
return;
|
||||
}
|
||||
CFW_LOGV("");
|
||||
MOZ_ASSERT(mCallback);
|
||||
ClearDelayedOutput();
|
||||
mCallback->Error();
|
||||
}
|
||||
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::InputExhausted()
|
||||
{
|
||||
if (!mTaskQueue->IsCurrentThreadIn()) {
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::InputExhausted);
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
return;
|
||||
}
|
||||
if (!mDontDelayInputExhausted && !mDelayedOutput.empty()) {
|
||||
MediaDataAndInputExhausted& last = mDelayedOutput.back();
|
||||
CFW_LOGD("InputExhausted delayed until after output of sample@%lld",
|
||||
last.first()->mTime);
|
||||
last.second() = true;
|
||||
return;
|
||||
}
|
||||
CFW_LOGV("");
|
||||
MOZ_ASSERT(mCallback);
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::DrainComplete()
|
||||
{
|
||||
if (!mTaskQueue->IsCurrentThreadIn()) {
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::DrainComplete);
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mCallback);
|
||||
if (mDelayedOutput.empty()) {
|
||||
// No queued output -> Draining is complete now.
|
||||
CFW_LOGV("No delayed output -> DrainComplete now");
|
||||
mCallback->DrainComplete();
|
||||
} else {
|
||||
// Queued output waiting -> Make sure we call DrainComplete when it's empty.
|
||||
CFW_LOGD("Delayed output -> DrainComplete later");
|
||||
mDraining = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::ReleaseMediaResources()
|
||||
{
|
||||
if (!mTaskQueue->IsCurrentThreadIn()) {
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::ReleaseMediaResources);
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
return;
|
||||
}
|
||||
CFW_LOGV("");
|
||||
MOZ_ASSERT(mCallback);
|
||||
mCallback->ReleaseMediaResources();
|
||||
}
|
||||
|
||||
bool
|
||||
DecoderCallbackFuzzingWrapper::OnReaderTaskQueue()
|
||||
{
|
||||
CFW_LOGV("");
|
||||
MOZ_ASSERT(mCallback);
|
||||
return mCallback->OnReaderTaskQueue();
|
||||
}
|
||||
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::ScheduleOutputDelayedFrame()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
nsRefPtr<DecoderCallbackFuzzingWrapper> self = this;
|
||||
mDelayedOutputTimer->WaitUntil(
|
||||
mPreviousOutput + mFrameOutputMinimumInterval,
|
||||
__func__)
|
||||
->Then(mTaskQueue, __func__,
|
||||
[self] () -> void { self->OutputDelayedFrame(); },
|
||||
[self] () -> void { self->OutputDelayedFrame(); });
|
||||
}
|
||||
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::OutputDelayedFrame()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
if (mDelayedOutput.empty()) {
|
||||
if (mDraining) {
|
||||
// No more output, and we were draining -> Send DrainComplete.
|
||||
mDraining = false;
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
return;
|
||||
}
|
||||
MediaDataAndInputExhausted& data = mDelayedOutput.front();
|
||||
CFW_LOGD("Outputting delayed sample@%lld, remaining:%d",
|
||||
data.first()->mTime, int(mDelayedOutput.size() - 1));
|
||||
mPreviousOutput = TimeStamp::Now();
|
||||
mCallback->Output(data.first());
|
||||
if (data.second()) {
|
||||
CFW_LOGD("InputExhausted after delayed sample@%lld", data.first()->mTime);
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
mDelayedOutput.pop_front();
|
||||
if (!mDelayedOutput.empty()) {
|
||||
// More output -> Send it later.
|
||||
ScheduleOutputDelayedFrame();
|
||||
} else if (mDraining) {
|
||||
// No more output, and we were draining -> Send DrainComplete.
|
||||
CFW_LOGD("DrainComplete");
|
||||
mDraining = false;
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::ClearDelayedOutput()
|
||||
{
|
||||
if (!mTaskQueue->IsCurrentThreadIn()) {
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::ClearDelayedOutput);
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
return;
|
||||
}
|
||||
mDelayedOutputTimer = nullptr;
|
||||
mDelayedOutput.clear();
|
||||
}
|
||||
|
||||
void
|
||||
DecoderCallbackFuzzingWrapper::Shutdown()
|
||||
{
|
||||
DFW_LOGV("Shutting down mTaskQueue");
|
||||
mTaskQueue->BeginShutdown();
|
||||
mTaskQueue->AwaitIdle();
|
||||
DFW_LOGV("mTaskQueue shut down");
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,120 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(FuzzingWrapper_h_)
|
||||
#define FuzzingWrapper_h_
|
||||
|
||||
#include "mozilla/Pair.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
#include <deque>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Fuzzing wrapper for media decoders.
|
||||
//
|
||||
// DecoderFuzzingWrapper owns the DecoderCallbackFuzzingWrapper, and inserts
|
||||
// itself between the reader and the decoder.
|
||||
// DecoderCallbackFuzzingWrapper inserts itself between a decoder and its
|
||||
// callback.
|
||||
// Together they are used to introduce some fuzzing, (e.g. delay output).
|
||||
//
|
||||
// Normally:
|
||||
// ====================================>
|
||||
// reader decoder
|
||||
// <------------------------------------
|
||||
//
|
||||
// With fuzzing:
|
||||
// ======> DecoderFuzzingWrapper ======>
|
||||
// reader v decoder
|
||||
// <-- DecoderCallbackFuzzingWrapper <--
|
||||
//
|
||||
// Creation order should be:
|
||||
// 1. Create DecoderCallbackFuzzingWrapper, give the expected callback target.
|
||||
// 2. Create actual decoder, give DecoderCallbackFuzzingWrapper as callback.
|
||||
// 3. Create DecoderFuzzingWrapper, give decoder and DecoderCallbackFuzzingWrapper.
|
||||
// DecoderFuzzingWrapper is what the reader sees as decoder, it owns the
|
||||
// real decoder and the DecoderCallbackFuzzingWrapper.
|
||||
|
||||
class DecoderCallbackFuzzingWrapper : public MediaDataDecoderCallback
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecoderCallbackFuzzingWrapper)
|
||||
|
||||
explicit DecoderCallbackFuzzingWrapper(MediaDataDecoderCallback* aCallback);
|
||||
|
||||
// Enforce a minimum interval between output frames (i.e., limit frame rate).
|
||||
// Of course, if the decoder is even slower, this won't have any effect.
|
||||
void SetVideoOutputMinimumInterval(TimeDuration aFrameOutputMinimumInterval);
|
||||
// If false (default), if frames are delayed, any InputExhausted is delayed to
|
||||
// be later sent after the corresponding delayed frame.
|
||||
// If true, InputExhausted are passed through immediately; This could result
|
||||
// in lots of frames being decoded and queued for delayed output!
|
||||
void SetDontDelayInputExhausted(bool aDontDelayInputExhausted);
|
||||
|
||||
private:
|
||||
virtual ~DecoderCallbackFuzzingWrapper();
|
||||
|
||||
// MediaDataDecoderCallback implementation.
|
||||
void Output(MediaData* aData) override;
|
||||
void Error() override;
|
||||
void InputExhausted() override;
|
||||
void DrainComplete() override;
|
||||
void ReleaseMediaResources() override;
|
||||
bool OnReaderTaskQueue() override;
|
||||
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
|
||||
// Settings for minimum frame output interval & InputExhausted,
|
||||
// should be set during init and then only read on mTaskQueue.
|
||||
TimeDuration mFrameOutputMinimumInterval;
|
||||
bool mDontDelayInputExhausted;
|
||||
// Members for minimum frame output interval & InputExhausted,
|
||||
// should only be accessed on mTaskQueue.
|
||||
TimeStamp mPreviousOutput;
|
||||
// First member is the frame to be delayed.
|
||||
// Second member is true if an 'InputExhausted' arrived after that frame; in
|
||||
// which case an InputExhausted will be sent after finally outputting the frame.
|
||||
typedef Pair<nsRefPtr<MediaData>, bool> MediaDataAndInputExhausted;
|
||||
std::deque<MediaDataAndInputExhausted> mDelayedOutput;
|
||||
nsRefPtr<MediaTimer> mDelayedOutputTimer;
|
||||
// If draining, a 'DrainComplete' will be sent after all delayed frames have
|
||||
// been output.
|
||||
bool mDraining;
|
||||
// All callbacks are redirected through this task queue, both to avoid locking
|
||||
// and to have a consistent sequencing of callbacks.
|
||||
nsRefPtr<TaskQueue> mTaskQueue;
|
||||
void ScheduleOutputDelayedFrame();
|
||||
void OutputDelayedFrame();
|
||||
public: // public for the benefit of DecoderFuzzingWrapper.
|
||||
void ClearDelayedOutput();
|
||||
void Shutdown();
|
||||
};
|
||||
|
||||
class DecoderFuzzingWrapper : public MediaDataDecoder
|
||||
{
|
||||
public:
|
||||
DecoderFuzzingWrapper(already_AddRefed<MediaDataDecoder> aDecoder,
|
||||
already_AddRefed<DecoderCallbackFuzzingWrapper> aCallbackWrapper);
|
||||
virtual ~DecoderFuzzingWrapper();
|
||||
|
||||
private:
|
||||
// MediaDataDecoder implementation.
|
||||
nsRefPtr<InitPromise> Init() override;
|
||||
nsresult Input(MediaRawData* aSample) override;
|
||||
nsresult Flush() override;
|
||||
nsresult Drain() override;
|
||||
nsresult Shutdown() override;
|
||||
bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
|
||||
nsresult ConfigurationChanged(const TrackInfo& aConfig) override;
|
||||
|
||||
nsRefPtr<MediaDataDecoder> mDecoder;
|
||||
nsRefPtr<DecoderCallbackFuzzingWrapper> mCallbackWrapper;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -29,7 +29,6 @@ H264Converter::H264Converter(PlatformDecoderModule* aPDM,
|
||||
, mCallback(aCallback)
|
||||
, mDecoder(nullptr)
|
||||
, mNeedAVCC(aPDM->DecoderNeedsConversion(aConfig) == PlatformDecoderModule::kNeedAVCC)
|
||||
, mDecoderInitializing(false)
|
||||
, mLastError(NS_OK)
|
||||
{
|
||||
CreateDecoder();
|
||||
@@ -46,8 +45,9 @@ H264Converter::Init()
|
||||
return mDecoder->Init();
|
||||
}
|
||||
|
||||
return MediaDataDecoder::InitPromise::CreateAndReject(
|
||||
MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
|
||||
// We haven't been able to initialize a decoder due to a missing SPS/PPS.
|
||||
return MediaDataDecoder::InitPromise::CreateAndResolve(
|
||||
TrackType::kVideoTrack, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -63,7 +63,7 @@ H264Converter::Input(MediaRawData* aSample)
|
||||
}
|
||||
}
|
||||
|
||||
if (mDecoderInitializing) {
|
||||
if (mInitPromiseRequest.Exists()) {
|
||||
mMediaRawSamples.AppendElement(aSample);
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -163,14 +163,13 @@ H264Converter::CreateDecoderAndInit(MediaRawData* aSample)
|
||||
nsresult rv = CreateDecoder();
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mDecoderInitializing = true;
|
||||
// Queue the incoming sample.
|
||||
mMediaRawSamples.AppendElement(aSample);
|
||||
|
||||
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,
|
||||
->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__, this,
|
||||
&H264Converter::OnDecoderInitDone,
|
||||
&H264Converter::OnDecoderInitFailed));
|
||||
}
|
||||
@@ -187,7 +186,6 @@ H264Converter::OnDecoderInitDone(const TrackType aTrackType)
|
||||
}
|
||||
}
|
||||
mMediaRawSamples.Clear();
|
||||
mDecoderInitializing = false;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -62,7 +62,6 @@ private:
|
||||
nsRefPtr<MediaDataDecoder> mDecoder;
|
||||
MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
|
||||
bool mNeedAVCC;
|
||||
bool mDecoderInitializing;
|
||||
nsresult mLastError;
|
||||
};
|
||||
|
||||
|
||||
@@ -26,11 +26,6 @@ RawReader::~RawReader()
|
||||
MOZ_COUNT_DTOR(RawReader);
|
||||
}
|
||||
|
||||
nsresult RawReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult RawReader::ResetDecode()
|
||||
{
|
||||
mCurrentFrame = 0;
|
||||
|
||||
@@ -20,7 +20,6 @@ protected:
|
||||
~RawReader();
|
||||
|
||||
public:
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor) override;
|
||||
virtual nsresult ResetDecode() override;
|
||||
virtual bool DecodeAudioData() override;
|
||||
|
||||
|
||||
@@ -116,11 +116,6 @@ WaveReader::~WaveReader()
|
||||
MOZ_COUNT_DTOR(WaveReader);
|
||||
}
|
||||
|
||||
nsresult WaveReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult WaveReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
{
|
||||
|
||||
@@ -22,7 +22,6 @@ protected:
|
||||
~WaveReader();
|
||||
|
||||
public:
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor) override;
|
||||
virtual bool DecodeAudioData() override;
|
||||
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold) override;
|
||||
|
||||
@@ -206,7 +206,7 @@ MediaDecodeTask::CreateReader()
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = mDecoderReader->Init(nullptr);
|
||||
nsresult rv = mDecoderReader->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -47,14 +47,14 @@ ogg_packet InitOggPacket(const unsigned char* aData, size_t aLength,
|
||||
class VorbisDecoder : public WebMAudioDecoder
|
||||
{
|
||||
public:
|
||||
nsRefPtr<InitPromise> Init() override;
|
||||
void Shutdown();
|
||||
nsresult ResetDecode();
|
||||
nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
|
||||
nsresult FinishInit(AudioInfo& aInfo);
|
||||
nsresult Init() override;
|
||||
void Shutdown() override;
|
||||
nsresult ResetDecode() override;
|
||||
nsresult DecodeHeader(const unsigned char* aData, size_t aLength) override;
|
||||
nsresult FinishInit(AudioInfo& aInfo) override;
|
||||
bool Decode(const unsigned char* aData, size_t aLength,
|
||||
int64_t aOffset, uint64_t aTstampUsecs,
|
||||
int64_t aDiscardPadding, int32_t* aTotalFrames);
|
||||
int64_t aDiscardPadding, int32_t* aTotalFrames) override;
|
||||
explicit VorbisDecoder(WebMReader* aReader);
|
||||
~VorbisDecoder();
|
||||
private:
|
||||
@@ -94,14 +94,14 @@ VorbisDecoder::Shutdown()
|
||||
mReader = nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<InitPromise>
|
||||
nsresult
|
||||
VorbisDecoder::Init()
|
||||
{
|
||||
vorbis_info_init(&mVorbisInfo);
|
||||
vorbis_comment_init(&mVorbisComment);
|
||||
PodZero(&mVorbisDsp);
|
||||
PodZero(&mVorbisBlock);
|
||||
return InitPromise::CreateAndResolve(TrackType::kAudioTrack, __func__);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -229,14 +229,14 @@ VorbisDecoder::Decode(const unsigned char* aData, size_t aLength,
|
||||
class OpusDecoder : public WebMAudioDecoder
|
||||
{
|
||||
public:
|
||||
nsRefPtr<InitPromise> Init() override;
|
||||
void Shutdown();
|
||||
nsresult ResetDecode();
|
||||
nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
|
||||
nsresult Init() override;
|
||||
void Shutdown() override;
|
||||
nsresult ResetDecode() override;
|
||||
nsresult DecodeHeader(const unsigned char* aData, size_t aLength) override;
|
||||
nsresult FinishInit(AudioInfo& aInfo);
|
||||
bool Decode(const unsigned char* aData, size_t aLength,
|
||||
int64_t aOffset, uint64_t aTstampUsecs,
|
||||
int64_t aDiscardPadding, int32_t* aTotalFrames);
|
||||
int64_t aDiscardPadding, int32_t* aTotalFrames) override;
|
||||
explicit OpusDecoder(WebMReader* aReader);
|
||||
~OpusDecoder();
|
||||
private:
|
||||
@@ -277,10 +277,10 @@ OpusDecoder::Shutdown()
|
||||
mReader = nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<InitPromise>
|
||||
nsresult
|
||||
OpusDecoder::Init()
|
||||
{
|
||||
return InitPromise::CreateAndResolve(TrackType::kAudioTrack, __func__);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -1,443 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "IntelWebMVideoDecoder.h"
|
||||
|
||||
#include "mozilla/TaskQueue.h"
|
||||
|
||||
#include "gfx2DGlue.h"
|
||||
#include "Layers.h"
|
||||
#include "MediaResource.h"
|
||||
#include "mozilla/dom/HTMLMediaElement.h"
|
||||
#include "nsError.h"
|
||||
#include "mozilla/SharedThreadPool.h"
|
||||
#include "VorbisUtils.h"
|
||||
#include "nestegg/nestegg.h"
|
||||
|
||||
#define VPX_DONT_DEFINE_STDINT_TYPES
|
||||
#include "vpx/vp8dx.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
|
||||
#undef LOG
|
||||
PRLogModuleInfo* GetDemuxerLog();
|
||||
#define LOG(...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using layers::Image;
|
||||
using layers::LayerManager;
|
||||
using layers::LayersBackend;
|
||||
|
||||
class VP8Sample : public MediaRawData
|
||||
{
|
||||
public:
|
||||
VP8Sample(int64_t aTimestamp,
|
||||
int64_t aDuration,
|
||||
int64_t aByteOffset,
|
||||
uint8_t* aData,
|
||||
size_t aSize,
|
||||
bool aSyncPoint)
|
||||
: MediaRawData(aData, aSize)
|
||||
{
|
||||
mTimecode = -1;
|
||||
mTime = aTimestamp;
|
||||
mDuration = aDuration;
|
||||
mOffset = aByteOffset;
|
||||
mKeyframe = aSyncPoint;
|
||||
}
|
||||
};
|
||||
|
||||
IntelWebMVideoDecoder::IntelWebMVideoDecoder(WebMReader* aReader)
|
||||
: WebMVideoDecoder()
|
||||
, mReader(aReader)
|
||||
, mMonitor("IntelWebMVideoDecoder")
|
||||
, mNumSamplesInput(0)
|
||||
, mNumSamplesOutput(0)
|
||||
, mDecodeAhead(2)
|
||||
, mInputExhausted(false)
|
||||
, mDrainComplete(false)
|
||||
, mError(false)
|
||||
, mEOS(false)
|
||||
, mIsFlushing(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(IntelWebMVideoDecoder);
|
||||
}
|
||||
|
||||
IntelWebMVideoDecoder::~IntelWebMVideoDecoder()
|
||||
{
|
||||
MOZ_COUNT_DTOR(IntelWebMVideoDecoder);
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
IntelWebMVideoDecoder::Shutdown()
|
||||
{
|
||||
if (mMediaDataDecoder) {
|
||||
Flush();
|
||||
mMediaDataDecoder->Shutdown();
|
||||
mMediaDataDecoder = nullptr;
|
||||
}
|
||||
|
||||
mTaskQueue = nullptr;
|
||||
|
||||
mQueuedVideoSample = nullptr;
|
||||
mReader = nullptr;
|
||||
}
|
||||
|
||||
/* static */
|
||||
WebMVideoDecoder*
|
||||
IntelWebMVideoDecoder::Create(WebMReader* aReader)
|
||||
{
|
||||
nsAutoPtr<IntelWebMVideoDecoder> decoder(new IntelWebMVideoDecoder(aReader));
|
||||
|
||||
decoder->mTaskQueue = aReader->GetVideoTaskQueue();
|
||||
NS_ENSURE_TRUE(decoder->mTaskQueue, nullptr);
|
||||
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
IntelWebMVideoDecoder::IsSupportedVideoMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return (aMimeType.EqualsLiteral("video/webm; codecs=vp8") ||
|
||||
aMimeType.EqualsLiteral("video/webm; codecs=vp9")) &&
|
||||
mPlatform->SupportsMimeType(aMimeType);
|
||||
}
|
||||
|
||||
nsRefPtr<InitPromise>
|
||||
IntelWebMVideoDecoder::Init(unsigned int aWidth, unsigned int aHeight)
|
||||
{
|
||||
mPlatform = PlatformDecoderModule::Create();
|
||||
if (!mPlatform) {
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
mDecoderConfig = new VideoInfo();
|
||||
mDecoderConfig->mDuration = 0;
|
||||
mDecoderConfig->mDisplay.width = aWidth;
|
||||
mDecoderConfig->mDisplay.height = aHeight;
|
||||
|
||||
switch (mReader->GetVideoCodec()) {
|
||||
case NESTEGG_CODEC_VP8:
|
||||
mDecoderConfig->mMimeType = "video/webm; codecs=vp8";
|
||||
break;
|
||||
case NESTEGG_CODEC_VP9:
|
||||
mDecoderConfig->mMimeType = "video/webm; codecs=vp9";
|
||||
break;
|
||||
default:
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
const VideoInfo& video = *mDecoderConfig;
|
||||
if (!IsSupportedVideoMimeType(video.mMimeType)) {
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
mMediaDataDecoder =
|
||||
mPlatform->CreateDecoder(video,
|
||||
mTaskQueue,
|
||||
this,
|
||||
mReader->GetLayersBackendType(),
|
||||
mReader->GetDecoder()->GetImageContainer());
|
||||
if (!mMediaDataDecoder) {
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
|
||||
return mMediaDataDecoder->Init();
|
||||
}
|
||||
|
||||
bool
|
||||
IntelWebMVideoDecoder::Demux(nsRefPtr<VP8Sample>& aSample, bool* aEOS)
|
||||
{
|
||||
nsRefPtr<NesteggPacketHolder> holder(mReader->NextPacket(WebMReader::VIDEO));
|
||||
if (!holder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nestegg_packet* packet = holder->Packet();
|
||||
unsigned int track = 0;
|
||||
int r = nestegg_packet_track(packet, &track);
|
||||
if (r == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int count = 0;
|
||||
r = nestegg_packet_count(packet, &count);
|
||||
if (r == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
NS_WARNING("Packet contains more than one video frame");
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t tstamp = holder->Timestamp();
|
||||
|
||||
// The end time of this frame is the start time of the next frame. Fetch
|
||||
// the timestamp of the next packet for this track. If we've reached the
|
||||
// end of the resource, use the file's duration as the end time of this
|
||||
// video frame.
|
||||
int64_t next_tstamp = 0;
|
||||
nsRefPtr<NesteggPacketHolder> next_holder(mReader->NextPacket(WebMReader::VIDEO));
|
||||
if (next_holder) {
|
||||
next_tstamp = holder->Timestamp();
|
||||
mReader->PushVideoPacket(next_holder);
|
||||
} else {
|
||||
next_tstamp = tstamp;
|
||||
next_tstamp += tstamp - mReader->GetLastVideoFrameTime();
|
||||
}
|
||||
mReader->SetLastVideoFrameTime(tstamp);
|
||||
|
||||
unsigned char* data;
|
||||
size_t length;
|
||||
r = nestegg_packet_data(packet, 0, &data, &length);
|
||||
if (r == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vpx_codec_stream_info_t si;
|
||||
memset(&si, 0, sizeof(si));
|
||||
si.sz = sizeof(si);
|
||||
if (mReader->GetVideoCodec() == NESTEGG_CODEC_VP8) {
|
||||
vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), data, length, &si);
|
||||
} else if (mReader->GetVideoCodec() == NESTEGG_CODEC_VP9) {
|
||||
vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), data, length, &si);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mPlatform && mMediaDataDecoder);
|
||||
|
||||
aSample = new VP8Sample(tstamp,
|
||||
next_tstamp - tstamp,
|
||||
0,
|
||||
data,
|
||||
length,
|
||||
si.is_kf);
|
||||
if (!aSample->Data()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IntelWebMVideoDecoder::Decode()
|
||||
{
|
||||
MOZ_ASSERT(mMediaDataDecoder);
|
||||
|
||||
mMonitor.Lock();
|
||||
uint64_t prevNumFramesOutput = mNumSamplesOutput;
|
||||
while (prevNumFramesOutput == mNumSamplesOutput) {
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
if (mError) {
|
||||
// Decode error!
|
||||
mMonitor.Unlock();
|
||||
return false;
|
||||
}
|
||||
while (prevNumFramesOutput == mNumSamplesOutput &&
|
||||
(mInputExhausted ||
|
||||
(mNumSamplesInput - mNumSamplesOutput) < mDecodeAhead) &&
|
||||
!mEOS) {
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
mMonitor.Unlock();
|
||||
nsRefPtr<VP8Sample> compressed(PopSample());
|
||||
if (!compressed) {
|
||||
// EOS, or error. Let the state machine know there are no more
|
||||
// frames coming.
|
||||
LOG("Draining Video");
|
||||
mMonitor.Lock();
|
||||
MOZ_ASSERT(!mEOS);
|
||||
mEOS = true;
|
||||
MOZ_ASSERT(!mDrainComplete);
|
||||
mDrainComplete = false;
|
||||
mMonitor.Unlock();
|
||||
mMediaDataDecoder->Drain();
|
||||
} else {
|
||||
#ifdef LOG_SAMPLE_DECODE
|
||||
LOG("PopSample %s time=%lld dur=%lld", TrackTypeToStr(aTrack),
|
||||
compressed->mTime, compressed->mDuration);
|
||||
#endif
|
||||
mMonitor.Lock();
|
||||
mDrainComplete = false;
|
||||
mInputExhausted = false;
|
||||
mNumSamplesInput++;
|
||||
mMonitor.Unlock();
|
||||
if (NS_FAILED(mMediaDataDecoder->Input(compressed))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
mMonitor.Lock();
|
||||
}
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
while (!mError &&
|
||||
prevNumFramesOutput == mNumSamplesOutput &&
|
||||
(!mInputExhausted || mEOS) &&
|
||||
!mDrainComplete) {
|
||||
mMonitor.Wait();
|
||||
}
|
||||
if (mError ||
|
||||
(mEOS && mDrainComplete)) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
bool rv = !(mEOS || mError);
|
||||
mMonitor.Unlock();
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
IntelWebMVideoDecoder::SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& aParsed)
|
||||
{
|
||||
MOZ_ASSERT(mReader->GetDecoder());
|
||||
|
||||
Flush();
|
||||
|
||||
// Loop until we reach the next keyframe after the threshold.
|
||||
while (true) {
|
||||
nsRefPtr<VP8Sample> compressed(PopSample());
|
||||
if (!compressed) {
|
||||
// EOS, or error. Let the state machine know.
|
||||
return false;
|
||||
}
|
||||
aParsed++;
|
||||
if (!compressed->mKeyframe ||
|
||||
compressed->mTime < aTimeThreshold) {
|
||||
continue;
|
||||
}
|
||||
mQueuedVideoSample = compressed;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IntelWebMVideoDecoder::DecodeVideoFrame(bool& aKeyframeSkip,
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
AbstractMediaDecoder::AutoNotifyDecoded a(mReader->GetDecoder());
|
||||
|
||||
MOZ_ASSERT(mPlatform && mReader->GetDecoder());
|
||||
|
||||
if (aKeyframeSkip) {
|
||||
bool ok = SkipVideoDemuxToNextKeyFrame(aTimeThreshold, a.mDropped);
|
||||
if (!ok) {
|
||||
NS_WARNING("Failed to skip demux up to next keyframe");
|
||||
return false;
|
||||
}
|
||||
a.mParsed = a.mDropped;
|
||||
aKeyframeSkip = false;
|
||||
nsresult rv = mMediaDataDecoder->Flush();
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mReader->OnTaskQueue());
|
||||
bool rv = Decode();
|
||||
{
|
||||
// Report the number of "decoded" frames as the difference in the
|
||||
// mNumSamplesOutput field since the last time we were called.
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
uint64_t delta = mNumSamplesOutput - mLastReportedNumDecodedFrames;
|
||||
a.mDecoded = static_cast<uint32_t>(delta);
|
||||
mLastReportedNumDecodedFrames = mNumSamplesOutput;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
already_AddRefed<VP8Sample>
|
||||
IntelWebMVideoDecoder::PopSample()
|
||||
{
|
||||
if (mQueuedVideoSample) {
|
||||
return mQueuedVideoSample.forget();
|
||||
}
|
||||
nsRefPtr<VP8Sample> sample;
|
||||
while (mSampleQueue.empty()) {
|
||||
bool eos = false;
|
||||
bool ok = Demux(sample, &eos);
|
||||
if (!ok || eos) {
|
||||
MOZ_ASSERT(!sample);
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(sample);
|
||||
mSampleQueue.push_back(sample.forget());
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mSampleQueue.empty());
|
||||
sample = mSampleQueue.front().forget();
|
||||
mSampleQueue.pop_front();
|
||||
return sample.forget();
|
||||
}
|
||||
|
||||
void
|
||||
IntelWebMVideoDecoder::Output(MediaData* aSample)
|
||||
{
|
||||
#ifdef LOG_SAMPLE_DECODE
|
||||
LOG("Decoded video sample time=%lld dur=%lld",
|
||||
aSample->mTime, aSample->mDuration);
|
||||
#endif
|
||||
|
||||
// Don't accept output while we're flushing.
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
if (mIsFlushing) {
|
||||
mon.NotifyAll();
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aSample->mType == MediaData::VIDEO_DATA);
|
||||
mReader->VideoQueue().Push(static_cast<VideoData*>(aSample));
|
||||
|
||||
mNumSamplesOutput++;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
IntelWebMVideoDecoder::DrainComplete()
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mDrainComplete = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
IntelWebMVideoDecoder::InputExhausted()
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mInputExhausted = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
IntelWebMVideoDecoder::Error()
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mError = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
nsresult
|
||||
IntelWebMVideoDecoder::Flush()
|
||||
{
|
||||
if (!mReader->GetDecoder()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// Purge the current decoder's state.
|
||||
// Set a flag so that we ignore all output while we call
|
||||
// MediaDataDecoder::Flush().
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mIsFlushing = true;
|
||||
mDrainComplete = false;
|
||||
mEOS = false;
|
||||
}
|
||||
mMediaDataDecoder->Flush();
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mIsFlushing = false;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
@@ -1,97 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#if !defined(IntelWebMVideoDecoder_h_)
|
||||
#define IntelWebMVideoDecoder_h_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "WebMReader.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
#include "MediaInfo.h"
|
||||
#include "MediaData.h"
|
||||
|
||||
class TaskQueue;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class VP8Sample;
|
||||
|
||||
typedef std::deque<nsRefPtr<VP8Sample>> VP8SampleQueue;
|
||||
|
||||
class IntelWebMVideoDecoder : public WebMVideoDecoder, public MediaDataDecoderCallback
|
||||
{
|
||||
public:
|
||||
static WebMVideoDecoder* Create(WebMReader* aReader);
|
||||
virtual nsRefPtr<InitPromise> Init(unsigned int aWidth = 0,
|
||||
unsigned int aHeight = 0) override;
|
||||
virtual nsresult Flush() override;
|
||||
virtual void Shutdown() override;
|
||||
|
||||
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold) override;
|
||||
|
||||
virtual void Output(MediaData* aSample) override;
|
||||
|
||||
virtual void DrainComplete() override;
|
||||
|
||||
virtual void InputExhausted() override;
|
||||
virtual void Error() override;
|
||||
|
||||
virtual bool OnReaderTaskQueue() override
|
||||
{
|
||||
return mReader->OnTaskQueue();
|
||||
}
|
||||
|
||||
IntelWebMVideoDecoder(WebMReader* aReader);
|
||||
~IntelWebMVideoDecoder();
|
||||
|
||||
private:
|
||||
void InitLayersBackendType();
|
||||
|
||||
bool Decode();
|
||||
|
||||
bool Demux(nsRefPtr<VP8Sample>& aSample, bool* aEOS);
|
||||
|
||||
bool SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed);
|
||||
|
||||
bool IsSupportedVideoMimeType(const nsACString& aMimeType);
|
||||
|
||||
already_AddRefed<VP8Sample> PopSample();
|
||||
|
||||
nsRefPtr<WebMReader> mReader;
|
||||
nsRefPtr<PlatformDecoderModule> mPlatform;
|
||||
nsRefPtr<MediaDataDecoder> mMediaDataDecoder;
|
||||
|
||||
// TaskQueue on which decoder can choose to decode.
|
||||
// Only non-null up until the decoder is created.
|
||||
nsRefPtr<FlushableTaskQueue> mTaskQueue;
|
||||
|
||||
// Monitor that protects all non-threadsafe state; the primitives
|
||||
// that follow.
|
||||
Monitor mMonitor;
|
||||
nsAutoPtr<VideoInfo> mDecoderConfig;
|
||||
|
||||
VP8SampleQueue mSampleQueue;
|
||||
nsRefPtr<VP8Sample> mQueuedVideoSample;
|
||||
uint64_t mNumSamplesInput;
|
||||
uint64_t mNumSamplesOutput;
|
||||
uint64_t mLastReportedNumDecodedFrames;
|
||||
uint32_t mDecodeAhead;
|
||||
|
||||
// Whether this stream exists in the media.
|
||||
bool mInputExhausted;
|
||||
bool mDrainComplete;
|
||||
bool mError;
|
||||
bool mEOS;
|
||||
bool mIsFlushing;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -54,16 +54,10 @@ SoftwareWebMVideoDecoder::Create(WebMReader* aReader)
|
||||
return new SoftwareWebMVideoDecoder(aReader);
|
||||
}
|
||||
|
||||
nsRefPtr<InitPromise>
|
||||
nsresult
|
||||
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__);
|
||||
return InitDecoder(aWidth, aHeight);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -17,8 +17,8 @@ class SoftwareWebMVideoDecoder : public WebMVideoDecoder
|
||||
public:
|
||||
static WebMVideoDecoder* Create(WebMReader* aReader);
|
||||
|
||||
virtual nsRefPtr<InitPromise> Init(unsigned int aWidth = 0,
|
||||
unsigned int aHeight = 0) override;
|
||||
virtual nsresult Init(unsigned int aWidth = 0,
|
||||
unsigned int aHeight = 0) override;
|
||||
|
||||
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold) override;
|
||||
|
||||
@@ -20,11 +20,6 @@
|
||||
#include "vpx/vp8dx.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
|
||||
// IntelWebMVideoDecoder uses the WMF backend, which is Windows Vista+ only.
|
||||
#if defined(MOZ_PDM_VPX)
|
||||
#include "IntelWebMVideoDecoder.h"
|
||||
#endif
|
||||
|
||||
// Un-comment to enable logging of seek bisections.
|
||||
//#define SEEK_LOGGING
|
||||
|
||||
@@ -125,12 +120,8 @@ static void webm_log(nestegg * context,
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#if defined(MOZ_PDM_VPX)
|
||||
static bool sIsIntelDecoderEnabled = false;
|
||||
#endif
|
||||
|
||||
WebMReader::WebMReader(AbstractMediaDecoder* aDecoder, TaskQueue* aBorrowedTaskQueue)
|
||||
: MediaDecoderReader(aDecoder, aBorrowedTaskQueue)
|
||||
WebMReader::WebMReader(AbstractMediaDecoder* aDecoder)
|
||||
: MediaDecoderReader(aDecoder)
|
||||
, mContext(nullptr)
|
||||
, mVideoTrack(0)
|
||||
, mAudioTrack(0)
|
||||
@@ -149,10 +140,6 @@ WebMReader::WebMReader(AbstractMediaDecoder* aDecoder, TaskQueue* aBorrowedTaskQ
|
||||
if (!gNesteggLog) {
|
||||
gNesteggLog = PR_NewLogModule("Nestegg");
|
||||
}
|
||||
|
||||
#if defined(MOZ_PDM_VPX)
|
||||
sIsIntelDecoderEnabled = Preferences::GetBool("media.webm.intel_decoder.enabled", false);
|
||||
#endif
|
||||
}
|
||||
|
||||
WebMReader::~WebMReader()
|
||||
@@ -168,12 +155,6 @@ WebMReader::~WebMReader()
|
||||
nsRefPtr<ShutdownPromise>
|
||||
WebMReader::Shutdown()
|
||||
{
|
||||
#if defined(MOZ_PDM_VPX)
|
||||
if (mVideoTaskQueue) {
|
||||
mVideoTaskQueue->BeginShutdown();
|
||||
mVideoTaskQueue->AwaitShutdownAndIdle();
|
||||
}
|
||||
#endif
|
||||
if (mAudioDecoder) {
|
||||
mAudioDecoder->Shutdown();
|
||||
mAudioDecoder = nullptr;
|
||||
@@ -187,26 +168,9 @@ WebMReader::Shutdown()
|
||||
return MediaDecoderReader::Shutdown();
|
||||
}
|
||||
|
||||
nsresult WebMReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
nsresult WebMReader::Init()
|
||||
{
|
||||
#if defined(MOZ_PDM_VPX)
|
||||
if (sIsIntelDecoderEnabled) {
|
||||
PlatformDecoderModule::Init();
|
||||
|
||||
InitLayersBackendType();
|
||||
|
||||
mVideoTaskQueue = new FlushableTaskQueue(
|
||||
SharedThreadPool::Get(NS_LITERAL_CSTRING("IntelVP8 Video Decode")));
|
||||
NS_ENSURE_TRUE(mVideoTaskQueue, NS_ERROR_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aCloneDonor) {
|
||||
mBufferedState = static_cast<WebMReader*>(aCloneDonor)->mBufferedState;
|
||||
} else {
|
||||
mBufferedState = new WebMBufferedState;
|
||||
}
|
||||
|
||||
mBufferedState = new WebMBufferedState;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -324,23 +288,13 @@ WebMReader::RetrieveWebMMetadata(MediaInfo* aInfo)
|
||||
|
||||
mVideoCodec = nestegg_track_codec_id(mContext, track);
|
||||
|
||||
#if defined(MOZ_PDM_VPX)
|
||||
if (sIsIntelDecoderEnabled) {
|
||||
mVideoDecoder = IntelWebMVideoDecoder::Create(this);
|
||||
}
|
||||
#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) {
|
||||
mInitPromises.AppendElement(mVideoDecoder->Init(params.display_width,
|
||||
params.display_height));
|
||||
}
|
||||
|
||||
if (!mVideoDecoder) {
|
||||
if (!mVideoDecoder ||
|
||||
NS_FAILED(mVideoDecoder->Init(params.display_width,
|
||||
params.display_height))) {
|
||||
Cleanup();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -421,9 +375,7 @@ WebMReader::RetrieveWebMMetadata(MediaInfo* aInfo)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mAudioDecoder) {
|
||||
mInitPromises.AppendElement(mAudioDecoder->Init());
|
||||
} else {
|
||||
if (!mAudioDecoder || NS_FAILED(mAudioDecoder->Init())) {
|
||||
Cleanup();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "FlushableTaskQueue.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "MediaResource.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "nsAutoRef.h"
|
||||
#include "nestegg/nestegg.h"
|
||||
|
||||
@@ -26,9 +25,7 @@ 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;
|
||||
@@ -39,7 +36,7 @@ class WebMReader;
|
||||
class WebMVideoDecoder
|
||||
{
|
||||
public:
|
||||
virtual nsRefPtr<InitPromise> Init(unsigned int aWidth = 0, unsigned int aHeight = 0) = 0;
|
||||
virtual nsresult 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,
|
||||
@@ -52,7 +49,7 @@ public:
|
||||
class WebMAudioDecoder
|
||||
{
|
||||
public:
|
||||
virtual nsRefPtr<InitPromise> Init() = 0;
|
||||
virtual nsresult Init() = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual nsresult ResetDecode() = 0;
|
||||
virtual nsresult DecodeHeader(const unsigned char* aData, size_t aLength) = 0;
|
||||
@@ -66,14 +63,14 @@ public:
|
||||
class WebMReader : public MediaDecoderReader
|
||||
{
|
||||
public:
|
||||
explicit WebMReader(AbstractMediaDecoder* aDecoder, TaskQueue* aBorrowedTaskQueue = nullptr);
|
||||
explicit WebMReader(AbstractMediaDecoder* aDecoder);
|
||||
|
||||
protected:
|
||||
~WebMReader();
|
||||
|
||||
public:
|
||||
virtual nsRefPtr<ShutdownPromise> Shutdown() override;
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor) override;
|
||||
virtual nsresult Init() override;
|
||||
virtual nsresult ResetDecode() override;
|
||||
virtual bool DecodeAudioData() override;
|
||||
|
||||
@@ -122,7 +119,6 @@ public:
|
||||
int64_t GetLastVideoFrameTime();
|
||||
void SetLastVideoFrameTime(int64_t aFrameTime);
|
||||
layers::LayersBackend GetLayersBackendType() { return mLayersBackendType; }
|
||||
FlushableTaskQueue* GetVideoTaskQueue() { return mVideoTaskQueue; }
|
||||
uint64_t GetCodecDelay() { return mCodecDelay; }
|
||||
|
||||
protected:
|
||||
@@ -167,8 +163,6 @@ 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;
|
||||
@@ -212,9 +206,6 @@ private:
|
||||
|
||||
layers::LayersBackend mLayersBackendType;
|
||||
|
||||
// For hardware video decoding.
|
||||
nsRefPtr<FlushableTaskQueue> mVideoTaskQueue;
|
||||
|
||||
// Booleans to indicate if we have audio and/or video data
|
||||
bool mHasVideo;
|
||||
bool mHasAudio;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS += [
|
||||
'IntelWebMVideoDecoder.h',
|
||||
'NesteggPacketHolder.h',
|
||||
'SoftwareWebMVideoDecoder.h',
|
||||
'WebMBufferedParser.h',
|
||||
@@ -23,10 +22,6 @@ UNIFIED_SOURCES += [
|
||||
'WebMReader.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_FMP4'] and CONFIG['MOZ_WMF']:
|
||||
DEFINES['MOZ_PDM_VPX'] = True
|
||||
UNIFIED_SOURCES += ['IntelWebMVideoDecoder.cpp']
|
||||
|
||||
if CONFIG['MOZ_WEBM_ENCODER']:
|
||||
EXPORTS += ['WebMWriter.h']
|
||||
UNIFIED_SOURCES += ['EbmlComposer.cpp',
|
||||
|
||||
@@ -17,7 +17,7 @@ Adts::GetFrequencyIndex(uint32_t aSamplesPerSecond)
|
||||
{
|
||||
static const uint32_t freq_lookup[] = { 96000, 88200, 64000, 48000, 44100,
|
||||
32000, 24000, 22050, 16000, 12000,
|
||||
11025, 8000, 7350, 0 };
|
||||
11025, 8000, 7350, 0};
|
||||
|
||||
int8_t i = 0;
|
||||
while (freq_lookup[i] && aSamplesPerSecond < freq_lookup[i]) {
|
||||
@@ -60,12 +60,12 @@ Adts::ConvertSample(uint16_t aChannelCount, int8_t aFrequencyIndex,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aSample->mCrypto.valid) {
|
||||
if (aSample->mCrypto.plain_sizes.Length() == 0) {
|
||||
writer->mCrypto.plain_sizes.AppendElement(kADTSHeaderSize);
|
||||
writer->mCrypto.encrypted_sizes.AppendElement(aSample->Size() - kADTSHeaderSize);
|
||||
if (aSample->mCrypto.mValid) {
|
||||
if (aSample->mCrypto.mPlainSizes.Length() == 0) {
|
||||
writer->mCrypto.mPlainSizes.AppendElement(kADTSHeaderSize);
|
||||
writer->mCrypto.mEncryptedSizes.AppendElement(aSample->Size() - kADTSHeaderSize);
|
||||
} else {
|
||||
writer->mCrypto.plain_sizes[0] += kADTSHeaderSize;
|
||||
writer->mCrypto.mPlainSizes[0] += kADTSHeaderSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,9 +113,9 @@ UpdateTrackInfo(mozilla::TrackInfo& aConfig,
|
||||
aConfig.mDuration = FindInt64(aMetaData, kKeyDuration);
|
||||
aConfig.mMediaTime = FindInt64(aMetaData, kKeyMediaTime);
|
||||
aConfig.mTrackId = FindInt32(aMetaData, kKeyTrackID);
|
||||
aConfig.mCrypto.valid = aMetaData->findInt32(kKeyCryptoMode, &crypto.mode) &&
|
||||
aMetaData->findInt32(kKeyCryptoDefaultIVSize, &crypto.iv_size) &&
|
||||
FindData(aMetaData, kKeyCryptoKey, &crypto.key);
|
||||
aConfig.mCrypto.mValid = aMetaData->findInt32(kKeyCryptoMode, &crypto.mMode) &&
|
||||
aMetaData->findInt32(kKeyCryptoDefaultIVSize, &crypto.mIVSize) &&
|
||||
FindData(aMetaData, kKeyCryptoKey, &crypto.mKeyId);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -134,10 +134,10 @@ already_AddRefed<MediaRawData> SampleIterator::GetNext()
|
||||
return nullptr;
|
||||
}
|
||||
ByteReader reader(cenc);
|
||||
writer->mCrypto.valid = true;
|
||||
writer->mCrypto.iv_size = ivSize;
|
||||
writer->mCrypto.mValid = true;
|
||||
writer->mCrypto.mIVSize = ivSize;
|
||||
|
||||
if (!reader.ReadArray(writer->mCrypto.iv, ivSize)) {
|
||||
if (!reader.ReadArray(writer->mCrypto.mIV, ivSize)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -149,13 +149,13 @@ already_AddRefed<MediaRawData> SampleIterator::GetNext()
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
writer->mCrypto.plain_sizes.AppendElement(reader.ReadU16());
|
||||
writer->mCrypto.encrypted_sizes.AppendElement(reader.ReadU32());
|
||||
writer->mCrypto.mPlainSizes.AppendElement(reader.ReadU16());
|
||||
writer->mCrypto.mEncryptedSizes.AppendElement(reader.ReadU32());
|
||||
}
|
||||
} else {
|
||||
// No subsample information means the entire sample is encrypted.
|
||||
writer->mCrypto.plain_sizes.AppendElement(0);
|
||||
writer->mCrypto.encrypted_sizes.AppendElement(sample->Size());
|
||||
writer->mCrypto.mPlainSizes.AppendElement(0);
|
||||
writer->mCrypto.mEncryptedSizes.AppendElement(sample->Size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
namespace IPC {
|
||||
template<typename T> struct ParamTraits;
|
||||
}
|
||||
} // namespace IPC
|
||||
|
||||
#ifdef XP_WIN
|
||||
// defines TimeStampValue as a complex value keeping both
|
||||
@@ -258,6 +258,14 @@ public:
|
||||
{
|
||||
return mValue != aOther.mValue;
|
||||
}
|
||||
bool IsZero() const
|
||||
{
|
||||
return mValue == 0;
|
||||
}
|
||||
explicit operator bool() const
|
||||
{
|
||||
return mValue != 0;
|
||||
}
|
||||
|
||||
// Return a best guess at the system's current timing resolution,
|
||||
// which might be variable. BaseTimeDurations below this order of
|
||||
@@ -399,7 +407,7 @@ public:
|
||||
* False on Windows 7
|
||||
* UNTESTED ON OTHER PLATFORMS
|
||||
*/
|
||||
#if defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_COCOA)
|
||||
#if defined(MOZ_WIDGET_GONK) || defined(XP_DARWIN)
|
||||
static TimeStamp FromSystemTime(int64_t aSystemTime)
|
||||
{
|
||||
static_assert(sizeof(aSystemTime) == sizeof(TimeStampValue),
|
||||
@@ -413,6 +421,15 @@ public:
|
||||
*/
|
||||
bool IsNull() const { return mValue == 0; }
|
||||
|
||||
/**
|
||||
* Return true if this is not the "null" moment, may be used in tests, e.g.:
|
||||
* |if (timestamp) { ... }|
|
||||
*/
|
||||
explicit operator bool() const
|
||||
{
|
||||
return mValue != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a timestamp reflecting the current elapsed system time. This
|
||||
* is monotonically increasing (i.e., does not decrease) over the
|
||||
@@ -426,8 +443,8 @@ public:
|
||||
* lower precision, usually 15.6 ms, but with very good performance benefit.
|
||||
* Use it for measurements of longer times, like >200ms timeouts.
|
||||
*/
|
||||
static MFBT_API TimeStamp Now() { return Now(true); }
|
||||
static MFBT_API TimeStamp NowLoRes() { return Now(false); }
|
||||
static TimeStamp Now() { return Now(true); }
|
||||
static TimeStamp NowLoRes() { return Now(false); }
|
||||
|
||||
/**
|
||||
* Return a timestamp representing the time when the current process was
|
||||
@@ -485,13 +502,27 @@ public:
|
||||
TimeStamp& operator+=(const TimeDuration& aOther)
|
||||
{
|
||||
MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
|
||||
mValue += aOther.mValue;
|
||||
TimeStampValue value = mValue + aOther.mValue;
|
||||
// Check for underflow.
|
||||
// (We don't check for overflow because it's not obvious what the error
|
||||
// behavior should be in that case.)
|
||||
if (aOther.mValue < 0 && value > mValue) {
|
||||
value = 0;
|
||||
}
|
||||
mValue = value;
|
||||
return *this;
|
||||
}
|
||||
TimeStamp& operator-=(const TimeDuration& aOther)
|
||||
{
|
||||
MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
|
||||
mValue -= aOther.mValue;
|
||||
TimeStampValue value = mValue - aOther.mValue;
|
||||
// Check for underflow.
|
||||
// (We don't check for overflow because it's not obvious what the error
|
||||
// behavior should be in that case.)
|
||||
if (aOther.mValue > 0 && value > mValue) {
|
||||
value = 0;
|
||||
}
|
||||
mValue = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -571,6 +602,6 @@ private:
|
||||
TimeStampValue mValue;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_TimeStamp_h */
|
||||
|
||||
Reference in New Issue
Block a user