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/. */
7 #include "DecoderTraits.h"
8 #include "MediaContainerType.h"
9 #include "mozilla/Preferences.h"
11 #include "OggDecoder.h"
12 #include "OggDemuxer.h"
14 #include "WebMDecoder.h"
15 #include "WebMDemuxer.h"
17 #ifdef MOZ_ANDROID_HLS_SUPPORT
18 # include "HLSDecoder.h"
21 # include "MP4Decoder.h"
22 # include "MP4Demuxer.h"
24 #include "MediaFormatReader.h"
26 #include "MP3Decoder.h"
27 #include "MP3Demuxer.h"
29 #include "WaveDecoder.h"
30 #include "WaveDemuxer.h"
32 #include "ADTSDecoder.h"
33 #include "ADTSDemuxer.h"
35 #include "FlacDecoder.h"
36 #include "FlacDemuxer.h"
38 #include "nsPluginHost.h"
43 bool DecoderTraits::IsHttpLiveStreamingType(const MediaContainerType
& aType
) {
44 const auto& mimeType
= aType
.Type();
46 // https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-10
47 mimeType
== MEDIAMIMETYPE("application/vnd.apple.mpegurl") ||
48 // Some sites serve these as the informal m3u type.
49 mimeType
== MEDIAMIMETYPE("application/x-mpegurl") ||
50 mimeType
== MEDIAMIMETYPE("audio/mpegurl") ||
51 mimeType
== MEDIAMIMETYPE("audio/x-mpegurl");
55 bool DecoderTraits::IsMatroskaType(const MediaContainerType
& aType
) {
56 const auto& mimeType
= aType
.Type();
57 // https://matroska.org/technical/specs/notes.html
58 return mimeType
== MEDIAMIMETYPE("audio/x-matroska") ||
59 mimeType
== MEDIAMIMETYPE("video/x-matroska");
63 bool DecoderTraits::IsMP4SupportedType(const MediaContainerType
& aType
,
64 DecoderDoctorDiagnostics
* aDiagnostics
) {
66 return MP4Decoder::IsSupportedType(aType
, aDiagnostics
);
72 static CanPlayStatus
CanHandleCodecsType(
73 const MediaContainerType
& aType
, DecoderDoctorDiagnostics
* aDiagnostics
) {
74 // We should have been given a codecs string, though it may be empty.
75 MOZ_ASSERT(aType
.ExtendedType().HaveCodecs());
77 // Container type with the MIME type, no codecs.
78 const MediaContainerType
mimeType(aType
.Type());
80 if (OggDecoder::IsSupportedType(mimeType
)) {
81 if (OggDecoder::IsSupportedType(aType
)) {
84 // We can only reach this position if a particular codec was requested,
85 // ogg is supported and working: the codec must be invalid.
88 if (WaveDecoder::IsSupportedType(MediaContainerType(mimeType
))) {
89 if (WaveDecoder::IsSupportedType(aType
)) {
92 // We can only reach this position if a particular codec was requested, wave
93 // is supported and working: the codec must be invalid or not supported.
96 if (WebMDecoder::IsSupportedType(mimeType
)) {
97 if (WebMDecoder::IsSupportedType(aType
)) {
100 // We can only reach this position if a particular codec was requested,
101 // webm is supported and working: the codec must be invalid.
105 if (MP4Decoder::IsSupportedType(mimeType
,
106 /* DecoderDoctorDiagnostics* */ nullptr)) {
107 if (MP4Decoder::IsSupportedType(aType
, aDiagnostics
)) {
110 // We can only reach this position if a particular codec was requested,
111 // fmp4 is supported and working: the codec must be invalid.
115 if (MP3Decoder::IsSupportedType(mimeType
)) {
116 if (MP3Decoder::IsSupportedType(aType
)) {
119 // We can only reach this position if a particular codec was requested,
120 // mp3 is supported and working: the codec must be invalid.
123 if (ADTSDecoder::IsSupportedType(mimeType
)) {
124 if (ADTSDecoder::IsSupportedType(aType
)) {
127 // We can only reach this position if a particular codec was requested,
128 // adts is supported and working: the codec must be invalid.
131 if (FlacDecoder::IsSupportedType(mimeType
)) {
132 if (FlacDecoder::IsSupportedType(aType
)) {
135 // We can only reach this position if a particular codec was requested,
136 // flac is supported and working: the codec must be invalid.
140 return CANPLAY_MAYBE
;
143 static CanPlayStatus
CanHandleMediaType(
144 const MediaContainerType
& aType
, DecoderDoctorDiagnostics
* aDiagnostics
) {
145 if (DecoderTraits::IsHttpLiveStreamingType(aType
)) {
146 Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_REQUESTED
, true);
148 #ifdef MOZ_ANDROID_HLS_SUPPORT
149 if (HLSDecoder::IsSupportedType(aType
)) {
150 Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_SUPPORTED
, true);
151 return CANPLAY_MAYBE
;
155 if (DecoderTraits::IsMatroskaType(aType
)) {
156 Telemetry::Accumulate(Telemetry::MEDIA_MKV_CANPLAY_REQUESTED
, true);
159 if (aType
.ExtendedType().HaveCodecs()) {
160 CanPlayStatus result
= CanHandleCodecsType(aType
, aDiagnostics
);
161 if (result
== CANPLAY_NO
|| result
== CANPLAY_YES
) {
166 // Container type with just the MIME type/subtype, no codecs.
167 const MediaContainerType
mimeType(aType
.Type());
169 if (OggDecoder::IsSupportedType(mimeType
)) {
170 return CANPLAY_MAYBE
;
172 if (WaveDecoder::IsSupportedType(mimeType
)) {
173 return CANPLAY_MAYBE
;
176 if (MP4Decoder::IsSupportedType(mimeType
, aDiagnostics
)) {
177 return CANPLAY_MAYBE
;
180 if (WebMDecoder::IsSupportedType(mimeType
)) {
181 return CANPLAY_MAYBE
;
183 if (MP3Decoder::IsSupportedType(mimeType
)) {
184 return CANPLAY_MAYBE
;
186 if (ADTSDecoder::IsSupportedType(mimeType
)) {
187 return CANPLAY_MAYBE
;
189 if (FlacDecoder::IsSupportedType(mimeType
)) {
190 return CANPLAY_MAYBE
;
196 CanPlayStatus
DecoderTraits::CanHandleContainerType(
197 const MediaContainerType
& aContainerType
,
198 DecoderDoctorDiagnostics
* aDiagnostics
) {
199 return CanHandleMediaType(aContainerType
, aDiagnostics
);
203 bool DecoderTraits::ShouldHandleMediaType(
204 const char* aMIMEType
, DecoderDoctorDiagnostics
* aDiagnostics
) {
205 Maybe
<MediaContainerType
> containerType
= MakeMediaContainerType(aMIMEType
);
206 if (!containerType
) {
210 if (WaveDecoder::IsSupportedType(*containerType
)) {
211 // We should not return true for Wave types, since there are some
212 // Wave codecs actually in use in the wild that we don't support, and
213 // we should allow those to be handled by plugins or helper apps.
214 // Furthermore people can play Wave files on most platforms by other
219 // If an external plugin which can handle quicktime video is available
220 // (and not disabled), prefer it over native playback as there several
221 // codecs found in the wild that we do not handle.
222 if (containerType
->Type() == MEDIAMIMETYPE("video/quicktime")) {
223 RefPtr
<nsPluginHost
> pluginHost
= nsPluginHost::GetInst();
225 pluginHost
->HavePluginForType(containerType
->Type().AsString())) {
230 return CanHandleMediaType(*containerType
, aDiagnostics
) != CANPLAY_NO
;
234 already_AddRefed
<MediaDataDemuxer
> DecoderTraits::CreateDemuxer(
235 const MediaContainerType
& aType
, MediaResource
* aResource
) {
236 MOZ_ASSERT(NS_IsMainThread());
237 RefPtr
<MediaDataDemuxer
> demuxer
;
240 if (MP4Decoder::IsSupportedType(aType
,
241 /* DecoderDoctorDiagnostics* */ nullptr)) {
242 demuxer
= new MP4Demuxer(aResource
);
245 if (MP3Decoder::IsSupportedType(aType
)) {
246 demuxer
= new MP3Demuxer(aResource
);
247 } else if (ADTSDecoder::IsSupportedType(aType
)) {
248 demuxer
= new ADTSDemuxer(aResource
);
249 } else if (WaveDecoder::IsSupportedType(aType
)) {
250 demuxer
= new WAVDemuxer(aResource
);
251 } else if (FlacDecoder::IsSupportedType(aType
)) {
252 demuxer
= new FlacDemuxer(aResource
);
253 } else if (OggDecoder::IsSupportedType(aType
)) {
254 demuxer
= new OggDemuxer(aResource
);
255 } else if (WebMDecoder::IsSupportedType(aType
)) {
256 demuxer
= new WebMDemuxer(aResource
);
259 return demuxer
.forget();
263 MediaFormatReader
* DecoderTraits::CreateReader(const MediaContainerType
& aType
,
264 MediaFormatReaderInit
& aInit
) {
265 MOZ_ASSERT(NS_IsMainThread());
267 RefPtr
<MediaDataDemuxer
> demuxer
= CreateDemuxer(aType
, aInit
.mResource
);
272 MediaFormatReader
* decoderReader
= new MediaFormatReader(aInit
, demuxer
);
274 if (OggDecoder::IsSupportedType(aType
)) {
275 static_cast<OggDemuxer
*>(demuxer
.get())
276 ->SetChainingEvents(&decoderReader
->TimedMetadataProducer(),
277 &decoderReader
->MediaNotSeekableProducer());
280 return decoderReader
;
284 bool DecoderTraits::IsSupportedInVideoDocument(const nsACString
& aType
) {
285 // Forbid playing media in video documents if the user has opted
286 // not to, using either the legacy WMF specific pref, or the newer
288 if (!Preferences::GetBool("media.wmf.play-stand-alone", true) ||
289 !Preferences::GetBool("media.play-stand-alone", true)) {
293 Maybe
<MediaContainerType
> type
= MakeMediaContainerType(aType
);
298 return OggDecoder::IsSupportedType(*type
) ||
299 WebMDecoder::IsSupportedType(*type
) ||
301 MP4Decoder::IsSupportedType(*type
,
302 /* DecoderDoctorDiagnostics* */ nullptr) ||
304 MP3Decoder::IsSupportedType(*type
) ||
305 ADTSDecoder::IsSupportedType(*type
) ||
306 FlacDecoder::IsSupportedType(*type
) ||
307 #ifdef MOZ_ANDROID_HLS_SUPPORT
308 HLSDecoder::IsSupportedType(*type
) ||
314 nsTArray
<UniquePtr
<TrackInfo
>> DecoderTraits::GetTracksInfo(
315 const MediaContainerType
& aType
) {
316 // Container type with just the MIME type/subtype, no codecs.
317 const MediaContainerType
mimeType(aType
.Type());
319 if (OggDecoder::IsSupportedType(mimeType
)) {
320 return OggDecoder::GetTracksInfo(aType
);
322 if (WaveDecoder::IsSupportedType(mimeType
)) {
323 return WaveDecoder::GetTracksInfo(aType
);
326 if (MP4Decoder::IsSupportedType(mimeType
, nullptr)) {
327 return MP4Decoder::GetTracksInfo(aType
);
330 if (WebMDecoder::IsSupportedType(mimeType
)) {
331 return WebMDecoder::GetTracksInfo(aType
);
333 if (MP3Decoder::IsSupportedType(mimeType
)) {
334 return MP3Decoder::GetTracksInfo(aType
);
336 if (ADTSDecoder::IsSupportedType(mimeType
)) {
337 return ADTSDecoder::GetTracksInfo(aType
);
339 if (FlacDecoder::IsSupportedType(mimeType
)) {
340 return FlacDecoder::GetTracksInfo(aType
);
342 return nsTArray
<UniquePtr
<TrackInfo
>>();
345 } // namespace mozilla