diff options
Diffstat (limited to '')
-rw-r--r-- | dom/media/DecoderTraits.cpp | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/dom/media/DecoderTraits.cpp b/dom/media/DecoderTraits.cpp new file mode 100644 index 0000000000..ad64dca729 --- /dev/null +++ b/dom/media/DecoderTraits.cpp @@ -0,0 +1,345 @@ +/* -*- 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 "MediaContainerType.h" +#include "mozilla/Preferences.h" + +#include "OggDecoder.h" +#include "OggDemuxer.h" + +#include "WebMDecoder.h" +#include "WebMDemuxer.h" + +#ifdef MOZ_ANDROID_HLS_SUPPORT +# include "HLSDecoder.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" + +namespace mozilla { + +/* 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 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 DecoderTraits::IsMP4SupportedType(const MediaContainerType& aType, + DecoderDoctorDiagnostics* aDiagnostics) { +#ifdef MOZ_FMP4 + return MP4Decoder::IsSupportedType(aType, aDiagnostics); +#else + return false; +#endif +} + +static CanPlayStatus CanHandleCodecsType( + const MediaContainerType& aType, DecoderDoctorDiagnostics* aDiagnostics) { + // We should have been given a codecs string, though it may be empty. + MOZ_ASSERT(aType.ExtendedType().HaveCodecs()); + + // Container type with the MIME type, no codecs. + const MediaContainerType mimeType(aType.Type()); + + if (OggDecoder::IsSupportedType(mimeType)) { + if (OggDecoder::IsSupportedType(aType)) { + return CANPLAY_YES; + } + // 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 (WaveDecoder::IsSupportedType(MediaContainerType(mimeType))) { + if (WaveDecoder::IsSupportedType(aType)) { + return CANPLAY_YES; + } + // We can only reach this position if a particular codec was requested, wave + // is supported and working: the codec must be invalid or not supported. + return CANPLAY_NO; + } + if (WebMDecoder::IsSupportedType(mimeType)) { + if (WebMDecoder::IsSupportedType(aType)) { + return CANPLAY_YES; + } + // 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; + } +#ifdef MOZ_FMP4 + if (MP4Decoder::IsSupportedType(mimeType, + /* DecoderDoctorDiagnostics* */ nullptr)) { + if (MP4Decoder::IsSupportedType(aType, aDiagnostics)) { + return CANPLAY_YES; + } + // 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 (MP3Decoder::IsSupportedType(mimeType)) { + if (MP3Decoder::IsSupportedType(aType)) { + return CANPLAY_YES; + } + // We can only reach this position if a particular codec was requested, + // mp3 is supported and working: the codec must be invalid. + return CANPLAY_NO; + } + if (ADTSDecoder::IsSupportedType(mimeType)) { + if (ADTSDecoder::IsSupportedType(aType)) { + return CANPLAY_YES; + } + // We can only reach this position if a particular codec was requested, + // adts is supported and working: the codec must be invalid. + return CANPLAY_NO; + } + if (FlacDecoder::IsSupportedType(mimeType)) { + if (FlacDecoder::IsSupportedType(aType)) { + return CANPLAY_YES; + } + // We can only reach this position if a particular codec was requested, + // flac is supported and working: the codec must be invalid. + return CANPLAY_NO; + } + + return CANPLAY_MAYBE; +} + +static CanPlayStatus CanHandleMediaType( + const MediaContainerType& aType, DecoderDoctorDiagnostics* aDiagnostics) { + if (DecoderTraits::IsHttpLiveStreamingType(aType)) { + Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_REQUESTED, true); + } +#ifdef MOZ_ANDROID_HLS_SUPPORT + if (HLSDecoder::IsSupportedType(aType)) { + Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_SUPPORTED, true); + return CANPLAY_MAYBE; + } +#endif + + if (DecoderTraits::IsMatroskaType(aType)) { + Telemetry::Accumulate(Telemetry::MEDIA_MKV_CANPLAY_REQUESTED, true); + } + + if (aType.ExtendedType().HaveCodecs()) { + CanPlayStatus result = CanHandleCodecsType(aType, aDiagnostics); + if (result == CANPLAY_NO || result == CANPLAY_YES) { + return result; + } + } + + // Container type with just the MIME type/subtype, no codecs. + const MediaContainerType mimeType(aType.Type()); + + if (OggDecoder::IsSupportedType(mimeType)) { + return CANPLAY_MAYBE; + } + if (WaveDecoder::IsSupportedType(mimeType)) { + return CANPLAY_MAYBE; + } +#ifdef MOZ_FMP4 + if (MP4Decoder::IsSupportedType(mimeType, aDiagnostics)) { + return CANPLAY_MAYBE; + } +#endif + if (WebMDecoder::IsSupportedType(mimeType)) { + return CANPLAY_MAYBE; + } + if (MP3Decoder::IsSupportedType(mimeType)) { + return CANPLAY_MAYBE; + } + if (ADTSDecoder::IsSupportedType(mimeType)) { + return CANPLAY_MAYBE; + } + if (FlacDecoder::IsSupportedType(mimeType)) { + return CANPLAY_MAYBE; + } + return CANPLAY_NO; +} + +/* static */ +CanPlayStatus DecoderTraits::CanHandleContainerType( + const MediaContainerType& aContainerType, + DecoderDoctorDiagnostics* aDiagnostics) { + return CanHandleMediaType(aContainerType, aDiagnostics); +} + +/* 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 */ +already_AddRefed<MediaDataDemuxer> DecoderTraits::CreateDemuxer( + const MediaContainerType& aType, MediaResource* aResource) { + MOZ_ASSERT(NS_IsMainThread()); + RefPtr<MediaDataDemuxer> demuxer; + +#ifdef MOZ_FMP4 + if (MP4Decoder::IsSupportedType(aType, + /* DecoderDoctorDiagnostics* */ nullptr)) { + demuxer = new MP4Demuxer(aResource); + } else +#endif + if (MP3Decoder::IsSupportedType(aType)) { + demuxer = new MP3Demuxer(aResource); + } else if (ADTSDecoder::IsSupportedType(aType)) { + demuxer = new ADTSDemuxer(aResource); + } else if (WaveDecoder::IsSupportedType(aType)) { + demuxer = new WAVDemuxer(aResource); + } else if (FlacDecoder::IsSupportedType(aType)) { + demuxer = new FlacDemuxer(aResource); + } else if (OggDecoder::IsSupportedType(aType)) { + demuxer = new OggDemuxer(aResource); + } else if (WebMDecoder::IsSupportedType(aType)) { + demuxer = new WebMDemuxer(aResource); + } + + return demuxer.forget(); +} + +/* static */ +MediaFormatReader* DecoderTraits::CreateReader(const MediaContainerType& aType, + MediaFormatReaderInit& aInit) { + MOZ_ASSERT(NS_IsMainThread()); + + RefPtr<MediaDataDemuxer> demuxer = CreateDemuxer(aType, aInit.mResource); + if (!demuxer) { + return nullptr; + } + + MediaFormatReader* decoderReader = new MediaFormatReader(aInit, demuxer); + + if (OggDecoder::IsSupportedType(aType)) { + static_cast<OggDemuxer*>(demuxer.get()) + ->SetChainingEvents(&decoderReader->TimedMetadataProducer(), + &decoderReader->MediaNotSeekableProducer()); + } + + 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.wmf.play-stand-alone", true) || + !Preferences::GetBool("media.play-stand-alone", true)) { + return false; + } + + Maybe<MediaContainerType> type = MakeMediaContainerType(aType); + if (!type) { + return false; + } + + return OggDecoder::IsSupportedType(*type) || + WebMDecoder::IsSupportedType(*type) || +#ifdef MOZ_FMP4 + MP4Decoder::IsSupportedType(*type, + /* DecoderDoctorDiagnostics* */ nullptr) || +#endif + MP3Decoder::IsSupportedType(*type) || + ADTSDecoder::IsSupportedType(*type) || + FlacDecoder::IsSupportedType(*type) || +#ifdef MOZ_ANDROID_HLS_SUPPORT + HLSDecoder::IsSupportedType(*type) || +#endif + false; +} + +/* static */ +nsTArray<UniquePtr<TrackInfo>> DecoderTraits::GetTracksInfo( + const MediaContainerType& aType) { + // Container type with just the MIME type/subtype, no codecs. + const MediaContainerType mimeType(aType.Type()); + + if (OggDecoder::IsSupportedType(mimeType)) { + return OggDecoder::GetTracksInfo(aType); + } + if (WaveDecoder::IsSupportedType(mimeType)) { + return WaveDecoder::GetTracksInfo(aType); + } +#ifdef MOZ_FMP4 + if (MP4Decoder::IsSupportedType(mimeType, nullptr)) { + return MP4Decoder::GetTracksInfo(aType); + } +#endif + if (WebMDecoder::IsSupportedType(mimeType)) { + return WebMDecoder::GetTracksInfo(aType); + } + if (MP3Decoder::IsSupportedType(mimeType)) { + return MP3Decoder::GetTracksInfo(aType); + } + if (ADTSDecoder::IsSupportedType(mimeType)) { + return ADTSDecoder::GetTracksInfo(aType); + } + if (FlacDecoder::IsSupportedType(mimeType)) { + return FlacDecoder::GetTracksInfo(aType); + } + return nsTArray<UniquePtr<TrackInfo>>(); +} + +} // namespace mozilla |