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 #include "MediaContainerType.h"
9 #include "nsMimeTypes.h"
10 #include "mozilla/Preferences.h"
11
12 #include "OggDecoder.h"
13 #include "OggDemuxer.h"
14
15 #include "WebMDecoder.h"
16 #include "WebMDemuxer.h"
17
18 #ifdef MOZ_ANDROID_HLS_SUPPORT
19 # include "HLSDecoder.h"
20 #endif
21 #ifdef MOZ_FMP4
22 # include "MP4Decoder.h"
23 # include "MP4Demuxer.h"
24 #endif
25 #include "MediaFormatReader.h"
26
27 #include "MP3Decoder.h"
28 #include "MP3Demuxer.h"
29
30 #include "WaveDecoder.h"
31 #include "WaveDemuxer.h"
32
33 #include "ADTSDecoder.h"
34 #include "ADTSDemuxer.h"
35
36 #include "FlacDecoder.h"
37 #include "FlacDemuxer.h"
38
39 #include "nsPluginHost.h"
40
41 namespace mozilla {
42
43 /* static */
IsHttpLiveStreamingType(const MediaContainerType & aType)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 */
IsMatroskaType(const MediaContainerType & aType)56 bool DecoderTraits::IsMatroskaType(const MediaContainerType& aType) {
57 const auto& mimeType = aType.Type();
58 // https://matroska.org/technical/specs/notes.html
59 return mimeType == MEDIAMIMETYPE("audio/x-matroska") ||
60 mimeType == MEDIAMIMETYPE("video/x-matroska");
61 }
62
63 /* static */
IsMP4SupportedType(const MediaContainerType & aType,DecoderDoctorDiagnostics * aDiagnostics)64 bool DecoderTraits::IsMP4SupportedType(const MediaContainerType& aType,
65 DecoderDoctorDiagnostics* aDiagnostics) {
66 #ifdef MOZ_FMP4
67 return MP4Decoder::IsSupportedType(aType, aDiagnostics);
68 #else
69 return false;
70 #endif
71 }
72
CanHandleCodecsType(const MediaContainerType & aType,DecoderDoctorDiagnostics * aDiagnostics)73 static CanPlayStatus CanHandleCodecsType(
74 const MediaContainerType& aType, DecoderDoctorDiagnostics* aDiagnostics) {
75 // We should have been given a codecs string, though it may be empty.
76 MOZ_ASSERT(aType.ExtendedType().HaveCodecs());
77
78 // Container type with the MIME type, no codecs.
79 const MediaContainerType mimeType(aType.Type());
80
81 if (OggDecoder::IsSupportedType(mimeType)) {
82 if (OggDecoder::IsSupportedType(aType)) {
83 return CANPLAY_YES;
84 }
85 // We can only reach this position if a particular codec was requested,
86 // ogg is supported and working: the codec must be invalid.
87 return CANPLAY_NO;
88 }
89 if (WaveDecoder::IsSupportedType(MediaContainerType(mimeType))) {
90 if (WaveDecoder::IsSupportedType(aType)) {
91 return CANPLAY_YES;
92 }
93 // We can only reach this position if a particular codec was requested, wave
94 // is supported and working: the codec must be invalid or not supported.
95 return CANPLAY_NO;
96 }
97 if (WebMDecoder::IsSupportedType(mimeType)) {
98 if (WebMDecoder::IsSupportedType(aType)) {
99 return CANPLAY_YES;
100 }
101 // We can only reach this position if a particular codec was requested,
102 // webm is supported and working: the codec must be invalid.
103 return CANPLAY_NO;
104 }
105 #ifdef MOZ_FMP4
106 if (MP4Decoder::IsSupportedType(mimeType,
107 /* DecoderDoctorDiagnostics* */ nullptr)) {
108 if (MP4Decoder::IsSupportedType(aType, aDiagnostics)) {
109 return CANPLAY_YES;
110 }
111 // We can only reach this position if a particular codec was requested,
112 // fmp4 is supported and working: the codec must be invalid.
113 return CANPLAY_NO;
114 }
115 #endif
116 if (MP3Decoder::IsSupportedType(mimeType)) {
117 if (MP3Decoder::IsSupportedType(aType)) {
118 return CANPLAY_YES;
119 }
120 // We can only reach this position if a particular codec was requested,
121 // mp3 is supported and working: the codec must be invalid.
122 return CANPLAY_NO;
123 }
124 if (ADTSDecoder::IsSupportedType(mimeType)) {
125 if (ADTSDecoder::IsSupportedType(aType)) {
126 return CANPLAY_YES;
127 }
128 // We can only reach this position if a particular codec was requested,
129 // adts is supported and working: the codec must be invalid.
130 return CANPLAY_NO;
131 }
132 if (FlacDecoder::IsSupportedType(mimeType)) {
133 if (FlacDecoder::IsSupportedType(aType)) {
134 return CANPLAY_YES;
135 }
136 // We can only reach this position if a particular codec was requested,
137 // flac is supported and working: the codec must be invalid.
138 return CANPLAY_NO;
139 }
140
141 return CANPLAY_MAYBE;
142 }
143
CanHandleMediaType(const MediaContainerType & aType,DecoderDoctorDiagnostics * aDiagnostics)144 static CanPlayStatus CanHandleMediaType(
145 const MediaContainerType& aType, DecoderDoctorDiagnostics* aDiagnostics) {
146 if (DecoderTraits::IsHttpLiveStreamingType(aType)) {
147 Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_REQUESTED, true);
148 }
149 #ifdef MOZ_ANDROID_HLS_SUPPORT
150 if (HLSDecoder::IsSupportedType(aType)) {
151 Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_SUPPORTED, true);
152 return CANPLAY_MAYBE;
153 }
154 #endif
155
156 if (DecoderTraits::IsMatroskaType(aType)) {
157 Telemetry::Accumulate(Telemetry::MEDIA_MKV_CANPLAY_REQUESTED, true);
158 }
159
160 if (aType.ExtendedType().HaveCodecs()) {
161 CanPlayStatus result = CanHandleCodecsType(aType, aDiagnostics);
162 if (result == CANPLAY_NO || result == CANPLAY_YES) {
163 return result;
164 }
165 }
166
167 // Container type with just the MIME type/subtype, no codecs.
168 const MediaContainerType mimeType(aType.Type());
169
170 if (OggDecoder::IsSupportedType(mimeType)) {
171 return CANPLAY_MAYBE;
172 }
173 if (WaveDecoder::IsSupportedType(mimeType)) {
174 return CANPLAY_MAYBE;
175 }
176 #ifdef MOZ_FMP4
177 if (MP4Decoder::IsSupportedType(mimeType, aDiagnostics)) {
178 return CANPLAY_MAYBE;
179 }
180 #endif
181 if (WebMDecoder::IsSupportedType(mimeType)) {
182 return CANPLAY_MAYBE;
183 }
184 if (MP3Decoder::IsSupportedType(mimeType)) {
185 return CANPLAY_MAYBE;
186 }
187 if (ADTSDecoder::IsSupportedType(mimeType)) {
188 return CANPLAY_MAYBE;
189 }
190 if (FlacDecoder::IsSupportedType(mimeType)) {
191 return CANPLAY_MAYBE;
192 }
193 return CANPLAY_NO;
194 }
195
196 /* static */
CanHandleContainerType(const MediaContainerType & aContainerType,DecoderDoctorDiagnostics * aDiagnostics)197 CanPlayStatus DecoderTraits::CanHandleContainerType(
198 const MediaContainerType& aContainerType,
199 DecoderDoctorDiagnostics* aDiagnostics) {
200 return CanHandleMediaType(aContainerType, aDiagnostics);
201 }
202
203 /* static */
ShouldHandleMediaType(const char * aMIMEType,DecoderDoctorDiagnostics * aDiagnostics)204 bool DecoderTraits::ShouldHandleMediaType(
205 const char* aMIMEType, DecoderDoctorDiagnostics* aDiagnostics) {
206 Maybe<MediaContainerType> containerType = MakeMediaContainerType(aMIMEType);
207 if (!containerType) {
208 return false;
209 }
210
211 if (WaveDecoder::IsSupportedType(*containerType)) {
212 // We should not return true for Wave types, since there are some
213 // Wave codecs actually in use in the wild that we don't support, and
214 // we should allow those to be handled by plugins or helper apps.
215 // Furthermore people can play Wave files on most platforms by other
216 // means.
217 return false;
218 }
219
220 // If an external plugin which can handle quicktime video is available
221 // (and not disabled), prefer it over native playback as there several
222 // codecs found in the wild that we do not handle.
223 if (containerType->Type() == MEDIAMIMETYPE("video/quicktime")) {
224 RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
225 if (pluginHost &&
226 pluginHost->HavePluginForType(containerType->Type().AsString())) {
227 return false;
228 }
229 }
230
231 return CanHandleMediaType(*containerType, aDiagnostics) != CANPLAY_NO;
232 }
233
234 /* static */
CreateDemuxer(const MediaContainerType & aType,MediaResource * aResource)235 already_AddRefed<MediaDataDemuxer> DecoderTraits::CreateDemuxer(
236 const MediaContainerType& aType, MediaResource* aResource) {
237 MOZ_ASSERT(NS_IsMainThread());
238 RefPtr<MediaDataDemuxer> demuxer;
239
240 #ifdef MOZ_FMP4
241 if (MP4Decoder::IsSupportedType(aType,
242 /* DecoderDoctorDiagnostics* */ nullptr)) {
243 demuxer = new MP4Demuxer(aResource);
244 } else
245 #endif
246 if (MP3Decoder::IsSupportedType(aType)) {
247 demuxer = new MP3Demuxer(aResource);
248 } else if (ADTSDecoder::IsSupportedType(aType)) {
249 demuxer = new ADTSDemuxer(aResource);
250 } else if (WaveDecoder::IsSupportedType(aType)) {
251 demuxer = new WAVDemuxer(aResource);
252 } else if (FlacDecoder::IsSupportedType(aType)) {
253 demuxer = new FlacDemuxer(aResource);
254 } else if (OggDecoder::IsSupportedType(aType)) {
255 demuxer = new OggDemuxer(aResource);
256 } else if (WebMDecoder::IsSupportedType(aType)) {
257 demuxer = new WebMDemuxer(aResource);
258 }
259
260 return demuxer.forget();
261 }
262
263 /* static */
CreateReader(const MediaContainerType & aType,MediaFormatReaderInit & aInit)264 MediaFormatReader* DecoderTraits::CreateReader(const MediaContainerType& aType,
265 MediaFormatReaderInit& aInit) {
266 MOZ_ASSERT(NS_IsMainThread());
267
268 RefPtr<MediaDataDemuxer> demuxer = CreateDemuxer(aType, aInit.mResource);
269 if (!demuxer) {
270 return nullptr;
271 }
272
273 MediaFormatReader* decoderReader = new MediaFormatReader(aInit, demuxer);
274
275 if (OggDecoder::IsSupportedType(aType)) {
276 static_cast<OggDemuxer*>(demuxer.get())
277 ->SetChainingEvents(&decoderReader->TimedMetadataProducer(),
278 &decoderReader->MediaNotSeekableProducer());
279 }
280
281 return decoderReader;
282 }
283
284 /* static */
IsSupportedInVideoDocument(const nsACString & aType)285 bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType) {
286 // Forbid playing media in video documents if the user has opted
287 // not to, using either the legacy WMF specific pref, or the newer
288 // catch-all pref.
289 if (!Preferences::GetBool("media.wmf.play-stand-alone", true) ||
290 !Preferences::GetBool("media.play-stand-alone", true)) {
291 return false;
292 }
293
294 Maybe<MediaContainerType> type = MakeMediaContainerType(aType);
295 if (!type) {
296 return false;
297 }
298
299 return OggDecoder::IsSupportedType(*type) ||
300 WebMDecoder::IsSupportedType(*type) ||
301 #ifdef MOZ_FMP4
302 MP4Decoder::IsSupportedType(*type,
303 /* DecoderDoctorDiagnostics* */ nullptr) ||
304 #endif
305 MP3Decoder::IsSupportedType(*type) ||
306 ADTSDecoder::IsSupportedType(*type) ||
307 FlacDecoder::IsSupportedType(*type) ||
308 #ifdef MOZ_ANDROID_HLS_SUPPORT
309 HLSDecoder::IsSupportedType(*type) ||
310 #endif
311 false;
312 }
313
314 /* static */
GetTracksInfo(const MediaContainerType & aType)315 nsTArray<UniquePtr<TrackInfo>> DecoderTraits::GetTracksInfo(
316 const MediaContainerType& aType) {
317 // Container type with just the MIME type/subtype, no codecs.
318 const MediaContainerType mimeType(aType.Type());
319
320 if (OggDecoder::IsSupportedType(mimeType)) {
321 return OggDecoder::GetTracksInfo(aType);
322 }
323 if (WaveDecoder::IsSupportedType(mimeType)) {
324 return WaveDecoder::GetTracksInfo(aType);
325 }
326 #ifdef MOZ_FMP4
327 if (MP4Decoder::IsSupportedType(mimeType, nullptr)) {
328 return MP4Decoder::GetTracksInfo(aType);
329 }
330 #endif
331 if (WebMDecoder::IsSupportedType(mimeType)) {
332 return WebMDecoder::GetTracksInfo(aType);
333 }
334 if (MP3Decoder::IsSupportedType(mimeType)) {
335 return MP3Decoder::GetTracksInfo(aType);
336 }
337 if (ADTSDecoder::IsSupportedType(mimeType)) {
338 return ADTSDecoder::GetTracksInfo(aType);
339 }
340 if (FlacDecoder::IsSupportedType(mimeType)) {
341 return FlacDecoder::GetTracksInfo(aType);
342 }
343 return nsTArray<UniquePtr<TrackInfo>>();
344 }
345
346 } // namespace mozilla
347