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