// Create a webcompat-friendly description of a MediaResult. static nsString MediaResultDescription(const MediaResult& aResult, bool aIsError) { nsCString name; GetErrorName(aResult.Code(), name); return NS_ConvertUTF8toUTF16( nsPrintfCString( "%s Code: %s (0x%08" PRIx32 ")%s%s", aIsError ? "Error" : "Warning", name.get(), static_cast<uint32_t>(aResult.Code()), aResult.Message().IsEmpty() ? "" : "\nDetails: ", aResult.Message().get())); }
static bool AllowDecodeIssue(const MediaResult& aDecodeIssue, bool aDecodeIssueIsError) { if (aDecodeIssue == NS_OK) { // 'NS_OK' means we are not actually reporting a decode issue, so we // allow the report. return true; } // "media.decoder-doctor.decode-{errors,warnings}-allowed" controls which // decode issues may be dispatched to the front-end. It either contains: // - '*' -> Allow everything. // - Comma-separater list of ids -> Allow if the issue name is one of them. // - Nothing (missing or empty) -> Disable everything. nsAutoCString filter; Preferences::GetCString(aDecodeIssueIsError ? "media.decoder-doctor.decode-errors-allowed" : "media.decoder-doctor.decode-warnings-allowed", filter); if (filter.EqualsLiteral("*")) { return true; } nsCString decodeIssueName; GetErrorName(aDecodeIssue.Code(), static_cast<nsACString&>(decodeIssueName)); return StringListContains(filter, decodeIssueName); }
void MediaSource::EndOfStream(const MediaResult& aError) { MOZ_ASSERT(NS_IsMainThread()); MSE_API("EndOfStream(aError=%s)", aError.ErrorName().get()); SetReadyState(MediaSourceReadyState::Ended); mSourceBuffers->Ended(); mDecoder->DecodeError(aError); }
void DecoderDoctorDiagnostics::StoreDecodeWarning(nsIDocument* aDocument, const MediaResult& aWarning, const nsString& aMediaSrc, const char* aCallSite) { MOZ_ASSERT(NS_IsMainThread()); // Make sure we only store once. MOZ_ASSERT(mDiagnosticsType == eUnsaved); mDiagnosticsType = eDecodeWarning; if (NS_WARN_IF(!aDocument)) { DD_WARN("DecoderDoctorDiagnostics[%p]::StoreDecodeWarning(" "nsIDocument* aDocument=nullptr, aWarning=%s," " aMediaSrc=<provided>, call site '%s')", this, aWarning.Description().get(), aCallSite); return; } RefPtr<DecoderDoctorDocumentWatcher> watcher = DecoderDoctorDocumentWatcher::RetrieveOrCreate(aDocument); if (NS_WARN_IF(!watcher)) { DD_WARN("DecoderDoctorDiagnostics[%p]::StoreDecodeWarning(" "nsIDocument* aDocument=%p, aWarning='%s', aMediaSrc=<provided>," " call site '%s') - Could not create document watcher", this, aDocument, aWarning.Description().get(), aCallSite); return; } mDecodeIssue = aWarning; mDecodeIssueMediaSrc = aMediaSrc; // StoreDecodeWarning should only be called once, after all data is // available, so it is safe to std::move() from this object. watcher->AddDiagnostics(std::move(*this), aCallSite); // Even though it's moved-from, the type should stay set // (Only used to ensure that we do store only once.) MOZ_ASSERT(mDiagnosticsType == eDecodeWarning); }
RefPtr<ReaderProxy::AudioDataPromise> ReaderProxy::OnAudioDataRequestFailed(const MediaResult& aError) { MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); if (mSeamlessLoopingBlocked || !mSeamlessLoopingEnabled || aError.Code() != NS_ERROR_DOM_MEDIA_END_OF_STREAM) { return AudioDataPromise::CreateAndReject(aError, __func__); } // The data time in the audio queue is assumed to be increased linearly, // so we need to add the last ending time as the offset to correct the // audio data time in the next round when seamless looping is enabled. mLoopingOffset = mLastAudioEndTime; // Save the duration of the audio track if it hasn't been set. if (!mAudioDuration.IsValid()) { mAudioDuration = mLastAudioEndTime; } // For seamless looping, the demuxer is sought to the beginning and then // keep requesting decoded data in advance, upon receiving EOS. // The MDSM will not be aware of the EOS and keep receiving decoded data // as usual while looping is on. RefPtr<ReaderProxy> self = this; RefPtr<MediaFormatReader> reader = mReader; ResetDecode(TrackInfo::kAudioTrack); return SeekInternal(SeekTarget(media::TimeUnit::Zero(), SeekTarget::Accurate)) ->Then(mReader->OwnerThread(), __func__, [reader]() { return reader->RequestAudioData(); }, [](const SeekRejectValue& aReject) { return AudioDataPromise::CreateAndReject(aReject.mError, __func__); }) ->Then(mOwnerThread, __func__, [self](RefPtr<AudioData> aAudio) { return self->OnAudioDataRequestCompleted(aAudio.forget()); }, [](const MediaResult& aError) { return AudioDataPromise::CreateAndReject(aError, __func__); }); }
MP4Metadata::ResultAndTrackInfo MP4Metadata::GetTrackInfo( mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const { Maybe<uint32_t> trackIndex = TrackTypeToGlobalTrackIndex(aType, aTrackNumber); if (trackIndex.isNothing()) { return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("No %s tracks", TrackTypeToStr(aType))), nullptr}; } Mp4parseTrackInfo info; auto rv = mp4parse_get_track_info(mParser.get(), trackIndex.value(), &info); if (rv != MP4PARSE_STATUS_OK) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("mp4parse_get_track_info returned %d", rv)); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("Cannot find %s track #%zu", TrackTypeToStr(aType), aTrackNumber)), nullptr}; } #ifdef DEBUG bool haveSampleInfo = false; const char* codecString = "unrecognized"; Mp4parseCodec codecType = MP4PARSE_CODEC_UNKNOWN; if (info.track_type == MP4PARSE_TRACK_TYPE_AUDIO) { Mp4parseTrackAudioInfo audio; auto rv = mp4parse_get_track_audio_info(mParser.get(), trackIndex.value(), &audio); if (rv == MP4PARSE_STATUS_OK && audio.sample_info_count > 0) { codecType = audio.sample_info[0].codec_type; haveSampleInfo = true; } } else if (info.track_type == MP4PARSE_TRACK_TYPE_VIDEO) { Mp4parseTrackVideoInfo video; auto rv = mp4parse_get_track_video_info(mParser.get(), trackIndex.value(), &video); if (rv == MP4PARSE_STATUS_OK && video.sample_info_count > 0) { codecType = video.sample_info[0].codec_type; haveSampleInfo = true; } } if (haveSampleInfo) { switch (codecType) { case MP4PARSE_CODEC_UNKNOWN: codecString = "unknown"; break; case MP4PARSE_CODEC_AAC: codecString = "aac"; break; case MP4PARSE_CODEC_OPUS: codecString = "opus"; break; case MP4PARSE_CODEC_FLAC: codecString = "flac"; break; case MP4PARSE_CODEC_ALAC: codecString = "alac"; break; case MP4PARSE_CODEC_AVC: codecString = "h.264"; break; case MP4PARSE_CODEC_VP9: codecString = "vp9"; break; case MP4PARSE_CODEC_AV1: codecString = "av1"; break; case MP4PARSE_CODEC_MP3: codecString = "mp3"; break; case MP4PARSE_CODEC_MP4V: codecString = "mp4v"; break; case MP4PARSE_CODEC_JPEG: codecString = "jpeg"; break; case MP4PARSE_CODEC_AC3: codecString = "ac-3"; break; case MP4PARSE_CODEC_EC3: codecString = "ec-3"; break; } } MOZ_LOG(gMP4MetadataLog, LogLevel::Debug, ("track codec %s (%u)\n", codecString, codecType)); #endif // This specialization interface is crazy. UniquePtr<mozilla::TrackInfo> e; switch (aType) { case TrackInfo::TrackType::kAudioTrack: { Mp4parseTrackAudioInfo audio; auto rv = mp4parse_get_track_audio_info(mParser.get(), trackIndex.value(), &audio); if (rv != MP4PARSE_STATUS_OK) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("mp4parse_get_track_audio_info returned error %d", rv)); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("Cannot parse %s track #%zu", TrackTypeToStr(aType), aTrackNumber)), nullptr}; } auto track = mozilla::MakeUnique<MP4AudioInfo>(); MediaResult updateStatus = track->Update(&info, &audio); if (NS_FAILED(updateStatus)) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("Updating audio track failed with %s", updateStatus.Message().get())); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL( "Failed to update %s track #%zu with error: %s", TrackTypeToStr(aType), aTrackNumber, updateStatus.Message().get())), nullptr}; } e = std::move(track); } break; case TrackInfo::TrackType::kVideoTrack: { Mp4parseTrackVideoInfo video; auto rv = mp4parse_get_track_video_info(mParser.get(), trackIndex.value(), &video); if (rv != MP4PARSE_STATUS_OK) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("mp4parse_get_track_video_info returned error %d", rv)); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("Cannot parse %s track #%zu", TrackTypeToStr(aType), aTrackNumber)), nullptr}; } auto track = mozilla::MakeUnique<MP4VideoInfo>(); MediaResult updateStatus = track->Update(&info, &video); if (NS_FAILED(updateStatus)) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("Updating video track failed with %s", updateStatus.Message().get())); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL( "Failed to update %s track #%zu with error: %s", TrackTypeToStr(aType), aTrackNumber, updateStatus.Message().get())), nullptr}; } e = std::move(track); } break; default: MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("unhandled track type %d", aType)); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("Cannot handle %s track #%zu", TrackTypeToStr(aType), aTrackNumber)), nullptr}; } // No duration in track, use fragment_duration. if (e && !e->mDuration.IsPositive()) { Mp4parseFragmentInfo info; auto rv = mp4parse_get_fragment_info(mParser.get(), &info); if (rv == MP4PARSE_STATUS_OK) { e->mDuration = TimeUnit::FromMicroseconds(info.fragment_duration); } } if (e && e->IsValid()) { return {NS_OK, std::move(e)}; } MOZ_LOG(gMP4MetadataLog, LogLevel::Debug, ("TrackInfo didn't validate")); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("Invalid %s track #%zu", TrackTypeToStr(aType), aTrackNumber)), nullptr}; }