tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DecoderTraits.cpp (11222B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "DecoderTraits.h"
      8 
      9 #include "MediaContainerType.h"
     10 #include "OggDecoder.h"
     11 #include "OggDemuxer.h"
     12 #include "WebMDecoder.h"
     13 #include "WebMDemuxer.h"
     14 #include "mozilla/Logging.h"
     15 #include "mozilla/Preferences.h"
     16 #include "mozilla/glean/DomMediaHlsMetrics.h"
     17 #include "mozilla/glean/DomMediaMetrics.h"
     18 #include "nsMimeTypes.h"
     19 
     20 #ifdef MOZ_ANDROID_HLS_SUPPORT
     21 #  include "HLSDecoder.h"
     22 #endif
     23 #include "ADTSDecoder.h"
     24 #include "ADTSDemuxer.h"
     25 #include "FlacDecoder.h"
     26 #include "FlacDemuxer.h"
     27 #include "MP3Decoder.h"
     28 #include "MP3Demuxer.h"
     29 #include "MP4Decoder.h"
     30 #include "MP4Demuxer.h"
     31 #include "MatroskaDecoder.h"
     32 #include "MatroskaDemuxer.h"
     33 #include "MediaFormatReader.h"
     34 #include "WaveDecoder.h"
     35 #include "WaveDemuxer.h"
     36 
     37 namespace mozilla {
     38 
     39 extern LazyLogModule gMediaDecoderLog;
     40 #define LOGD(x, ...) \
     41  MOZ_LOG_FMT(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)
     42 
     43 /* static */
     44 bool DecoderTraits::IsHttpLiveStreamingType(const MediaContainerType& aType) {
     45  const auto& mimeType = aType.Type();
     46  return  // For m3u8.
     47          // https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-10
     48      mimeType == MEDIAMIMETYPE("application/vnd.apple.mpegurl") ||
     49      // Some sites serve these as the informal m3u type.
     50      mimeType == MEDIAMIMETYPE("application/x-mpegurl") ||
     51      mimeType == MEDIAMIMETYPE("audio/mpegurl") ||
     52      mimeType == MEDIAMIMETYPE("audio/x-mpegurl");
     53 }
     54 
     55 static CanPlayStatus CanHandleCodecsType(
     56    const MediaContainerType& aType, DecoderDoctorDiagnostics* aDiagnostics) {
     57  // We should have been given a codecs string, though it may be empty.
     58  MOZ_ASSERT(aType.ExtendedType().HaveCodecs());
     59 
     60  // Container type with the MIME type, no codecs.
     61  const MediaContainerType mimeType(aType.Type());
     62 
     63  if (OggDecoder::IsSupportedType(mimeType)) {
     64    if (OggDecoder::IsSupportedType(aType)) {
     65      return CANPLAY_YES;
     66    }
     67    // We can only reach this position if a particular codec was requested,
     68    // ogg is supported and working: the codec must be invalid.
     69    return CANPLAY_NO;
     70  }
     71  if (WaveDecoder::IsSupportedType(MediaContainerType(mimeType))) {
     72    if (WaveDecoder::IsSupportedType(aType)) {
     73      return CANPLAY_YES;
     74    }
     75    // We can only reach this position if a particular codec was requested, wave
     76    // is supported and working: the codec must be invalid or not supported.
     77    return CANPLAY_NO;
     78  }
     79  if (WebMDecoder::IsSupportedType(mimeType)) {
     80    if (WebMDecoder::IsSupportedType(aType)) {
     81      return CANPLAY_YES;
     82    }
     83    // We can only reach this position if a particular codec was requested,
     84    // webm is supported and working: the codec must be invalid.
     85    return CANPLAY_NO;
     86  }
     87  if (MP4Decoder::IsSupportedType(mimeType,
     88                                  /* DecoderDoctorDiagnostics* */ nullptr)) {
     89    if (MP4Decoder::IsSupportedType(aType, aDiagnostics)) {
     90      return CANPLAY_YES;
     91    }
     92    // We can only reach this position if a particular codec was requested,
     93    // fmp4 is supported and working: the codec must be invalid.
     94    return CANPLAY_NO;
     95  }
     96  if (MP3Decoder::IsSupportedType(mimeType)) {
     97    if (MP3Decoder::IsSupportedType(aType)) {
     98      return CANPLAY_YES;
     99    }
    100    // We can only reach this position if a particular codec was requested,
    101    // mp3 is supported and working: the codec must be invalid.
    102    return CANPLAY_NO;
    103  }
    104  if (ADTSDecoder::IsSupportedType(mimeType)) {
    105    if (ADTSDecoder::IsSupportedType(aType)) {
    106      return CANPLAY_YES;
    107    }
    108    // We can only reach this position if a particular codec was requested,
    109    // adts is supported and working: the codec must be invalid.
    110    return CANPLAY_NO;
    111  }
    112  if (FlacDecoder::IsSupportedType(mimeType)) {
    113    if (FlacDecoder::IsSupportedType(aType)) {
    114      return CANPLAY_YES;
    115    }
    116    // We can only reach this position if a particular codec was requested,
    117    // flac is supported and working: the codec must be invalid.
    118    return CANPLAY_NO;
    119  }
    120  if (MatroskaDecoder::IsSupportedType(
    121          mimeType,
    122          /* DecoderDoctorDiagnostics* */ nullptr)) {
    123    if (MatroskaDecoder::IsSupportedType(aType, aDiagnostics)) {
    124      return CANPLAY_YES;
    125    }
    126    // We can only reach this position if a particular codec was requested,
    127    // mkv is supported and working: the codec must be invalid.
    128    return CANPLAY_NO;
    129  }
    130 
    131  return CANPLAY_MAYBE;
    132 }
    133 
    134 static CanPlayStatus CanHandleMediaType(
    135    const MediaContainerType& aType, DecoderDoctorDiagnostics* aDiagnostics) {
    136  if (DecoderTraits::IsHttpLiveStreamingType(aType)) {
    137    glean::hls::canplay_requested.Add();
    138  }
    139  if (MatroskaDecoder::IsMatroskaType(aType)) {
    140    glean::media::mkv_content_count.Add();
    141  }
    142 #ifdef MOZ_ANDROID_HLS_SUPPORT
    143  if (HLSDecoder::IsSupportedType(aType)) {
    144    glean::hls::canplay_supported.Add();
    145    return CANPLAY_MAYBE;
    146  }
    147 #endif
    148 
    149  if (aType.ExtendedType().HaveCodecs()) {
    150    CanPlayStatus result = CanHandleCodecsType(aType, aDiagnostics);
    151    if (result == CANPLAY_NO || result == CANPLAY_YES) {
    152      return result;
    153    }
    154  }
    155 
    156  // Container type with just the MIME type/subtype, no codecs.
    157  const MediaContainerType mimeType(aType.Type());
    158 
    159  if (OggDecoder::IsSupportedType(mimeType)) {
    160    return CANPLAY_MAYBE;
    161  }
    162  if (WaveDecoder::IsSupportedType(mimeType)) {
    163    return CANPLAY_MAYBE;
    164  }
    165  if (MP4Decoder::IsSupportedType(mimeType, aDiagnostics)) {
    166    return CANPLAY_MAYBE;
    167  }
    168  if (WebMDecoder::IsSupportedType(mimeType)) {
    169    return CANPLAY_MAYBE;
    170  }
    171  if (MP3Decoder::IsSupportedType(mimeType)) {
    172    return CANPLAY_MAYBE;
    173  }
    174  if (ADTSDecoder::IsSupportedType(mimeType)) {
    175    return CANPLAY_MAYBE;
    176  }
    177  if (FlacDecoder::IsSupportedType(mimeType)) {
    178    return CANPLAY_MAYBE;
    179  }
    180  if (MatroskaDecoder::IsSupportedType(mimeType, aDiagnostics)) {
    181    return CANPLAY_MAYBE;
    182  }
    183  return CANPLAY_NO;
    184 }
    185 
    186 /* static */
    187 CanPlayStatus DecoderTraits::CanHandleContainerType(
    188    const MediaContainerType& aContainerType,
    189    DecoderDoctorDiagnostics* aDiagnostics) {
    190  return CanHandleMediaType(aContainerType, aDiagnostics);
    191 }
    192 
    193 /* static */
    194 bool DecoderTraits::ShouldHandleMediaType(
    195    const nsACString& aMIMEType, DecoderDoctorDiagnostics* aDiagnostics) {
    196  Maybe<MediaContainerType> containerType = MakeMediaContainerType(aMIMEType);
    197  if (!containerType) {
    198    return false;
    199  }
    200 
    201  if (WaveDecoder::IsSupportedType(*containerType)) {
    202    // We should not return true for Wave types, since there are some
    203    // Wave codecs actually in use in the wild that we don't support, and
    204    // we should allow those to be handled by plugins or helper apps.
    205    // Furthermore people can play Wave files on most platforms by other
    206    // means.
    207    return false;
    208  }
    209 
    210  return CanHandleMediaType(*containerType, aDiagnostics) != CANPLAY_NO;
    211 }
    212 
    213 /* static */
    214 already_AddRefed<MediaDataDemuxer> DecoderTraits::CreateDemuxer(
    215    const MediaContainerType& aType, MediaResource* aResource) {
    216  MOZ_ASSERT(NS_IsMainThread());
    217  RefPtr<MediaDataDemuxer> demuxer;
    218 
    219  if (MP4Decoder::IsSupportedType(aType,
    220                                  /* DecoderDoctorDiagnostics* */ nullptr)) {
    221    demuxer = new MP4Demuxer(aResource);
    222  } else if (MP3Decoder::IsSupportedType(aType)) {
    223    demuxer = new MP3Demuxer(aResource);
    224  } else if (ADTSDecoder::IsSupportedType(aType)) {
    225    demuxer = new ADTSDemuxer(aResource);
    226  } else if (WaveDecoder::IsSupportedType(aType)) {
    227    demuxer = new WAVDemuxer(aResource);
    228  } else if (FlacDecoder::IsSupportedType(aType)) {
    229    demuxer = new FlacDemuxer(aResource);
    230  } else if (OggDecoder::IsSupportedType(aType)) {
    231    demuxer = new OggDemuxer(aResource);
    232  } else if (WebMDecoder::IsSupportedType(aType)) {
    233    demuxer = new WebMDemuxer(aResource);
    234  } else if (MatroskaDecoder::IsSupportedType(
    235                 aType,
    236                 /* DecoderDoctorDiagnostics* */ nullptr)) {
    237    demuxer = new MatroskaDemuxer(aResource);
    238  } else {
    239    LOGD("CreateDemuxer: unsupported type {}", aType.OriginalString().get());
    240  }
    241 
    242  return demuxer.forget();
    243 }
    244 
    245 /* static */
    246 MediaFormatReader* DecoderTraits::CreateReader(const MediaContainerType& aType,
    247                                               MediaFormatReaderInit& aInit) {
    248  MOZ_ASSERT(NS_IsMainThread());
    249 
    250  RefPtr<MediaDataDemuxer> demuxer = CreateDemuxer(aType, aInit.mResource);
    251  if (!demuxer) {
    252    return nullptr;
    253  }
    254 
    255  MediaFormatReader* decoderReader = new MediaFormatReader(aInit, demuxer);
    256 
    257  if (OggDecoder::IsSupportedType(aType)) {
    258    static_cast<OggDemuxer*>(demuxer.get())
    259        ->SetChainingEvents(&decoderReader->TimedMetadataProducer(),
    260                            &decoderReader->MediaNotSeekableProducer());
    261  }
    262 
    263  return decoderReader;
    264 }
    265 
    266 /* static */
    267 bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType) {
    268  // Forbid playing media in video documents if the user has opted
    269  // not to, using either the legacy WMF specific pref, or the newer
    270  // catch-all pref.
    271  if (!Preferences::GetBool("media.wmf.play-stand-alone", true) ||
    272      !Preferences::GetBool("media.play-stand-alone", true)) {
    273    return false;
    274  }
    275 
    276  Maybe<MediaContainerType> type = MakeMediaContainerType(aType);
    277  if (!type) {
    278    return false;
    279  }
    280 
    281  return OggDecoder::IsSupportedType(*type) ||
    282         WebMDecoder::IsSupportedType(*type) ||
    283         MP4Decoder::IsSupportedType(*type,
    284                                     /* DecoderDoctorDiagnostics* */ nullptr) ||
    285         MP3Decoder::IsSupportedType(*type) ||
    286         ADTSDecoder::IsSupportedType(*type) ||
    287         FlacDecoder::IsSupportedType(*type) ||
    288         MatroskaDecoder::IsSupportedType(
    289             *type,
    290             /* DecoderDoctorDiagnostics* */ nullptr) ||
    291 #ifdef MOZ_ANDROID_HLS_SUPPORT
    292         HLSDecoder::IsSupportedType(*type) ||
    293 #endif
    294         false;
    295 }
    296 
    297 /* static */
    298 nsTArray<UniquePtr<TrackInfo>> DecoderTraits::GetTracksInfo(
    299    const MediaContainerType& aType) {
    300  // Container type with just the MIME type/subtype, no codecs.
    301  const MediaContainerType mimeType(aType.Type());
    302 
    303  if (OggDecoder::IsSupportedType(mimeType)) {
    304    return OggDecoder::GetTracksInfo(aType);
    305  }
    306  if (WaveDecoder::IsSupportedType(mimeType)) {
    307    return WaveDecoder::GetTracksInfo(aType);
    308  }
    309  if (MP4Decoder::IsSupportedType(mimeType, nullptr)) {
    310    return MP4Decoder::GetTracksInfo(aType);
    311  }
    312  if (WebMDecoder::IsSupportedType(mimeType)) {
    313    return WebMDecoder::GetTracksInfo(aType);
    314  }
    315  if (MP3Decoder::IsSupportedType(mimeType)) {
    316    return MP3Decoder::GetTracksInfo(aType);
    317  }
    318  if (ADTSDecoder::IsSupportedType(mimeType)) {
    319    return ADTSDecoder::GetTracksInfo(aType);
    320  }
    321  if (FlacDecoder::IsSupportedType(mimeType)) {
    322    return FlacDecoder::GetTracksInfo(aType);
    323  }
    324  if (MatroskaDecoder::IsSupportedType(mimeType, nullptr)) {
    325    return MatroskaDecoder::GetTracksInfo(aType);
    326  }
    327  return nsTArray<UniquePtr<TrackInfo>>();
    328 }
    329 
    330 }  // namespace mozilla
    331 
    332 // avoid redefined macro in unified build
    333 #undef LOGD