import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1275016 - Rename Endian.h to EndianUtils.h to avoid #include confusion with Android's endian.h stdlib header. r=froydnj (b54a25f572)
- add crashreporter stuff (aa7ef15337)
- Bug 1261168 - Add AlignedAutoTArray type in Web Audio; r=padenot (285d2cb88b)
- Bug 1273390. Part 1 - move some functions to private. r=jya. (07a3037e59)
- Bug 1273390. Part 2 - add assertions. r=jya. (2cae7c596a)
- Bug 1273390. Part 3 - rename some functions to be consistent with other sub-classes of MediaDataDecoder. r=jya. (c48c7060ce)
- Bug 1273390. Part 4 - remove use of FlushableTaskQueue::Flush(). r=jya. (00565a65f4)
- Bug 1273390. Part 5 - remove use of FlushableTaskQueue. r=jya. (30600b204e)
- Bug 1273774. Part 1 - remove unused members and thread assertions. r=jya (f5177ed641)
- Bug 1273774. Part 2 - do decoding jobs synchronously without dispatching. r=jya. (62d840d27c)
- Bug 1273774. Part 3 - remove members no longer used. r=jya. (e957ca512a)
- Bug 1244410: [ffmpeg] Ensure the last drained frame has the proper duration set. r=gerald (d5521bfdd4)
- Bug 1271508. Part 1 - refactor FFmpegAudioDecoder code to be similar to FFmpegVideoDecoder::Input() so it would be easier to extract common code to the parent class. r=jya. (613e6c624c)
- Bug 1271508. Part 2 - rename functions so they are the same as those of FFmpegAudioDecoder so it would be easier to extract common code to the parent class. r=jya. (cb281cba26)
- Bug 1270350 - per comment 0, use SyncRunnable to repalce the boilerplate code. r=jya. (b99460e571)
- Bug 1271508. Part 3 - extract code to the parent class and remove use of mTaskQueue from sub-classes. r=jya. (2a7ff4dd1e)
- Bug 1274216 - remove use of FlushableTaskQueue from PlatformDecoderModule. r=jya. (eb160c5fa2)
- Bug 1271517. Part 1 - remove use of FlushableTaskQueue::Flush() from FFmpegDataDecoder::Flush(). r=jya. (fdf10da4ab)
- Bug 1271517. Part 2 - remove use of FlushableTaskQueue. r=jya. (a7016d8506)
- Bug 1273397. Part 1 - rename some functions to be consistent with other MediaDataDecoder sub-classes. r=jya. (7eecb164be)
- Bug 1273397. Part 2 - constify some members. r=jya. (e4482f9a23)
- Bug 1273397. Part 3 - remove use of FlushableTaskQueue::Flush(). r=jya. (0b7ee073fe)
- Bug 1273397. Part 4 - remove use of FlushableTaskQueue. r=jya. (6a612161d5)
- Bug 1273397. Part 5 - add assertions. r=jya. (ff3a62a6fb)
- Bug 1274199 - remove use of FlushableTaskQueue. r=cpearce. (adc4c84ede)
- Bug 1273405. Part 1 - rename some functions to be consistent with other MediaDataDecoder sub-classes. r=jya. (af123d6c21)
- Bug 1273405. Part 2 - remove use of FlushableTaskQueue::Flush(). r=jya. (2d144bfbcd)
- Bug 1273405. Part 3 - remove use of FlushableTaskQueue. r=jya. (1e9ea3c2c7)
- Bug 1273405. Part 4 - add assertions. r=jya. (b400647323)
- Bug 1271491: [WMF] P1. Don't use main thread only preferences methods. r=cpearce (7177454dfb)
- Bug 1262427. Don't try D3D11 harder. r=dvander (404147d6fa)
- Use gfxConfig for D3D9 preferences. (bug 1270650, r=jrmuizel) (40d89c154c)
- Bug 1271491: P2. Allow initialization of WMFPlatformDecoderModule from any threads. r=mattwoodrow (c8fe0bf009)
- Bug 1271491: P3. Remove refcounting the number of time apple's linkers are called. r=cpearce (0324ffe876)
- Bug 1271491: [ffmpeg] P4. Remove requirements to call Init on the main thread. r=cpearce (b511d7dfd5)
- Bug 1271491: [GMP] P5. Allow GMPDecoderModule::Init() to be called off the main thread. r=cpearce (2131eb0b2e)
- Bug 1266102 - Don't run VP9 benchmark on Android r=jya (57d7b573fe)
- Bug 1271491: P6. Remove the need to call PDMFactory::Init(). r=cpearce (5726cfe49c)
- Bug 1271491: P7. Remove unused members. r=alfredo (0f8a9dde73)
- Bug 1268905 - Disable D3D11 with some Toshiba DLLs - r=cpearce (b5bf77442e)
- Bug 1269204 - Disable D3D11 with idg10umd32 9.17.10.2857 - r=cpearce (7eb6a3d96b)
- Bug 1273406 - Disable D3D11 with some iSonyVideoProcessor DLLs - r=cpearce (d9b6f0cefe)
- Bug 1273406 - Ugly macros transform into beautiful constexpr goodness - r=cpearce (0671483695)
- Bug 1273691 - Implement 'media.wmf.disable-d3d11-for-dlls' pref - r=cpearce (193ae53070)
- Bug 1272225. Part 1 - add assertions to make thread constraints clear. r=jya. (83c620470e)
- Bug 1272225. Part 2 - remove use of FlushableTaskQueue::Flush(). r=jya. (9473e092d1)
- Bug 1272553. Part 1 - move code around to be able to extract common code in P2. r=jya. (d727f97ee8)
- Bug 1272553. Part 2 - extract common code to the parent class. r=jya. (2fb3cd4bd9)
- Bug 1272553. Part 3 - make mTaskQueue private. r=jya. (93fea98cb6)
- Bug 1272232. Part 1 - move code around so we can extract common code in P2. r=jya. (8cdaab9078)
- Bug 1272232. Part 2 - extract common code to the parent class. r=jya. (27156668b3)
- Bug 1272232. Part 3 - constify some members and make them private when possible. r=jya. (550b963d97)
- Bug 1272232. Part 4 - remove use of FlushableTaskQueue::Flush(). r=jya. (bdbfdeb6bc)
- Bug 1272232. Part 5 - remove use of FlushableTaskQueue. r=jya. (640f889a9d)
- Bug 1274913 - Move PDM log definition to header. r=njn (823b07f42b)
- Bug 1275538: P1. Abort early if a skip request is in progress. r=gerald,kamidphish (d67b8a2236)
- Bug 1272422 - Part 1: Expose control of suspending background video. r=cpearce (ec7193773f)
- Bug 1272422 - Part 2: Vidoe -> Video. r=cpearce (97390aee69)
- Bug 1272422 - Part 3: Don't reset audio queue. r=jya (e183db1062)
- Bug 1272964: P1. Only activate skip to next keyframe logic when next keyframe time is known. r=gerald (1be74df027)
- Bug 1272964: P2. Don't activate skip to next keyframe until we passed the internal seek target. r=gerald (c55b6ae003)
- Bug 1258922: [MSE] P1. Initialise variable. r=gerald (56a5acb345)
- Bug 1258922: [MSE] P2. Do not go over gap when attempting to find the next key frame. r=gerald (db1319f080)
- Bug 1258922: [MSE] P3. Check that the data we are attempting to skip to is buffered. r=gerald (621d71d5d6)
- Bug 1258922: [MSE] P4. Set draining flag to true when skip to next keyframe failed. r=gerald (6c75613faf)
- Bug 1272916: [MSE] P1. Don't rely only on dts gap to establish if we have a gap in our source buffer. r=gerald (8770113b83)
- Bug 1272964: [MSE] P3. Do not skip over gaps when searching for the next keyframe. r=gerald (76916c5ac6)
- Bug 1272964: P4. Only flush decoder if skip to next keyframe actually succeeds. r=cpearce (5394708eef)
- Bug 1270323: P1. Don't reset flag indicating that new data was received. r=cpearce (d32f06ef34)
- Bug 1270323: P2. Don't process new incoming data while a skip to next keyframe is pending. r=cpearce (bca7909de9)
- Bug 1270323: [ffmpeg] P3. Use the dts of the last sample input, not the dts of the last decoded sample (0d768c33ef)
- Bug 1270323: P4. Don't drain decoder if we're already waiting for new data. r=cpearce (679302cb6e)
- Bug 1270323: P5. Prevent potential null deref. r=cpearce (cc63270e06)
- Bug 1275538: P2. Drop decoded frames that we know are already too late. r=kamidphish (4e7af9398c)
- Bug 1273018: P1. Rename some members. r=gerald (3a92fbd994)
- Bug 1273018: P2. Don't reject audio waiting promise when performing a video only seek. r=gerald (34e4988db1)
- Bug 1273018: P3. Adjust range of audio assertions. r=gerald (feb2afd0ae)
- Bug 1249706 - Backout a085ea2d24bb for blowing telemetry server's mind. r=backout (d61fb51f52)
- Bug 1249706 - Fix 8fe22dd4fc8a (backout of a085ea2d24bb). r=bustage (ba65251db7)
- Bug 1272964: [MSE] P5. Default to skipping to the next keyframe if no keyframe was found past currentTime. (29086fcf56)
- Bug 1272964: P6. Exclude frames dropped due to internal seeking from calculations. r=cpearce (bf6faa7612)
- Bug 1068151 - keep decoding a corrupted video. r=jya (3b5462e5b6)
- Bug 1273947 - Update ResetDecode() to ResetDecode(TargetQueue) r=jya (6c28d46974)
- Bug 1277508: P1. Don't attempt to demux new samples while we're currently draining. r=kamidphish (64f200b921)
- Bug 1274933: Reject data promise when EOS is encountered following waiting for data. r=gerald (5bba4a7853)
- Bug 1277508: P2. Add HasPendingDrain convenience method. r=kamidphish (3d89a90a97)
This commit is contained in:
2024-10-08 23:23:56 +08:00
parent 1beb91ad2b
commit 93f846cd1f
163 changed files with 1368 additions and 1121 deletions
@@ -15,7 +15,7 @@
#include "mozilla/dom/BluetoothMapParametersBinding.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/dom/File.h"
#include "mozilla/RefPtr.h"
@@ -13,7 +13,7 @@
#include "BluetoothUuidHelper.h"
#include "mozilla/dom/BluetoothPbapParametersBinding.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/RefPtr.h"
@@ -9,7 +9,7 @@
#include <unistd.h>
#include <sys/socket.h>
#include "BluetoothInterface.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "nsClassHashtable.h"
BEGIN_BLUETOOTH_NAMESPACE
+1 -1
View File
@@ -9,7 +9,7 @@
#include <algorithm>
#include "mozilla/Compiler.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/Observer.h"
#include "mozilla/UniquePtr.h"
#include "nsPrintfCString.h"
+1 -1
View File
@@ -8,7 +8,7 @@
#define mozilla_dom_bluetooth_ObexBase_h
#include "BluetoothCommon.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
+1 -1
View File
@@ -84,7 +84,7 @@
#include "mozilla/dom/PBrowserParent.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Helpers.h"
#include "mozilla/gfx/PathHelpers.h"
+1 -1
View File
@@ -50,7 +50,7 @@
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ImageData.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtrExtensions.h"
+1 -1
View File
@@ -47,7 +47,7 @@
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ImageData.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
namespace mozilla {
+1 -1
View File
@@ -21,7 +21,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/AppProcessChecker.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/Hal.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/Maybe.h"
+1 -1
View File
@@ -23,7 +23,7 @@
#include "js/Date.h"
#include "js/StructuredClone.h"
#include "KeyPath.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Move.h"
#include "mozilla/dom/BindingUtils.h"
+1 -1
View File
@@ -12,7 +12,7 @@
#include "js/Date.h"
#include "js/Value.h"
#include "jsfriendapi.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/FloatingPoint.h"
#include "mozIStorageStatement.h"
#include "mozIStorageValueArray.h"
-1
View File
@@ -32,7 +32,6 @@ ADTSDecoder::CreateStateMachine()
/* static */ bool
ADTSDecoder::IsEnabled()
{
PDMFactory::Init();
RefPtr<PDMFactory> platform = new PDMFactory();
return (platform && platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"),
/* DecoderDoctorDiagnostics* */ nullptr));
-1
View File
@@ -31,7 +31,6 @@ MP3Decoder::CreateStateMachine() {
/* static */
bool
MP3Decoder::IsEnabled() {
PDMFactory::Init();
RefPtr<PDMFactory> platform = new PDMFactory();
return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mpeg"),
/* DecoderDoctorDiagnostics* */ nullptr);
+1 -1
View File
@@ -10,7 +10,7 @@
#include <algorithm>
#include "mozilla/Assertions.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "VideoUtils.h"
#include "TimeUnits.h"
#include "prenv.h"
+1 -1
View File
@@ -279,7 +279,7 @@ MediaDecoderReaderWrapper::IsRequestingAudioData() const
}
bool
MediaDecoderReaderWrapper::IsRequestingVidoeData() const
MediaDecoderReaderWrapper::IsRequestingVideoData() const
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
return mVideoDataRequest.Exists();
+1 -1
View File
@@ -247,7 +247,7 @@ public:
void RequestVideoData(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold);
bool IsRequestingAudioData() const;
bool IsRequestingVidoeData() const;
bool IsRequestingVideoData() const;
RefPtr<SeekPromise> Seek(SeekTarget aTarget, media::TimeUnit aEndTime);
RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType);
+3 -3
View File
@@ -1764,7 +1764,7 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
return NS_OK;
}
if (!IsVideoDecoding() || mReader->IsRequestingVidoeData() ||
if (!IsVideoDecoding() || mReader->IsRequestingVideoData() ||
mVideoWaitRequest.Exists()) {
return NS_OK;
}
@@ -2277,7 +2277,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
"Don't yet have a strategy for non-heuristic + non-WaitForData");
DispatchDecodeTasksIfNeeded();
MOZ_ASSERT_IF(!mMinimizePreroll && OutOfDecodedAudio(), mReader->IsRequestingAudioData() || mAudioWaitRequest.Exists());
MOZ_ASSERT_IF(!mMinimizePreroll && OutOfDecodedVideo(), mReader->IsRequestingVidoeData() || mVideoWaitRequest.Exists());
MOZ_ASSERT_IF(!mMinimizePreroll && OutOfDecodedVideo(), mReader->IsRequestingVideoData() || mVideoWaitRequest.Exists());
DECODER_LOG("In buffering mode, waiting to be notified: outOfAudio: %d, "
"mAudioStatus: %s, outOfVideo: %d, mVideoStatus: %s",
OutOfDecodedAudio(), AudioRequestStatus(),
@@ -2860,7 +2860,7 @@ const char*
MediaDecoderStateMachine::VideoRequestStatus() const
{
MOZ_ASSERT(OnTaskQueue());
if (mReader->IsRequestingVidoeData()) {
if (mReader->IsRequestingVideoData()) {
MOZ_DIAGNOSTIC_ASSERT(!mVideoWaitRequest.Exists());
return "pending";
} else if (mVideoWaitRequest.Exists()) {
+193 -168
View File
@@ -60,8 +60,10 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
VideoFrameContainer* aVideoFrameContainer,
layers::LayersBackend aLayersBackend)
: 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))
, mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2),
Preferences::GetUint("media.audio-max-decode-error", 3))
, mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2),
Preferences::GetUint("media.video-max-decode-error", 2))
, mDemuxer(aDemuxer)
, mDemuxerInitDone(false)
, mLastReportedNumDecodedFrames(0)
@@ -87,10 +89,6 @@ MediaFormatReader::Shutdown()
{
MOZ_ASSERT(OnTaskQueue());
if (HasVideo()) {
ReportDroppedFramesTelemetry();
}
mDemuxerInitRequest.DisconnectIfExists();
mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__);
mSeekPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
@@ -172,7 +170,6 @@ nsresult
MediaFormatReader::Init()
{
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
PDMFactory::Init();
InitLayersBackendType();
@@ -483,8 +480,8 @@ MediaFormatReader::ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThr
}
return (nextKeyframe < aTimeThreshold ||
(mVideo.mTimeThreshold &&
mVideo.mTimeThreshold.ref().mTime < aTimeThreshold)) &&
nextKeyframe.ToMicroseconds() >= 0;
mVideo.mTimeThreshold.ref().EndTime() < aTimeThreshold)) &&
nextKeyframe.ToMicroseconds() >= 0 && !nextKeyframe.IsInfinite();
}
RefPtr<MediaDecoderReader::MediaDataPromise>
@@ -496,7 +493,6 @@ MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe,
MOZ_DIAGNOSTIC_ASSERT(!mVideo.HasPromise(), "No duplicate sample requests");
MOZ_DIAGNOSTIC_ASSERT(!mVideo.mSeekRequest.Exists() ||
mVideo.mTimeThreshold.isSome());
MOZ_DIAGNOSTIC_ASSERT(!mSkipRequest.Exists(), "called mid-skipping");
MOZ_DIAGNOSTIC_ASSERT(!IsSeeking(), "called mid-seek");
LOGV("RequestVideoData(%d, %lld)", aSkipToNextKeyframe, aTimeThreshold);
@@ -541,6 +537,9 @@ MediaFormatReader::OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure
decoder.mDemuxRequest.Complete();
switch (aFailure) {
case DemuxerFailureReason::END_OF_STREAM:
if (!decoder.mWaitingForData) {
decoder.mNeedDraining = true;
}
NotifyEndOfStream(aTrack);
break;
case DemuxerFailureReason::DEMUXER_ERROR:
@@ -552,7 +551,7 @@ MediaFormatReader::OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure
}
NotifyWaitingForData(aTrack);
break;
case DemuxerFailureReason::CANCELED:
case DemuxerFailureReason::CANCELED: MOZ_FALLTHROUGH;
case DemuxerFailureReason::SHUTDOWN:
if (decoder.HasPromise()) {
decoder.RejectPromise(CANCELED, __func__);
@@ -589,11 +588,13 @@ RefPtr<MediaDecoderReader::MediaDataPromise>
MediaFormatReader::RequestAudioData()
{
MOZ_ASSERT(OnTaskQueue());
MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), "No sample requests allowed while seeking");
MOZ_DIAGNOSTIC_ASSERT(!mAudio.mSeekRequest.Exists() ||
mAudio.mTimeThreshold.isSome());
MOZ_DIAGNOSTIC_ASSERT(!mAudio.HasPromise(), "No duplicate sample requests");
MOZ_DIAGNOSTIC_ASSERT(!IsSeeking(), "called mid-seek");
MOZ_DIAGNOSTIC_ASSERT(IsVideoSeeking() || mSeekPromise.IsEmpty(),
"No sample requests allowed while seeking");
MOZ_DIAGNOSTIC_ASSERT(IsVideoSeeking() ||
!mAudio.mSeekRequest.Exists() ||
mAudio.mTimeThreshold.isSome());
MOZ_DIAGNOSTIC_ASSERT(IsVideoSeeking() || !IsSeeking(), "called mid-seek");
LOGV("");
if (!HasAudio()) {
@@ -651,8 +652,7 @@ MediaFormatReader::NotifyNewOutput(TrackType aTrack, MediaData* aSample)
}
decoder.mOutput.AppendElement(aSample);
decoder.mNumSamplesOutput++;
decoder.mNumSamplesOutputTotal++;
decoder.mNumSamplesOutputTotalSinceTelemetry++;
decoder.mNumOfConsecutiveError = 0;
ScheduleUpdate(aTrack);
}
@@ -681,12 +681,12 @@ MediaFormatReader::NotifyDrainComplete(TrackType aTrack)
}
void
MediaFormatReader::NotifyError(TrackType aTrack)
MediaFormatReader::NotifyError(TrackType aTrack, MediaDataDecoderError aError)
{
MOZ_ASSERT(OnTaskQueue());
LOGV("%s Decoding error", TrackTypeToStr(aTrack));
auto& decoder = GetDecoderData(aTrack);
decoder.mError = true;
decoder.mError = decoder.HasFatalError() ? decoder.mError : Some(aError);
ScheduleUpdate(aTrack);
}
@@ -708,7 +708,6 @@ MediaFormatReader::NotifyEndOfStream(TrackType aTrack)
MOZ_ASSERT(OnTaskQueue());
auto& decoder = GetDecoderData(aTrack);
decoder.mDemuxEOS = true;
decoder.mNeedDraining = true;
ScheduleUpdate(aTrack);
}
@@ -730,8 +729,8 @@ MediaFormatReader::NeedInput(DecoderData& aDecoder)
// run of input than we input, decoders fire an "input exhausted" callback,
// which overrides our "few more samples" threshold.
return
!aDecoder.mDraining &&
!aDecoder.mError &&
!aDecoder.HasPendingDrain() &&
!aDecoder.HasFatalError() &&
aDecoder.mDecodingRequested &&
!aDecoder.mDemuxRequest.Exists() &&
!aDecoder.HasInternalSeekPending() &&
@@ -781,13 +780,19 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack)
// Nothing more to do until this operation complete.
return true;
}
if (aTrack == TrackType::kVideoTrack && mSkipRequest.Exists()) {
LOGV("Skipping in progress, nothing more to do");
return true;
}
if (decoder.mDemuxRequest.Exists()) {
// We may have pending operations to process, so we want to continue
// after UpdateReceivedNewData returns.
return false;
}
if (decoder.mDrainComplete || decoder.mDraining) {
if (decoder.HasPendingDrain()) {
// We do not want to clear mWaitingForData or mDemuxEOS while
// a drain is in progress in order to properly complete the operation.
return false;
@@ -809,17 +814,21 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack)
}
decoder.mWaitingForData = false;
if (decoder.mError) {
if (decoder.HasFatalError()) {
return false;
}
if (!mSeekPromise.IsEmpty()) {
if (!mSeekPromise.IsEmpty() &&
(!IsVideoSeeking() || aTrack == TrackInfo::kVideoTrack)) {
MOZ_ASSERT(!decoder.HasPromise());
MOZ_DIAGNOSTIC_ASSERT(!mAudio.mTimeThreshold && !mVideo.mTimeThreshold,
MOZ_DIAGNOSTIC_ASSERT((IsVideoSeeking() || !mAudio.mTimeThreshold) &&
!mVideo.mTimeThreshold,
"InternalSeek must have been aborted when Seek was first called");
MOZ_DIAGNOSTIC_ASSERT(!mAudio.HasWaitingPromise() && !mVideo.HasWaitingPromise(),
MOZ_DIAGNOSTIC_ASSERT((IsVideoSeeking() || !mAudio.HasWaitingPromise()) &&
!mVideo.HasWaitingPromise(),
"Waiting promises must have been rejected when Seek was first called");
if (mVideo.mSeekRequest.Exists() || mAudio.mSeekRequest.Exists()) {
if (mVideo.mSeekRequest.Exists() ||
(!IsVideoSeeking() && mAudio.mSeekRequest.Exists())) {
// Already waiting for a seek to complete. Nothing more to do.
return true;
}
@@ -922,9 +931,6 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
LOG("%s stream id has changed from:%d to:%d, draining decoder.",
TrackTypeToStr(aTrack), decoder.mLastStreamSourceID,
info->GetID());
if (aTrack == TrackType::kVideoTrack) {
ReportDroppedFramesTelemetry();
}
decoder.mNeedDraining = true;
decoder.mNextStreamSourceID = Some(info->GetID());
ScheduleUpdate(aTrack);
@@ -945,10 +951,13 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
decoder.mQueuedSamples.AppendElements(Move(samples));
NotifyDecodingRequested(aTrack);
} else {
TimeInterval time =
TimeInterval(TimeUnit::FromMicroseconds(sample->mTime),
TimeUnit::FromMicroseconds(sample->GetEndTime()));
InternalSeekTarget seekTarget =
decoder.mTimeThreshold.refOr(InternalSeekTarget(TimeUnit::FromMicroseconds(sample->mTime), false));
decoder.mTimeThreshold.refOr(InternalSeekTarget(time, false));
LOG("Stream change occurred on a non-keyframe. Seeking to:%lld",
seekTarget.mTime.ToMicroseconds());
sample->mTime);
InternalSeek(aTrack, seekTarget);
}
return;
@@ -988,14 +997,14 @@ MediaFormatReader::InternalSeek(TrackType aTrack, const InternalSeekTarget& aTar
{
MOZ_ASSERT(OnTaskQueue());
LOG("%s internal seek to %f",
TrackTypeToStr(aTrack), aTarget.mTime.ToSeconds());
TrackTypeToStr(aTrack), aTarget.Time().ToSeconds());
auto& decoder = GetDecoderData(aTrack);
decoder.Flush();
decoder.ResetDemuxer();
decoder.mTimeThreshold = Some(aTarget);
RefPtr<MediaFormatReader> self = this;
decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref().mTime)
decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref().Time())
->Then(OwnerThread(), __func__,
[self, aTrack] (media::TimeUnit aTime) {
auto& decoder = self->GetDecoderData(aTrack);
@@ -1016,7 +1025,7 @@ MediaFormatReader::InternalSeek(TrackType aTrack, const InternalSeekTarget& aTar
decoder.mTimeThreshold.reset();
self->NotifyEndOfStream(aTrack);
break;
case DemuxerFailureReason::CANCELED:
case DemuxerFailureReason::CANCELED: MOZ_FALLTHROUGH;
case DemuxerFailureReason::SHUTDOWN:
decoder.mTimeThreshold.reset();
break;
@@ -1071,6 +1080,11 @@ MediaFormatReader::Update(TrackType aTrack)
return;
}
if (aTrack == TrackType::kVideoTrack && mSkipRequest.Exists()) {
LOGV("Skipping in progress, nothing more to do");
return;
}
if (UpdateReceivedNewData(aTrack)) {
LOGV("Nothing more to do");
return;
@@ -1095,23 +1109,32 @@ MediaFormatReader::Update(TrackType aTrack)
RefPtr<MediaData>& output = decoder.mOutput[0];
InternalSeekTarget target = decoder.mTimeThreshold.ref();
media::TimeUnit time = media::TimeUnit::FromMicroseconds(output->mTime);
if (time >= target.mTime) {
if (time >= target.Time()) {
// We have reached our internal seek target.
decoder.mTimeThreshold.reset();
}
if (time < target.mTime || (target.mDropTarget && time == target.mTime)) {
if (time < target.Time() || (target.mDropTarget && target.Contains(time))) {
LOGV("Internal Seeking: Dropping %s frame time:%f wanted:%f (kf:%d)",
TrackTypeToStr(aTrack),
media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(),
target.mTime.ToSeconds(),
target.Time().ToSeconds(),
output->mKeyframe);
decoder.mOutput.RemoveElementAt(0);
decoder.mSizeOfQueue -= 1;
}
}
if (decoder.HasPromise()) {
needOutput = true;
if (decoder.mOutput.Length()) {
RefPtr<MediaData> output = decoder.mOutput[0];
decoder.mOutput.RemoveElementAt(0);
decoder.mSizeOfQueue -= 1;
decoder.mLastSampleTime =
Some(TimeInterval(TimeUnit::FromMicroseconds(output->mTime),
TimeUnit::FromMicroseconds(output->GetEndTime())));
decoder.mNumSamplesOutputTotal++;
ReturnOutput(output, aTrack);
// We have a decoded sample ready to be returned.
if (aTrack == TrackType::kVideoTrack) {
uint64_t delta =
@@ -1122,13 +1145,7 @@ MediaFormatReader::Update(TrackType aTrack)
mVideo.mIsHardwareAccelerated =
mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error);
}
RefPtr<MediaData> output = decoder.mOutput[0];
decoder.mOutput.RemoveElementAt(0);
decoder.mSizeOfQueue -= 1;
decoder.mLastSampleTime =
Some(media::TimeUnit::FromMicroseconds(output->mTime));
ReturnOutput(output, aTrack);
} else if (decoder.mError) {
} else if (decoder.HasFatalError()) {
LOG("Rejecting %s promise: DECODE_ERROR", TrackTypeToStr(aTrack));
decoder.RejectPromise(DECODE_ERROR, __func__);
return;
@@ -1146,7 +1163,7 @@ MediaFormatReader::Update(TrackType aTrack)
// Set up the internal seek machinery to be able to resume from the
// last sample decoded.
LOG("Seeking to last sample time: %lld",
decoder.mLastSampleTime.ref().ToMicroseconds());
decoder.mLastSampleTime.ref().mStart.ToMicroseconds());
InternalSeek(aTrack, InternalSeekTarget(decoder.mLastSampleTime.ref(), true));
}
if (!decoder.mReceivedNewData) {
@@ -1161,6 +1178,14 @@ MediaFormatReader::Update(TrackType aTrack)
LOGV("Nothing more to do");
return;
}
} else if (decoder.mDemuxEOS && !decoder.mNeedDraining &&
!decoder.HasPendingDrain() && decoder.mQueuedSamples.IsEmpty()) {
// It is possible to transition from WAITING_FOR_DATA directly to EOS
// state during the internal seek; in which case no draining would occur.
// There is no more samples left to be decoded and we are already in
// EOS state. We can immediately reject the data promise.
LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack));
decoder.RejectPromise(END_OF_STREAM, __func__);
}
}
@@ -1169,6 +1194,23 @@ MediaFormatReader::Update(TrackType aTrack)
return;
}
if (decoder.mError &&
decoder.mError.ref() == MediaDataDecoderError::DECODE_ERROR) {
decoder.mError.reset();
if (++decoder.mNumOfConsecutiveError > decoder.mMaxConsecutiveError) {
NotifyError(aTrack);
return;
}
LOG("%s decoded error count %d", TrackTypeToStr(aTrack),
decoder.mNumOfConsecutiveError);
media::TimeUnit nextKeyframe;
if (aTrack == TrackType::kVideoTrack && !decoder.HasInternalSeekPending() &&
NS_SUCCEEDED(decoder.mTrackDemuxer->GetNextRandomAccessPoint(&nextKeyframe))) {
SkipVideoDemuxToNextKeyFrame(decoder.mLastSampleTime.refOr(TimeInterval()).Length());
return;
}
}
bool needInput = NeedInput(decoder);
LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d ahead:%d sid:%u",
@@ -1284,15 +1326,17 @@ MediaFormatReader::ResetDecode(TargetQueues aQueues)
mSkipRequest.DisconnectIfExists();
// Do the same for any data wait promises.
mAudio.mWaitingPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::CANCELED), __func__);
if (aQueues == AUDIO_VIDEO) {
mAudio.mWaitingPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::CANCELED), __func__);
}
mVideo.mWaitingPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::CANCELED), __func__);
// Reset miscellaneous seeking state.
mPendingSeekTime.reset();
ResetDemuxers();
if (HasVideo()) {
mVideo.ResetDemuxer();
mVideo.ResetState();
Reset(TrackInfo::kVideoTrack);
if (mVideo.HasPromise()) {
mVideo.RejectPromise(CANCELED, __func__);
@@ -1300,6 +1344,8 @@ MediaFormatReader::ResetDecode(TargetQueues aQueues)
}
if (HasAudio() && aQueues == AUDIO_VIDEO) {
mAudio.ResetDemuxer();
mAudio.ResetState();
Reset(TrackInfo::kAudioTrack);
if (mAudio.HasPromise()) {
mAudio.RejectPromise(CANCELED, __func__);
@@ -1308,32 +1354,19 @@ MediaFormatReader::ResetDecode(TargetQueues aQueues)
return MediaDecoderReader::ResetDecode(aQueues);
}
void
MediaFormatReader::ResetDemuxers()
{
if (HasVideo()) {
mVideo.ResetDemuxer();
mVideo.ResetState();
}
if (HasAudio()) {
mAudio.ResetDemuxer();
mAudio.ResetState();
}
}
void
MediaFormatReader::Output(TrackType aTrack, MediaData* aSample)
{
LOGV("Decoded %s sample time=%lld timecode=%lld kf=%d dur=%lld",
TrackTypeToStr(aTrack), aSample->mTime, aSample->mTimecode,
aSample->mKeyframe, aSample->mDuration);
if (!aSample) {
NS_WARNING("MediaFormatReader::Output() passed a null sample");
Error(aTrack);
return;
}
LOGV("Decoded %s sample time=%lld timecode=%lld kf=%d dur=%lld",
TrackTypeToStr(aTrack), aSample->mTime, aSample->mTimecode,
aSample->mKeyframe, aSample->mDuration);
RefPtr<nsIRunnable> task =
NewRunnableMethod<TrackType, MediaData*>(
this, &MediaFormatReader::NotifyNewOutput, aTrack, aSample);
@@ -1359,11 +1392,11 @@ MediaFormatReader::InputExhausted(TrackType aTrack)
}
void
MediaFormatReader::Error(TrackType aTrack)
MediaFormatReader::Error(TrackType aTrack, MediaDataDecoderError aError)
{
RefPtr<nsIRunnable> task =
NewRunnableMethod<TrackType>(
this, &MediaFormatReader::NotifyError, aTrack);
NewRunnableMethod<TrackType, MediaDataDecoderError>(
this, &MediaFormatReader::NotifyError, aTrack, aError);
OwnerThread()->Dispatch(task.forget());
}
@@ -1381,34 +1414,41 @@ MediaFormatReader::Reset(TrackType aTrack)
LOG("Reset(%s) END", TrackTypeToStr(aTrack));
}
void
MediaFormatReader::DropDecodedSamples(TrackType aTrack)
{
MOZ_ASSERT(OnTaskQueue());
auto& decoder = GetDecoderData(aTrack);
size_t lengthDecodedQueue = decoder.mOutput.Length();
if (lengthDecodedQueue && decoder.mTimeThreshold.isSome()) {
TimeUnit time =
TimeUnit::FromMicroseconds(decoder.mOutput.LastElement()->mTime);
if (time >= decoder.mTimeThreshold.ref().Time()) {
// We would have reached our internal seek target.
decoder.mTimeThreshold.reset();
}
}
decoder.mOutput.Clear();
decoder.mSizeOfQueue -= lengthDecodedQueue;
if (aTrack == TrackInfo::kVideoTrack && mDecoder) {
mDecoder->NotifyDecodedFrames(0, 0, lengthDecodedQueue);
}
}
void
MediaFormatReader::SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold)
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(mVideo.HasPromise());
LOG("Skipping up to %lld", aTimeThreshold.ToMicroseconds());
// Cancel any pending demux request and pending demuxed samples.
mVideo.mDemuxRequest.DisconnectIfExists();
// I think it's still possible for an output to have been sent from the decoder
// and is currently sitting in our event queue waiting to be processed. The following
// flush won't clear it, and when we return to the event loop it'll be added to our
// output queue and be used.
// This code will count that as dropped, which was the intent, but not quite true.
mDecoder->NotifyDecodedFrames(0, 0, SizeOfVideoQueueInFrames());
if (mVideo.mTimeThreshold) {
LOGV("Internal Seek pending, cancelling it");
}
Reset(TrackInfo::kVideoTrack);
if (mVideo.mError) {
// We have flushed the decoder, and we are in error state, we can
// immediately reject the promise as there is nothing more to do.
mVideo.RejectPromise(DECODE_ERROR, __func__);
return;
}
// We've reached SkipVideoDemuxToNextKeyFrame when our decoding is late.
// As such we can drop all already decoded samples and discard all pending
// samples.
// TODO: Ideally we should set mOutputRequested to false so that all pending
// frames are dropped too. However, we can't do such thing as the code assumes
// that the decoder just got flushed. Once bug 1257107 land, we could set the
// decoder threshold to the value of currentTime.
DropDecodedSamples(TrackInfo::kVideoTrack);
mSkipRequest.Begin(mVideo.mTrackDemuxer->SkipToNextRandomAccessPoint(aTimeThreshold)
->Then(OwnerThread(), __func__, this,
@@ -1417,19 +1457,39 @@ MediaFormatReader::SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold)
return;
}
void
MediaFormatReader::VideoSkipReset(uint32_t aSkipped)
{
MOZ_ASSERT(OnTaskQueue());
// Some frames may have been output by the decoder since we initiated the
// videoskip process and we know they would be late.
DropDecodedSamples(TrackInfo::kVideoTrack);
// Report the pending frames as dropped.
if (mDecoder) {
mDecoder->NotifyDecodedFrames(0, 0, SizeOfVideoQueueInFrames());
}
// Cancel any pending demux request and pending demuxed samples.
mVideo.mDemuxRequest.DisconnectIfExists();
Reset(TrackType::kVideoTrack);
if (mDecoder) {
mDecoder->NotifyDecodedFrames(aSkipped, 0, aSkipped);
}
mVideo.mNumSamplesSkippedTotal += aSkipped;
}
void
MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped)
{
MOZ_ASSERT(OnTaskQueue());
LOG("Skipping succeeded, skipped %u frames", aSkipped);
mSkipRequest.Complete();
if (mDecoder) {
mDecoder->NotifyDecodedFrames(aSkipped, 0, aSkipped);
}
mVideo.mNumSamplesSkippedTotal += aSkipped;
mVideo.mNumSamplesSkippedTotalSinceTelemetry += aSkipped;
MOZ_ASSERT(!mVideo.mError); // We have flushed the decoder, no frame could
// have been decoded (and as such errored)
VideoSkipReset(aSkipped);
NotifyDecodingRequested(TrackInfo::kVideoTrack);
}
@@ -1439,18 +1499,18 @@ MediaFormatReader::OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailu
MOZ_ASSERT(OnTaskQueue());
LOG("Skipping failed, skipped %u frames", aFailure.mSkipped);
mSkipRequest.Complete();
if (mDecoder) {
mDecoder->NotifyDecodedFrames(aFailure.mSkipped, 0, aFailure.mSkipped);
}
MOZ_ASSERT(mVideo.HasPromise());
switch (aFailure.mFailure) {
case DemuxerFailureReason::END_OF_STREAM:
NotifyEndOfStream(TrackType::kVideoTrack);
break;
case DemuxerFailureReason::END_OF_STREAM: MOZ_FALLTHROUGH;
case DemuxerFailureReason::WAITING_FOR_DATA:
NotifyWaitingForData(TrackType::kVideoTrack);
// Some frames may have been output by the decoder since we initiated the
// videoskip process and we know they would be late.
DropDecodedSamples(TrackInfo::kVideoTrack);
// We can't complete the skip operation, will just service a video frame
// normally.
NotifyDecodingRequested(TrackInfo::kVideoTrack);
break;
case DemuxerFailureReason::CANCELED:
case DemuxerFailureReason::CANCELED: MOZ_FALLTHROUGH;
case DemuxerFailureReason::SHUTDOWN:
if (mVideo.HasPromise()) {
mVideo.RejectPromise(CANCELED, __func__);
@@ -1485,8 +1545,8 @@ MediaFormatReader::Seek(SeekTarget aTarget, int64_t aUnused)
return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
mOriginalSeekTarget = Some(aTarget);
mPendingSeekTime = Some(aTarget.GetTime());
mOriginalSeekTarget = aTarget;
mFallbackSeekTime = mPendingSeekTime = Some(aTarget.GetTime());
RefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
@@ -1515,7 +1575,20 @@ MediaFormatReader::AttemptSeek()
if (mPendingSeekTime.isNothing()) {
return;
}
ResetDemuxers();
if (HasVideo()) {
mVideo.ResetDemuxer();
mVideo.ResetState();
}
// Don't reset the audio demuxer not state when seeking video only
// as it will cause the audio to seek back to the beginning
// resulting in out-of-sync audio from video.
if (HasAudio() && !mOriginalSeekTarget.IsVideoOnly()) {
mAudio.ResetDemuxer();
mAudio.ResetState();
}
if (HasVideo()) {
DoVideoSeek();
} else if (HasAudio()) {
@@ -1538,8 +1611,8 @@ MediaFormatReader::OnSeekFailed(TrackType aTrack, DemuxerFailureReason aResult)
if (aResult == DemuxerFailureReason::WAITING_FOR_DATA) {
if (HasVideo() && aTrack == TrackType::kAudioTrack &&
mOriginalSeekTarget.isSome() &&
mPendingSeekTime.ref() != mOriginalSeekTarget.ref().GetTime()) {
mFallbackSeekTime.isSome() &&
mPendingSeekTime.ref() != mFallbackSeekTime.ref()) {
// We have failed to seek audio where video seeked to earlier.
// Attempt to seek instead to the closest point that we know we have in
// order to limit A/V sync discrepency.
@@ -1555,11 +1628,11 @@ MediaFormatReader::OnSeekFailed(TrackType aTrack, DemuxerFailureReason aResult)
}
}
if (nextSeekTime.isNothing() ||
nextSeekTime.ref() > mOriginalSeekTarget.ref().GetTime()) {
nextSeekTime = Some(mOriginalSeekTarget.ref().GetTime());
nextSeekTime.ref() > mFallbackSeekTime.ref()) {
nextSeekTime = Some(mFallbackSeekTime.ref());
LOG("Unable to seek audio to video seek time. A/V sync may be broken");
} else {
mOriginalSeekTarget.reset();
mFallbackSeekTime.reset();
}
mPendingSeekTime = nextSeekTime;
DoAudioSeek();
@@ -1592,10 +1665,9 @@ MediaFormatReader::OnVideoSeekCompleted(media::TimeUnit aTime)
LOGV("Video seeked to %lld", aTime.ToMicroseconds());
mVideo.mSeekRequest.Complete();
MOZ_ASSERT(mOriginalSeekTarget.isSome());
if (HasAudio() && !mOriginalSeekTarget->IsVideoOnly()) {
if (HasAudio() && !mOriginalSeekTarget.IsVideoOnly()) {
MOZ_ASSERT(mPendingSeekTime.isSome());
if (mOriginalSeekTarget->IsFast()) {
if (mOriginalSeekTarget.IsFast()) {
// We are performing a fast seek. We need to seek audio to where the
// video seeked to, to ensure proper A/V sync once playback resume.
mPendingSeekTime = Some(aTime);
@@ -1781,7 +1853,7 @@ MediaFormatReader::GetMozDebugReaderData(nsAString& aString)
int(mAudio.mQueuedSamples.Length()),
mAudio.mDecodingRequested,
mAudio.mTimeThreshold
? mAudio.mTimeThreshold.ref().mTime.ToSeconds()
? mAudio.mTimeThreshold.ref().Time().ToSeconds()
: -1.0,
mAudio.mTimeThreshold
? mAudio.mTimeThreshold.ref().mHasSeeked
@@ -1805,7 +1877,7 @@ MediaFormatReader::GetMozDebugReaderData(nsAString& aString)
int(mVideo.mQueuedSamples.Length()),
mVideo.mDecodingRequested,
mVideo.mTimeThreshold
? mVideo.mTimeThreshold.ref().mTime.ToSeconds()
? mVideo.mTimeThreshold.ref().Time().ToSeconds()
: -1.0,
mVideo.mTimeThreshold
? mVideo.mTimeThreshold.ref().mHasSeeked
@@ -1818,51 +1890,4 @@ MediaFormatReader::GetMozDebugReaderData(nsAString& aString)
aString += NS_ConvertUTF8toUTF16(result);
}
void
MediaFormatReader::ReportDroppedFramesTelemetry()
{
MOZ_ASSERT(OnTaskQueue());
const VideoInfo* info =
mVideo.mInfo ? mVideo.mInfo->GetAsVideoInfo() : &mInfo.mVideo;
if (!info || !mVideo.mDecoder) {
return;
}
nsCString keyPhrase = nsCString("MimeType=");
keyPhrase.Append(info->mMimeType);
keyPhrase.Append("; ");
keyPhrase.Append("Resolution=");
keyPhrase.AppendInt(info->mDisplay.width);
keyPhrase.Append('x');
keyPhrase.AppendInt(info->mDisplay.height);
keyPhrase.Append("; ");
keyPhrase.Append("HardwareAcceleration=");
if (VideoIsHardwareAccelerated()) {
keyPhrase.Append(mVideo.mDecoder->GetDescriptionName());
keyPhrase.Append("enabled");
} else {
keyPhrase.Append("disabled");
}
if (mVideo.mNumSamplesOutputTotalSinceTelemetry) {
uint32_t percentage =
100 * mVideo.mNumSamplesSkippedTotalSinceTelemetry /
mVideo.mNumSamplesOutputTotalSinceTelemetry;
nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([=]() -> void {
LOG("Reporting telemetry DROPPED_FRAMES_IN_VIDEO_PLAYBACK");
Telemetry::Accumulate(Telemetry::VIDEO_DETAILED_DROPPED_FRAMES_PROPORTION,
keyPhrase,
percentage);
});
AbstractThread::MainThread()->Dispatch(task.forget());
}
mVideo.mNumSamplesSkippedTotalSinceTelemetry = 0;
mVideo.mNumSamplesOutputTotalSinceTelemetry = 0;
}
} // namespace mozilla
+48 -23
View File
@@ -133,14 +133,21 @@ private:
MediaRawData* aSample);
struct InternalSeekTarget {
InternalSeekTarget(const media::TimeUnit& aTime, bool aDropTarget)
InternalSeekTarget(const media::TimeInterval& aTime, bool aDropTarget)
: mTime(aTime)
, mDropTarget(aDropTarget)
, mWaiting(false)
, mHasSeeked(false)
{}
media::TimeUnit mTime;
media::TimeUnit Time() const { return mTime.mStart; }
media::TimeUnit EndTime() const { return mTime.mEnd; }
bool Contains(const media::TimeUnit& aTime) const
{
return mTime.Contains(aTime);
}
media::TimeInterval mTime;
bool mDropTarget;
bool mWaiting;
bool mHasSeeked;
@@ -155,7 +162,7 @@ private:
void NotifyNewOutput(TrackType aTrack, MediaData* aSample);
void NotifyInputExhausted(TrackType aTrack);
void NotifyDrainComplete(TrackType aTrack);
void NotifyError(TrackType aTrack);
void NotifyError(TrackType aTrack, MediaDataDecoderError aError = MediaDataDecoderError::FATAL_ERROR);
void NotifyWaitingForData(TrackType aTrack);
void NotifyEndOfStream(TrackType aTrack);
void NotifyDecodingRequested(TrackType aTrack);
@@ -169,12 +176,12 @@ private:
// functions.
void Output(TrackType aType, MediaData* aSample);
void InputExhausted(TrackType aTrack);
void Error(TrackType aTrack);
void Error(TrackType aTrack, MediaDataDecoderError aError = MediaDataDecoderError::FATAL_ERROR);
void Reset(TrackType aTrack);
void DrainComplete(TrackType aTrack);
void DropDecodedSamples(TrackType aTrack);
bool ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold);
void ResetDemuxers();
size_t SizeOfQueue(TrackType aTrack);
@@ -193,8 +200,8 @@ private:
void InputExhausted() override {
mReader->InputExhausted(mType);
}
void Error() override {
mReader->Error(mType);
void Error(MediaDataDecoderError aError) override {
mReader->Error(mType, aError);
}
void DrainComplete() override {
mReader->DrainComplete(mType);
@@ -214,7 +221,8 @@ private:
struct DecoderData {
DecoderData(MediaFormatReader* aOwner,
MediaData::Type aType,
uint32_t aDecodeAhead)
uint32_t aDecodeAhead,
uint32_t aNumOfMaxError)
: mOwner(aOwner)
, mType(aType)
, mMonitor("DecoderData")
@@ -229,16 +237,15 @@ private:
, mDecodingRequested(false)
, mOutputRequested(false)
, mInputExhausted(false)
, mError(false)
, mNeedDraining(false)
, mDraining(false)
, mDrainComplete(false)
, mNumOfConsecutiveError(0)
, mMaxConsecutiveError(aNumOfMaxError)
, mNumSamplesInput(0)
, mNumSamplesOutput(0)
, mNumSamplesOutputTotal(0)
, mNumSamplesSkippedTotal(0)
, mNumSamplesOutputTotalSinceTelemetry(0)
, mNumSamplesSkippedTotalSinceTelemetry(0)
, mSizeOfQueue(0)
, mIsHardwareAccelerated(false)
, mLastStreamSourceID(UINT32_MAX)
@@ -300,16 +307,30 @@ private:
bool mDecodingRequested;
bool mOutputRequested;
bool mInputExhausted;
bool mError;
bool mNeedDraining;
bool mDraining;
bool mDrainComplete;
bool HasPendingDrain() const
{
return mDraining || mDrainComplete;
}
uint32_t mNumOfConsecutiveError;
uint32_t mMaxConsecutiveError;
Maybe<MediaDataDecoderError> mError;
bool HasFatalError() const
{
return mError.isSome() && mError.ref() == MediaDataDecoderError::FATAL_ERROR;
}
// If set, all decoded samples prior mTimeThreshold will be dropped.
// Used for internal seeking when a change of stream is detected or when
// encountering data discontinuity.
Maybe<InternalSeekTarget> mTimeThreshold;
// Time of last sample returned.
Maybe<media::TimeUnit> mLastSampleTime;
Maybe<media::TimeInterval> mLastSampleTime;
// Decoded samples returned my mDecoder awaiting being returned to
// state machine upon request.
@@ -319,9 +340,6 @@ private:
uint64_t mNumSamplesOutputTotal;
uint64_t mNumSamplesSkippedTotal;
uint64_t mNumSamplesOutputTotalSinceTelemetry;
uint64_t mNumSamplesSkippedTotalSinceTelemetry;
// These get overridden in the templated concrete class.
// Indicate if we have a pending promise for decoded frame.
// Rejecting the promise will stop the reader from decoding ahead.
@@ -368,7 +386,6 @@ private:
MOZ_ASSERT(mOwner->OnTaskQueue());
mDemuxEOS = false;
mWaitingForData = false;
mReceivedNewData = false;
mDiscontinuity = true;
mQueuedSamples.Clear();
mDecodingRequested = false;
@@ -384,6 +401,9 @@ private:
mNumSamplesOutput = 0;
mSizeOfQueue = 0;
mNextStreamSourceID.reset();
if (!HasFatalError()) {
mError.reset();
}
}
bool HasInternalSeekPending() const
@@ -408,8 +428,9 @@ private:
public:
DecoderDataWithPromise(MediaFormatReader* aOwner,
MediaData::Type aType,
uint32_t aDecodeAhead)
: DecoderData(aOwner, aType, aDecodeAhead)
uint32_t aDecodeAhead,
uint32_t aNumOfMaxError)
: DecoderData(aOwner, aType, aDecodeAhead, aNumOfMaxError)
, mHasPromise(false)
{}
@@ -479,6 +500,7 @@ private:
void SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold);
MozPromiseRequestHolder<MediaTrackDemuxer::SkipAccessPointPromise> mSkipRequest;
void VideoSkipReset(uint32_t aSkipped);
void OnVideoSkipCompleted(uint32_t aSkipped);
void OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailure);
@@ -508,6 +530,10 @@ private:
// Seeking objects.
bool IsSeeking() const { return mPendingSeekTime.isSome(); }
bool IsVideoSeeking() const
{
return IsSeeking() && mOriginalSeekTarget.IsVideoOnly();
}
void ScheduleSeek();
void AttemptSeek();
void OnSeekFailed(TrackType aTrack, DemuxerFailureReason aFailure);
@@ -525,11 +551,10 @@ private:
{
OnSeekFailed(TrackType::kAudioTrack, aFailure);
}
void ReportDroppedFramesTelemetry();
// The SeekTarget that was last given to Seek()
SeekTarget mOriginalSeekTarget;
// Temporary seek information while we wait for the data
Maybe<SeekTarget> mOriginalSeekTarget;
Maybe<media::TimeUnit> mFallbackSeekTime;
Maybe<media::TimeUnit> mPendingSeekTime;
MozPromiseHolder<SeekPromise> mSeekPromise;
+3
View File
@@ -95,6 +95,9 @@ private:
DECL_MEDIA_PREF("media.webm.intel_decoder.enabled", PDMWMFIntelDecoderEnabled, bool, false);
DECL_MEDIA_PREF("media.wmf.low-latency.enabled", PDMWMFLowLatencyEnabled, bool, false);
DECL_MEDIA_PREF("media.wmf.decoder.thread-count", PDMWMFThreadCount, int32_t, -1);
DECL_MEDIA_PREF("media.wmf.skip-blacklist", PDMWMFSkipBlacklist, bool, false);
DECL_MEDIA_PREF("media.windows-media-foundation.max-dxva-videos", PDMWMFMaxDXVAVideos, uint32_t, 8);
DECL_MEDIA_PREF("media.windows-media-foundation.allow-d3d11-dxva", PDMWMFAllowD3D11, bool, true);
#endif
DECL_MEDIA_PREF("media.decoder.fuzzing.enabled", PDMFuzzingEnabled, bool, false);
DECL_MEDIA_PREF("media.decoder.fuzzing.video-output-minimum-interval-ms", PDMFuzzingInterval, uint32_t, 0);
+2 -2
View File
@@ -237,7 +237,7 @@ SeekTask::EnsureVideoDecodeTaskQueued()
IsVideoDecoding(), VideoRequestStatus());
if (!IsVideoDecoding() ||
mReader->IsRequestingVidoeData() ||
mReader->IsRequestingVideoData() ||
mVideoWaitRequest.Exists() ||
mSeekRequest.Exists()) {
return NS_OK;
@@ -264,7 +264,7 @@ const char*
SeekTask::VideoRequestStatus()
{
AssertOwnerThread();
if (mReader->IsRequestingVidoeData()) {
if (mReader->IsRequestingVideoData()) {
MOZ_DIAGNOSTIC_ASSERT(!mVideoWaitRequest.Exists());
return "pending";
} else if (mVideoWaitRequest.Exists()) {
+1 -1
View File
@@ -6,7 +6,7 @@
#ifndef ISOCOMPOSITOR_H_
#define ISOCOMPOSITOR_H_
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "nsTArray.h"
#include "ISOTrackMetadata.h"
#include "EncodedFrameContainer.h"
-3
View File
@@ -153,7 +153,6 @@ MP4Decoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs,
}
// Verify that we have a PDM that supports the whitelisted types.
PDMFactory::Init();
RefPtr<PDMFactory> platform = new PDMFactory();
for (const nsCString& codecMime : codecMimes) {
if (!platform->SupportsMimeType(codecMime, aDiagnostics)) {
@@ -232,8 +231,6 @@ CreateTestH264Decoder(layers::LayersBackend aBackend,
aConfig.mExtraData->AppendElements(sTestH264ExtraData,
MOZ_ARRAY_LENGTH(sTestH264ExtraData));
PDMFactory::Init();
RefPtr<PDMFactory> platform = new PDMFactory();
RefPtr<MediaDataDecoder> decoder(
platform->CreateDecoder(aConfig, aTaskQueue, nullptr,
+1 -1
View File
@@ -10,7 +10,7 @@
#include "GMPParent.h"
#include "gmp-storage.h"
#include "mozilla/unused.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "nsClassHashtable.h"
#include "prio.h"
#include "mozIGeckoMediaPluginService.h"
+1 -1
View File
@@ -7,7 +7,7 @@
#include "ContainerParser.h"
#include "WebMBufferedParser.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/ErrorResult.h"
#include "mp4_demuxer/MoofParser.h"
#include "mozilla/Logging.h"
+19 -8
View File
@@ -337,7 +337,8 @@ MediaSourceTrackDemuxer::Reset()
{
MonitorAutoLock mon(self->mMonitor);
self->mNextRandomAccessPoint =
self->mManager->GetNextRandomAccessPoint(self->mType);
self->mManager->GetNextRandomAccessPoint(self->mType,
MediaSourceDemuxer::EOS_FUZZ);
}
});
mParent->GetTaskQueue()->Dispatch(task.forget());
@@ -410,7 +411,8 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
mReset = false;
{
MonitorAutoLock mon(mMonitor);
mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint(mType);
mNextRandomAccessPoint =
mManager->GetNextRandomAccessPoint(mType, MediaSourceDemuxer::EOS_FUZZ);
}
return SeekPromise::CreateAndResolve(seekTime, __func__);
}
@@ -451,7 +453,8 @@ MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples)
samples->mSamples.AppendElement(sample);
if (mNextRandomAccessPoint.ToMicroseconds() <= sample->mTime) {
MonitorAutoLock mon(mMonitor);
mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint(mType);
mNextRandomAccessPoint =
mManager->GetNextRandomAccessPoint(mType, MediaSourceDemuxer::EOS_FUZZ);
}
return SamplesPromise::CreateAndResolve(samples, __func__);
}
@@ -459,11 +462,19 @@ MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples)
RefPtr<MediaSourceTrackDemuxer::SkipAccessPointPromise>
MediaSourceTrackDemuxer::DoSkipToNextRandomAccessPoint(media::TimeUnit aTimeThreadshold)
{
bool found;
uint32_t parsed =
mManager->SkipToNextRandomAccessPoint(mType, aTimeThreadshold, found);
if (found) {
return SkipAccessPointPromise::CreateAndResolve(parsed, __func__);
uint32_t parsed = 0;
// Ensure that the data we are about to skip to is still available.
TimeIntervals buffered = mManager->Buffered(mType);
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
if (buffered.Contains(aTimeThreadshold)) {
bool found;
parsed = mManager->SkipToNextRandomAccessPoint(mType,
aTimeThreadshold,
MediaSourceDemuxer::EOS_FUZZ,
found);
if (found) {
return SkipAccessPointPromise::CreateAndResolve(parsed, __func__);
}
}
SkipFailureHolder holder(
mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
+149 -47
View File
@@ -1920,32 +1920,114 @@ TrackBuffersManager::Seek(TrackInfo::TrackType aTrack,
uint32_t
TrackBuffersManager::SkipToNextRandomAccessPoint(TrackInfo::TrackType aTrack,
const TimeUnit& aTimeThreadshold,
const media::TimeUnit& aFuzz,
bool& aFound)
{
MOZ_ASSERT(OnTaskQueue());
uint32_t parsed = 0;
auto& trackData = GetTracksData(aTrack);
const TrackBuffer& track = GetTrackBuffer(aTrack);
aFound = false;
uint32_t nextSampleIndex = trackData.mNextGetSampleIndex.valueOr(0);
for (uint32_t i = nextSampleIndex; i < track.Length(); i++) {
const RefPtr<MediaRawData>& sample = track[i];
// SkipToNextRandomAccessPoint can only be called if aTimeThreadshold is known
// to be buffered.
// So first determine the current position in the track buffer if necessary.
if (trackData.mNextGetSampleIndex.isNothing()) {
if (trackData.mNextSampleTimecode == TimeUnit()) {
// First demux, get first sample.
trackData.mNextGetSampleIndex = Some(0u);
} else {
int32_t pos = FindCurrentPosition(aTrack, aFuzz);
if (pos < 0) {
return 0;
}
trackData.mNextGetSampleIndex = Some(uint32_t(pos));
}
}
TimeUnit nextSampleTimecode = trackData.mNextSampleTimecode;
TimeUnit nextSampleTime = trackData.mNextSampleTime;
uint32_t i = trackData.mNextGetSampleIndex.ref();
int32_t originalPos = i;
for (; i < track.Length(); i++) {
const MediaRawData* sample =
GetSample(aTrack,
i,
nextSampleTimecode,
nextSampleTime,
aFuzz);
if (!sample) {
break;
}
if (sample->mKeyframe &&
sample->mTime >= aTimeThreadshold.ToMicroseconds()) {
trackData.mNextSampleTimecode =
TimeUnit::FromMicroseconds(sample->mTimecode);
trackData.mNextSampleTime =
TimeUnit::FromMicroseconds(sample->mTime);
trackData.mNextGetSampleIndex = Some(i);
aFound = true;
break;
}
nextSampleTimecode =
TimeUnit::FromMicroseconds(sample->mTimecode + sample->mDuration);
nextSampleTime = TimeUnit::FromMicroseconds(sample->GetEndTime());
parsed++;
}
// Adjust the next demux time and index so that the next call to
// SkipToNextRandomAccessPoint will not count again the parsed sample as
// skipped.
if (aFound) {
trackData.mNextSampleTimecode = nextSampleTimecode;
trackData.mNextSampleTime = nextSampleTime;
trackData.mNextGetSampleIndex = Some(i);
} else if (i > 0) {
// Go back to the previous keyframe or the original position so the next
// demux can succeed and be decoded.
for (int j = i - 1; j >= originalPos; j--) {
const RefPtr<MediaRawData>& sample = track[j];
if (sample->mKeyframe) {
trackData.mNextSampleTimecode =
TimeUnit::FromMicroseconds(sample->mTimecode);
trackData.mNextSampleTime = TimeUnit::FromMicroseconds(sample->mTime);
trackData.mNextGetSampleIndex = Some(uint32_t(j));
// We are unable to skip to a keyframe past aTimeThreshold, however
// we are speeding up decoding by dropping the unplayable frames.
// So we can mark aFound as true.
aFound = true;
break;
}
parsed--;
}
}
return parsed;
}
const MediaRawData*
TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
size_t aIndex,
const TimeUnit& aExpectedDts,
const TimeUnit& aExpectedPts,
const TimeUnit& aFuzz)
{
MOZ_ASSERT(OnTaskQueue());
const TrackBuffer& track = GetTrackBuffer(aTrack);
if (aIndex >= track.Length()) {
// reached the end.
return nullptr;
}
const RefPtr<MediaRawData>& sample = track[aIndex];
if (!aIndex || sample->mTimecode <= (aExpectedDts + aFuzz).ToMicroseconds() ||
sample->mTime <= (aExpectedPts + aFuzz).ToMicroseconds()) {
return sample;
}
// Gap is too big. End of Stream or Waiting for Data.
// TODO, check that we have continuous data based on the sanitized buffered
// range instead.
return nullptr;
}
already_AddRefed<MediaRawData>
TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
const TimeUnit& aFuzz,
@@ -1957,9 +2039,7 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
aError = false;
if (!track.Length() ||
(trackData.mNextGetSampleIndex.isSome() &&
trackData.mNextGetSampleIndex.ref() >= track.Length())) {
if (!track.Length()) {
return nullptr;
}
if (trackData.mNextGetSampleIndex.isNothing() &&
@@ -1969,11 +2049,13 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
}
if (trackData.mNextGetSampleIndex.isSome()) {
const RefPtr<MediaRawData>& sample =
track[trackData.mNextGetSampleIndex.ref()];
if (trackData.mNextGetSampleIndex.ref() &&
sample->mTimecode > (trackData.mNextSampleTimecode + aFuzz).ToMicroseconds()) {
// Gap is too big. End of Stream or Waiting for Data.
const MediaRawData* sample =
GetSample(aTrack,
trackData.mNextGetSampleIndex.ref(),
trackData.mNextSampleTimecode,
trackData.mNextSampleTime,
aFuzz);
if (!sample) {
return nullptr;
}
@@ -1992,6 +2074,37 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
}
// Our previous index has been overwritten, attempt to find the new one.
int32_t pos = FindCurrentPosition(aTrack, aFuzz);
if (pos < 0) {
MSE_DEBUG("Couldn't find sample (pts:%lld dts:%lld)",
trackData.mNextSampleTime.ToMicroseconds(),
trackData.mNextSampleTimecode.ToMicroseconds());
return nullptr;
}
const RefPtr<MediaRawData>& sample = track[pos];
RefPtr<MediaRawData> p = sample->Clone();
if (!p) {
// OOM
aError = true;
return nullptr;
}
trackData.mNextGetSampleIndex = Some(uint32_t(pos)+1);
trackData.mNextSampleTimecode =
TimeUnit::FromMicroseconds(sample->mTimecode + sample->mDuration);
trackData.mNextSampleTime =
TimeUnit::FromMicroseconds(sample->GetEndTime());
return p.forget();
}
int32_t
TrackBuffersManager::FindCurrentPosition(TrackInfo::TrackType aTrack,
const TimeUnit& aFuzz)
{
MOZ_ASSERT(OnTaskQueue());
auto& trackData = GetTracksData(aTrack);
const TrackBuffer& track = GetTrackBuffer(aTrack);
for (uint32_t i = 0; i < track.Length(); i++) {
const RefPtr<MediaRawData>& sample = track[i];
TimeInterval sampleInterval{
@@ -2000,23 +2113,13 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
aFuzz};
if (sampleInterval.ContainsWithStrictEnd(trackData.mNextSampleTimecode)) {
RefPtr<MediaRawData> p = sample->Clone();
if (!p) {
// OOM
aError = true;
return nullptr;
}
trackData.mNextGetSampleIndex = Some(i+1);
trackData.mNextSampleTimecode = sampleInterval.mEnd;
trackData.mNextSampleTime =
TimeUnit::FromMicroseconds(sample->GetEndTime());
return p.forget();
return i;
}
}
// We couldn't find our sample by decode timestamp. Attempt to find it using
// presentation timestamp. There will likely be small jerkiness.
for (uint32_t i = 0; i < track.Length(); i++) {
for (uint32_t i = 0; i < track.Length(); i++) {
const RefPtr<MediaRawData>& sample = track[i];
TimeInterval sampleInterval{
TimeUnit::FromMicroseconds(sample->mTime),
@@ -2024,42 +2127,41 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
aFuzz};
if (sampleInterval.ContainsWithStrictEnd(trackData.mNextSampleTimecode)) {
RefPtr<MediaRawData> p = sample->Clone();
if (!p) {
// OOM
aError = true;
return nullptr;
}
trackData.mNextGetSampleIndex = Some(i+1);
// Estimate decode timestamp of the next sample.
trackData.mNextSampleTimecode = sampleInterval.mEnd;
trackData.mNextSampleTime =
TimeUnit::FromMicroseconds(sample->GetEndTime());
return p.forget();
return i;
}
}
MSE_DEBUG("Couldn't find sample (pts:%lld dts:%lld)",
trackData.mNextSampleTime.ToMicroseconds(),
trackData.mNextSampleTimecode.ToMicroseconds());
return nullptr;
// Still not found.
return -1;
}
TimeUnit
TrackBuffersManager::GetNextRandomAccessPoint(TrackInfo::TrackType aTrack)
TrackBuffersManager::GetNextRandomAccessPoint(TrackInfo::TrackType aTrack,
const TimeUnit& aFuzz)
{
MOZ_ASSERT(OnTaskQueue());
auto& trackData = GetTracksData(aTrack);
MOZ_ASSERT(trackData.mNextGetSampleIndex.isSome());
const TrackBuffersManager::TrackBuffer& track = GetTrackBuffer(aTrack);
uint32_t i = trackData.mNextGetSampleIndex.ref();
TimeUnit nextSampleTimecode = trackData.mNextSampleTimecode;
TimeUnit nextSampleTime = trackData.mNextSampleTime;
for (; i < track.Length(); i++) {
const RefPtr<MediaRawData>& sample = track[i];
const MediaRawData* sample =
GetSample(aTrack, i, nextSampleTimecode, nextSampleTime, aFuzz);
if (!sample) {
break;
}
if (sample->mKeyframe) {
return TimeUnit::FromMicroseconds(sample->mTime);
}
nextSampleTimecode =
TimeUnit::FromMicroseconds(sample->mTimecode + sample->mDuration);
nextSampleTime = TimeUnit::FromMicroseconds(sample->GetEndTime());
}
return media::TimeUnit::FromInfinity();
return TimeUnit::FromInfinity();
}
void
+10 -1
View File
@@ -141,11 +141,15 @@ public:
const media::TimeUnit& aFuzz);
uint32_t SkipToNextRandomAccessPoint(TrackInfo::TrackType aTrack,
const media::TimeUnit& aTimeThreadshold,
const media::TimeUnit& aFuzz,
bool& aFound);
already_AddRefed<MediaRawData> GetSample(TrackInfo::TrackType aTrack,
const media::TimeUnit& aFuzz,
bool& aError);
media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack);
int32_t FindCurrentPosition(TrackInfo::TrackType aTrack,
const media::TimeUnit& aFuzz);
media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack,
const media::TimeUnit& aFuzz);
void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes);
@@ -339,6 +343,11 @@ private:
// Find index of sample. Return a negative value if not found.
uint32_t FindSampleIndex(const TrackBuffer& aTrackBuffer,
const media::TimeInterval& aInterval);
const MediaRawData* GetSample(TrackInfo::TrackType aTrack,
size_t aIndex,
const media::TimeUnit& aExpectedDts,
const media::TimeUnit& aExpectedPts,
const media::TimeUnit& aFuzz);
void UpdateBufferedRanges();
void RejectProcessing(nsresult aRejectValue, const char* aName);
void ResolveProcessing(bool aResolveValue, const char* aName);
+1 -1
View File
@@ -7,7 +7,7 @@
#include <string.h>
#include "mozilla/DebugOnly.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include <stdint.h>
#include "nsDebug.h"
+1 -1
View File
@@ -7,7 +7,7 @@
#include <string.h>
#include "mozilla/DebugOnly.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include <stdint.h>
#include "OpusParser.h"
+1 -1
View File
@@ -176,7 +176,7 @@ void MediaOmxReader::ReleaseMediaResources()
mMediaResourceRequest.DisconnectIfExists();
mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__);
ResetDecode();
ResetDecode(AUDIO_VIDEO);
// Before freeing a video codec, all video buffers needed to be released
// even from graphics pipeline.
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
+2 -2
View File
@@ -74,11 +74,11 @@ protected:
void NotifyDataArrivedInternal() override;
public:
nsresult ResetDecode() override
nsresult ResetDecode(TargetQueues aQueues) override
{
mSeekRequest.DisconnectIfExists();
mSeekPromise.RejectIfExists(NS_OK, __func__);
return MediaDecoderReader::ResetDecode();
return MediaDecoderReader::ResetDecode(aQueues);
}
bool DecodeAudioData() override;
+37 -25
View File
@@ -23,9 +23,10 @@
#endif
#include "GMPDecoderModule.h"
#include "mozilla/TaskQueue.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/SharedThreadPool.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TaskQueue.h"
#include "MediaInfo.h"
#include "MediaPrefs.h"
@@ -46,34 +47,29 @@ namespace mozilla {
extern already_AddRefed<PlatformDecoderModule> CreateAgnosticDecoderModule();
extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
/* static */
void
PDMFactory::Init()
{
MOZ_ASSERT(NS_IsMainThread());
static bool alreadyInitialized = false;
if (alreadyInitialized) {
return;
}
alreadyInitialized = true;
// Ensure MediaPrefs are initialized.
MediaPrefs::GetSingleton();
class PDMFactoryImpl final {
public:
PDMFactoryImpl()
{
#ifdef XP_WIN
WMFDecoderModule::Init();
WMFDecoderModule::Init();
#endif
#ifdef MOZ_APPLEMEDIA
AppleDecoderModule::Init();
AppleDecoderModule::Init();
#endif
#ifdef MOZ_FFMPEG
FFmpegRuntimeLinker::Link();
FFmpegRuntimeLinker::Link();
#endif
GMPDecoderModule::Init();
}
GMPDecoderModule::Init();
}
};
StaticAutoPtr<PDMFactoryImpl> PDMFactory::sInstance;
StaticMutex PDMFactory::sMonitor;
PDMFactory::PDMFactory()
{
EnsureInit();
CreatePDMs();
}
@@ -81,13 +77,29 @@ PDMFactory::~PDMFactory()
{
}
void
PDMFactory::EnsureInit() const
{
StaticMutexAutoLock mon(sMonitor);
if (!sInstance) {
sInstance = new PDMFactoryImpl();
if (NS_IsMainThread()) {
ClearOnShutdown(&sInstance);
} else {
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableFunction([]() { ClearOnShutdown(&sInstance); });
NS_DispatchToMainThread(runnable);
}
}
}
already_AddRefed<MediaDataDecoder>
PDMFactory::CreateDecoder(const TrackInfo& aConfig,
FlushableTaskQueue* aTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer)
layers::ImageContainer* aImageContainer) const
{
bool isEncrypted = mEMEPDM && aConfig.mCrypto.mValid;
@@ -138,11 +150,11 @@ PDMFactory::CreateDecoder(const TrackInfo& aConfig,
already_AddRefed<MediaDataDecoder>
PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
const TrackInfo& aConfig,
FlushableTaskQueue* aTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer)
layers::ImageContainer* aImageContainer) const
{
MOZ_ASSERT(aPDM);
RefPtr<MediaDataDecoder> m;
+12 -8
View File
@@ -8,12 +8,15 @@
#define PDMFactory_h_
#include "PlatformDecoderModule.h"
#include "mozilla/StaticMutex.h"
class CDMProxy;
namespace mozilla {
class DecoderDoctorDiagnostics;
class PDMFactoryImpl;
template<class T> class StaticAutoPtr;
class PDMFactory final {
public:
@@ -21,10 +24,6 @@ public:
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
@@ -32,11 +31,11 @@ public:
// This is called on the decode task queue.
already_AddRefed<MediaDataDecoder>
CreateDecoder(const TrackInfo& aConfig,
FlushableTaskQueue* aTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics,
layers::LayersBackend aLayersBackend = layers::LayersBackend::LAYERS_NONE,
layers::ImageContainer* aImageContainer = nullptr);
layers::ImageContainer* aImageContainer = nullptr) const;
bool SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const;
@@ -63,11 +62,11 @@ private:
already_AddRefed<MediaDataDecoder>
CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
const TrackInfo& aConfig,
FlushableTaskQueue* aTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer);
layers::ImageContainer* aImageContainer) const;
nsTArray<RefPtr<PlatformDecoderModule>> mCurrentPDMs;
RefPtr<PlatformDecoderModule> mEMEPDM;
@@ -75,6 +74,11 @@ private:
bool mWMFFailedToLoad = false;
bool mFFmpegFailedToLoad = false;
bool mGMPPDMFailedToStartup = false;
void EnsureInit() const;
template<class T> friend class StaticAutoPtr;
static StaticAutoPtr<PDMFactoryImpl> sInstance;
static StaticMutex sMonitor;
};
} // namespace mozilla
@@ -1,12 +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 "PlatformDecoderModule.h"
mozilla::LogModule* GetPDMLog() {
static mozilla::LazyLogModule log("PlatformDecoderModule");
return log;
}
+11 -5
View File
@@ -7,7 +7,6 @@
#if !defined(PlatformDecoderModule_h_)
#define PlatformDecoderModule_h_
#include "FlushableTaskQueue.h"
#include "MediaDecoderReader.h"
#include "mozilla/MozPromise.h"
#include "mozilla/layers/LayersTypes.h"
@@ -28,9 +27,11 @@ class ImageContainer;
class MediaDataDecoder;
class MediaDataDecoderCallback;
class FlushableTaskQueue;
class TaskQueue;
class CDMProxy;
static LazyLogModule sPDMLog("PlatformDecoderModule");
// The PlatformDecoderModule interface is used by the MediaFormatReader to
// abstract access to decoders provided by various
// platforms.
@@ -90,7 +91,7 @@ protected:
CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) = 0;
@@ -106,11 +107,16 @@ protected:
// This is called on the decode task queue.
virtual already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) = 0;
};
enum MediaDataDecoderError {
FATAL_ERROR,
DECODE_ERROR
};
// A callback used by MediaDataDecoder to return output/errors to the
// MediaFormatReader.
// Implementation is threadsafe, and can be called on any thread.
@@ -123,7 +129,7 @@ public:
// Denotes an error in the decoding process. The reader will stop calling
// the decoder.
virtual void Error() = 0;
virtual void Error(MediaDataDecoderError aError) = 0;
// Denotes that the last input sample has been inserted into the decoder,
// and no more output can be produced unless more input is sent.
@@ -26,7 +26,7 @@ already_AddRefed<MediaDataDecoder>
AgnosticDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
@@ -35,7 +35,7 @@ AgnosticDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
if (VPXDecoder::IsVPX(aConfig.mMimeType)) {
m = new VPXDecoder(*aConfig.GetAsVideoInfo(),
aImageContainer,
aVideoTaskQueue,
aTaskQueue,
aCallback);
}
@@ -44,7 +44,7 @@ AgnosticDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
already_AddRefed<MediaDataDecoder>
AgnosticDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
@@ -52,16 +52,14 @@ AgnosticDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
if (VorbisDataDecoder::IsVorbis(aConfig.mMimeType)) {
m = new VorbisDataDecoder(*aConfig.GetAsAudioInfo(),
aAudioTaskQueue,
aTaskQueue,
aCallback);
} else if (OpusDataDecoder::IsOpus(aConfig.mMimeType)) {
m = new OpusDataDecoder(*aConfig.GetAsAudioInfo(),
aAudioTaskQueue,
aTaskQueue,
aCallback);
} else if (WaveDataDecoder::IsWave(aConfig.mMimeType)) {
m = new WaveDataDecoder(*aConfig.GetAsAudioInfo(),
aAudioTaskQueue,
aCallback);
m = new WaveDataDecoder(*aConfig.GetAsAudioInfo(), aCallback);
}
return m.forget();
@@ -25,14 +25,14 @@ protected:
CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
// Decode thread.
already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
};
@@ -25,11 +25,9 @@ class BlankMediaDataDecoder : public MediaDataDecoder {
public:
BlankMediaDataDecoder(BlankMediaDataCreator* aCreator,
FlushableTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
TrackInfo::TrackType aType)
: mCreator(aCreator)
, mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mType(aType)
{
@@ -43,46 +41,21 @@ public:
return NS_OK;
}
class OutputEvent : public Runnable {
public:
OutputEvent(MediaRawData* aSample,
MediaDataDecoderCallback* aCallback,
BlankMediaDataCreator* aCreator)
: mSample(aSample)
, mCreator(aCreator)
, mCallback(aCallback)
{
}
NS_IMETHOD Run() override
{
RefPtr<MediaData> data =
mCreator->Create(media::TimeUnit::FromMicroseconds(mSample->mTime),
media::TimeUnit::FromMicroseconds(mSample->mDuration),
mSample->mOffset);
if (!data) {
return NS_ERROR_OUT_OF_MEMORY;
}
mCallback->Output(data);
return NS_OK;
}
private:
RefPtr<MediaRawData> mSample;
BlankMediaDataCreator* mCreator;
MediaDataDecoderCallback* mCallback;
};
nsresult Input(MediaRawData* aSample) override
{
// The MediaDataDecoder must delete the sample when we're finished
// with it, so the OutputEvent stores it in an nsAutoPtr and deletes
// it once it's run.
RefPtr<nsIRunnable> r(new OutputEvent(aSample, mCallback, mCreator));
mTaskQueue->Dispatch(r.forget());
RefPtr<MediaData> data =
mCreator->Create(media::TimeUnit::FromMicroseconds(aSample->mTime),
media::TimeUnit::FromMicroseconds(aSample->mDuration),
aSample->mOffset);
if (!data) {
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
} else {
mCallback->Output(data);
}
return NS_OK;
}
nsresult Flush() override {
mTaskQueue->Flush();
return NS_OK;
}
@@ -98,7 +71,6 @@ public:
private:
nsAutoPtr<BlankMediaDataCreator> mCreator;
RefPtr<FlushableTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
TrackInfo::TrackType mType;
};
@@ -229,14 +201,13 @@ public:
CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override {
BlankVideoDataCreator* creator = new BlankVideoDataCreator(
aConfig.mDisplay.width, aConfig.mDisplay.height, aImageContainer);
RefPtr<MediaDataDecoder> decoder =
new BlankMediaDataDecoder<BlankVideoDataCreator>(creator,
aVideoTaskQueue,
aCallback,
TrackInfo::kVideoTrack);
return decoder.forget();
@@ -245,7 +216,7 @@ public:
// Decode thread.
already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override {
BlankAudioDataCreator* creator = new BlankAudioDataCreator(
@@ -253,7 +224,6 @@ public:
RefPtr<MediaDataDecoder> decoder =
new BlankMediaDataDecoder<BlankAudioDataCreator>(creator,
aAudioTaskQueue,
aCallback,
TrackInfo::kAudioTrack);
return decoder.forget();
+28 -18
View File
@@ -8,15 +8,14 @@
#include "TimeUnits.h"
#include "VorbisUtils.h"
#include "VorbisDecoder.h" // For VorbisLayout
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/PodOperations.h"
#include "mozilla/SyncRunnable.h"
#include <stdint.h>
#include <inttypes.h> // For PRId64
extern mozilla::LogModule* GetPDMLog();
#define OPUS_DEBUG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, \
#define OPUS_DEBUG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, \
("OpusDataDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
namespace mozilla {
@@ -146,14 +145,25 @@ OpusDataDecoder::ProcessDecode(MediaRawData* aSample)
if (mIsFlushing) {
return;
}
if (DoDecode(aSample) == -1) {
mCallback->Error();
} else if(mTaskQueue->IsEmpty()) {
DecodeError err = DoDecode(aSample);
switch (err) {
case DecodeError::FATAL_ERROR:
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return;
case DecodeError::DECODE_ERROR:
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
break;
case DecodeError::DECODE_SUCCESS:
break;
}
if (mTaskQueue->IsEmpty()) {
mCallback->InputExhausted();
}
}
int
OpusDataDecoder::DecodeError
OpusDataDecoder::DoDecode(MediaRawData* aSample)
{
int64_t aDiscardPadding = 0;
@@ -166,7 +176,7 @@ OpusDataDecoder::DoDecode(MediaRawData* aSample)
// Discard padding should be used only on the final packet, so
// decoding after a padding discard is invalid.
OPUS_DEBUG("Opus error, discard padding on interstitial packet");
return -1;
return FATAL_ERROR;
}
if (!mLastFrameTime || mLastFrameTime.ref() != aSample->mTime) {
@@ -181,7 +191,7 @@ OpusDataDecoder::DoDecode(MediaRawData* aSample)
if (frames_number <= 0) {
OPUS_DEBUG("Invalid packet header: r=%ld length=%ld",
frames_number, aSample->Size());
return -1;
return FATAL_ERROR;
}
int32_t samples = opus_packet_get_samples_per_frame(aSample->Data(),
@@ -192,12 +202,12 @@ OpusDataDecoder::DoDecode(MediaRawData* aSample)
int32_t frames = frames_number*samples;
if (frames < 120 || frames > 5760) {
OPUS_DEBUG("Invalid packet frames: %ld", frames);
return -1;
return FATAL_ERROR;
}
AlignedAudioBuffer buffer(frames * channels);
if (!buffer) {
return -1;
return FATAL_ERROR;
}
// Decode to the appropriate sample type.
@@ -211,7 +221,7 @@ OpusDataDecoder::DoDecode(MediaRawData* aSample)
buffer.get(), frames, false);
#endif
if (ret < 0) {
return -1;
return DECODE_ERROR;
}
NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
CheckedInt64 startTime = aSample->mTime;
@@ -232,7 +242,7 @@ OpusDataDecoder::DoDecode(MediaRawData* aSample)
if (aDiscardPadding < 0) {
// Negative discard padding is invalid.
OPUS_DEBUG("Opus error, negative discard padding");
return -1;
return FATAL_ERROR;
}
if (aDiscardPadding > 0) {
OPUS_DEBUG("OpusDecoder discardpadding %" PRId64 "", aDiscardPadding);
@@ -241,12 +251,12 @@ OpusDataDecoder::DoDecode(MediaRawData* aSample)
mOpusParser->mRate);
if (!discardFrames.isValid()) {
NS_WARNING("Int overflow in DiscardPadding");
return -1;
return FATAL_ERROR;
}
if (discardFrames.value() > frames) {
// Discarding more than the entire packet is invalid.
OPUS_DEBUG("Opus error, discard padding larger than packet");
return -1;
return FATAL_ERROR;
}
OPUS_DEBUG("Opus decoder discarding %d of %d frames",
int32_t(discardFrames.value()), frames);
@@ -281,14 +291,14 @@ OpusDataDecoder::DoDecode(MediaRawData* aSample)
CheckedInt64 duration = FramesToUsecs(frames, mOpusParser->mRate);
if (!duration.isValid()) {
NS_WARNING("OpusDataDecoder: Int overflow converting WebM audio duration");
return -1;
return FATAL_ERROR;
}
CheckedInt64 time =
startTime - FramesToUsecs(mOpusParser->mPreSkip, mOpusParser->mRate) +
FramesToUsecs(mFrames, mOpusParser->mRate);
if (!time.isValid()) {
NS_WARNING("OpusDataDecoder: Int overflow shifting tstamp by codec delay");
return -1;
return FATAL_ERROR;
};
mCallback->Output(new AudioData(aSample->mOffset,
@@ -299,7 +309,7 @@ OpusDataDecoder::DoDecode(MediaRawData* aSample)
mOpusParser->mChannels,
mOpusParser->mRate));
mFrames += frames;
return frames;
return DECODE_SUCCESS;
}
void
+7 -1
View File
@@ -36,10 +36,16 @@ public:
static bool IsOpus(const nsACString& aMimeType);
private:
enum DecodeError {
DECODE_SUCCESS,
DECODE_ERROR,
FATAL_ERROR
};
nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
void ProcessDecode(MediaRawData* aSample);
int DoDecode(MediaRawData* aSample);
DecodeError DoDecode(MediaRawData* aSample);
void ProcessDrain();
const AudioInfo& mInfo;
+37 -19
View File
@@ -9,36 +9,41 @@
#include "nsError.h"
#include "TimeUnits.h"
#include "mozilla/PodOperations.h"
#include "mozilla/SyncRunnable.h"
#include "prsystem.h"
#include <algorithm>
#undef LOG
extern mozilla::LogModule* GetPDMLog();
#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("VPXDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("VPXDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
namespace mozilla {
using namespace gfx;
using namespace layers;
static int MimeTypeToCodec(const nsACString& aMimeType)
{
if (aMimeType.EqualsLiteral("video/webm; codecs=vp8")) {
return VPXDecoder::Codec::VP8;
} else if (aMimeType.EqualsLiteral("video/webm; codecs=vp9")) {
return VPXDecoder::Codec::VP9;
}
return -1;
}
VPXDecoder::VPXDecoder(const VideoInfo& aConfig,
ImageContainer* aImageContainer,
FlushableTaskQueue* aTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
: mImageContainer(aImageContainer)
, mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mIsFlushing(false)
, mInfo(aConfig)
, mCodec(MimeTypeToCodec(aConfig.mMimeType))
{
MOZ_COUNT_CTOR(VPXDecoder);
if (aConfig.mMimeType.EqualsLiteral("video/webm; codecs=vp8")) {
mCodec = Codec::VP8;
} else if (aConfig.mMimeType.EqualsLiteral("video/webm; codecs=vp9")) {
mCodec = Codec::VP9;
} else {
mCodec = -1;
}
PodZero(&mVPX);
}
@@ -85,13 +90,20 @@ VPXDecoder::Init()
nsresult
VPXDecoder::Flush()
{
mTaskQueue->Flush();
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
mIsFlushing = true;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([this] () {
// nothing to do for now.
});
SyncRunnable::DispatchToThread(mTaskQueue, r);
mIsFlushing = false;
return NS_OK;
}
int
VPXDecoder::DoDecodeFrame(MediaRawData* aSample)
VPXDecoder::DoDecode(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
#if defined(DEBUG)
vpx_codec_stream_info_t si;
PodZero(&si);
@@ -174,10 +186,14 @@ VPXDecoder::DoDecodeFrame(MediaRawData* aSample)
}
void
VPXDecoder::DecodeFrame(MediaRawData* aSample)
VPXDecoder::ProcessDecode(MediaRawData* aSample)
{
if (DoDecodeFrame(aSample) == -1) {
mCallback->Error();
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
if (mIsFlushing) {
return;
}
if (DoDecode(aSample) == -1) {
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
} else if (mTaskQueue->IsEmpty()) {
mCallback->InputExhausted();
}
@@ -186,23 +202,25 @@ VPXDecoder::DecodeFrame(MediaRawData* aSample)
nsresult
VPXDecoder::Input(MediaRawData* aSample)
{
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
this, &VPXDecoder::DecodeFrame,
RefPtr<MediaRawData>(aSample)));
this, &VPXDecoder::ProcessDecode, aSample));
return NS_OK;
}
void
VPXDecoder::DoDrain()
VPXDecoder::ProcessDrain()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
mCallback->DrainComplete();
}
nsresult
VPXDecoder::Drain()
{
mTaskQueue->Dispatch(NewRunnableMethod(this, &VPXDecoder::DoDrain));
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
mTaskQueue->Dispatch(NewRunnableMethod(this, &VPXDecoder::ProcessDrain));
return NS_OK;
}
+9 -9
View File
@@ -16,14 +16,14 @@
namespace mozilla {
using namespace layers;
using namespace layers;
class VPXDecoder : public MediaDataDecoder
{
public:
VPXDecoder(const VideoInfo& aConfig,
ImageContainer* aImageContainer,
FlushableTaskQueue* aTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback);
~VPXDecoder();
@@ -47,21 +47,21 @@ public:
static bool IsVPX(const nsACString& aMimeType, uint8_t aCodecMask=VP8|VP9);
private:
void DecodeFrame (MediaRawData* aSample);
int DoDecodeFrame (MediaRawData* aSample);
void DoDrain ();
void OutputDelayedFrames ();
void ProcessDecode(MediaRawData* aSample);
int DoDecode(MediaRawData* aSample);
void ProcessDrain();
RefPtr<ImageContainer> mImageContainer;
RefPtr<FlushableTaskQueue> mTaskQueue;
const RefPtr<ImageContainer> mImageContainer;
const RefPtr<TaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
Atomic<bool> mIsFlushing;
// VPx decoder state
vpx_codec_ctx_t mVPX;
const VideoInfo& mInfo;
int mCodec;
const int mCodec;
};
} // namespace mozilla
+29 -15
View File
@@ -9,11 +9,11 @@
#include "XiphExtradata.h"
#include "mozilla/PodOperations.h"
#include "mozilla/SyncRunnable.h"
#include "nsAutoPtr.h"
#undef LOG
extern mozilla::LogModule* GetPDMLog();
#define LOG(type, msg) MOZ_LOG(GetPDMLog(), type, msg)
#define LOG(type, msg) MOZ_LOG(sPDMLog, type, msg)
namespace mozilla {
@@ -32,13 +32,14 @@ ogg_packet InitVorbisPacket(const unsigned char* aData, size_t aLength,
}
VorbisDataDecoder::VorbisDataDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
: mInfo(aConfig)
, mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mPacketCount(0)
, mFrames(0)
, mIsFlushing(false)
{
// Zero these member vars to avoid crashes in Vorbis clear functions when
// destructor is called before |Init|.
@@ -129,18 +130,22 @@ VorbisDataDecoder::DecodeHeader(const unsigned char* aData, size_t aLength)
nsresult
VorbisDataDecoder::Input(MediaRawData* aSample)
{
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
this, &VorbisDataDecoder::Decode,
RefPtr<MediaRawData>(aSample)));
this, &VorbisDataDecoder::ProcessDecode, aSample));
return NS_OK;
}
void
VorbisDataDecoder::Decode(MediaRawData* aSample)
VorbisDataDecoder::ProcessDecode(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
if (mIsFlushing) {
return;
}
if (DoDecode(aSample) == -1) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
} else if (mTaskQueue->IsEmpty()) {
mCallback->InputExhausted();
}
@@ -149,6 +154,8 @@ VorbisDataDecoder::Decode(MediaRawData* aSample)
int
VorbisDataDecoder::DoDecode(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
const unsigned char* aData = aSample->Data();
size_t aLength = aSample->Size();
int64_t aOffset = aSample->mOffset;
@@ -255,27 +262,34 @@ VorbisDataDecoder::DoDecode(MediaRawData* aSample)
}
void
VorbisDataDecoder::DoDrain()
VorbisDataDecoder::ProcessDrain()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
mCallback->DrainComplete();
}
nsresult
VorbisDataDecoder::Drain()
{
mTaskQueue->Dispatch(NewRunnableMethod(this, &VorbisDataDecoder::DoDrain));
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
mTaskQueue->Dispatch(NewRunnableMethod(this, &VorbisDataDecoder::ProcessDrain));
return NS_OK;
}
nsresult
VorbisDataDecoder::Flush()
{
mTaskQueue->Flush();
// Ignore failed results from vorbis_synthesis_restart. They
// aren't fatal and it fails when ResetDecode is called at a
// time when no vorbis data has been read.
vorbis_synthesis_restart(&mVorbisDsp);
mLastFrameTime.reset();
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
mIsFlushing = true;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([this] () {
// Ignore failed results from vorbis_synthesis_restart. They
// aren't fatal and it fails when ResetDecode is called at a
// time when no vorbis data has been read.
vorbis_synthesis_restart(&mVorbisDsp);
mLastFrameTime.reset();
});
SyncRunnable::DispatchToThread(mTaskQueue, r);
mIsFlushing = false;
return NS_OK;
}
+6 -5
View File
@@ -22,7 +22,7 @@ class VorbisDataDecoder : public MediaDataDecoder
{
public:
VorbisDataDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback);
~VorbisDataDecoder();
@@ -43,12 +43,12 @@ public:
private:
nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
void Decode (MediaRawData* aSample);
int DoDecode (MediaRawData* aSample);
void DoDrain ();
void ProcessDecode(MediaRawData* aSample);
int DoDecode(MediaRawData* aSample);
void ProcessDrain();
const AudioInfo& mInfo;
RefPtr<FlushableTaskQueue> mTaskQueue;
const RefPtr<TaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
// Vorbis decoder state
@@ -61,6 +61,7 @@ private:
int64_t mFrames;
Maybe<int64_t> mLastFrameTime;
UniquePtr<AudioConverter> mAudioConverter;
Atomic<bool> mIsFlushing;
};
} // namespace mozilla
+4 -26
View File
@@ -7,6 +7,7 @@
#include "WAVDecoder.h"
#include "AudioSampleFormat.h"
#include "nsAutoPtr.h"
#include "mozilla/SyncRunnable.h"
using mp4_demuxer::ByteReader;
@@ -46,12 +47,9 @@ DecodeULawSample(uint8_t aValue)
}
WaveDataDecoder::WaveDataDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
: mInfo(aConfig)
, mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mFrames(0)
{
}
@@ -69,22 +67,11 @@ WaveDataDecoder::Init()
nsresult
WaveDataDecoder::Input(MediaRawData* aSample)
{
mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
this, &WaveDataDecoder::Decode,
RefPtr<MediaRawData>(aSample)));
return NS_OK;
}
void
WaveDataDecoder::Decode(MediaRawData* aSample)
{
if (!DoDecode(aSample)) {
mCallback->Error();
} else if (mTaskQueue->IsEmpty()) {
mCallback->InputExhausted();
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
}
return NS_OK;
}
bool
@@ -142,29 +129,20 @@ WaveDataDecoder::DoDecode(MediaRawData* aSample)
Move(buffer),
mInfo.mChannels,
mInfo.mRate));
mFrames += frames;
return true;
}
void
WaveDataDecoder::DoDrain()
{
mCallback->DrainComplete();
}
nsresult
WaveDataDecoder::Drain()
{
mTaskQueue->Dispatch(NewRunnableMethod(this, &WaveDataDecoder::DoDrain));
mCallback->DrainComplete();
return NS_OK;
}
nsresult
WaveDataDecoder::Flush()
{
mTaskQueue->Flush();
mFrames = 0;
return NS_OK;
}
+6 -12
View File
@@ -16,9 +16,12 @@ class WaveDataDecoder : public MediaDataDecoder
{
public:
WaveDataDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback);
// Return true if mimetype is Wave
static bool IsWave(const nsACString& aMimeType);
private:
RefPtr<InitPromise> Init() override;
nsresult Input(MediaRawData* aSample) override;
nsresult Flush() override;
@@ -29,20 +32,11 @@ public:
return "wave audio decoder";
}
// Return true if mimetype is Wave
static bool IsWave(const nsACString& aMimeType);
private:
void Decode (MediaRawData* aSample);
bool DoDecode (MediaRawData* aSample);
void DoDrain ();
bool DoDecode(MediaRawData* aSample);
const AudioInfo& mInfo;
RefPtr<FlushableTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
int64_t mFrames;
};
} // namespace mozilla
#endif
#endif
@@ -31,7 +31,7 @@ AudioCallbackAdapter::Decoded(const nsTArray<int16_t>& aPCM, uint64_t aTimeStamp
if (aRate == 0 || aChannels == 0) {
NS_WARNING("Invalid rate or num channels returned on GMP audio samples");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return;
}
@@ -39,7 +39,7 @@ AudioCallbackAdapter::Decoded(const nsTArray<int16_t>& aPCM, uint64_t aTimeStamp
MOZ_ASSERT((aPCM.Length() % aChannels) == 0);
AlignedAudioBuffer audioData(aPCM.Length());
if (!audioData) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return;
}
@@ -52,7 +52,7 @@ AudioCallbackAdapter::Decoded(const nsTArray<int16_t>& aPCM, uint64_t aTimeStamp
auto timestamp = UsecsToFrames(aTimeStamp, aRate);
if (!timestamp.isValid()) {
NS_WARNING("Invalid timestamp");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return;
}
mAudioFrameOffset = timestamp.value();
@@ -62,7 +62,7 @@ AudioCallbackAdapter::Decoded(const nsTArray<int16_t>& aPCM, uint64_t aTimeStamp
auto timestamp = FramesToUsecs(mAudioFrameOffset + mAudioFrameSum, aRate);
if (!timestamp.isValid()) {
NS_WARNING("Invalid timestamp on audio samples");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return;
}
mAudioFrameSum += numFrames;
@@ -70,7 +70,7 @@ AudioCallbackAdapter::Decoded(const nsTArray<int16_t>& aPCM, uint64_t aTimeStamp
auto duration = FramesToUsecs(numFrames, aRate);
if (!duration.isValid()) {
NS_WARNING("Invalid duration on audio samples");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return;
}
@@ -116,14 +116,14 @@ void
AudioCallbackAdapter::Error(GMPErr aErr)
{
MOZ_ASSERT(IsOnGMPThread());
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
}
void
AudioCallbackAdapter::Terminated()
{
NS_WARNING("AAC GMP decoder terminated.");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
}
void
@@ -205,7 +205,7 @@ GMPAudioDecoder::Input(MediaRawData* aSample)
RefPtr<MediaRawData> sample(aSample);
if (!mGMP) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return NS_ERROR_FAILURE;
}
@@ -214,7 +214,7 @@ GMPAudioDecoder::Input(MediaRawData* aSample)
gmp::GMPAudioSamplesImpl samples(sample, mConfig.mChannels, mConfig.mRate);
nsresult rv = mGMP->Decode(samples);
if (NS_FAILED(rv)) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
return rv;
}
@@ -13,6 +13,7 @@
#include "mozIGeckoMediaPluginService.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/SyncRunnable.h"
#include "gmp-audio-decode.h"
#include "gmp-video-decode.h"
#ifdef XP_WIN
@@ -48,7 +49,7 @@ already_AddRefed<MediaDataDecoder>
GMPDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
@@ -67,14 +68,14 @@ GMPDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
wrapper->SetProxyTarget(new GMPVideoDecoder(aConfig,
aLayersBackend,
aImageContainer,
aVideoTaskQueue,
aTaskQueue,
wrapper->Callback()));
return wrapper.forget();
}
already_AddRefed<MediaDataDecoder>
GMPDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
@@ -91,7 +92,7 @@ GMPDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
RefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback);
wrapper->SetProxyTarget(new GMPAudioDecoder(aConfig,
aAudioTaskQueue,
aTaskQueue,
wrapper->Callback()));
return wrapper.forget();
}
@@ -112,7 +113,6 @@ HasGMPFor(const nsACString& aAPI,
const nsACString& aCodec,
const nsACString& aGMP)
{
MOZ_ASSERT(NS_IsMainThread());
#ifdef XP_WIN
// gmp-clearkey uses WMF for decoding, so if we're using clearkey we must
// verify that WMF works before continuing.
@@ -130,6 +130,8 @@ HasGMPFor(const nsACString& aAPI,
}
}
#endif
MOZ_ASSERT(NS_IsMainThread(),
"HasPluginForAPI must be called on the main thread");
nsTArray<nsCString> tags;
tags.AppendElement(aCodec);
tags.AppendElement(aGMP);
@@ -178,8 +180,13 @@ GMPDecoderModule::UpdateUsableCodecs()
void
GMPDecoderModule::Init()
{
MOZ_ASSERT(NS_IsMainThread());
if (!NS_IsMainThread()) {
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableFunction([]() { Init(); });
SyncRunnable::DispatchToThread(mainThread, runnable);
return;
}
// GMPService::HasPluginForAPI is main thread only, so to implement
// SupportsMimeType() we build a table of the codecs which each whitelisted
// GMP has and update it when any GMPs are removed or added at runtime.
@@ -36,14 +36,14 @@ public:
CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
// Decode thread.
already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
@@ -6,7 +6,7 @@
#include "GMPVideoDecoder.h"
#include "GMPVideoHost.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "prsystem.h"
#include "MediaData.h"
#include "GMPDecoderModule.h"
@@ -52,7 +52,7 @@ VideoCallbackAdapter::Decoded(GMPVideoi420Frame* aDecodedFrame)
if (v) {
mCallback->Output(v);
} else {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
}
}
@@ -93,7 +93,7 @@ void
VideoCallbackAdapter::Error(GMPErr aErr)
{
MOZ_ASSERT(IsOnGMPThread());
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
}
void
@@ -101,7 +101,7 @@ VideoCallbackAdapter::Terminated()
{
// Note that this *may* be called from the proxy thread also.
NS_WARNING("H.264 GMP decoder terminated.");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
}
void
@@ -127,14 +127,14 @@ GMPVideoDecoder::CreateFrame(MediaRawData* aSample)
GMPVideoFrame* ftmp = nullptr;
GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
if (GMP_FAILED(err)) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return nullptr;
}
GMPUniquePtr<GMPVideoEncodedFrame> frame(static_cast<GMPVideoEncodedFrame*>(ftmp));
err = frame->CreateEmptyFrame(aSample->Size());
if (GMP_FAILED(err)) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return nullptr;
}
@@ -249,7 +249,7 @@ GMPVideoDecoder::Input(MediaRawData* aSample)
RefPtr<MediaRawData> sample(aSample);
if (!mGMP) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return NS_ERROR_FAILURE;
}
@@ -257,13 +257,13 @@ GMPVideoDecoder::Input(MediaRawData* aSample)
GMPUniquePtr<GMPVideoEncodedFrame> frame = CreateFrame(sample);
if (!frame) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return NS_ERROR_FAILURE;
}
nsTArray<uint8_t> info; // No codec specific per-frame info to pass.
nsresult rv = mGMP->Decode(Move(frame), false, info, 0);
if (NS_FAILED(rv)) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
return rv;
}
@@ -10,9 +10,9 @@
namespace mozilla {
void
MediaDataDecoderCallbackProxy::Error()
MediaDataDecoderCallbackProxy::Error(MediaDataDecoderError aError)
{
mProxyCallback->Error();
mProxyCallback->Error(aError);
}
void
@@ -74,7 +74,7 @@ public:
mProxyCallback->Output(aData);
}
void Error() override;
void Error(MediaDataDecoderError aError) override;
void InputExhausted() override {
mProxyCallback->InputExhausted();
@@ -269,7 +269,7 @@ AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType,
aMimeType.EqualsLiteral("audio/wave; codecs=7") ||
aMimeType.EqualsLiteral("audio/wave; codecs=65534")) {
return false;
}
}
if ((VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP8) &&
!GetFeatureStatus(nsIGfxInfo::FEATURE_VP8_HW_DECODE)) ||
@@ -285,7 +285,7 @@ AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType,
already_AddRefed<MediaDataDecoder>
AndroidDecoderModule::CreateVideoDecoder(
const VideoInfo& aConfig, layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue,
layers::ImageContainer* aImageContainer, TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
@@ -305,7 +305,7 @@ AndroidDecoderModule::CreateVideoDecoder(
already_AddRefed<MediaDataDecoder>
AndroidDecoderModule::CreateAudioDecoder(
const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue,
const AudioInfo& aConfig, TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
@@ -378,7 +378,7 @@ MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface)
{
mDecoder = CreateDecoder(mMimeType);
if (!mDecoder) {
INVOKE_CALLBACK(Error);
INVOKE_CALLBACK(Error, MediaDataDecoderError::FATAL_ERROR);
return NS_ERROR_FAILURE;
}
@@ -405,7 +405,7 @@ static const int64_t kDecoderTimeout = 10000;
INVOKE_CALLBACK(DrainComplete); \
State(kDecoding); \
} \
INVOKE_CALLBACK(Error); \
INVOKE_CALLBACK(Error, MediaDataDecoderError::FATAL_ERROR); \
break; \
}
@@ -636,7 +636,7 @@ MediaCodecDataDecoder::DecoderLoop()
BREAK_ON_DECODER_ERROR();
} else if (outputStatus < 0) {
NS_WARNING("Unknown error from decoder!");
INVOKE_CALLBACK(Error);
INVOKE_CALLBACK(Error, MediaDataDecoderError::DECODE_ERROR);
// Don't break here just in case it's recoverable. If it's not, other
// stuff will fail later and we'll bail out.
} else {
@@ -24,13 +24,13 @@ public:
CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
+40 -9
View File
@@ -10,10 +10,10 @@
#include "MediaInfo.h"
#include "AppleATDecoder.h"
#include "mozilla/Logging.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/UniquePtr.h"
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
#define FourCC2Str(n) ((char[5]){(char)(n >> 24), (char)(n >> 16), (char)(n >> 8), (char)(n), 0})
namespace mozilla {
@@ -27,6 +27,7 @@ AppleATDecoder::AppleATDecoder(const AudioInfo& aConfig,
, mCallback(aCallback)
, mConverter(nullptr)
, mStream(nullptr)
, mIsFlushing(false)
{
MOZ_COUNT_CTOR(AppleATDecoder);
LOG("Creating Apple AudioToolbox decoder");
@@ -65,6 +66,7 @@ AppleATDecoder::Init()
nsresult
AppleATDecoder::Input(MediaRawData* aSample)
{
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
LOG("mp4 input sample %p %lld us %lld pts%s %llu bytes audio",
aSample,
aSample->mDuration,
@@ -83,23 +85,34 @@ AppleATDecoder::Input(MediaRawData* aSample)
return NS_OK;
}
nsresult
AppleATDecoder::Flush()
void
AppleATDecoder::ProcessFlush()
{
LOG("Flushing AudioToolbox AAC decoder");
mTaskQueue->Flush();
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
mQueuedSamples.Clear();
OSStatus rv = AudioConverterReset(mConverter);
if (rv) {
LOG("Error %d resetting AudioConverter", rv);
return NS_ERROR_FAILURE;
}
}
nsresult
AppleATDecoder::Flush()
{
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
LOG("Flushing AudioToolbox AAC decoder");
mIsFlushing = true;
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod(this, &AppleATDecoder::ProcessFlush);
SyncRunnable::DispatchToThread(mTaskQueue, runnable);
mIsFlushing = false;
return NS_OK;
}
nsresult
AppleATDecoder::Drain()
{
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
LOG("Draining AudioToolbox AAC decoder");
mTaskQueue->AwaitIdle();
mCallback->DrainComplete();
@@ -109,6 +122,8 @@ AppleATDecoder::Drain()
nsresult
AppleATDecoder::Shutdown()
{
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
LOG("Shutdown: Apple AudioToolbox AAC decoder");
mQueuedSamples.Clear();
OSStatus rv = AudioConverterDispose(mConverter);
@@ -173,11 +188,17 @@ _PassthroughInputDataCallback(AudioConverterRef aAudioConverter,
void
AppleATDecoder::SubmitSample(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
if (mIsFlushing) {
return;
}
nsresult rv = NS_OK;
if (!mConverter) {
rv = SetupDecoder(aSample);
if (rv != NS_OK && rv != NS_ERROR_NOT_INITIALIZED) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return;
}
}
@@ -188,7 +209,7 @@ AppleATDecoder::SubmitSample(MediaRawData* aSample)
for (size_t i = 0; i < mQueuedSamples.Length(); i++) {
if (NS_FAILED(DecodeSample(mQueuedSamples[i]))) {
mQueuedSamples.Clear();
mCallback->Error();
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
return;
}
}
@@ -203,6 +224,8 @@ AppleATDecoder::SubmitSample(MediaRawData* aSample)
nsresult
AppleATDecoder::DecodeSample(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
// Array containing the queued decoded audio frames, about to be output.
nsTArray<AudioDataValue> outputData;
UInt32 channels = mOutputFormat.mChannelsPerFrame;
@@ -308,6 +331,8 @@ nsresult
AppleATDecoder::GetInputAudioDescription(AudioStreamBasicDescription& aDesc,
const nsTArray<uint8_t>& aExtraData)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
// Request the properties from CoreAudio using the codec magic cookie
AudioFormatInfo formatInfo;
PodZero(&formatInfo.mASBD);
@@ -411,6 +436,8 @@ ConvertChannelLabel(AudioChannelLabel id)
nsresult
AppleATDecoder::SetupChannelLayout()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
// Determine the channel layout.
UInt32 propertySize;
UInt32 size;
@@ -510,6 +537,8 @@ AppleATDecoder::SetupChannelLayout()
nsresult
AppleATDecoder::SetupDecoder(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
if (mFormatID == kAudioFormatMPEG4AAC &&
mConfig.mExtendedProfile == 2) {
// Check for implicit SBR signalling if stream is AAC-LC
@@ -615,6 +644,8 @@ _SampleCallback(void* aSBR,
nsresult
AppleATDecoder::GetImplicitAACMagicCookie(const MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
// Prepend ADTS header to AAC audio.
RefPtr<MediaRawData> adtssample(aSample->Clone());
if (!adtssample) {
@@ -56,7 +56,9 @@ private:
nsTArray<RefPtr<MediaRawData>> mQueuedSamples;
UniquePtr<AudioConfig::ChannelLayout> mChannelLayout;
UniquePtr<AudioConverter> mAudioConverter;
Atomic<bool> mIsFlushing;
void ProcessFlush();
void SubmitSample(MediaRawData* aSample);
nsresult DecodeSample(MediaRawData* aSample);
nsresult GetInputAudioDescription(AudioStreamBasicDescription& aDesc,
+2 -14
View File
@@ -7,7 +7,6 @@
#include <dlfcn.h>
#include "AppleCMLinker.h"
#include "MainThreadUtils.h"
#include "mozilla/ArrayUtils.h"
#include "nsDebug.h"
@@ -15,8 +14,7 @@
#include "nsCocoaFeatures.h"
#endif
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
namespace mozilla {
@@ -24,7 +22,6 @@ AppleCMLinker::LinkStatus
AppleCMLinker::sLinkStatus = LinkStatus_INIT;
void* AppleCMLinker::sLink = nullptr;
nsrefcnt AppleCMLinker::sRefCount = 0;
CFStringRef AppleCMLinker::skPropExtensionAtoms = nullptr;
CFStringRef AppleCMLinker::skPropFullRangeVideo = nullptr;
@@ -35,12 +32,6 @@ CFStringRef AppleCMLinker::skPropFullRangeVideo = nullptr;
/* static */ bool
AppleCMLinker::Link()
{
// Bump our reference count every time we're called.
// Add a lock or change the thread assertion if
// you need to call this off the main thread.
MOZ_ASSERT(NS_IsMainThread());
++sRefCount;
if (sLinkStatus) {
return sLinkStatus == LinkStatus_SUCCEEDED;
}
@@ -116,10 +107,7 @@ fail:
/* static */ void
AppleCMLinker::Unlink()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sRefCount > 0, "Unbalanced Unlink()");
--sRefCount;
if (sLink && sRefCount < 1) {
if (sLink) {
LOG("Unlinking CoreMedia framework.");
dlclose(sLink);
sLink = nullptr;
@@ -27,7 +27,6 @@ public:
private:
static void* sLink;
static nsrefcnt sRefCount;
static enum LinkStatus {
LinkStatus_INIT = 0,
@@ -37,8 +37,6 @@ AppleDecoderModule::~AppleDecoderModule()
void
AppleDecoderModule::Init()
{
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
if (sInitialized) {
return;
}
@@ -79,7 +77,7 @@ already_AddRefed<MediaDataDecoder>
AppleDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
@@ -88,7 +86,7 @@ AppleDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
if (sIsVDAAvailable && (!sIsVTHWAvailable || MediaPrefs::AppleForceVDA())) {
decoder =
AppleVDADecoder::CreateVDADecoder(aConfig,
aVideoTaskQueue,
aTaskQueue,
aCallback,
aImageContainer);
if (decoder) {
@@ -99,19 +97,19 @@ AppleDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
// supported by the current platform.
if (sIsVTAvailable) {
decoder =
new AppleVTDecoder(aConfig, aVideoTaskQueue, aCallback, aImageContainer);
new AppleVTDecoder(aConfig, aTaskQueue, aCallback, aImageContainer);
}
return decoder.forget();
}
already_AddRefed<MediaDataDecoder>
AppleDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
RefPtr<MediaDataDecoder> decoder =
new AppleATDecoder(aConfig, aAudioTaskQueue, aCallback);
new AppleATDecoder(aConfig, aTaskQueue, aCallback);
return decoder.forget();
}
@@ -23,14 +23,14 @@ public:
CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
// Decode thread.
already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
+62 -54
View File
@@ -15,6 +15,7 @@
#include "MP4Decoder.h"
#include "MediaData.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/SyncRunnable.h"
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
#include "mozilla/Logging.h"
@@ -27,23 +28,42 @@
#include "MacIOSurfaceImage.h"
#endif
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
//#define LOG_MEDIA_SHA1
namespace mozilla {
static uint32_t ComputeMaxRefFrames(const MediaByteBuffer* aExtraData)
{
uint32_t maxRefFrames = 4;
// Retrieve video dimensions from H264 SPS NAL.
mp4_demuxer::SPSData spsdata;
if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata)) {
// max_num_ref_frames determines the size of the sliding window
// we need to queue that many frames in order to guarantee proper
// pts frames ordering. Use a minimum of 4 to ensure proper playback of
// non compliant videos.
maxRefFrames =
std::min(std::max(maxRefFrames, spsdata.max_num_ref_frames + 1), 16u);
}
return maxRefFrames;
}
AppleVDADecoder::AppleVDADecoder(const VideoInfo& aConfig,
FlushableTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer)
: mTaskQueue(aVideoTaskQueue)
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer)
: mExtraData(aConfig.mExtraData)
, mCallback(aCallback)
, mImageContainer(aImageContainer)
, mPictureWidth(aConfig.mImage.width)
, mPictureHeight(aConfig.mImage.height)
, mDisplayWidth(aConfig.mDisplay.width)
, mDisplayHeight(aConfig.mDisplay.height)
, mQueuedSamples(0)
, mTaskQueue(aTaskQueue)
, mDecoder(nullptr)
, mMaxRefFrames(ComputeMaxRefFrames(aConfig.mExtraData))
, mImageContainer(aImageContainer)
, mInputIncoming(0)
, mIsShutDown(false)
#ifdef MOZ_WIDGET_UIKIT
@@ -52,28 +72,13 @@ AppleVDADecoder::AppleVDADecoder(const VideoInfo& aConfig,
#else
, mUseSoftwareImages(false)
, mIs106(!nsCocoaFeatures::OnLionOrLater())
#endif
, mQueuedSamples(0)
#endif
, mMonitor("AppleVideoDecoder")
, mIsFlushing(false)
, mDecoder(nullptr)
{
MOZ_COUNT_CTOR(AppleVDADecoder);
// TODO: Verify aConfig.mime_type.
mExtraData = aConfig.mExtraData;
mMaxRefFrames = 4;
// Retrieve video dimensions from H264 SPS NAL.
mp4_demuxer::SPSData spsdata;
if (mp4_demuxer::H264::DecodeSPSFromExtraData(mExtraData, spsdata)) {
// max_num_ref_frames determines the size of the sliding window
// we need to queue that many frames in order to guarantee proper
// pts frames ordering. Use a minimum of 4 to ensure proper playback of
// non compliant videos.
mMaxRefFrames =
std::min(std::max(mMaxRefFrames, spsdata.max_num_ref_frames + 1), 16u);
}
LOG("Creating AppleVDADecoder for %dx%d (%dx%d) h.264 video",
mPictureWidth,
mPictureHeight,
@@ -132,12 +137,8 @@ AppleVDADecoder::Input(MediaRawData* aSample)
mInputIncoming++;
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod<RefPtr<MediaRawData>>(
this,
&AppleVDADecoder::SubmitFrame,
RefPtr<MediaRawData>(aSample));
mTaskQueue->Dispatch(runnable.forget());
mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
this, &AppleVDADecoder::ProcessDecode, aSample));
return NS_OK;
}
@@ -146,15 +147,12 @@ AppleVDADecoder::Flush()
{
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
mIsFlushing = true;
mTaskQueue->Flush();
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod(this, &AppleVDADecoder::ProcessFlush);
MonitorAutoLock mon(mMonitor);
mTaskQueue->Dispatch(runnable.forget());
while (mIsFlushing) {
mon.Wait();
}
mInputIncoming = 0;
SyncRunnable::DispatchToThread(mTaskQueue, runnable);
mIsFlushing = false;
// All ProcessDecode() tasks should be done.
MOZ_ASSERT(mInputIncoming == 0);
return NS_OK;
}
@@ -171,7 +169,7 @@ AppleVDADecoder::Drain()
void
AppleVDADecoder::ProcessFlush()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
AssertOnTaskQueueThread();
OSStatus rv = VDADecoderFlush(mDecoder, 0 /*dont emit*/);
if (rv != noErr) {
@@ -179,15 +177,12 @@ AppleVDADecoder::ProcessFlush()
"with error:%d.", rv);
}
ClearReorderedFrames();
MonitorAutoLock mon(mMonitor);
mIsFlushing = false;
mon.NotifyAll();
}
void
AppleVDADecoder::ProcessDrain()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
AssertOnTaskQueueThread();
OSStatus rv = VDADecoderFlush(mDecoder, kVDADecoderFlush_EmitFrames);
if (rv != noErr) {
@@ -348,7 +343,7 @@ AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
CVReturn rv = CVPixelBufferLockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
if (rv != kCVReturnSuccess) {
NS_ERROR("error locking pixel data");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
return NS_ERROR_FAILURE;
}
// Y plane.
@@ -416,7 +411,7 @@ AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
if (!data) {
NS_ERROR("Couldn't create VideoData for frame");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return NS_ERROR_FAILURE;
}
@@ -434,12 +429,31 @@ AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
}
nsresult
AppleVDADecoder::SubmitFrame(MediaRawData* aSample)
AppleVDADecoder::ProcessDecode(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
AssertOnTaskQueueThread();
mInputIncoming--;
if (mIsFlushing) {
return NS_OK;
}
auto rv = DoDecode(aSample);
// Ask for more data.
if (NS_SUCCEEDED(rv) && !mInputIncoming && mQueuedSamples <= mMaxRefFrames) {
LOG("%s task queue empty; requesting more data", GetDescriptionName());
mCallback->InputExhausted();
}
return rv;
}
nsresult
AppleVDADecoder::DoDecode(MediaRawData* aSample)
{
AssertOnTaskQueueThread();
AutoCFRelease<CFDataRef> block =
CFDataCreate(kCFAllocatorDefault, aSample->Data(), aSample->Size());
if (!block) {
@@ -499,7 +513,7 @@ AppleVDADecoder::SubmitFrame(MediaRawData* aSample)
if (rv != noErr) {
NS_WARNING("AppleVDADecoder: Couldn't pass frame to decoder");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
return NS_ERROR_FAILURE;
}
@@ -514,12 +528,6 @@ AppleVDADecoder::SubmitFrame(MediaRawData* aSample)
CFRetain(frameInfo);
}
// Ask for more data.
if (!mInputIncoming && mQueuedSamples <= mMaxRefFrames) {
LOG("AppleVDADecoder task queue empty; requesting more data");
mCallback->InputExhausted();
}
return NS_OK;
}
@@ -662,7 +670,7 @@ AppleVDADecoder::CreateOutputConfiguration()
already_AddRefed<AppleVDADecoder>
AppleVDADecoder::CreateVDADecoder(
const VideoInfo& aConfig,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer)
{
@@ -672,7 +680,7 @@ AppleVDADecoder::CreateVDADecoder(
}
RefPtr<AppleVDADecoder> decoder =
new AppleVDADecoder(aConfig, aVideoTaskQueue, aCallback, aImageContainer);
new AppleVDADecoder(aConfig, aTaskQueue, aCallback, aImageContainer);
if (NS_FAILED(decoder->InitializeSession())) {
return nullptr;
+42 -37
View File
@@ -19,7 +19,7 @@
namespace mozilla {
class FlushableTaskQueue;
class TaskQueue;
class MediaDataDecoderCallback;
namespace layers {
class ImageContainer;
@@ -62,15 +62,16 @@ public:
// not supported by current configuration.
static already_AddRefed<AppleVDADecoder> CreateVDADecoder(
const VideoInfo& aConfig,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer);
AppleVDADecoder(const VideoInfo& aConfig,
FlushableTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer);
virtual ~AppleVDADecoder();
// Access from the taskqueue and the decoder's thread.
// OutputFrame is thread-safe.
nsresult OutputFrame(CVPixelBufferRef aImage,
AppleFrameRef aFrameRef);
private:
RefPtr<InitPromise> Init() override;
nsresult Input(MediaRawData* aSample) override;
nsresult Flush() override;
@@ -86,46 +87,52 @@ public:
return "apple VDA decoder";
}
// Access from the taskqueue and the decoder's thread.
// OutputFrame is thread-safe.
nsresult OutputFrame(CVPixelBufferRef aImage,
AppleFrameRef aFrameRef);
protected:
// Flush and Drain operation, always run
virtual void ProcessFlush();
virtual void ProcessDrain();
virtual void ProcessShutdown();
AppleVDADecoder(const VideoInfo& aConfig,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer);
virtual ~AppleVDADecoder();
void AssertOnTaskQueueThread()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
}
AppleFrameRef* CreateAppleFrameRef(const MediaRawData* aSample);
void DrainReorderedFrames();
void ClearReorderedFrames();
CFDictionaryRef CreateOutputConfiguration();
RefPtr<MediaByteBuffer> mExtraData;
RefPtr<FlushableTaskQueue> mTaskQueue;
const RefPtr<MediaByteBuffer> mExtraData;
MediaDataDecoderCallback* mCallback;
RefPtr<layers::ImageContainer> mImageContainer;
uint32_t mPictureWidth;
uint32_t mPictureHeight;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
// Accessed on multiple threads, but only set in constructor.
uint32_t mMaxRefFrames;
// Increased when Input is called, and decreased when ProcessFrame runs.
// Reaching 0 indicates that there's no pending Input.
Atomic<uint32_t> mInputIncoming;
Atomic<bool> mIsShutDown;
const bool mUseSoftwareImages;
const bool mIs106;
const uint32_t mPictureWidth;
const uint32_t mPictureHeight;
const uint32_t mDisplayWidth;
const uint32_t mDisplayHeight;
// Number of times a sample was queued via Input(). Will be decreased upon
// the decoder's callback being invoked.
// This is used to calculate how many frames has been buffered by the decoder.
Atomic<uint32_t> mQueuedSamples;
// For wait on mIsFlushing during Shutdown() process.
private:
// Flush and Drain operation, always run
virtual void ProcessFlush();
virtual void ProcessDrain();
virtual void ProcessShutdown();
const RefPtr<TaskQueue> mTaskQueue;
VDADecoder mDecoder;
const uint32_t mMaxRefFrames;
const RefPtr<layers::ImageContainer> mImageContainer;
// Increased when Input is called, and decreased when ProcessFrame runs.
// Reaching 0 indicates that there's no pending Input.
Atomic<uint32_t> mInputIncoming;
Atomic<bool> mIsShutDown;
const bool mUseSoftwareImages;
const bool mIs106;
// Protects mReorderQueue.
Monitor mMonitor;
// Set on reader/decode thread calling Flush() to indicate that output is
@@ -134,14 +141,12 @@ protected:
Atomic<bool> mIsFlushing;
ReorderQueue mReorderQueue;
private:
VDADecoder mDecoder;
// Method to set up the decompression session.
nsresult InitializeSession();
// Method to pass a frame to VideoToolbox for decoding.
nsresult SubmitFrame(MediaRawData* aSample);
nsresult ProcessDecode(MediaRawData* aSample);
virtual nsresult DoDecode(MediaRawData* aSample);
CFDictionaryRef CreateDecoderSpecification();
};
+2 -18
View File
@@ -7,11 +7,9 @@
#include <dlfcn.h>
#include "AppleVDALinker.h"
#include "MainThreadUtils.h"
#include "nsDebug.h"
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
namespace mozilla {
@@ -19,7 +17,6 @@ AppleVDALinker::LinkStatus
AppleVDALinker::sLinkStatus = LinkStatus_INIT;
void* AppleVDALinker::sLink = nullptr;
nsrefcnt AppleVDALinker::sRefCount = 0;
CFStringRef AppleVDALinker::skPropWidth = nullptr;
CFStringRef AppleVDALinker::skPropHeight = nullptr;
CFStringRef AppleVDALinker::skPropSourceFormat = nullptr;
@@ -32,12 +29,6 @@ CFStringRef AppleVDALinker::skPropAVCCData = nullptr;
/* static */ bool
AppleVDALinker::Link()
{
// Bump our reference count every time we're called.
// Add a lock or change the thread assertion if
// you need to call this off the main thread.
MOZ_ASSERT(NS_IsMainThread());
++sRefCount;
if (sLinkStatus) {
return sLinkStatus == LinkStatus_SUCCEEDED;
}
@@ -82,14 +73,7 @@ fail:
/* static */ void
AppleVDALinker::Unlink()
{
// We'll be called by multiple Decoders, one intantiated for
// each media element. Therefore we receive must maintain a
// reference count to avoidunloading our symbols when other
// instances still need them.
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sRefCount > 0, "Unbalanced Unlink()");
--sRefCount;
if (sLink && sRefCount < 1) {
if (sLink) {
LOG("Unlinking VideoDecodeAcceleration framework.");
#define LINK_FUNC(func) \
func = nullptr;
+9 -70
View File
@@ -20,21 +20,15 @@
#include "VideoUtils.h"
#include "gfxPlatform.h"
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
//#define LOG_MEDIA_SHA1
#ifdef LOG_MEDIA_SHA1
#include "mozilla/SHA1.h"
#endif
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
namespace mozilla {
AppleVTDecoder::AppleVTDecoder(const VideoInfo& aConfig,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer)
: AppleVDADecoder(aConfig, aVideoTaskQueue, aCallback, aImageContainer)
: AppleVDADecoder(aConfig, aTaskQueue, aCallback, aImageContainer)
, mFormat(nullptr)
, mSession(nullptr)
, mIsHardwareAccelerated(false)
@@ -80,58 +74,22 @@ AppleVTDecoder::ProcessShutdown()
}
}
nsresult
AppleVTDecoder::Input(MediaRawData* aSample)
{
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
LOG("mp4 input sample %p pts %lld duration %lld us%s %d bytes",
aSample,
aSample->mTime,
aSample->mDuration,
aSample->mKeyframe ? " keyframe" : "",
aSample->Size());
#ifdef LOG_MEDIA_SHA1
SHA1Sum hash;
hash.update(aSample->data, aSample->size);
uint8_t digest_buf[SHA1Sum::kHashSize];
hash.finish(digest_buf);
nsAutoCString digest;
for (size_t i = 0; i < sizeof(digest_buf); i++) {
digest.AppendPrintf("%02x", digest_buf[i]);
}
LOG(" sha1 %s", digest.get());
#endif // LOG_MEDIA_SHA1
mInputIncoming++;
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod<RefPtr<MediaRawData>>(
this, &AppleVTDecoder::SubmitFrame, aSample);
mTaskQueue->Dispatch(runnable.forget());
return NS_OK;
}
void
AppleVTDecoder::ProcessFlush()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
AssertOnTaskQueueThread();
nsresult rv = WaitForAsynchronousFrames();
if (NS_FAILED(rv)) {
LOG("AppleVTDecoder::Flush failed waiting for platform decoder "
"with error:%d.", rv);
}
ClearReorderedFrames();
MonitorAutoLock mon(mMonitor);
mIsFlushing = false;
mon.NotifyAll();
}
void
AppleVTDecoder::ProcessDrain()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
AssertOnTaskQueueThread();
nsresult rv = WaitForAsynchronousFrames();
if (NS_FAILED(rv)) {
LOG("AppleVTDecoder::Drain failed waiting for platform decoder "
@@ -205,10 +163,10 @@ TimingInfoFromSample(MediaRawData* aSample)
}
nsresult
AppleVTDecoder::SubmitFrame(MediaRawData* aSample)
AppleVTDecoder::DoDecode(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
mInputIncoming--;
AssertOnTaskQueueThread();
// For some reason this gives me a double-free error with stagefright.
AutoCFRelease<CMBlockBufferRef> block = nullptr;
AutoCFRelease<CMSampleBufferRef> sample = nullptr;
@@ -251,16 +209,10 @@ AppleVTDecoder::SubmitFrame(MediaRawData* aSample)
if (rv != noErr && !(infoFlags & kVTDecodeInfo_FrameDropped)) {
LOG("AppleVTDecoder: Error %d VTDecompressionSessionDecodeFrame", rv);
NS_WARNING("Couldn't pass frame to decoder");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
return NS_ERROR_FAILURE;
}
// Ask for more data.
if (!mInputIncoming && mQueuedSamples <= mMaxRefFrames) {
LOG("AppleVTDecoder task queue empty; requesting more data");
mCallback->InputExhausted();
}
return NS_OK;
}
@@ -269,19 +221,6 @@ AppleVTDecoder::InitializeSession()
{
OSStatus rv;
#ifdef LOG_MEDIA_SHA1
SHA1Sum avc_hash;
avc_hash.update(mExtraData->Elements(),mExtraData->Length());
uint8_t digest_buf[SHA1Sum::kHashSize];
avc_hash.finish(digest_buf);
nsAutoCString avc_digest;
for (size_t i = 0; i < sizeof(digest_buf); i++) {
avc_digest.AppendPrintf("%02x", digest_buf[i]);
}
LOG("AVCDecoderConfig %ld bytes sha1 %s",
mExtraData->Length(), avc_digest.get());
#endif // LOG_MEDIA_SHA1
AutoCFRelease<CFDictionaryRef> extensions = CreateDecoderExtensions();
rv = CMVideoFormatDescriptionCreate(kCFAllocatorDefault,
+4 -5
View File
@@ -16,12 +16,13 @@ namespace mozilla {
class AppleVTDecoder : public AppleVDADecoder {
public:
AppleVTDecoder(const VideoInfo& aConfig,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer);
private:
virtual ~AppleVTDecoder();
RefPtr<InitPromise> Init() override;
nsresult Input(MediaRawData* aSample) override;
bool IsHardwareAccelerated(nsACString& aFailureReason) const override
{
return mIsHardwareAccelerated;
@@ -34,17 +35,15 @@ public:
: "apple software VT decoder";
}
protected:
void ProcessFlush() override;
void ProcessDrain() override;
void ProcessShutdown() override;
private:
CMVideoFormatDescriptionRef mFormat;
VTDecompressionSessionRef mSession;
// Method to pass a frame to VideoToolbox for decoding.
nsresult SubmitFrame(MediaRawData* aSample);
nsresult DoDecode(MediaRawData* aSample) override;
// Method to set up the decompression session.
nsresult InitializeSession();
nsresult WaitForAsynchronousFrames();
+2 -18
View File
@@ -7,12 +7,10 @@
#include <dlfcn.h>
#include "AppleVTLinker.h"
#include "MainThreadUtils.h"
#include "mozilla/ArrayUtils.h"
#include "nsDebug.h"
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
namespace mozilla {
@@ -20,7 +18,6 @@ AppleVTLinker::LinkStatus
AppleVTLinker::sLinkStatus = LinkStatus_INIT;
void* AppleVTLinker::sLink = nullptr;
nsrefcnt AppleVTLinker::sRefCount = 0;
CFStringRef AppleVTLinker::skPropEnableHWAccel = nullptr;
CFStringRef AppleVTLinker::skPropUsingHWAccel = nullptr;
@@ -31,12 +28,6 @@ CFStringRef AppleVTLinker::skPropUsingHWAccel = nullptr;
/* static */ bool
AppleVTLinker::Link()
{
// Bump our reference count every time we're called.
// Add a lock or change the thread assertion if
// you need to call this off the main thread.
MOZ_ASSERT(NS_IsMainThread());
++sRefCount;
if (sLinkStatus) {
return sLinkStatus == LinkStatus_SUCCEEDED;
}
@@ -85,14 +76,7 @@ fail:
/* static */ void
AppleVTLinker::Unlink()
{
// We'll be called by multiple Decoders, one intantiated for
// each media element. Therefore we receive must maintain a
// reference count to avoidunloading our symbols when other
// instances still need them.
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sRefCount > 0, "Unbalanced Unlink()");
--sRefCount;
if (sLink && sRefCount < 1) {
if (sLink) {
LOG("Unlinking VideoToolbox framework.");
#define LINK_FUNC(func) \
func = nullptr;
@@ -27,7 +27,6 @@ public:
private:
static void* sLink;
static nsrefcnt sRefCount;
static enum LinkStatus {
LinkStatus_INIT = 0,
@@ -20,7 +20,7 @@ static int (*avcodec_decode_audio4)(AVCodecContext*,AVFrame*,
static void (*av_init_packet1)(AVPacket*) = nullptr;
FFmpegAudioDecoder<LIBAV_VER>::FFmpegAudioDecoder(
FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
TaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
const AudioInfo& aConfig)
: FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
{
@@ -153,10 +153,9 @@ CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames)
return audio;
}
void
FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
FFmpegAudioDecoder<LIBAV_VER>::DecodeResult
FFmpegAudioDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
AVPacket packet;
av_init_packet1(&packet);
@@ -165,8 +164,7 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
if (!PrepareFrame()) {
NS_WARNING("FFmpeg audio decoder failed to allocate frame.");
mCallback->Error();
return;
return DecodeResult::FATAL_ERROR;
}
int64_t samplePosition = aSample->mOffset;
@@ -179,16 +177,14 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
if (bytesConsumed < 0) {
NS_WARNING("FFmpeg audio decoder error.");
mCallback->Error();
return;
return DecodeResult::DECODE_ERROR;
}
if (decoded) {
uint32_t numChannels = mCodecContext->channels;
AudioConfig::ChannelLayout layout(numChannels);
if (!layout.IsValid()) {
mCallback->Error();
return;
return DecodeResult::FATAL_ERROR;
}
uint32_t samplingRate = mCodecContext->sample_rate;
@@ -200,8 +196,7 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
FramesToTimeUnit(mFrame->nb_samples, samplingRate);
if (!audio || !duration.IsValid()) {
NS_WARNING("Invalid count of accumulated audio samples");
mCallback->Error();
return;
return DecodeResult::DECODE_ERROR;
}
RefPtr<AudioData> data = new AudioData(samplePosition,
@@ -215,8 +210,7 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
pts += duration;
if (!pts.IsValid()) {
NS_WARNING("Invalid count of accumulated audio samples");
mCallback->Error();
return;
return DecodeResult::DECODE_ERROR;
}
}
packet.data += bytesConsumed;
@@ -224,24 +218,12 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
samplePosition += bytesConsumed;
}
if (mTaskQueue->IsEmpty()) {
mCallback->InputExhausted();
}
}
nsresult
FFmpegAudioDecoder<LIBAV_VER>::Input(MediaRawData* aSample)
{
nsCOMPtr<nsIRunnable> runnable(NewRunnableMethod<RefPtr<MediaRawData>>(
this, &FFmpegAudioDecoder::DecodePacket, RefPtr<MediaRawData>(aSample)));
mTaskQueue->Dispatch(runnable.forget());
return NS_OK;
return DecodeResult::DECODE_FRAME;
}
void
FFmpegAudioDecoder<LIBAV_VER>::ProcessDrain()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
ProcessFlush();
mCallback->DrainComplete();
}
@@ -20,14 +20,12 @@ template <>
class FFmpegAudioDecoder<LIBAV_VER> : public FFmpegDataDecoder<LIBAV_VER>
{
public:
FFmpegAudioDecoder(FlushableTaskQueue* aTaskQueue,
FFmpegAudioDecoder(TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
const AudioInfo& aConfig);
virtual ~FFmpegAudioDecoder();
RefPtr<InitPromise> Init() override;
nsresult Input(MediaRawData* aSample) override;
void ProcessDrain() override;
void InitCodecContext() override;
static AVCodecID GetCodecId(const nsACString& aMimeType);
const char* GetDescriptionName() const override
@@ -36,7 +34,8 @@ public:
}
private:
void DecodePacket(MediaRawData* aSample);
DecodeResult DoDecode(MediaRawData* aSample) override;
void ProcessDrain() override;
};
} // namespace mozilla
@@ -4,6 +4,7 @@
* 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/SyncRunnable.h"
#include "mozilla/TaskQueue.h"
#include <string.h>
@@ -23,16 +24,15 @@ namespace mozilla
bool FFmpegDataDecoder<LIBAV_VER>::sFFmpegInitDone = false;
StaticMutex FFmpegDataDecoder<LIBAV_VER>::sMonitor;
FFmpegDataDecoder<LIBAV_VER>::FFmpegDataDecoder(FlushableTaskQueue* aTaskQueue,
FFmpegDataDecoder<LIBAV_VER>::FFmpegDataDecoder(TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
AVCodecID aCodecID)
: mTaskQueue(aTaskQueue)
, mCallback(aCallback)
: mCallback(aCallback)
, mCodecContext(nullptr)
, mFrame(NULL)
, mExtraData(nullptr)
, mCodecID(aCodecID)
, mMonitor("FFMpegaDataDecoder")
, mTaskQueue(aTaskQueue)
, mIsFlushing(false)
{
MOZ_COUNT_CTOR(FFmpegDataDecoder);
@@ -135,19 +135,44 @@ FFmpegDataDecoder<LIBAV_VER>::Shutdown()
return NS_OK;
}
void
FFmpegDataDecoder<LIBAV_VER>::ProcessDecode(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
if (mIsFlushing) {
return;
}
switch (DoDecode(aSample)) {
case DecodeResult::DECODE_ERROR:
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
break;
case DecodeResult::FATAL_ERROR:
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
break;
default:
if (mTaskQueue->IsEmpty()) {
mCallback->InputExhausted();
}
}
}
nsresult
FFmpegDataDecoder<LIBAV_VER>::Input(MediaRawData* aSample)
{
mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
this, &FFmpegDataDecoder::ProcessDecode, aSample));
return NS_OK;
}
nsresult
FFmpegDataDecoder<LIBAV_VER>::Flush()
{
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
mIsFlushing = true;
mTaskQueue->Flush();
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod(this, &FFmpegDataDecoder<LIBAV_VER>::ProcessFlush);
MonitorAutoLock mon(mMonitor);
mTaskQueue->Dispatch(runnable.forget());
while (mIsFlushing) {
mon.Wait();
}
SyncRunnable::DispatchToThread(mTaskQueue, runnable);
mIsFlushing = false;
return NS_OK;
}
@@ -172,9 +197,6 @@ FFmpegDataDecoder<LIBAV_VER>::ProcessFlush()
avcodec_flush_buffers(mCodecContext);
#endif
}
MonitorAutoLock mon(mMonitor);
mIsFlushing = false;
mon.NotifyAll();
}
void
+17 -12
View File
@@ -23,7 +23,7 @@ template <>
class FFmpegDataDecoder<LIBAV_VER> : public MediaDataDecoder
{
public:
FFmpegDataDecoder(FlushableTaskQueue* aTaskQueue,
FFmpegDataDecoder(TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
AVCodecID aCodecID);
virtual ~FFmpegDataDecoder();
@@ -31,7 +31,7 @@ public:
static bool Link();
RefPtr<InitPromise> Init() override = 0;
nsresult Input(MediaRawData* aSample) override = 0;
nsresult Input(MediaRawData* aSample) override;
nsresult Flush() override;
nsresult Drain() override;
nsresult Shutdown() override;
@@ -39,15 +39,20 @@ public:
static AVCodec* FindAVCodec(AVCodecID aCodec);
protected:
enum DecodeResult {
DECODE_FRAME,
DECODE_NO_FRAME,
DECODE_ERROR,
FATAL_ERROR
};
// Flush and Drain operation, always run
virtual void ProcessFlush();
virtual void ProcessDrain() = 0;
virtual void ProcessShutdown();
virtual void InitCodecContext() {}
AVFrame* PrepareFrame();
nsresult InitDecoder();
RefPtr<FlushableTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
AVCodecContext* mCodecContext;
@@ -55,17 +60,17 @@ protected:
RefPtr<MediaByteBuffer> mExtraData;
AVCodecID mCodecID;
// For wait on mIsFlushing during Shutdown() process.
// Protects mReorderQueue.
Monitor mMonitor;
// Set on reader/decode thread calling Flush() to indicate that output is
// not required and so input samples on mTaskQueue need not be processed.
// Cleared on mTaskQueue in ProcessDrain().
Atomic<bool> mIsFlushing;
private:
void ProcessDecode(MediaRawData* aSample);
virtual DecodeResult DoDecode(MediaRawData* aSample) = 0;
virtual void ProcessDrain() = 0;
static bool sFFmpegInitDone;
static StaticMutex sMonitor;
const RefPtr<TaskQueue> mTaskQueue;
// Set/cleared on reader thread calling Flush() to indicate that output is
// not required and so input samples on mTaskQueue need not be processed.
Atomic<bool> mIsFlushing;
};
} // namespace mozilla
@@ -33,24 +33,24 @@ public:
CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override
{
RefPtr<MediaDataDecoder> decoder =
new FFmpegH264Decoder<V>(aVideoTaskQueue, aCallback, aConfig,
new FFmpegH264Decoder<V>(aTaskQueue, aCallback, aConfig,
aImageContainer);
return decoder.forget();
}
already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override
{
RefPtr<MediaDataDecoder> decoder =
new FFmpegAudioDecoder<V>(aAudioTaskQueue, aCallback, aConfig);
new FFmpegAudioDecoder<V>(aTaskQueue, aCallback, aConfig);
return decoder.forget();
}
@@ -110,13 +110,14 @@ FFmpegH264Decoder<LIBAV_VER>::PtsCorrectionContext::Reset()
}
FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
TaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
const VideoInfo& aConfig,
ImageContainer* aImageContainer)
: FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
, mImageContainer(aImageContainer)
, mInfo(aConfig)
, mCodecParser(nullptr)
, mLastInputDts(INT64_MIN)
{
MOZ_COUNT_CTOR(FFmpegH264Decoder);
// Use a new MediaByteBuffer as the object will be modified during initialization.
@@ -183,10 +184,8 @@ FFmpegH264Decoder<LIBAV_VER>::InitCodecContext()
}
FFmpegH264Decoder<LIBAV_VER>::DecodeResult
FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
FFmpegH264Decoder<LIBAV_VER>::DoDecode(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
uint8_t* inputData = const_cast<uint8_t*>(aSample->Data());
size_t inputSize = aSample->Size();
@@ -217,13 +216,12 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
aSample->mOffset);
#endif
if (size_t(len) > inputSize) {
mCallback->Error();
return DecodeResult::DECODE_ERROR;
}
inputData += len;
inputSize -= len;
if (size) {
switch (DoDecodeFrame(aSample, data, size)) {
switch (DoDecode(aSample, data, size)) {
case DecodeResult::DECODE_ERROR:
return DecodeResult::DECODE_ERROR;
case DecodeResult::DECODE_FRAME:
@@ -237,12 +235,12 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
return gotFrame ? DecodeResult::DECODE_FRAME : DecodeResult::DECODE_NO_FRAME;
}
#endif
return DoDecodeFrame(aSample, inputData, inputSize);
return DoDecode(aSample, inputData, inputSize);
}
FFmpegH264Decoder<LIBAV_VER>::DecodeResult
FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
uint8_t* aData, int aSize)
FFmpegH264Decoder<LIBAV_VER>::DoDecode(MediaRawData* aSample,
uint8_t* aData, int aSize)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
@@ -251,7 +249,7 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
packet.data = aData;
packet.size = aSize;
packet.dts = aSample->mTimecode;
packet.dts = mLastInputDts = aSample->mTimecode;
packet.pts = aSample->mTime;
packet.flags = aSample->mKeyframe ? AV_PKT_FLAG_KEY : 0;
packet.pos = aSample->mOffset;
@@ -265,8 +263,7 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
if (!PrepareFrame()) {
NS_WARNING("FFmpeg h264 decoder failed to allocate frame.");
mCallback->Error();
return DecodeResult::DECODE_ERROR;
return DecodeResult::FATAL_ERROR;
}
// Required with old version of FFmpeg/LibAV
@@ -284,15 +281,12 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
if (bytesConsumed < 0) {
NS_WARNING("FFmpeg video decoder error.");
mCallback->Error();
return DecodeResult::DECODE_ERROR;
}
// If we've decoded a frame then we need to output it
if (decoded) {
int64_t pts = mPtsContext.GuessCorrectPts(mFrame->pkt_pts, mFrame->pkt_dts);
FFMPEG_LOG("Got one frame output with pts=%lld opaque=%lld",
pts, mCodecContext->reordered_opaque);
// Retrieve duration from dts.
// We use the first entry found matching this dts (this is done to
// handle damaged file with multiple frames with the same dts)
@@ -306,6 +300,8 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
// against the map becoming extremely big.
mDurationMap.Clear();
}
FFMPEG_LOG("Got one frame output with pts=%lld dts=%lld duration=%lld opaque=%lld",
pts, mFrame->pkt_dts, duration, mCodecContext->reordered_opaque);
VideoData::YCbCrBuffer b;
b.mPlanes[0].mData = mFrame->data[0];
@@ -343,8 +339,7 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
if (!v) {
NS_WARNING("image allocation error.");
mCallback->Error();
return DecodeResult::DECODE_ERROR;
return DecodeResult::FATAL_ERROR;
}
mCallback->Output(v);
return DecodeResult::DECODE_FRAME;
@@ -352,35 +347,12 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
return DecodeResult::DECODE_NO_FRAME;
}
void
FFmpegH264Decoder<LIBAV_VER>::DecodeFrame(MediaRawData* aSample)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
if (DoDecodeFrame(aSample) != DecodeResult::DECODE_ERROR &&
mTaskQueue->IsEmpty()) {
mCallback->InputExhausted();
}
}
nsresult
FFmpegH264Decoder<LIBAV_VER>::Input(MediaRawData* aSample)
{
nsCOMPtr<nsIRunnable> runnable(
NewRunnableMethod<RefPtr<MediaRawData>>(
this, &FFmpegH264Decoder<LIBAV_VER>::DecodeFrame,
RefPtr<MediaRawData>(aSample)));
mTaskQueue->Dispatch(runnable.forget());
return NS_OK;
}
void
FFmpegH264Decoder<LIBAV_VER>::ProcessDrain()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
RefPtr<MediaRawData> empty(new MediaRawData());
while (DoDecodeFrame(empty) == DecodeResult::DECODE_FRAME) {
empty->mTimecode = mLastInputDts;
while (DoDecode(empty) == DecodeResult::DECODE_FRAME) {
}
mCallback->DrainComplete();
}
+13 -20
View File
@@ -25,23 +25,14 @@ class FFmpegH264Decoder<LIBAV_VER> : public FFmpegDataDecoder<LIBAV_VER>
typedef mozilla::layers::Image Image;
typedef mozilla::layers::ImageContainer ImageContainer;
enum DecodeResult {
DECODE_FRAME,
DECODE_NO_FRAME,
DECODE_ERROR
};
public:
FFmpegH264Decoder(FlushableTaskQueue* aTaskQueue,
FFmpegH264Decoder(TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
const VideoInfo& aConfig,
ImageContainer* aImageContainer);
virtual ~FFmpegH264Decoder();
RefPtr<InitPromise> Init() override;
nsresult Input(MediaRawData* aSample) override;
void ProcessDrain() override;
void ProcessFlush() override;
void InitCodecContext() override;
const char* GetDescriptionName() const override
{
@@ -54,10 +45,10 @@ public:
static AVCodecID GetCodecId(const nsACString& aMimeType);
private:
void DecodeFrame(MediaRawData* aSample);
DecodeResult DoDecodeFrame(MediaRawData* aSample);
DecodeResult DoDecodeFrame(MediaRawData* aSample, uint8_t* aData, int aSize);
void DoDrain();
DecodeResult DoDecode(MediaRawData* aSample) override;
DecodeResult DoDecode(MediaRawData* aSample, uint8_t* aData, int aSize);
void ProcessDrain() override;
void ProcessFlush() override;
void OutputDelayedFrames();
/**
@@ -80,6 +71,7 @@ private:
PtsCorrectionContext();
int64_t GuessCorrectPts(int64_t aPts, int64_t aDts);
void Reset();
int64_t LastDts() const { return mLastDts; }
private:
int64_t mNumFaultyPts; /// Number of incorrect PTS values so far
@@ -89,24 +81,25 @@ private:
};
PtsCorrectionContext mPtsContext;
int64_t mLastInputDts;
class DurationMap {
public:
typedef Pair<int64_t, int64_t> DurationElement;
// Insert Dts and Duration pair at the end of our map.
void Insert(int64_t aDts, int64_t aDuration)
// Insert Key and Duration pair at the end of our map.
void Insert(int64_t aKey, int64_t aDuration)
{
mMap.AppendElement(MakePair(aDts, aDuration));
mMap.AppendElement(MakePair(aKey, aDuration));
}
// Sets aDuration matching aDts and remove it from the map if found.
// Sets aDuration matching aKey and remove it from the map if found.
// The element returned is the first one found.
// Returns true if found, false otherwise.
bool Find(int64_t aDts, int64_t& aDuration)
bool Find(int64_t aKey, int64_t& aDuration)
{
for (uint32_t i = 0; i < mMap.Length(); i++) {
DurationElement& element = mMap[i];
if (element.first() == aDts) {
if (element.first() == aKey) {
aDuration = element.second();
mMap.RemoveElementAt(i);
return true;
+1 -2
View File
@@ -7,7 +7,6 @@
#ifndef __FFmpegLog_h__
#define __FFmpegLog_h__
extern mozilla::LogModule* GetPDMLog();
#define FFMPEG_LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define FFMPEG_LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
#endif // __FFmpegLog_h__
@@ -66,8 +66,6 @@ FFmpegRuntimeLinker::Link()
return sLinkStatus == LinkStatus_SUCCEEDED;
}
MOZ_ASSERT(NS_IsMainThread());
#if defined(XP_WIN)
HKEY aKey;
DWORD d;
@@ -27,8 +27,7 @@
#include <android/log.h>
#define GADM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkAudioDecoderManager", __VA_ARGS__)
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
using namespace android;
typedef android::MediaCodecProxy MediaCodecProxy;
@@ -18,18 +18,11 @@ GonkDecoderModule::~GonkDecoderModule()
{
}
/* static */
void
GonkDecoderModule::Init()
{
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
}
already_AddRefed<MediaDataDecoder>
GonkDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
mozilla::layers::LayersBackend aLayersBackend,
mozilla::layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
@@ -41,7 +34,7 @@ GonkDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
already_AddRefed<MediaDataDecoder>
GonkDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
+2 -4
View File
@@ -21,19 +21,17 @@ public:
CreateVideoDecoder(const VideoInfo& aConfig,
mozilla::layers::LayersBackend aLayersBackend,
mozilla::layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
// Decode thread.
already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
static void Init();
ConversionRequired
DecoderNeedsConversion(const TrackInfo& aConfig) const override;
@@ -20,8 +20,7 @@
#include <utils/AndroidThreads.h>
#endif
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
using namespace android;
@@ -32,8 +32,7 @@
#include <android/log.h>
#define GVDM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkVideoDecoderManager", __VA_ARGS__)
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
using namespace mozilla::layers;
using namespace android;
typedef android::MediaCodecProxy MediaCodecProxy;
-1
View File
@@ -24,7 +24,6 @@ UNIFIED_SOURCES += [
'agnostic/VPXDecoder.cpp',
'agnostic/WAVDecoder.cpp',
'PDMFactory.cpp',
'PlatformDecoderModule.cpp',
'wrappers/FuzzingWrapper.cpp',
'wrappers/H264Converter.cpp'
]
+5 -9
View File
@@ -12,10 +12,10 @@
#include "D3D9SurfaceImage.h"
#include "mozilla/layers/D3D11ShareHandleImage.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "MediaTelemetryConstants.h"
#include "mfapi.h"
#include "MediaPrefs.h"
#include "MFTDecoder.h"
#include "DriverCrashGuard.h"
#include "nsPrintfCString.h"
@@ -390,8 +390,7 @@ D3D9DXVA2Manager::Init(nsACString& aFailureReason)
return hr;
}
if (adapter.VendorId == 0x1022 &&
!Preferences::GetBool("media.wmf.skip-blacklist", false)) {
if (adapter.VendorId == 0x1022 && !MediaPrefs::PDMWMFSkipBlacklist()) {
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sAMDPreUVD4); i++) {
if (adapter.DeviceId == sAMDPreUVD4[i]) {
mIsAMDPreUVD4 = true;
@@ -474,8 +473,7 @@ DXVA2Manager::CreateD3D9DXVA(nsACString& aFailureReason)
// DXVA processing takes up a lot of GPU resources, so limit the number of
// videos we use DXVA with at any one time.
const uint32_t dxvaLimit =
Preferences::GetInt("media.windows-media-foundation.max-dxva-videos", 8);
const uint32_t dxvaLimit = MediaPrefs::PDMWMFMaxDXVAVideos();
if (sDXVAVideosCount == dxvaLimit) {
aFailureReason.AssignLiteral("Too many DXVA videos playing");
return nullptr;
@@ -702,8 +700,7 @@ D3D11DXVA2Manager::Init(nsACString& aFailureReason)
return hr;
}
if (adapterDesc.VendorId == 0x1022 &&
!Preferences::GetBool("media.wmf.skip-blacklist", false)) {
if (adapterDesc.VendorId == 0x1022 && !MediaPrefs::PDMWMFSkipBlacklist()) {
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sAMDPreUVD4); i++) {
if (adapterDesc.DeviceId == sAMDPreUVD4[i]) {
mIsAMDPreUVD4 = true;
@@ -871,8 +868,7 @@ DXVA2Manager::CreateD3D11DXVA(nsACString& aFailureReason)
{
// DXVA processing takes up a lot of GPU resources, so limit the number of
// videos we use DXVA with at any one time.
const uint32_t dxvaLimit =
Preferences::GetInt("media.windows-media-foundation.max-dxva-videos", 8);
const uint32_t dxvaLimit = MediaPrefs::PDMWMFMaxDXVAVideos();
if (sDXVAVideosCount == dxvaLimit) {
aFailureReason.AssignLiteral("Too many DXVA videos playing");
return nullptr;
+1 -2
View File
@@ -9,8 +9,7 @@
#include "WMFUtils.h"
#include "mozilla/Logging.h"
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
namespace mozilla {
@@ -13,8 +13,7 @@
#include "mozilla/Telemetry.h"
#include "mozilla/Logging.h"
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
namespace mozilla {
+5 -6
View File
@@ -27,7 +27,7 @@
namespace mozilla {
static bool sDXVAEnabled = false;
static Atomic<bool> sDXVAEnabled(false);
WMFDecoderModule::WMFDecoderModule()
: mWMFInitialized(false)
@@ -46,7 +46,6 @@ WMFDecoderModule::~WMFDecoderModule()
void
WMFDecoderModule::Init()
{
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
sDXVAEnabled = gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding();
}
@@ -79,7 +78,7 @@ already_AddRefed<MediaDataDecoder>
WMFDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
@@ -94,14 +93,14 @@ WMFDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
}
RefPtr<MediaDataDecoder> decoder =
new WMFMediaDataDecoder(manager.forget(), aVideoTaskQueue, aCallback);
new WMFMediaDataDecoder(manager.forget(), aTaskQueue, aCallback);
return decoder.forget();
}
already_AddRefed<MediaDataDecoder>
WMFDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
{
@@ -112,7 +111,7 @@ WMFDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
}
RefPtr<MediaDataDecoder> decoder =
new WMFMediaDataDecoder(manager.forget(), aAudioTaskQueue, aCallback);
new WMFMediaDataDecoder(manager.forget(), aTaskQueue, aCallback);
return decoder.forget();
}
+2 -2
View File
@@ -23,13 +23,13 @@ public:
CreateVideoDecoder(const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics) override;
+13 -26
View File
@@ -10,19 +10,18 @@
#include "nsTArray.h"
#include "mozilla/Logging.h"
#include "mozilla/SyncRunnable.h"
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
namespace mozilla {
WMFMediaDataDecoder::WMFMediaDataDecoder(MFTManager* aMFTManager,
FlushableTaskQueue* aTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
: mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mMFTManager(aMFTManager)
, mMonitor("WMFMediaDataDecoder")
, mIsFlushing(false)
, mIsShutDown(false)
{
@@ -84,18 +83,15 @@ WMFMediaDataDecoder::Input(MediaRawData* aSample)
void
WMFMediaDataDecoder::ProcessDecode(MediaRawData* aSample)
{
{
MonitorAutoLock mon(mMonitor);
if (mIsFlushing) {
// Skip sample, to be released by runnable.
return;
}
if (mIsFlushing) {
// Skip sample, to be released by runnable.
return;
}
HRESULT hr = mMFTManager->Input(aSample);
if (FAILED(hr)) {
NS_WARNING("MFTManager rejected sample");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
if (!mRecordedError) {
//SendTelemetry(hr);
mRecordedError = true;
@@ -124,7 +120,7 @@ WMFMediaDataDecoder::ProcessOutput()
}
} else if (FAILED(hr)) {
NS_WARNING("WMFMediaDataDecoder failed to output data");
mCallback->Error();
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
if (!mRecordedError) {
//SendTelemetry(hr);
mRecordedError = true;
@@ -138,9 +134,6 @@ WMFMediaDataDecoder::ProcessFlush()
if (mMFTManager) {
mMFTManager->Flush();
}
MonitorAutoLock mon(mMonitor);
mIsFlushing = false;
mon.NotifyAll();
}
nsresult
@@ -149,24 +142,18 @@ WMFMediaDataDecoder::Flush()
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown);
MonitorAutoLock mon(mMonitor);
mIsFlushing = true;
mTaskQueue->Dispatch(NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessFlush));
while (mIsFlushing) {
mon.Wait();
}
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessFlush);
SyncRunnable::DispatchToThread(mTaskQueue, runnable);
mIsFlushing = false;
return NS_OK;
}
void
WMFMediaDataDecoder::ProcessDrain()
{
bool isFlushing;
{
MonitorAutoLock mon(mMonitor);
isFlushing = mIsFlushing;
}
if (!isFlushing && mMFTManager) {
if (!mIsFlushing && mMFTManager) {
// Order the decoder to drain...
mMFTManager->Drain();
// Then extract all available output.
@@ -69,7 +69,7 @@ protected:
class WMFMediaDataDecoder : public MediaDataDecoder {
public:
WMFMediaDataDecoder(MFTManager* aOutputSource,
FlushableTaskQueue* aAudioTaskQueue,
TaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback);
~WMFMediaDataDecoder();
@@ -116,7 +116,7 @@ private:
// different configuration (typically resolution change).
void ProcessConfigurationChanged(UniquePtr<TrackInfo>&& aConfig);
RefPtr<FlushableTaskQueue> mTaskQueue;
RefPtr<TaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
nsAutoPtr<MFTManager> mMFTManager;
@@ -125,12 +125,10 @@ private:
// This is used to approximate the decoder's position in the media resource.
int64_t mLastStreamOffset;
// For access to and waiting on mIsFlushing
Monitor mMonitor;
// Set on reader/decode thread calling Flush() to indicate that output is
// not required and so input samples on mTaskQueue need not be processed.
// Cleared on mTaskQueue.
bool mIsFlushing;
Atomic<bool> mIsFlushing;
bool mIsShutDown;
+140 -20
View File
@@ -14,20 +14,23 @@
#include "DXVA2Manager.h"
#include "nsThreadUtils.h"
#include "Layers.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/layers/LayersTypes.h"
#include "MediaInfo.h"
#include "MediaPrefs.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "nsWindowsHelpers.h"
#include "gfx2DGlue.h"
#include "gfxWindowsPlatform.h"
#include "IMFYCbCrImage.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "nsPrintfCString.h"
#include "MediaTelemetryConstants.h"
#include "GMPUtils.h" // For SplitAt. TODO: Move SplitAt to a central place.
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
using mozilla::layers::Image;
using mozilla::layers::IMFYCbCrImage;
@@ -150,6 +153,127 @@ WMFVideoMFTManager::GetMediaSubtypeGUID()
};
}
struct D3D11BlacklistingCache
{
// D3D11-blacklist pref last seen.
nsCString mBlacklistPref;
// Non-empty if a D3D11-blacklisted DLL was found.
nsCString mBlacklistedDLL;
};
StaticAutoPtr<D3D11BlacklistingCache> sD3D11BlacklistingCache;
// If a blacklisted DLL is found, return its information, otherwise "".
static const nsACString&
FindD3D11BlacklistedDLL()
{
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
if (!sD3D11BlacklistingCache) {
// First time here, create persistent data that will be reused in all
// D3D11-blacklisting checks.
sD3D11BlacklistingCache = new D3D11BlacklistingCache();
ClearOnShutdown(&sD3D11BlacklistingCache);
}
nsAdoptingCString blacklist =
Preferences::GetCString("media.wmf.disable-d3d11-for-dlls");
if (blacklist.IsEmpty()) {
// Empty blacklist -> No blacklisting.
sD3D11BlacklistingCache->mBlacklistPref.SetLength(0);
sD3D11BlacklistingCache->mBlacklistedDLL.SetLength(0);
return sD3D11BlacklistingCache->mBlacklistedDLL;
}
// Detect changes in pref.
if (sD3D11BlacklistingCache->mBlacklistPref.Equals(blacklist)) {
// Same blacklist -> Return same result (i.e., don't check DLLs again).
return sD3D11BlacklistingCache->mBlacklistedDLL;
}
// Adopt new pref now, so we don't work on it again.
sD3D11BlacklistingCache->mBlacklistPref = blacklist;
// media.wmf.disable-d3d11-for-dlls format: (whitespace is trimmed)
// "dll1.dll: 1.2.3.4[, more versions...][; more dlls...]"
nsTArray<nsCString> dlls;
SplitAt(";", blacklist, dlls);
for (const auto& dll : dlls) {
nsTArray<nsCString> nameAndVersions;
SplitAt(":", dll, nameAndVersions);
if (nameAndVersions.Length() != 2) {
NS_WARNING("Skipping incorrect 'media.wmf.disable-d3d11-for-dlls' dll:versions format");
continue;
}
nameAndVersions[0].CompressWhitespace();
NS_ConvertUTF8toUTF16 name(nameAndVersions[0]);
WCHAR systemPath[MAX_PATH + 1];
if (!ConstructSystem32Path(name.get(), systemPath, MAX_PATH + 1)) {
// Cannot build path -> Assume it's not the blacklisted DLL.
continue;
}
DWORD zero;
DWORD infoSize = GetFileVersionInfoSizeW(systemPath, &zero);
if (infoSize == 0) {
// Can't get file info -> Assume we don't have the blacklisted DLL.
continue;
}
// vInfo is a pointer into infoData, that's why we keep it outside of the loop.
auto infoData = MakeUnique<unsigned char[]>(infoSize);
VS_FIXEDFILEINFO *vInfo;
UINT vInfoLen;
if (!GetFileVersionInfoW(systemPath, 0, infoSize, infoData.get())
|| !VerQueryValueW(infoData.get(), L"\\", (LPVOID*)&vInfo, &vInfoLen)
|| !vInfo) {
// Can't find version -> Assume it's not blacklisted.
continue;
}
nsTArray<nsCString> versions;
SplitAt(",", nameAndVersions[1], versions);
for (const auto& version : versions) {
nsTArray<nsCString> numberStrings;
SplitAt(".", version, numberStrings);
if (numberStrings.Length() != 4) {
NS_WARNING("Skipping incorrect 'media.wmf.disable-d3d11-for-dlls' a.b.c.d version format");
continue;
}
DWORD numbers[4];
nsresult errorCode = NS_OK;
for (int i = 0; i < 4; ++i) {
numberStrings[i].CompressWhitespace();
numbers[i] = DWORD(numberStrings[i].ToInteger(&errorCode));
if (NS_FAILED(errorCode)) {
break;
}
if (numbers[i] > UINT16_MAX) {
errorCode = NS_ERROR_FAILURE;
break;
}
}
if (NS_FAILED(errorCode)) {
NS_WARNING("Skipping incorrect 'media.wmf.disable-d3d11-for-dlls' a.b.c.d version format");
continue;
}
if (vInfo->dwFileVersionMS == ((numbers[0] << 16) | numbers[1])
&& vInfo->dwFileVersionLS == ((numbers[2] << 16) | numbers[3])) {
// Blacklisted! Record bad DLL.
sD3D11BlacklistingCache->mBlacklistedDLL.SetLength(0);
sD3D11BlacklistingCache->mBlacklistedDLL.AppendPrintf(
"%s (%lu.%lu.%lu.%lu)",
nameAndVersions[0].get(), numbers[0], numbers[1], numbers[2], numbers[3]);
return sD3D11BlacklistingCache->mBlacklistedDLL;
}
}
}
// No blacklisted DLL.
sD3D11BlacklistingCache->mBlacklistedDLL.SetLength(0);
return sD3D11BlacklistingCache->mBlacklistedDLL;
}
class CreateDXVAManagerEvent : public Runnable {
public:
CreateDXVAManagerEvent(LayersBackend aBackend, nsCString& aFailureReason)
@@ -162,11 +286,16 @@ public:
nsACString* failureReason = &mFailureReason;
nsCString secondFailureReason;
if (mBackend == LayersBackend::LAYERS_D3D11 &&
Preferences::GetBool("media.windows-media-foundation.allow-d3d11-dxva", true) &&
IsWin8OrLater()) {
mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA(*failureReason);
if (mDXVA2Manager) {
return NS_OK;
MediaPrefs::PDMWMFAllowD3D11() && IsWin8OrLater()) {
const nsACString& blacklistedDLL = FindD3D11BlacklistedDLL();
if (!blacklistedDLL.IsEmpty()) {
failureReason->AppendPrintf("D3D11 blacklisted with DLL %s",
blacklistedDLL);
} else {
mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA(*failureReason);
if (mDXVA2Manager) {
return NS_OK;
}
}
// Try again with d3d9, but record the failure reason
// into a new var to avoid overwriting the d3d11 failure.
@@ -202,7 +331,9 @@ WMFVideoMFTManager::InitializeDXVA(bool aForceD3D9)
// The DXVA manager must be created on the main thread.
RefPtr<CreateDXVAManagerEvent> event =
new CreateDXVAManagerEvent(aForceD3D9 ? LayersBackend::LAYERS_D3D9 : mLayersBackend, mDXVAFailureReason);
new CreateDXVAManagerEvent(aForceD3D9 ? LayersBackend::LAYERS_D3D9
: mLayersBackend,
mDXVAFailureReason);
if (NS_IsMainThread()) {
event->Run();
@@ -671,17 +802,6 @@ WMFVideoMFTManager::IsHardwareAccelerated(nsACString& aFailureReason) const
return mDecoder && mUseHwAccel;
}
const char*
WMFVideoMFTManager::GetDescriptionName() const
{
if (mDecoder && mUseHwAccel && mDXVA2Manager) {
return (mDXVA2Manager->IsD3D11()) ?
"D3D11 Hardware Decoder" : "D3D9 Hardware Decoder";
} else {
return "wmf software video decoder";
}
}
void
WMFVideoMFTManager::ConfigurationChanged(const TrackInfo& aConfig)
{
+6 -1
View File
@@ -41,7 +41,12 @@ public:
void ConfigurationChanged(const TrackInfo& aConfig) override;
const char* GetDescriptionName() const override;
const char* GetDescriptionName() const override
{
nsCString failureReason;
return IsHardwareAccelerated(failureReason)
? "wmf hardware video decoder" : "wmf software video decoder";
}
private:
@@ -173,16 +173,19 @@ DecoderCallbackFuzzingWrapper::Output(MediaData* aData)
}
void
DecoderCallbackFuzzingWrapper::Error()
DecoderCallbackFuzzingWrapper::Error(MediaDataDecoderError aError)
{
if (!mTaskQueue->IsCurrentThreadIn()) {
mTaskQueue->Dispatch(NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::Error));
mTaskQueue->Dispatch(
NewRunnableMethod<MediaDataDecoderError>(this,
&DecoderCallbackFuzzingWrapper::Error,
aError));
return;
}
CFW_LOGV("");
MOZ_ASSERT(mCallback);
ClearDelayedOutput();
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
}
void
@@ -60,7 +60,7 @@ private:
// MediaDataDecoderCallback implementation.
void Output(MediaData* aData) override;
void Error() override;
void Error(MediaDataDecoderError aError) override;
void InputExhausted() override;
void DrainComplete() override;
void ReleaseMediaResources() override;
@@ -19,7 +19,7 @@ H264Converter::H264Converter(PlatformDecoderModule* aPDM,
const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics)
: mPDM(aPDM)
@@ -27,7 +27,7 @@ H264Converter::H264Converter(PlatformDecoderModule* aPDM,
, mCurrentConfig(aConfig)
, mLayersBackend(aLayersBackend)
, mImageContainer(aImageContainer)
, mVideoTaskQueue(aVideoTaskQueue)
, mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mDecoder(nullptr)
, mNeedAVCC(aPDM->DecoderNeedsConversion(aConfig) == PlatformDecoderModule::kNeedAVCC)
@@ -149,7 +149,7 @@ H264Converter::CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics)
mDecoder = mPDM->CreateVideoDecoder(mNeedAVCC ? mCurrentConfig : mOriginalConfig,
mLayersBackend,
mImageContainer,
mVideoTaskQueue,
mTaskQueue,
mCallback,
aDiagnostics);
if (!mDecoder) {
@@ -191,7 +191,7 @@ H264Converter::OnDecoderInitDone(const TrackType aTrackType)
mInitPromiseRequest.Complete();
for (uint32_t i = 0 ; i < mMediaRawSamples.Length(); i++) {
if (NS_FAILED(mDecoder->Input(mMediaRawSamples[i]))) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
}
}
mMediaRawSamples.Clear();
@@ -201,7 +201,7 @@ void
H264Converter::OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason)
{
mInitPromiseRequest.Complete();
mCallback->Error();
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
}
nsresult
+2 -2
View File
@@ -25,7 +25,7 @@ public:
const VideoInfo& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableTaskQueue* aVideoTaskQueue,
TaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback,
DecoderDoctorDiagnostics* aDiagnostics);
virtual ~H264Converter();
@@ -65,7 +65,7 @@ private:
VideoInfo mCurrentConfig;
layers::LayersBackend mLayersBackend;
RefPtr<layers::ImageContainer> mImageContainer;
RefPtr<FlushableTaskQueue> mVideoTaskQueue;
const RefPtr<TaskQueue> mTaskQueue;
nsTArray<RefPtr<MediaRawData>> mMediaRawSamples;
MediaDataDecoderCallback* mCallback;
RefPtr<MediaDataDecoder> mDecoder;
-1
View File
@@ -40,7 +40,6 @@ WaveDecoder::IsEnabled()
if (!Preferences::GetBool("media.wave.decoder.enabled", false)) {
return false;
}
PDMFactory::Init();
RefPtr<PDMFactory> platform = new PDMFactory();
return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/x-wav"),
/* DecoderDoctorDiagnostics* */ nullptr);
+1 -1
View File
@@ -10,7 +10,7 @@
#include <algorithm>
#include "mozilla/Assertions.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "VideoUtils.h"
#include "TimeUnits.h"
#include "prenv.h"
+1 -1
View File
@@ -13,7 +13,7 @@
#include <stdint.h>
#include "mozilla/ArrayUtils.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/Endian.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/UniquePtr.h"
#include <algorithm>
+55
View File
@@ -10,6 +10,60 @@
#include "mozilla/Alignment.h"
#include "nsTArray.h"
/**
* E: element type, must be a POD type.
* N: N bytes alignment for the first element, defaults to 32
* S: S bytes of inline storage
*/
template <typename E, int S, int N = 32>
class AlignedAutoTArray : private AutoTArray<E, S + N>
{
static_assert((N & (N-1)) == 0, "N must be power of 2");
typedef AutoTArray<E, S + N> base_type;
public:
typedef E elem_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::index_type index_type;
AlignedAutoTArray() {}
explicit AlignedAutoTArray(size_type capacity) : base_type(capacity + sExtra) {}
elem_type* Elements() { return getAligned(base_type::Elements()); }
const elem_type* Elements() const { return getAligned(base_type::Elements()); }
elem_type& operator[](index_type i) { return Elements()[i];}
const elem_type& operator[](index_type i) const { return Elements()[i]; }
void SetLength(size_type newLen)
{
base_type::SetLength(newLen + sExtra);
}
MOZ_MUST_USE
bool SetLength(size_type newLen, const mozilla::fallible_t&)
{
return base_type::SetLength(newLen + sExtra, mozilla::fallible);
}
size_type Length() const {
return base_type::Length() <= sExtra ? 0 : base_type::Length() - sExtra;
}
using base_type::ShallowSizeOfExcludingThis;
using base_type::ShallowSizeOfIncludingThis;
private:
AlignedAutoTArray(const AlignedAutoTArray& other) = delete;
void operator=(const AlignedAutoTArray& other) = delete;
static const size_type sPadding = N <= MOZ_ALIGNOF(E) ? 0 : N - MOZ_ALIGNOF(E);
static const size_type sExtra = (sPadding + sizeof(E) - 1) / sizeof(E);
template <typename U>
static U* getAligned(U* p)
{
return reinterpret_cast<U*>(((uintptr_t)p + N - 1) & ~(N-1));
}
};
/**
* E: element type, must be a POD type.
* N: N bytes alignment for the first element, defaults to 32
@@ -63,4 +117,5 @@ private:
}
};
#endif // AlignedTArray_h__

Some files were not shown because too many files have changed in this diff Show More