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:
2022-10-13 09:51:06 +08:00
parent f12a92a2ed
commit 0be1a20aba
80 changed files with 1161 additions and 1818 deletions
+3 -4
View File
@@ -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")));
}
+3 -10
View File
@@ -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();
}
+9 -9
View File
@@ -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
+9 -22
View File
@@ -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
+2 -6
View File
@@ -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;
+1 -7
View File
@@ -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;
+23 -76
View File
@@ -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;
}
+7 -14
View File
@@ -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
-5
View File
@@ -36,11 +36,6 @@ AndroidMediaReader::AndroidMediaReader(AbstractMediaDecoder *aDecoder,
{
}
nsresult AndroidMediaReader::Init(MediaDecoderReader* aCloneDonor)
{
return NS_OK;
}
nsresult AndroidMediaReader::ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags)
{
+1 -2
View File
@@ -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();
+1 -1
View File
@@ -103,7 +103,7 @@ AppleMP3Reader::Read(uint32_t *aNumBytes, char *aData)
}
nsresult
AppleMP3Reader::Init(MediaDecoderReader* aCloneDonor)
AppleMP3Reader::Init()
{
AudioFileTypeID fileType = kAudioFileMP3Type;
+1 -1
View File
@@ -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.
-2
View File
@@ -43,8 +43,6 @@ public:
virtual ~DirectShowReader();
nsresult Init(MediaDecoderReader* aCloneDonor) override;
bool DecodeAudioData() override;
bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold) override;
+8 -160
View File
@@ -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
+4 -4
View File
@@ -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;
+2 -2
View File
@@ -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);
+6 -6
View File
@@ -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);
}
+5 -5
View File
@@ -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)
{
}
+1 -1
View File
@@ -115,7 +115,7 @@ GStreamerReader::~GStreamerReader()
NS_ASSERTION(!mPlayBin, "No Shutdown() after Init()");
}
nsresult GStreamerReader::Init(MediaDecoderReader* aCloneDonor)
nsresult GStreamerReader::Init()
{
GStreamerFormatHelper::Instance();
+1 -1
View File
@@ -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;
+10 -10
View File
@@ -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");
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
-6
View File
@@ -283,12 +283,6 @@ MediaCodecReader::~MediaCodecReader()
{
}
nsresult
MediaCodecReader::Init(MediaDecoderReader* aCloneDonor)
{
return NS_OK;
}
void
MediaCodecReader::ReleaseMediaResources()
{
-4
View File
@@ -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();
-5
View File
@@ -139,11 +139,6 @@ MediaOmxReader::~MediaOmxReader()
{
}
nsresult MediaOmxReader::Init(MediaDecoderReader* aCloneDonor)
{
return NS_OK;
}
already_AddRefed<AbstractMediaDecoder>
MediaOmxReader::SafeGetDecoder() {
nsRefPtr<AbstractMediaDecoder> decoder;
-2
View File
@@ -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:
+275
View File
@@ -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
+73
View File
@@ -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_ */
+5 -228
View File
@@ -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
+18 -67
View File
@@ -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
+2 -3
View File
@@ -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)
+2 -3
View File
@@ -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
+2 -2
View File
@@ -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 {
+2 -2
View File
@@ -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 {
+2 -2
View File
@@ -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 {
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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();
}
-17
View File
@@ -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;
}
+2 -2
View File
@@ -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;
+6 -3
View File
@@ -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',
]
+2 -2
View File
@@ -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
+8 -10
View File
@@ -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;
};
-5
View File
@@ -26,11 +26,6 @@ RawReader::~RawReader()
MOZ_COUNT_DTOR(RawReader);
}
nsresult RawReader::Init(MediaDecoderReader* aCloneDonor)
{
return NS_OK;
}
nsresult RawReader::ResetDecode()
{
mCurrentFrame = 0;
-1
View File
@@ -20,7 +20,6 @@ protected:
~RawReader();
public:
virtual nsresult Init(MediaDecoderReader* aCloneDonor) override;
virtual nsresult ResetDecode() override;
virtual bool DecodeAudioData() override;
-5
View File
@@ -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)
{
-1
View File
@@ -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;
+1 -1
View File
@@ -206,7 +206,7 @@ MediaDecodeTask::CreateReader()
return false;
}
nsresult rv = mDecoderReader->Init(nullptr);
nsresult rv = mDecoderReader->Init();
if (NS_FAILED(rv)) {
return false;
}
+15 -15
View File
@@ -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
-443
View File
@@ -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
-97
View File
@@ -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
+2 -8
View File
@@ -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
+2 -2
View File
@@ -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;
+8 -56
View File
@@ -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;
}
+4 -13
View File
@@ -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
View File
@@ -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',
+6 -6
View File
@@ -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;
}
}
+3 -3
View File
@@ -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
+7 -7
View File
@@ -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());
}
}
+38 -7
View File
@@ -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 */