mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 1068151 - keep decoding a corrupted video. r=jya
This commit is contained in:
parent
714d7d715a
commit
68adb6b579
@ -282,7 +282,7 @@ BenchmarkPlayback::Output(MediaData* aData)
|
||||
}
|
||||
|
||||
void
|
||||
BenchmarkPlayback::Error()
|
||||
BenchmarkPlayback::Error(MediaDataDecoderError aError)
|
||||
{
|
||||
RefPtr<Benchmark> ref(mMainThreadState);
|
||||
Dispatch(NS_NewRunnableFunction([this, ref]() { MainThreadShutdown(); }));
|
||||
|
@ -32,7 +32,7 @@ class BenchmarkPlayback : public QueueObject, private MediaDataDecoderCallback
|
||||
// MediaDataDecoderCallback
|
||||
// Those methods are called on the MediaDataDecoder's task queue.
|
||||
void Output(MediaData* aData) override;
|
||||
void Error() override;
|
||||
void Error(MediaDataDecoderError aError) override;
|
||||
void InputExhausted() override;
|
||||
void DrainComplete() override;
|
||||
bool OnReaderTaskQueue() override;
|
||||
|
@ -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)
|
||||
@ -512,7 +514,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);
|
||||
|
||||
@ -672,6 +673,7 @@ MediaFormatReader::NotifyNewOutput(TrackType aTrack, MediaData* aSample)
|
||||
}
|
||||
decoder.mOutput.AppendElement(aSample);
|
||||
decoder.mNumSamplesOutput++;
|
||||
decoder.mNumOfConsecutiveError = 0;
|
||||
ScheduleUpdate(aTrack);
|
||||
}
|
||||
|
||||
@ -700,12 +702,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);
|
||||
}
|
||||
|
||||
@ -749,7 +751,7 @@ MediaFormatReader::NeedInput(DecoderData& aDecoder)
|
||||
// which overrides our "few more samples" threshold.
|
||||
return
|
||||
!aDecoder.mDraining &&
|
||||
!aDecoder.mError &&
|
||||
!aDecoder.HasFatalError() &&
|
||||
aDecoder.mDecodingRequested &&
|
||||
!aDecoder.mDemuxRequest.Exists() &&
|
||||
!aDecoder.HasInternalSeekPending() &&
|
||||
@ -833,7 +835,7 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack)
|
||||
}
|
||||
decoder.mWaitingForData = false;
|
||||
|
||||
if (decoder.mError) {
|
||||
if (decoder.HasFatalError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1169,7 +1171,7 @@ MediaFormatReader::Update(TrackType aTrack)
|
||||
mVideo.mIsHardwareAccelerated =
|
||||
mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error);
|
||||
}
|
||||
} else if (decoder.mError) {
|
||||
} else if (decoder.HasFatalError()) {
|
||||
LOG("Rejecting %s promise: DECODE_ERROR", TrackTypeToStr(aTrack));
|
||||
decoder.RejectPromise(DECODE_ERROR, __func__);
|
||||
return;
|
||||
@ -1219,6 +1221,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",
|
||||
@ -1398,11 +1417,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());
|
||||
}
|
||||
|
||||
@ -1445,7 +1464,6 @@ void
|
||||
MediaFormatReader::SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(mVideo.HasPromise());
|
||||
LOG("Skipping up to %lld", aTimeThreshold.ToMicroseconds());
|
||||
|
||||
// We've reached SkipVideoDemuxToNextKeyFrame when our decoding is late.
|
||||
@ -1507,7 +1525,6 @@ MediaFormatReader::OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailu
|
||||
LOG("Skipping failed, skipped %u frames", aFailure.mSkipped);
|
||||
mSkipRequest.Complete();
|
||||
|
||||
MOZ_ASSERT(mVideo.HasPromise());
|
||||
switch (aFailure.mFailure) {
|
||||
case DemuxerFailureReason::END_OF_STREAM: MOZ_FALLTHROUGH;
|
||||
case DemuxerFailureReason::WAITING_FOR_DATA:
|
||||
|
@ -162,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);
|
||||
@ -176,7 +176,7 @@ 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);
|
||||
@ -200,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);
|
||||
@ -221,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")
|
||||
@ -236,10 +237,11 @@ 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)
|
||||
@ -305,10 +307,19 @@ private:
|
||||
bool mDecodingRequested;
|
||||
bool mOutputRequested;
|
||||
bool mInputExhausted;
|
||||
bool mError;
|
||||
bool mNeedDraining;
|
||||
bool mDraining;
|
||||
bool 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.
|
||||
@ -385,6 +396,9 @@ private:
|
||||
mNumSamplesOutput = 0;
|
||||
mSizeOfQueue = 0;
|
||||
mNextStreamSourceID.reset();
|
||||
if (!HasFatalError()) {
|
||||
mError.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool HasInternalSeekPending() const
|
||||
@ -410,8 +424,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)
|
||||
|
||||
{}
|
||||
|
@ -112,6 +112,11 @@ protected:
|
||||
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.
|
||||
@ -124,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.
|
||||
|
@ -48,7 +48,7 @@ public:
|
||||
media::TimeUnit::FromMicroseconds(aSample->mDuration),
|
||||
aSample->mOffset);
|
||||
if (!data) {
|
||||
mCallback->Error();
|
||||
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
|
||||
} else {
|
||||
mCallback->Output(data);
|
||||
}
|
||||
|
@ -145,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;
|
||||
@ -165,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) {
|
||||
@ -180,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(),
|
||||
@ -191,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.
|
||||
@ -210,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;
|
||||
@ -231,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);
|
||||
@ -240,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);
|
||||
@ -280,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,
|
||||
@ -298,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;
|
||||
|
@ -193,7 +193,7 @@ VPXDecoder::ProcessDecode(MediaRawData* aSample)
|
||||
return;
|
||||
}
|
||||
if (DoDecode(aSample) == -1) {
|
||||
mCallback->Error();
|
||||
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
|
||||
} else if (mTaskQueue->IsEmpty()) {
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ VorbisDataDecoder::ProcessDecode(MediaRawData* aSample)
|
||||
return;
|
||||
}
|
||||
if (DoDecode(aSample) == -1) {
|
||||
mCallback->Error();
|
||||
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
|
||||
} else if (mTaskQueue->IsEmpty()) {
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ nsresult
|
||||
WaveDataDecoder::Input(MediaRawData* aSample)
|
||||
{
|
||||
if (!DoDecode(aSample)) {
|
||||
mCallback->Error();
|
||||
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ public:
|
||||
Input(aDecrypted.mSample);
|
||||
} else if (GMP_FAILED(aDecrypted.mStatus)) {
|
||||
if (mCallback) {
|
||||
mCallback->Error();
|
||||
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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; \
|
||||
}
|
||||
|
||||
@ -645,7 +645,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 {
|
||||
|
@ -198,7 +198,7 @@ AppleATDecoder::SubmitSample(MediaRawData* aSample)
|
||||
if (!mConverter) {
|
||||
rv = SetupDecoder(aSample);
|
||||
if (rv != NS_OK && rv != NS_ERROR_NOT_INITIALIZED) {
|
||||
mCallback->Error();
|
||||
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -209,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;
|
||||
}
|
||||
}
|
||||
|
@ -343,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.
|
||||
@ -411,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;
|
||||
}
|
||||
|
||||
@ -513,7 +513,7 @@ AppleVDADecoder::DoDecode(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;
|
||||
}
|
||||
|
||||
|
@ -209,7 +209,7 @@ AppleVTDecoder::DoDecode(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;
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ FFmpegAudioDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample)
|
||||
|
||||
if (!PrepareFrame()) {
|
||||
NS_WARNING("FFmpeg audio decoder failed to allocate frame.");
|
||||
return DecodeResult::DECODE_ERROR;
|
||||
return DecodeResult::FATAL_ERROR;
|
||||
}
|
||||
|
||||
int64_t samplePosition = aSample->mOffset;
|
||||
@ -127,7 +127,7 @@ FFmpegAudioDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample)
|
||||
uint32_t numChannels = mCodecContext->channels;
|
||||
AudioConfig::ChannelLayout layout(numChannels);
|
||||
if (!layout.IsValid()) {
|
||||
return DecodeResult::DECODE_ERROR;
|
||||
return DecodeResult::FATAL_ERROR;
|
||||
}
|
||||
|
||||
uint32_t samplingRate = mCodecContext->sample_rate;
|
||||
|
@ -121,7 +121,10 @@ FFmpegDataDecoder<LIBAV_VER>::ProcessDecode(MediaRawData* aSample)
|
||||
}
|
||||
switch (DoDecode(aSample)) {
|
||||
case DecodeResult::DECODE_ERROR:
|
||||
mCallback->Error();
|
||||
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
|
||||
break;
|
||||
case DecodeResult::FATAL_ERROR:
|
||||
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
|
||||
break;
|
||||
default:
|
||||
if (mTaskQueue->IsEmpty()) {
|
||||
|
@ -43,7 +43,8 @@ protected:
|
||||
enum DecodeResult {
|
||||
DECODE_FRAME,
|
||||
DECODE_NO_FRAME,
|
||||
DECODE_ERROR
|
||||
DECODE_ERROR,
|
||||
FATAL_ERROR
|
||||
};
|
||||
|
||||
// Flush and Drain operation, always run
|
||||
|
@ -225,7 +225,7 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample,
|
||||
|
||||
if (!PrepareFrame()) {
|
||||
NS_WARNING("FFmpeg h264 decoder failed to allocate frame.");
|
||||
return DecodeResult::DECODE_ERROR;
|
||||
return DecodeResult::FATAL_ERROR;
|
||||
}
|
||||
|
||||
// Required with old version of FFmpeg/LibAV
|
||||
@ -301,7 +301,7 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample,
|
||||
|
||||
if (!v) {
|
||||
NS_WARNING("image allocation error.");
|
||||
return DecodeResult::DECODE_ERROR;
|
||||
return DecodeResult::FATAL_ERROR;
|
||||
}
|
||||
mCallback->Output(v);
|
||||
return DecodeResult::DECODE_FRAME;
|
||||
|
@ -438,10 +438,10 @@ OmxDataDecoder::EmptyBufferFailure(OmxBufferFailureHolder aFailureHolder)
|
||||
}
|
||||
|
||||
void
|
||||
OmxDataDecoder::NotifyError(OMX_ERRORTYPE aError, const char* aLine)
|
||||
OmxDataDecoder::NotifyError(OMX_ERRORTYPE aOmxError, const char* aLine, MediaDataDecoderError aError)
|
||||
{
|
||||
LOG("NotifyError %d at %s", aError, aLine);
|
||||
mCallback->Error();
|
||||
LOG("NotifyError %d (%d) at %s", aOmxError, aError, aLine);
|
||||
mCallback->Error(aError);
|
||||
}
|
||||
|
||||
void
|
||||
@ -696,6 +696,11 @@ OmxDataDecoder::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2)
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Got error during decoding, send msg to MFR skipping to next key frame.
|
||||
if (aEvent == OMX_EventError && mOmxState == OMX_StateExecuting) {
|
||||
NotifyError((OMX_ERRORTYPE)aData1, __func__, MediaDataDecoderError::DECODE_ERROR);
|
||||
return true;
|
||||
}
|
||||
LOG("WARNING: got none handle event: %d, aData1: %d, aData2: %d",
|
||||
aEvent, aData1, aData2);
|
||||
return false;
|
||||
|
@ -101,7 +101,9 @@ protected:
|
||||
|
||||
void EmptyBufferFailure(OmxBufferFailureHolder aFailureHolder);
|
||||
|
||||
void NotifyError(OMX_ERRORTYPE aError, const char* aLine);
|
||||
void NotifyError(OMX_ERRORTYPE aOmxError,
|
||||
const char* aLine,
|
||||
MediaDataDecoderError aError = MediaDataDecoderError::FATAL_ERROR);
|
||||
|
||||
// Configure audio/video codec.
|
||||
// Some codec may just ignore this and rely on codec specific data in
|
||||
|
@ -125,7 +125,7 @@ WMFMediaDataDecoder::ProcessDecode(MediaRawData* aSample)
|
||||
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;
|
||||
@ -154,7 +154,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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user