Line data Source code
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 "ChannelMediaDecoder.h"
8 : #include "DecoderTraits.h"
9 : #include "MediaContainerType.h"
10 : #include "nsMimeTypes.h"
11 : #include "mozilla/Preferences.h"
12 : #include "mozilla/Telemetry.h"
13 :
14 : #include "OggDecoder.h"
15 : #include "OggDemuxer.h"
16 :
17 : #include "WebMDecoder.h"
18 : #include "WebMDemuxer.h"
19 :
20 : #ifdef MOZ_ANDROID_OMX
21 : #include "AndroidMediaDecoder.h"
22 : #include "AndroidMediaReader.h"
23 : #include "AndroidMediaPluginHost.h"
24 : #endif
25 : #ifdef MOZ_ANDROID_HLS_SUPPORT
26 : #include "HLSDecoder.h"
27 : #endif
28 : #ifdef MOZ_FMP4
29 : #include "MP4Decoder.h"
30 : #include "MP4Demuxer.h"
31 : #endif
32 : #include "MediaFormatReader.h"
33 :
34 : #include "MP3Decoder.h"
35 : #include "MP3Demuxer.h"
36 :
37 : #include "WaveDecoder.h"
38 : #include "WaveDemuxer.h"
39 :
40 : #include "ADTSDecoder.h"
41 : #include "ADTSDemuxer.h"
42 :
43 : #include "FlacDecoder.h"
44 : #include "FlacDemuxer.h"
45 :
46 : #include "nsPluginHost.h"
47 : #include "MediaPrefs.h"
48 :
49 : namespace mozilla
50 : {
51 : #ifdef MOZ_ANDROID_OMX
52 : static bool
53 : IsAndroidMediaType(const MediaContainerType& aType)
54 : {
55 : if (!MediaDecoder::IsAndroidMediaPluginEnabled()) {
56 : return false;
57 : }
58 :
59 : return aType.Type() == MEDIAMIMETYPE("audio/mpeg")
60 : || aType.Type() == MEDIAMIMETYPE("audio/mp4")
61 : || aType.Type() == MEDIAMIMETYPE("video/mp4")
62 : || aType.Type() == MEDIAMIMETYPE("video/x-m4v");
63 : }
64 : #endif
65 :
66 :
67 : /* static */ bool
68 0 : DecoderTraits::IsHttpLiveStreamingType(const MediaContainerType& aType)
69 : {
70 0 : const auto& mimeType = aType.Type();
71 : return // For m3u8.
72 : // https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-10
73 0 : mimeType == MEDIAMIMETYPE("application/vnd.apple.mpegurl")
74 : // Some sites serve these as the informal m3u type.
75 0 : || mimeType == MEDIAMIMETYPE("application/x-mpegurl")
76 0 : || mimeType == MEDIAMIMETYPE("audio/mpegurl")
77 0 : || mimeType == MEDIAMIMETYPE("audio/x-mpegurl");
78 : }
79 :
80 : /* static */ bool
81 0 : DecoderTraits::IsMP4SupportedType(const MediaContainerType& aType,
82 : DecoderDoctorDiagnostics* aDiagnostics)
83 : {
84 : #ifdef MOZ_FMP4
85 0 : return MP4Decoder::IsSupportedType(aType, aDiagnostics);
86 : #else
87 : return false;
88 : #endif
89 : }
90 :
91 : static
92 : CanPlayStatus
93 0 : CanHandleCodecsType(const MediaContainerType& aType,
94 : DecoderDoctorDiagnostics* aDiagnostics)
95 : {
96 : // We should have been given a codecs string, though it may be empty.
97 0 : MOZ_ASSERT(aType.ExtendedType().HaveCodecs());
98 :
99 : // Container type with the MIME type, no codecs.
100 0 : const MediaContainerType mimeType(aType.Type());
101 :
102 0 : if (OggDecoder::IsSupportedType(mimeType)) {
103 0 : if (OggDecoder::IsSupportedType(aType)) {
104 0 : return CANPLAY_YES;
105 : }
106 : // We can only reach this position if a particular codec was requested,
107 : // ogg is supported and working: the codec must be invalid.
108 0 : return CANPLAY_NO;
109 : }
110 0 : if (WaveDecoder::IsSupportedType(MediaContainerType(mimeType))) {
111 0 : if (WaveDecoder::IsSupportedType(aType)) {
112 0 : return CANPLAY_YES;
113 : }
114 : // We can only reach this position if a particular codec was requested,
115 : // ogg is supported and working: the codec must be invalid.
116 0 : return CANPLAY_NO;
117 : }
118 : #if !defined(MOZ_OMX_WEBM_DECODER)
119 0 : if (WebMDecoder::IsSupportedType(mimeType)) {
120 0 : if (WebMDecoder::IsSupportedType(aType)) {
121 0 : return CANPLAY_YES;
122 : }
123 : // We can only reach this position if a particular codec was requested,
124 : // webm is supported and working: the codec must be invalid.
125 0 : return CANPLAY_NO;
126 : }
127 : #endif
128 : #ifdef MOZ_FMP4
129 0 : if (MP4Decoder::IsSupportedType(mimeType,
130 : /* DecoderDoctorDiagnostics* */ nullptr)) {
131 0 : if (MP4Decoder::IsSupportedType(aType, aDiagnostics)) {
132 0 : return CANPLAY_YES;
133 : }
134 : // We can only reach this position if a particular codec was requested,
135 : // fmp4 is supported and working: the codec must be invalid.
136 0 : return CANPLAY_NO;
137 : }
138 : #endif
139 0 : if (MP3Decoder::IsSupportedType(aType)) {
140 0 : return CANPLAY_YES;
141 : }
142 0 : if (ADTSDecoder::IsSupportedType(aType)) {
143 0 : return CANPLAY_YES;
144 : }
145 0 : if (FlacDecoder::IsSupportedType(aType)) {
146 0 : return CANPLAY_YES;
147 : }
148 :
149 0 : MediaCodecs supportedCodecs;
150 : #ifdef MOZ_ANDROID_OMX
151 : if (MediaDecoder::IsAndroidMediaPluginEnabled()) {
152 : EnsureAndroidMediaPluginHost()->FindDecoder(aType, &supportedCodecs);
153 : }
154 : #endif
155 0 : if (supportedCodecs.IsEmpty()) {
156 0 : return CANPLAY_MAYBE;
157 : }
158 :
159 0 : if (!supportedCodecs.ContainsAll(aType.ExtendedType().Codecs())) {
160 : // At least one requested codec is not supported.
161 0 : return CANPLAY_NO;
162 : }
163 0 : return CANPLAY_YES;
164 : }
165 :
166 : static
167 : CanPlayStatus
168 0 : CanHandleMediaType(const MediaContainerType& aType,
169 : DecoderDoctorDiagnostics* aDiagnostics)
170 : {
171 0 : MOZ_ASSERT(NS_IsMainThread());
172 :
173 : #ifdef MOZ_ANDROID_HLS_SUPPORT
174 : if (HLSDecoder::IsSupportedType(aType)) {
175 : return CANPLAY_MAYBE;
176 : }
177 : #endif
178 :
179 0 : if (DecoderTraits::IsHttpLiveStreamingType(aType)) {
180 0 : Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_REQUESTED, true);
181 : }
182 :
183 0 : if (aType.ExtendedType().HaveCodecs()) {
184 0 : CanPlayStatus result = CanHandleCodecsType(aType, aDiagnostics);
185 0 : if (result == CANPLAY_NO || result == CANPLAY_YES) {
186 0 : return result;
187 : }
188 : }
189 :
190 : // Container type with just the MIME type/subtype, no codecs.
191 0 : const MediaContainerType mimeType(aType.Type());
192 :
193 0 : if (OggDecoder::IsSupportedType(mimeType)) {
194 0 : return CANPLAY_MAYBE;
195 : }
196 0 : if (WaveDecoder::IsSupportedType(mimeType)) {
197 0 : return CANPLAY_MAYBE;
198 : }
199 : #ifdef MOZ_FMP4
200 0 : if (MP4Decoder::IsSupportedType(mimeType, aDiagnostics)) {
201 0 : return CANPLAY_MAYBE;
202 : }
203 : #endif
204 : #if !defined(MOZ_OMX_WEBM_DECODER)
205 0 : if (WebMDecoder::IsSupportedType(mimeType)) {
206 0 : return CANPLAY_MAYBE;
207 : }
208 : #endif
209 0 : if (MP3Decoder::IsSupportedType(mimeType)) {
210 0 : return CANPLAY_MAYBE;
211 : }
212 0 : if (ADTSDecoder::IsSupportedType(mimeType)) {
213 0 : return CANPLAY_MAYBE;
214 : }
215 0 : if (FlacDecoder::IsSupportedType(mimeType)) {
216 0 : return CANPLAY_MAYBE;
217 : }
218 : #ifdef MOZ_ANDROID_OMX
219 : if (MediaDecoder::IsAndroidMediaPluginEnabled() &&
220 : EnsureAndroidMediaPluginHost()->FindDecoder(mimeType, nullptr)) {
221 : return CANPLAY_MAYBE;
222 : }
223 : #endif
224 0 : return CANPLAY_NO;
225 : }
226 :
227 : /* static */
228 : CanPlayStatus
229 0 : DecoderTraits::CanHandleContainerType(const MediaContainerType& aContainerType,
230 : DecoderDoctorDiagnostics* aDiagnostics)
231 : {
232 0 : return CanHandleMediaType(aContainerType, aDiagnostics);
233 : }
234 :
235 : /* static */
236 0 : bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType,
237 : DecoderDoctorDiagnostics* aDiagnostics)
238 : {
239 0 : Maybe<MediaContainerType> containerType = MakeMediaContainerType(aMIMEType);
240 0 : if (!containerType) {
241 0 : return false;
242 : }
243 :
244 0 : if (WaveDecoder::IsSupportedType(*containerType)) {
245 : // We should not return true for Wave types, since there are some
246 : // Wave codecs actually in use in the wild that we don't support, and
247 : // we should allow those to be handled by plugins or helper apps.
248 : // Furthermore people can play Wave files on most platforms by other
249 : // means.
250 0 : return false;
251 : }
252 :
253 : // If an external plugin which can handle quicktime video is available
254 : // (and not disabled), prefer it over native playback as there several
255 : // codecs found in the wild that we do not handle.
256 0 : if (containerType->Type() == MEDIAMIMETYPE("video/quicktime")) {
257 0 : RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
258 0 : if (pluginHost &&
259 0 : pluginHost->HavePluginForType(containerType->Type().AsString())) {
260 0 : return false;
261 : }
262 : }
263 :
264 0 : return CanHandleMediaType(*containerType, aDiagnostics) != CANPLAY_NO;
265 : }
266 :
267 : // Instantiates but does not initialize decoder.
268 : static already_AddRefed<ChannelMediaDecoder>
269 0 : InstantiateDecoder(MediaDecoderInit& aInit,
270 : DecoderDoctorDiagnostics* aDiagnostics)
271 : {
272 0 : MOZ_ASSERT(NS_IsMainThread());
273 0 : RefPtr<ChannelMediaDecoder> decoder;
274 :
275 0 : const MediaContainerType& type = aInit.mContainerType;
276 :
277 : #ifdef MOZ_ANDROID_HLS_SUPPORT
278 : if (HLSDecoder::IsSupportedType(type)) {
279 : decoder = new HLSDecoder(aInit);
280 : return decoder.forget();
281 : }
282 : #endif
283 : #ifdef MOZ_FMP4
284 0 : if (MP4Decoder::IsSupportedType(type, aDiagnostics)) {
285 0 : decoder = new MP4Decoder(aInit);
286 0 : return decoder.forget();
287 : }
288 : #endif
289 0 : if (MP3Decoder::IsSupportedType(type)) {
290 0 : decoder = new MP3Decoder(aInit);
291 0 : return decoder.forget();
292 : }
293 0 : if (ADTSDecoder::IsSupportedType(type)) {
294 0 : decoder = new ADTSDecoder(aInit);
295 0 : return decoder.forget();
296 : }
297 0 : if (OggDecoder::IsSupportedType(type)) {
298 0 : decoder = new OggDecoder(aInit);
299 0 : return decoder.forget();
300 : }
301 0 : if (WaveDecoder::IsSupportedType(type)) {
302 0 : decoder = new WaveDecoder(aInit);
303 0 : return decoder.forget();
304 : }
305 0 : if (FlacDecoder::IsSupportedType(type)) {
306 0 : decoder = new FlacDecoder(aInit);
307 0 : return decoder.forget();
308 : }
309 : #ifdef MOZ_ANDROID_OMX
310 : if (MediaDecoder::IsAndroidMediaPluginEnabled() &&
311 : EnsureAndroidMediaPluginHost()->FindDecoder(type, nullptr)) {
312 : decoder = new AndroidMediaDecoder(aInit, type);
313 : return decoder.forget();
314 : }
315 : #endif
316 :
317 0 : if (WebMDecoder::IsSupportedType(type)) {
318 0 : decoder = new WebMDecoder(aInit);
319 0 : return decoder.forget();
320 : }
321 :
322 0 : if (DecoderTraits::IsHttpLiveStreamingType(type)) {
323 : // We don't have an HLS decoder.
324 0 : Telemetry::Accumulate(Telemetry::MEDIA_HLS_DECODER_SUCCESS, false);
325 : }
326 :
327 0 : return nullptr;
328 : }
329 :
330 : /* static */
331 : already_AddRefed<ChannelMediaDecoder>
332 0 : DecoderTraits::CreateDecoder(MediaDecoderInit& aInit,
333 : DecoderDoctorDiagnostics* aDiagnostics)
334 : {
335 0 : MOZ_ASSERT(NS_IsMainThread());
336 0 : return InstantiateDecoder(aInit, aDiagnostics);
337 : }
338 :
339 : /* static */
340 : MediaDecoderReader*
341 0 : DecoderTraits::CreateReader(const MediaContainerType& aType,
342 : const MediaDecoderReaderInit& aInit)
343 : {
344 0 : MOZ_ASSERT(NS_IsMainThread());
345 0 : MediaDecoderReader* decoderReader = nullptr;
346 :
347 0 : if (!aInit.mDecoder) {
348 0 : return decoderReader;
349 : }
350 :
351 0 : MediaResource* resource = aInit.mResource;
352 :
353 : #ifdef MOZ_FMP4
354 0 : if (MP4Decoder::IsSupportedType(aType,
355 : /* DecoderDoctorDiagnostics* */ nullptr)) {
356 0 : decoderReader = new MediaFormatReader(aInit, new MP4Demuxer(resource));
357 : } else
358 : #endif
359 0 : if (MP3Decoder::IsSupportedType(aType)) {
360 0 : decoderReader = new MediaFormatReader(aInit, new MP3Demuxer(resource));
361 : } else
362 0 : if (ADTSDecoder::IsSupportedType(aType)) {
363 0 : decoderReader = new MediaFormatReader(aInit, new ADTSDemuxer(resource));
364 : } else
365 0 : if (WaveDecoder::IsSupportedType(aType)) {
366 0 : decoderReader = new MediaFormatReader(aInit, new WAVDemuxer(resource));
367 : } else
368 0 : if (FlacDecoder::IsSupportedType(aType)) {
369 0 : decoderReader = new MediaFormatReader(aInit, new FlacDemuxer(resource));
370 : } else
371 0 : if (OggDecoder::IsSupportedType(aType)) {
372 0 : decoderReader = new MediaFormatReader(aInit, new OggDemuxer(resource));
373 : } else
374 : #ifdef MOZ_ANDROID_OMX
375 : if (MediaDecoder::IsAndroidMediaPluginEnabled() &&
376 : EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
377 : decoderReader = new AndroidMediaReader(aType, aInit);
378 : } else
379 : #endif
380 0 : if (WebMDecoder::IsSupportedType(aType)) {
381 0 : decoderReader = new MediaFormatReader(aInit, new WebMDemuxer(resource));
382 : }
383 :
384 0 : return decoderReader;
385 : }
386 :
387 : /* static */
388 0 : bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
389 : {
390 : // Forbid playing media in video documents if the user has opted
391 : // not to, using either the legacy WMF specific pref, or the newer
392 : // catch-all pref.
393 0 : if (!Preferences::GetBool("media.windows-media-foundation.play-stand-alone", true) ||
394 0 : !Preferences::GetBool("media.play-stand-alone", true)) {
395 0 : return false;
396 : }
397 :
398 0 : Maybe<MediaContainerType> type = MakeMediaContainerType(aType);
399 0 : if (!type) {
400 0 : return false;
401 : }
402 :
403 : return
404 0 : OggDecoder::IsSupportedType(*type) ||
405 0 : WebMDecoder::IsSupportedType(*type) ||
406 : #ifdef MOZ_ANDROID_OMX
407 : (MediaDecoder::IsAndroidMediaPluginEnabled() && IsAndroidMediaType(*type)) ||
408 : #endif
409 : #ifdef MOZ_FMP4
410 0 : MP4Decoder::IsSupportedType(*type, /* DecoderDoctorDiagnostics* */ nullptr) ||
411 : #endif
412 0 : MP3Decoder::IsSupportedType(*type) ||
413 0 : ADTSDecoder::IsSupportedType(*type) ||
414 0 : FlacDecoder::IsSupportedType(*type) ||
415 : #ifdef MOZ_ANDROID_HLS_SUPPORT
416 : HLSDecoder::IsSupportedType(*type) ||
417 : #endif
418 0 : false;
419 : }
420 :
421 : } // namespace mozilla
|