mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 05:11:03 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -279,7 +279,7 @@ MediaDecoderReaderWrapper::IsRequestingAudioData() const
|
||||
}
|
||||
|
||||
bool
|
||||
MediaDecoderReaderWrapper::IsRequestingVidoeData() const
|
||||
MediaDecoderReaderWrapper::IsRequestingVideoData() const
|
||||
{
|
||||
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
|
||||
return mVideoDataRequest.Exists();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -24,7 +24,6 @@ UNIFIED_SOURCES += [
|
||||
'agnostic/VPXDecoder.cpp',
|
||||
'agnostic/WAVDecoder.cpp',
|
||||
'PDMFactory.cpp',
|
||||
'PlatformDecoderModule.cpp',
|
||||
'wrappers/FuzzingWrapper.cpp',
|
||||
'wrappers/H264Converter.cpp'
|
||||
]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user