Bug 1068151 - keep decoding a corrupted video. r=jya

This commit is contained in:
Alfredo Yang 2016-05-30 18:24:00 +02:00
parent 714d7d715a
commit 68adb6b579
30 changed files with 161 additions and 93 deletions

View File

@ -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(); }));

View File

@ -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;

View File

@ -60,8 +60,10 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
VideoFrameContainer* aVideoFrameContainer,
layers::LayersBackend aLayersBackend)
: MediaDecoderReader(aDecoder)
, mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2))
, mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2))
, mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2),
Preferences::GetUint("media.audio-max-decode-error", 3))
, mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2),
Preferences::GetUint("media.video-max-decode-error", 2))
, mDemuxer(aDemuxer)
, mDemuxerInitDone(false)
, mLastReportedNumDecodedFrames(0)
@ -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:

View File

@ -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)
{}

View File

@ -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.

View File

@ -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);
}

View File

@ -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

View File

@ -36,10 +36,16 @@ public:
static bool IsOpus(const nsACString& aMimeType);
private:
enum DecodeError {
DECODE_SUCCESS,
DECODE_ERROR,
FATAL_ERROR
};
nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
void ProcessDecode(MediaRawData* aSample);
int DoDecode(MediaRawData* aSample);
DecodeError DoDecode(MediaRawData* aSample);
void ProcessDrain();
const AudioInfo& mInfo;

View File

@ -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();
}

View File

@ -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();
}

View File

@ -69,7 +69,7 @@ nsresult
WaveDataDecoder::Input(MediaRawData* aSample)
{
if (!DoDecode(aSample)) {
mCallback->Error();
mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
}
return NS_OK;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -10,9 +10,9 @@
namespace mozilla {
void
MediaDataDecoderCallbackProxy::Error()
MediaDataDecoderCallbackProxy::Error(MediaDataDecoderError aError)
{
mProxyCallback->Error();
mProxyCallback->Error(aError);
}
void

View File

@ -74,7 +74,7 @@ public:
mProxyCallback->Output(aData);
}
void Error() override;
void Error(MediaDataDecoderError aError) override;
void InputExhausted() override {
mProxyCallback->InputExhausted();

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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()) {

View File

@ -43,7 +43,8 @@ protected:
enum DecodeResult {
DECODE_FRAME,
DECODE_NO_FRAME,
DECODE_ERROR
DECODE_ERROR,
FATAL_ERROR
};
// Flush and Drain operation, always run

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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