diff options
Diffstat (limited to 'dom/media/DecoderTraits.cpp')
-rw-r--r-- | dom/media/DecoderTraits.cpp | 532 |
1 files changed, 532 insertions, 0 deletions
diff --git a/dom/media/DecoderTraits.cpp b/dom/media/DecoderTraits.cpp new file mode 100644 index 000000000..ddd35fe0d --- /dev/null +++ b/dom/media/DecoderTraits.cpp @@ -0,0 +1,532 @@ +/* -*- 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 "DecoderTraits.h" +#include "MediaContentType.h" +#include "MediaDecoder.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsMimeTypes.h" +#include "mozilla/Preferences.h" +#include "mozilla/Telemetry.h" + +#include "OggDecoder.h" +#include "OggDemuxer.h" + +#include "WebMDecoder.h" +#include "WebMDemuxer.h" + +#ifdef MOZ_ANDROID_OMX +#include "AndroidMediaDecoder.h" +#include "AndroidMediaReader.h" +#include "AndroidMediaPluginHost.h" +#endif +#ifdef MOZ_DIRECTSHOW +#include "DirectShowDecoder.h" +#include "DirectShowReader.h" +#endif +#ifdef MOZ_FMP4 +#include "MP4Decoder.h" +#include "MP4Demuxer.h" +#endif +#include "MediaFormatReader.h" + +#include "MP3Decoder.h" +#include "MP3Demuxer.h" + +#include "WaveDecoder.h" +#include "WaveDemuxer.h" + +#include "ADTSDecoder.h" +#include "ADTSDemuxer.h" + +#include "FlacDecoder.h" +#include "FlacDemuxer.h" + +#include "nsPluginHost.h" +#include "MediaPrefs.h" + +namespace mozilla +{ + +template <class String> +static bool +CodecListContains(char const *const * aCodecs, const String& aCodec) +{ + for (int32_t i = 0; aCodecs[i]; ++i) { + if (aCodec.EqualsASCII(aCodecs[i])) + return true; + } + return false; +} + +static bool +IsOggSupportedType(const nsACString& aType, + const nsAString& aCodecs = EmptyString()) +{ + return OggDecoder::CanHandleMediaType(aType, aCodecs); +} + +static bool +IsOggTypeAndEnabled(const nsACString& aType) +{ + return IsOggSupportedType(aType); +} + +static bool +IsWebMSupportedType(const nsACString& aType, + const nsAString& aCodecs = EmptyString()) +{ + return WebMDecoder::CanHandleMediaType(aType, aCodecs); +} + +/* static */ bool +DecoderTraits::IsWebMTypeAndEnabled(const nsACString& aType) +{ + return IsWebMSupportedType(aType); +} + +/* static */ bool +DecoderTraits::IsWebMAudioType(const nsACString& aType) +{ + return aType.EqualsASCII("audio/webm"); +} + +static char const *const gHttpLiveStreamingTypes[] = { + // For m3u8. + // https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-10 + "application/vnd.apple.mpegurl", + // Some sites serve these as the informal m3u type. + "application/x-mpegurl", + "audio/x-mpegurl", + nullptr +}; + +static bool +IsHttpLiveStreamingType(const nsACString& aType) +{ + return CodecListContains(gHttpLiveStreamingTypes, aType); +} + +#ifdef MOZ_ANDROID_OMX +static bool +IsAndroidMediaType(const nsACString& aType) +{ + if (!MediaDecoder::IsAndroidMediaPluginEnabled()) { + return false; + } + + static const char* supportedTypes[] = { + "audio/mpeg", "audio/mp4", "video/mp4", "video/x-m4v", nullptr + }; + return CodecListContains(supportedTypes, aType); +} +#endif + +#ifdef MOZ_DIRECTSHOW +static bool +IsDirectShowSupportedType(const nsACString& aType) +{ + return DirectShowDecoder::GetSupportedCodecs(aType, nullptr); +} +#endif + +#ifdef MOZ_FMP4 +static bool +IsMP4SupportedType(const MediaContentType& aParsedType, + DecoderDoctorDiagnostics* aDiagnostics) +{ + return MP4Decoder::CanHandleMediaType(aParsedType, aDiagnostics); +} +static bool +IsMP4SupportedType(const nsACString& aType, + DecoderDoctorDiagnostics* aDiagnostics) +{ + MediaContentType contentType{aType}; + return IsMP4SupportedType(contentType, aDiagnostics); +} +#endif + +/* static */ bool +DecoderTraits::IsMP4TypeAndEnabled(const nsACString& aType, + DecoderDoctorDiagnostics* aDiagnostics) +{ +#ifdef MOZ_FMP4 + return IsMP4SupportedType(aType, aDiagnostics); +#else + return false; +#endif +} + +static bool +IsMP3SupportedType(const nsACString& aType, + const nsAString& aCodecs = EmptyString()) +{ + return MP3Decoder::CanHandleMediaType(aType, aCodecs); +} + +static bool +IsAACSupportedType(const nsACString& aType, + const nsAString& aCodecs = EmptyString()) +{ + return ADTSDecoder::CanHandleMediaType(aType, aCodecs); +} + +static bool +IsWaveSupportedType(const nsACString& aType, + const nsAString& aCodecs = EmptyString()) +{ + return WaveDecoder::CanHandleMediaType(aType, aCodecs); +} + +static bool +IsFlacSupportedType(const nsACString& aType, + const nsAString& aCodecs = EmptyString()) +{ + return FlacDecoder::CanHandleMediaType(aType, aCodecs); +} + +static +CanPlayStatus +CanHandleCodecsType(const MediaContentType& aType, + DecoderDoctorDiagnostics* aDiagnostics) +{ + MOZ_ASSERT(aType.IsValid()); + // We should have been given a codecs string, though it may be empty. + MOZ_ASSERT(aType.HaveCodecs()); + + char const* const* codecList = nullptr; + if (IsOggTypeAndEnabled(aType.GetMIMEType())) { + if (IsOggSupportedType(aType.GetMIMEType(), aType.GetCodecs())) { + return CANPLAY_YES; + } else { + // We can only reach this position if a particular codec was requested, + // ogg is supported and working: the codec must be invalid. + return CANPLAY_NO; + } + } + if (IsWaveSupportedType(aType.GetMIMEType())) { + if (IsWaveSupportedType(aType.GetMIMEType(), aType.GetCodecs())) { + return CANPLAY_YES; + } else { + // We can only reach this position if a particular codec was requested, + // ogg is supported and working: the codec must be invalid. + return CANPLAY_NO; + } + } +#if !defined(MOZ_OMX_WEBM_DECODER) + if (DecoderTraits::IsWebMTypeAndEnabled(aType.GetMIMEType())) { + if (IsWebMSupportedType(aType.GetMIMEType(), aType.GetCodecs())) { + return CANPLAY_YES; + } else { + // We can only reach this position if a particular codec was requested, + // webm is supported and working: the codec must be invalid. + return CANPLAY_NO; + } + } +#endif +#ifdef MOZ_FMP4 + if (DecoderTraits::IsMP4TypeAndEnabled(aType.GetMIMEType(), aDiagnostics)) { + if (IsMP4SupportedType(aType, aDiagnostics)) { + return CANPLAY_YES; + } else { + // We can only reach this position if a particular codec was requested, + // fmp4 is supported and working: the codec must be invalid. + return CANPLAY_NO; + } + } +#endif + if (IsMP3SupportedType(aType.GetMIMEType(), aType.GetCodecs())) { + return CANPLAY_YES; + } + if (IsAACSupportedType(aType.GetMIMEType(), aType.GetCodecs())) { + return CANPLAY_YES; + } + if (IsFlacSupportedType(aType.GetMIMEType(), aType.GetCodecs())) { + return CANPLAY_YES; + } +#ifdef MOZ_DIRECTSHOW + DirectShowDecoder::GetSupportedCodecs(aType.GetMIMEType(), &codecList); +#endif +#ifdef MOZ_ANDROID_OMX + if (MediaDecoder::IsAndroidMediaPluginEnabled()) { + EnsureAndroidMediaPluginHost()->FindDecoder(aType.GetMIMEType(), &codecList); + } +#endif + if (!codecList) { + return CANPLAY_MAYBE; + } + + // See http://www.rfc-editor.org/rfc/rfc4281.txt for the description + // of the 'codecs' parameter + nsCharSeparatedTokenizer tokenizer(aType.GetCodecs(), ','); + bool expectMoreTokens = false; + while (tokenizer.hasMoreTokens()) { + const nsSubstring& token = tokenizer.nextToken(); + + if (!CodecListContains(codecList, token)) { + // Totally unsupported codec + return CANPLAY_NO; + } + expectMoreTokens = tokenizer.separatorAfterCurrentToken(); + } + if (expectMoreTokens) { + // Last codec name was empty + return CANPLAY_NO; + } + + return CANPLAY_YES; +} + +static +CanPlayStatus +CanHandleMediaType(const MediaContentType& aType, + DecoderDoctorDiagnostics* aDiagnostics) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (IsHttpLiveStreamingType(aType.GetMIMEType())) { + Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_REQUESTED, true); + } + + if (aType.HaveCodecs()) { + CanPlayStatus result = CanHandleCodecsType(aType, aDiagnostics); + if (result == CANPLAY_NO || result == CANPLAY_YES) { + return result; + } + } + if (IsOggTypeAndEnabled(aType.GetMIMEType())) { + return CANPLAY_MAYBE; + } + if (IsWaveSupportedType(aType.GetMIMEType())) { + return CANPLAY_MAYBE; + } + if (DecoderTraits::IsMP4TypeAndEnabled(aType.GetMIMEType(), aDiagnostics)) { + return CANPLAY_MAYBE; + } +#if !defined(MOZ_OMX_WEBM_DECODER) + if (DecoderTraits::IsWebMTypeAndEnabled(aType.GetMIMEType())) { + return CANPLAY_MAYBE; + } +#endif + if (IsMP3SupportedType(aType.GetMIMEType())) { + return CANPLAY_MAYBE; + } + if (IsAACSupportedType(aType.GetMIMEType())) { + return CANPLAY_MAYBE; + } + if (IsFlacSupportedType(aType.GetMIMEType())) { + return CANPLAY_MAYBE; + } +#ifdef MOZ_DIRECTSHOW + if (DirectShowDecoder::GetSupportedCodecs(aType.GetMIMEType(), nullptr)) { + return CANPLAY_MAYBE; + } +#endif +#ifdef MOZ_ANDROID_OMX + if (MediaDecoder::IsAndroidMediaPluginEnabled() && + EnsureAndroidMediaPluginHost()->FindDecoder(aType.GetMIMEType(), nullptr)) { + return CANPLAY_MAYBE; + } +#endif + return CANPLAY_NO; +} + +/* static */ +CanPlayStatus +DecoderTraits::CanHandleContentType(const MediaContentType& aContentType, + DecoderDoctorDiagnostics* aDiagnostics) +{ + if (!aContentType.IsValid()) { + return CANPLAY_NO; + } + + return CanHandleMediaType(aContentType, aDiagnostics); +} + +/* static */ +bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType, + DecoderDoctorDiagnostics* aDiagnostics) +{ + if (IsWaveSupportedType(nsDependentCString(aMIMEType))) { + // 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 (nsDependentCString(aMIMEType).EqualsASCII("video/quicktime")) { + RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst(); + if (pluginHost && + pluginHost->HavePluginForType(nsDependentCString(aMIMEType))) { + return false; + } + } + + MediaContentType parsed{nsDependentCString(aMIMEType)}; + return CanHandleMediaType(parsed, aDiagnostics) + != CANPLAY_NO; +} + +// Instantiates but does not initialize decoder. +static +already_AddRefed<MediaDecoder> +InstantiateDecoder(const nsACString& aType, + MediaDecoderOwner* aOwner, + DecoderDoctorDiagnostics* aDiagnostics) +{ + MOZ_ASSERT(NS_IsMainThread()); + RefPtr<MediaDecoder> decoder; + +#ifdef MOZ_FMP4 + if (IsMP4SupportedType(aType, aDiagnostics)) { + decoder = new MP4Decoder(aOwner); + return decoder.forget(); + } +#endif + if (IsMP3SupportedType(aType)) { + decoder = new MP3Decoder(aOwner); + return decoder.forget(); + } + if (IsAACSupportedType(aType)) { + decoder = new ADTSDecoder(aOwner); + return decoder.forget(); + } + if (IsOggSupportedType(aType)) { + decoder = new OggDecoder(aOwner); + return decoder.forget(); + } + if (IsWaveSupportedType(aType)) { + decoder = new WaveDecoder(aOwner); + return decoder.forget(); + } + if (IsFlacSupportedType(aType)) { + decoder = new FlacDecoder(aOwner); + return decoder.forget(); + } +#ifdef MOZ_ANDROID_OMX + if (MediaDecoder::IsAndroidMediaPluginEnabled() && + EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) { + decoder = new AndroidMediaDecoder(aOwner, aType); + return decoder.forget(); + } +#endif + + if (IsWebMSupportedType(aType)) { + decoder = new WebMDecoder(aOwner); + return decoder.forget(); + } + +#ifdef MOZ_DIRECTSHOW + // Note: DirectShow should come before WMF, so that we prefer DirectShow's + // MP3 support over WMF's. + if (IsDirectShowSupportedType(aType)) { + decoder = new DirectShowDecoder(aOwner); + return decoder.forget(); + } +#endif + + if (IsHttpLiveStreamingType(aType)) { + // We don't have an HLS decoder. + Telemetry::Accumulate(Telemetry::MEDIA_HLS_DECODER_SUCCESS, false); + } + + return nullptr; +} + +/* static */ +already_AddRefed<MediaDecoder> +DecoderTraits::CreateDecoder(const nsACString& aType, + MediaDecoderOwner* aOwner, + DecoderDoctorDiagnostics* aDiagnostics) +{ + MOZ_ASSERT(NS_IsMainThread()); + return InstantiateDecoder(aType, aOwner, aDiagnostics); +} + +/* static */ +MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, AbstractMediaDecoder* aDecoder) +{ + MOZ_ASSERT(NS_IsMainThread()); + MediaDecoderReader* decoderReader = nullptr; + + if (!aDecoder) { + return decoderReader; + } +#ifdef MOZ_FMP4 + if (IsMP4SupportedType(aType, /* DecoderDoctorDiagnostics* */ nullptr)) { + decoderReader = new MediaFormatReader(aDecoder, new MP4Demuxer(aDecoder->GetResource())); + } else +#endif + if (IsMP3SupportedType(aType)) { + decoderReader = new MediaFormatReader(aDecoder, new mp3::MP3Demuxer(aDecoder->GetResource())); + } else + if (IsAACSupportedType(aType)) { + decoderReader = new MediaFormatReader(aDecoder, new ADTSDemuxer(aDecoder->GetResource())); + } else + if (IsWaveSupportedType(aType)) { + decoderReader = new MediaFormatReader(aDecoder, new WAVDemuxer(aDecoder->GetResource())); + } else + if (IsFlacSupportedType(aType)) { + decoderReader = new MediaFormatReader(aDecoder, new FlacDemuxer(aDecoder->GetResource())); + } else + if (IsOggSupportedType(aType)) { + decoderReader = new MediaFormatReader(aDecoder, new OggDemuxer(aDecoder->GetResource())); + } else +#ifdef MOZ_ANDROID_OMX + if (MediaDecoder::IsAndroidMediaPluginEnabled() && + EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) { + decoderReader = new AndroidMediaReader(aDecoder, aType); + } else +#endif + if (IsWebMSupportedType(aType)) { + decoderReader = + new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource())); + } else +#ifdef MOZ_DIRECTSHOW + if (IsDirectShowSupportedType(aType)) { + decoderReader = new DirectShowReader(aDecoder); + } else +#endif + if (false) {} // dummy if to take care of the dangling else + + return decoderReader; +} + +/* static */ +bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType) +{ + // Forbid playing media in video documents if the user has opted + // not to, using either the legacy WMF specific pref, or the newer + // catch-all pref. + if (!Preferences::GetBool("media.windows-media-foundation.play-stand-alone", true) || + !Preferences::GetBool("media.play-stand-alone", true)) { + return false; + } + + return + IsOggSupportedType(aType) || + IsWebMSupportedType(aType) || +#ifdef MOZ_ANDROID_OMX + (MediaDecoder::IsAndroidMediaPluginEnabled() && IsAndroidMediaType(aType)) || +#endif +#ifdef MOZ_FMP4 + IsMP4SupportedType(aType, /* DecoderDoctorDiagnostics* */ nullptr) || +#endif + IsMP3SupportedType(aType) || + IsAACSupportedType(aType) || + IsFlacSupportedType(aType) || +#ifdef MOZ_DIRECTSHOW + IsDirectShowSupportedType(aType) || +#endif + false; +} + +} // namespace mozilla |