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"
20 #include "MP4Decoder.h"
21 #include "MP4Demuxer.h"
22 #include "MediaFormatReader.h"
24 #include "MP3Decoder.h"
25 #include "MP3Demuxer.h"
27 #include "WaveDecoder.h"
28 #include "WaveDemuxer.h"
30 #include "ADTSDecoder.h"
31 #include "ADTSDemuxer.h"
33 #include "FlacDecoder.h"
34 #include "FlacDemuxer.h"
39 bool DecoderTraits::IsHttpLiveStreamingType(const MediaContainerType
& aType
) {
40 const auto& mimeType
= aType
.Type();
42 // https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-10
43 mimeType
== MEDIAMIMETYPE("application/vnd.apple.mpegurl") ||
44 // Some sites serve these as the informal m3u type.
45 mimeType
== MEDIAMIMETYPE("application/x-mpegurl") ||
46 mimeType
== MEDIAMIMETYPE("audio/mpegurl") ||
47 mimeType
== MEDIAMIMETYPE("audio/x-mpegurl");
51 bool DecoderTraits::IsMatroskaType(const MediaContainerType
& aType
) {
52 const auto& mimeType
= aType
.Type();
53 // https://matroska.org/technical/specs/notes.html
54 return mimeType
== MEDIAMIMETYPE("audio/x-matroska") ||
55 mimeType
== MEDIAMIMETYPE("video/x-matroska");
58 static CanPlayStatus
CanHandleCodecsType(
59 const MediaContainerType
& aType
, DecoderDoctorDiagnostics
* aDiagnostics
) {
60 // We should have been given a codecs string, though it may be empty.
61 MOZ_ASSERT(aType
.ExtendedType().HaveCodecs());
63 // Container type with the MIME type, no codecs.
64 const MediaContainerType
mimeType(aType
.Type());
66 if (OggDecoder::IsSupportedType(mimeType
)) {
67 if (OggDecoder::IsSupportedType(aType
)) {
70 // We can only reach this position if a particular codec was requested,
71 // ogg is supported and working: the codec must be invalid.
74 if (WaveDecoder::IsSupportedType(MediaContainerType(mimeType
))) {
75 if (WaveDecoder::IsSupportedType(aType
)) {
78 // We can only reach this position if a particular codec was requested, wave
79 // is supported and working: the codec must be invalid or not supported.
82 if (WebMDecoder::IsSupportedType(mimeType
)) {
83 if (WebMDecoder::IsSupportedType(aType
)) {
86 // We can only reach this position if a particular codec was requested,
87 // webm is supported and working: the codec must be invalid.
90 if (MP4Decoder::IsSupportedType(mimeType
,
91 /* DecoderDoctorDiagnostics* */ nullptr)) {
92 if (MP4Decoder::IsSupportedType(aType
, aDiagnostics
)) {
95 // We can only reach this position if a particular codec was requested,
96 // fmp4 is supported and working: the codec must be invalid.
99 if (MP3Decoder::IsSupportedType(mimeType
)) {
100 if (MP3Decoder::IsSupportedType(aType
)) {
103 // We can only reach this position if a particular codec was requested,
104 // mp3 is supported and working: the codec must be invalid.
107 if (ADTSDecoder::IsSupportedType(mimeType
)) {
108 if (ADTSDecoder::IsSupportedType(aType
)) {
111 // We can only reach this position if a particular codec was requested,
112 // adts is supported and working: the codec must be invalid.
115 if (FlacDecoder::IsSupportedType(mimeType
)) {
116 if (FlacDecoder::IsSupportedType(aType
)) {
119 // We can only reach this position if a particular codec was requested,
120 // flac is supported and working: the codec must be invalid.
124 return CANPLAY_MAYBE
;
127 static CanPlayStatus
CanHandleMediaType(
128 const MediaContainerType
& aType
, DecoderDoctorDiagnostics
* aDiagnostics
) {
129 if (DecoderTraits::IsHttpLiveStreamingType(aType
)) {
130 Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_REQUESTED
, true);
132 #ifdef MOZ_ANDROID_HLS_SUPPORT
133 if (HLSDecoder::IsSupportedType(aType
)) {
134 Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_SUPPORTED
, true);
135 return CANPLAY_MAYBE
;
139 if (DecoderTraits::IsMatroskaType(aType
)) {
140 Telemetry::Accumulate(Telemetry::MEDIA_MKV_CANPLAY_REQUESTED
, true);
143 if (aType
.ExtendedType().HaveCodecs()) {
144 CanPlayStatus result
= CanHandleCodecsType(aType
, aDiagnostics
);
145 if (result
== CANPLAY_NO
|| result
== CANPLAY_YES
) {
150 // Container type with just the MIME type/subtype, no codecs.
151 const MediaContainerType
mimeType(aType
.Type());
153 if (OggDecoder::IsSupportedType(mimeType
)) {
154 return CANPLAY_MAYBE
;
156 if (WaveDecoder::IsSupportedType(mimeType
)) {
157 return CANPLAY_MAYBE
;
159 if (MP4Decoder::IsSupportedType(mimeType
, aDiagnostics
)) {
160 return CANPLAY_MAYBE
;
162 if (WebMDecoder::IsSupportedType(mimeType
)) {
163 return CANPLAY_MAYBE
;
165 if (MP3Decoder::IsSupportedType(mimeType
)) {
166 return CANPLAY_MAYBE
;
168 if (ADTSDecoder::IsSupportedType(mimeType
)) {
169 return CANPLAY_MAYBE
;
171 if (FlacDecoder::IsSupportedType(mimeType
)) {
172 return CANPLAY_MAYBE
;
178 CanPlayStatus
DecoderTraits::CanHandleContainerType(
179 const MediaContainerType
& aContainerType
,
180 DecoderDoctorDiagnostics
* aDiagnostics
) {
181 return CanHandleMediaType(aContainerType
, aDiagnostics
);
185 bool DecoderTraits::ShouldHandleMediaType(
186 const char* aMIMEType
, DecoderDoctorDiagnostics
* aDiagnostics
) {
187 Maybe
<MediaContainerType
> containerType
= MakeMediaContainerType(aMIMEType
);
188 if (!containerType
) {
192 if (WaveDecoder::IsSupportedType(*containerType
)) {
193 // We should not return true for Wave types, since there are some
194 // Wave codecs actually in use in the wild that we don't support, and
195 // we should allow those to be handled by plugins or helper apps.
196 // Furthermore people can play Wave files on most platforms by other
201 return CanHandleMediaType(*containerType
, aDiagnostics
) != CANPLAY_NO
;
205 already_AddRefed
<MediaDataDemuxer
> DecoderTraits::CreateDemuxer(
206 const MediaContainerType
& aType
, MediaResource
* aResource
) {
207 MOZ_ASSERT(NS_IsMainThread());
208 RefPtr
<MediaDataDemuxer
> demuxer
;
210 if (MP4Decoder::IsSupportedType(aType
,
211 /* DecoderDoctorDiagnostics* */ nullptr)) {
212 demuxer
= new MP4Demuxer(aResource
);
213 } else if (MP3Decoder::IsSupportedType(aType
)) {
214 demuxer
= new MP3Demuxer(aResource
);
215 } else if (ADTSDecoder::IsSupportedType(aType
)) {
216 demuxer
= new ADTSDemuxer(aResource
);
217 } else if (WaveDecoder::IsSupportedType(aType
)) {
218 demuxer
= new WAVDemuxer(aResource
);
219 } else if (FlacDecoder::IsSupportedType(aType
)) {
220 demuxer
= new FlacDemuxer(aResource
);
221 } else if (OggDecoder::IsSupportedType(aType
)) {
222 demuxer
= new OggDemuxer(aResource
);
223 } else if (WebMDecoder::IsSupportedType(aType
)) {
224 demuxer
= new WebMDemuxer(aResource
);
227 return demuxer
.forget();
231 MediaFormatReader
* DecoderTraits::CreateReader(const MediaContainerType
& aType
,
232 MediaFormatReaderInit
& aInit
) {
233 MOZ_ASSERT(NS_IsMainThread());
235 RefPtr
<MediaDataDemuxer
> demuxer
= CreateDemuxer(aType
, aInit
.mResource
);
240 MediaFormatReader
* decoderReader
= new MediaFormatReader(aInit
, demuxer
);
242 if (OggDecoder::IsSupportedType(aType
)) {
243 static_cast<OggDemuxer
*>(demuxer
.get())
244 ->SetChainingEvents(&decoderReader
->TimedMetadataProducer(),
245 &decoderReader
->MediaNotSeekableProducer());
248 return decoderReader
;
252 bool DecoderTraits::IsSupportedInVideoDocument(const nsACString
& aType
) {
253 // Forbid playing media in video documents if the user has opted
254 // not to, using either the legacy WMF specific pref, or the newer
256 if (!Preferences::GetBool("media.wmf.play-stand-alone", true) ||
257 !Preferences::GetBool("media.play-stand-alone", true)) {
261 Maybe
<MediaContainerType
> type
= MakeMediaContainerType(aType
);
266 return OggDecoder::IsSupportedType(*type
) ||
267 WebMDecoder::IsSupportedType(*type
) ||
268 MP4Decoder::IsSupportedType(*type
,
269 /* DecoderDoctorDiagnostics* */ nullptr) ||
270 MP3Decoder::IsSupportedType(*type
) ||
271 ADTSDecoder::IsSupportedType(*type
) ||
272 FlacDecoder::IsSupportedType(*type
) ||
273 #ifdef MOZ_ANDROID_HLS_SUPPORT
274 HLSDecoder::IsSupportedType(*type
) ||
280 nsTArray
<UniquePtr
<TrackInfo
>> DecoderTraits::GetTracksInfo(
281 const MediaContainerType
& aType
) {
282 // Container type with just the MIME type/subtype, no codecs.
283 const MediaContainerType
mimeType(aType
.Type());
285 if (OggDecoder::IsSupportedType(mimeType
)) {
286 return OggDecoder::GetTracksInfo(aType
);
288 if (WaveDecoder::IsSupportedType(mimeType
)) {
289 return WaveDecoder::GetTracksInfo(aType
);
291 if (MP4Decoder::IsSupportedType(mimeType
, nullptr)) {
292 return MP4Decoder::GetTracksInfo(aType
);
294 if (WebMDecoder::IsSupportedType(mimeType
)) {
295 return WebMDecoder::GetTracksInfo(aType
);
297 if (MP3Decoder::IsSupportedType(mimeType
)) {
298 return MP3Decoder::GetTracksInfo(aType
);
300 if (ADTSDecoder::IsSupportedType(mimeType
)) {
301 return ADTSDecoder::GetTracksInfo(aType
);
303 if (FlacDecoder::IsSupportedType(mimeType
)) {
304 return FlacDecoder::GetTracksInfo(aType
);
306 return nsTArray
<UniquePtr
<TrackInfo
>>();
309 } // namespace mozilla