/* static */ bool DecoderTraits::IsMatroskaType(const MediaContainerType& aType) { const auto& mimeType = aType.Type(); // https://matroska.org/technical/specs/notes.html return mimeType == MEDIAMIMETYPE("audio/x-matroska") || mimeType == MEDIAMIMETYPE("video/x-matroska"); }
static bool IsTypeValid(const MediaContainerType& aType) { // Whitelist MP4 types, so they explicitly match what we encounter on // the web, as opposed to what we use internally (i.e. what our demuxers // etc output). return aType.Type() == MEDIAMIMETYPE("audio/mp4") || aType.Type() == MEDIAMIMETYPE("audio/x-m4a") || aType.Type() == MEDIAMIMETYPE("video/mp4") || aType.Type() == MEDIAMIMETYPE("video/quicktime") || aType.Type() == MEDIAMIMETYPE("video/x-m4v"); }
/* static */ bool DecoderTraits::IsHttpLiveStreamingType(const MediaContainerType& aType) { const auto& mimeType = aType.Type(); return // For m3u8. // https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-10 mimeType == MEDIAMIMETYPE("application/vnd.apple.mpegurl") || // Some sites serve these as the informal m3u type. mimeType == MEDIAMIMETYPE("application/x-mpegurl") || mimeType == MEDIAMIMETYPE("audio/mpegurl") || mimeType == MEDIAMIMETYPE("audio/x-mpegurl"); }
/* static */ bool ADTSDecoder::IsSupportedType(const MediaContentType& aContentType) { if (aContentType.Type() == MEDIAMIMETYPE("audio/aac") || aContentType.Type() == MEDIAMIMETYPE("audio/aacp") || aContentType.Type() == MEDIAMIMETYPE("audio/x-aac")) { return IsEnabled() && (aContentType.ExtendedType().Codecs().IsEmpty() || aContentType.ExtendedType().Codecs().AsString().EqualsASCII("aac")); } return false; }
/* static */ bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType, DecoderDoctorDiagnostics* aDiagnostics) { Maybe<MediaContainerType> containerType = MakeMediaContainerType(aMIMEType); if (!containerType) { return false; } if (WaveDecoder::IsSupportedType(*containerType)) { // We should not return true for Wave types, since there are some // Wave codecs actually in use in the wild that we don't support, and // we should allow those to be handled by plugins or helper apps. // Furthermore people can play Wave files on most platforms by other // means. return false; } // If an external plugin which can handle quicktime video is available // (and not disabled), prefer it over native playback as there several // codecs found in the wild that we do not handle. if (containerType->Type() == MEDIAMIMETYPE("video/quicktime")) { RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst(); if (pluginHost && pluginHost->HavePluginForType(containerType->Type().AsString())) { return false; } } return CanHandleMediaType(*containerType, aDiagnostics) != CANPLAY_NO; }
// static bool VP9Benchmark::IsVP9DecodeFast() { MOZ_ASSERT(NS_IsMainThread()); #ifdef MOZ_WIDGET_ANDROID return false; #else bool hasPref = Preferences::HasUserValue(sBenchmarkFpsPref); uint32_t hadRecentUpdate = Preferences::GetUint(sBenchmarkFpsVersionCheck, 0U); if (!sHasRunTest && (!hasPref || hadRecentUpdate != sBenchmarkVersionID)) { sHasRunTest = true; RefPtr<WebMDemuxer> demuxer = new WebMDemuxer( new BufferMediaResource(sWebMSample, sizeof(sWebMSample), nullptr, MediaContainerType(MEDIAMIMETYPE("video/webm")))); RefPtr<Benchmark> estimiser = new Benchmark(demuxer, { Preferences::GetInt("media.benchmark.frames", 300), // frames to measure 1, // start benchmarking after decoding this frame. 8, // loop after decoding that many frames. TimeDuration::FromMilliseconds( Preferences::GetUint("media.benchmark.timeout", 1000)) }); estimiser->Run()->Then( // Non-DocGroup version of AbstractThread::MainThread for utility function. AbstractThread::MainThread(), __func__, [](uint32_t aDecodeFps) { if (XRE_IsContentProcess()) { dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); if (contentChild) { contentChild->SendNotifyBenchmarkResult(NS_LITERAL_STRING("VP9"), aDecodeFps); } } else { Preferences::SetUint(sBenchmarkFpsPref, aDecodeFps); Preferences::SetUint(sBenchmarkFpsVersionCheck, sBenchmarkVersionID); } Telemetry::Accumulate(Telemetry::HistogramID::VIDEO_VP9_BENCHMARK_FPS, aDecodeFps); }, []() { }); } if (!hasPref) { return false; } uint32_t decodeFps = Preferences::GetUint(sBenchmarkFpsPref); uint32_t threshold = Preferences::GetUint("media.benchmark.vp9.threshold", 150); return decodeFps >= threshold; #endif }
/* static */ bool MP4Decoder::IsSupportedType(const MediaContainerType& aType, DecoderDoctorDiagnostics* aDiagnostics) { if (!IsEnabled()) { return false; } MediaResult rv = NS_OK; auto tracks = GetTracksInfo(aType, rv); if (NS_FAILED(rv)) { return false; } if (tracks.IsEmpty()) { // No codecs specified. Assume H.264 or AAC if (aType.Type() == MEDIAMIMETYPE("audio/mp4") || aType.Type() == MEDIAMIMETYPE("audio/x-m4a")) { tracks.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/mp4a-latm"), aType)); } else { tracks.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/avc"), aType)); } } // Verify that we have a PDM that supports the whitelisted types. RefPtr<PDMFactory> platform = new PDMFactory(); for (const auto& track : tracks) { if (!track || !platform->Supports(*track, aDiagnostics)) { return false; } } return true; }
/* statis */ nsTArray<UniquePtr<TrackInfo>> MP4Decoder::GetTracksInfo(const MediaContainerType& aType, MediaResult& aError) { nsTArray<UniquePtr<TrackInfo>> tracks; if (!IsTypeValid(aType)) { aError = MediaResult( NS_ERROR_DOM_MEDIA_FATAL_ERR, RESULT_DETAIL("Invalid type:%s", aType.Type().AsString().get())); return tracks; } aError = NS_OK; const MediaCodecs& codecs = aType.ExtendedType().Codecs(); if (codecs.IsEmpty()) { return tracks; } const bool isVideo = aType.Type() == MEDIAMIMETYPE("video/mp4") || aType.Type() == MEDIAMIMETYPE("video/quicktime") || aType.Type() == MEDIAMIMETYPE("video/x-m4v"); for (const auto& codec : codecs.Range()) { if (IsAACCodecString(codec)) { tracks.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/mp4a-latm"), aType)); continue; } if (codec.EqualsLiteral("mp3")) { tracks.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/mpeg"), aType)); continue; } if (codec.EqualsLiteral("opus") || codec.EqualsLiteral("flac")) { tracks.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/") + NS_ConvertUTF16toUTF8(codec), aType)); continue; } if (IsVP9CodecString(codec)) { auto trackInfo = CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/vp9"), aType); uint8_t profile = 0; uint8_t level = 0; uint8_t bitDepth = 0; if (ExtractVPXCodecDetails(codec, profile, level, bitDepth)) { trackInfo->GetAsVideoInfo()->mBitDepth = bitDepth; } tracks.AppendElement(std::move(trackInfo)); continue; } if (isVideo && IsWhitelistedH264Codec(codec)) { auto trackInfo = CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/avc"), aType); uint8_t profile = 0, constraint = 0, level = 0; MOZ_ALWAYS_TRUE( ExtractH264CodecDetails(codec, profile, constraint, level)); uint32_t width = aType.ExtendedType().GetWidth().refOr(1280); uint32_t height = aType.ExtendedType().GetHeight().refOr(720); trackInfo->GetAsVideoInfo()->mExtraData = H264::CreateExtraData(profile, constraint, level, { width, height }); tracks.AppendElement(std::move(trackInfo)); continue; } // Unknown codec aError = MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, RESULT_DETAIL("Unknown codec:%s", NS_ConvertUTF16toUTF8(codec).get())); } return tracks; }
/* static */ bool MP4Decoder::IsSupportedType(const MediaContainerType& aType, DecoderDoctorDiagnostics* aDiagnostics) { if (!IsEnabled()) { return false; } // Whitelist MP4 types, so they explicitly match what we encounter on // the web, as opposed to what we use internally (i.e. what our demuxers // etc output). const bool isAudio = aType.Type() == MEDIAMIMETYPE("audio/mp4") || aType.Type() == MEDIAMIMETYPE("audio/x-m4a"); const bool isVideo = aType.Type() == MEDIAMIMETYPE("video/mp4") || aType.Type() == MEDIAMIMETYPE("video/quicktime") || aType.Type() == MEDIAMIMETYPE("video/x-m4v"); if (!isAudio && !isVideo) { return false; } nsTArray<UniquePtr<TrackInfo>> trackInfos; if (aType.ExtendedType().Codecs().IsEmpty()) { // No codecs specified. Assume H.264 if (isAudio) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/mp4a-latm"), aType)); } else { MOZ_ASSERT(isVideo); trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/avc"), aType)); } } else { // Verify that all the codecs specified are ones that we expect that // we can play. for (const auto& codec : aType.ExtendedType().Codecs().Range()) { if (IsAACCodecString(codec)) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/mp4a-latm"), aType)); continue; } if (codec.EqualsLiteral("mp3")) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/mpeg"), aType)); continue; } if (codec.EqualsLiteral("opus")) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/opus"), aType)); continue; } if (codec.EqualsLiteral("flac")) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/flac"), aType)); continue; } if (IsVP9CodecString(codec)) { auto trackInfo = CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/vp9"), aType); uint8_t profile = 0; uint8_t level = 0; uint8_t bitDepth = 0; if (ExtractVPXCodecDetails(codec, profile, level, bitDepth)) { trackInfo->GetAsVideoInfo()->mBitDepth = bitDepth; } trackInfos.AppendElement(Move(trackInfo)); continue; } // Note: Only accept H.264 in a video content type, not in an audio // content type. if (IsWhitelistedH264Codec(codec) && isVideo) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/avc"), aType)); continue; } // Some unsupported codec. return false; } } // Verify that we have a PDM that supports the whitelisted types. RefPtr<PDMFactory> platform = new PDMFactory(); for (const auto& trackInfo : trackInfos) { if (!trackInfo || !platform->Supports(*trackInfo, aDiagnostics)) { return false; } } return true; }