Coverage Report

Created: 2018-09-25 14:53

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