Source code

Revision control

Other Tools

1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "VideoUtils.h"
6
7
#include <functional>
8
#include <stdint.h>
9
10
#include "CubebUtils.h"
11
#include "ImageContainer.h"
12
#include "MediaContainerType.h"
13
#include "MediaResource.h"
14
#include "TimeUnits.h"
15
#include "VorbisUtils.h"
16
#include "mozilla/Base64.h"
17
#include "mozilla/dom/ContentChild.h"
18
#include "mozilla/SharedThreadPool.h"
19
#include "mozilla/StaticPrefs.h"
20
#include "mozilla/SystemGroup.h"
21
#include "mozilla/TaskCategory.h"
22
#include "mozilla/TaskQueue.h"
23
#include "mozilla/Telemetry.h"
24
#include "nsCharSeparatedTokenizer.h"
25
#include "nsContentTypeParser.h"
26
#include "nsIConsoleService.h"
27
#include "nsINetworkLinkService.h"
28
#include "nsIRandomGenerator.h"
29
#include "nsIServiceManager.h"
30
#include "nsMathUtils.h"
31
#include "nsNetCID.h"
32
#include "nsServiceManagerUtils.h"
33
#include "nsThreadUtils.h"
34
35
namespace mozilla {
36
37
using layers::PlanarYCbCrImage;
38
using media::TimeUnit;
39
40
CheckedInt64 SaferMultDiv(int64_t aValue, uint64_t aMul, uint64_t aDiv) {
41
if (aMul > INT64_MAX || aDiv > INT64_MAX) {
42
return CheckedInt64(INT64_MAX) + 1; // Return an invalid checked int.
43
}
44
int64_t mul = aMul;
45
int64_t div = aDiv;
46
int64_t major = aValue / div;
47
int64_t remainder = aValue % div;
48
return CheckedInt64(remainder) * mul / div + CheckedInt64(major) * mul;
49
}
50
51
// Converts from number of audio frames to microseconds, given the specified
52
// audio rate.
53
CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate) {
54
return SaferMultDiv(aFrames, USECS_PER_S, aRate);
55
}
56
57
TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate) {
58
int64_t major = aFrames / aRate;
59
int64_t remainder = aFrames % aRate;
60
return TimeUnit::FromMicroseconds(major) * USECS_PER_S +
61
(TimeUnit::FromMicroseconds(remainder) * USECS_PER_S) / aRate;
62
}
63
64
// Converts from microseconds to number of audio frames, given the specified
65
// audio rate.
66
CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate) {
67
return SaferMultDiv(aUsecs, aRate, USECS_PER_S);
68
}
69
70
// Format TimeUnit as number of frames at given rate.
71
CheckedInt64 TimeUnitToFrames(const TimeUnit& aTime, uint32_t aRate) {
72
return aTime.IsValid() ? UsecsToFrames(aTime.ToMicroseconds(), aRate)
73
: CheckedInt64(INT64_MAX) + 1;
74
}
75
76
nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs) {
77
if (aSeconds * double(USECS_PER_S) > INT64_MAX) {
78
return NS_ERROR_FAILURE;
79
}
80
aOutUsecs = int64_t(aSeconds * double(USECS_PER_S));
81
return NS_OK;
82
}
83
84
static int32_t ConditionDimension(float aValue) {
85
// This will exclude NaNs and too-big values.
86
if (aValue > 1.0 && aValue <= INT32_MAX) return int32_t(NS_round(aValue));
87
return 0;
88
}
89
90
void ScaleDisplayByAspectRatio(gfx::IntSize& aDisplay, float aAspectRatio) {
91
if (aAspectRatio > 1.0) {
92
// Increase the intrinsic width
93
aDisplay.width = ConditionDimension(aAspectRatio * aDisplay.width);
94
} else {
95
// Increase the intrinsic height
96
aDisplay.height = ConditionDimension(aDisplay.height / aAspectRatio);
97
}
98
}
99
100
static int64_t BytesToTime(int64_t offset, int64_t length, int64_t durationUs) {
101
NS_ASSERTION(length > 0, "Must have positive length");
102
double r = double(offset) / double(length);
103
if (r > 1.0) r = 1.0;
104
return int64_t(double(durationUs) * r);
105
}
106
107
media::TimeIntervals GetEstimatedBufferedTimeRanges(
108
mozilla::MediaResource* aStream, int64_t aDurationUsecs) {
109
media::TimeIntervals buffered;
110
// Nothing to cache if the media takes 0us to play.
111
if (aDurationUsecs <= 0 || !aStream) return buffered;
112
113
// Special case completely cached files. This also handles local files.
114
if (aStream->IsDataCachedToEndOfResource(0)) {
115
buffered += media::TimeInterval(TimeUnit::Zero(),
116
TimeUnit::FromMicroseconds(aDurationUsecs));
117
return buffered;
118
}
119
120
int64_t totalBytes = aStream->GetLength();
121
122
// If we can't determine the total size, pretend that we have nothing
123
// buffered. This will put us in a state of eternally-low-on-undecoded-data
124
// which is not great, but about the best we can do.
125
if (totalBytes <= 0) return buffered;
126
127
int64_t startOffset = aStream->GetNextCachedData(0);
128
while (startOffset >= 0) {
129
int64_t endOffset = aStream->GetCachedDataEnd(startOffset);
130
// Bytes [startOffset..endOffset] are cached.
131
NS_ASSERTION(startOffset >= 0, "Integer underflow in GetBuffered");
132
NS_ASSERTION(endOffset >= 0, "Integer underflow in GetBuffered");
133
134
int64_t startUs = BytesToTime(startOffset, totalBytes, aDurationUsecs);
135
int64_t endUs = BytesToTime(endOffset, totalBytes, aDurationUsecs);
136
if (startUs != endUs) {
137
buffered += media::TimeInterval(TimeUnit::FromMicroseconds(startUs),
138
TimeUnit::FromMicroseconds(endUs));
139
}
140
startOffset = aStream->GetNextCachedData(endOffset);
141
}
142
return buffered;
143
}
144
145
void DownmixStereoToMono(mozilla::AudioDataValue* aBuffer, uint32_t aFrames) {
146
MOZ_ASSERT(aBuffer);
147
const int channels = 2;
148
for (uint32_t fIdx = 0; fIdx < aFrames; ++fIdx) {
149
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
150
float sample = 0.0;
151
#else
152
int sample = 0;
153
#endif
154
// The sample of the buffer would be interleaved.
155
sample = (aBuffer[fIdx * channels] + aBuffer[fIdx * channels + 1]) * 0.5;
156
aBuffer[fIdx * channels] = aBuffer[fIdx * channels + 1] = sample;
157
}
158
}
159
160
uint32_t DecideAudioPlaybackChannels(const AudioInfo& info) {
161
if (StaticPrefs::accessibility_monoaudio_enable()) {
162
return 1;
163
}
164
165
if (StaticPrefs::MediaForcestereoEnabled()) {
166
return 2;
167
}
168
169
return info.mChannels;
170
}
171
172
bool IsDefaultPlaybackDeviceMono() {
173
return CubebUtils::MaxNumberOfChannels() == 1;
174
}
175
176
bool IsVideoContentType(const nsCString& aContentType) {
177
NS_NAMED_LITERAL_CSTRING(video, "video");
178
if (FindInReadable(video, aContentType)) {
179
return true;
180
}
181
return false;
182
}
183
184
bool IsValidVideoRegion(const gfx::IntSize& aFrame,
185
const gfx::IntRect& aPicture,
186
const gfx::IntSize& aDisplay) {
187
return aFrame.width <= PlanarYCbCrImage::MAX_DIMENSION &&
188
aFrame.height <= PlanarYCbCrImage::MAX_DIMENSION &&
189
aFrame.width * aFrame.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
190
aFrame.width * aFrame.height != 0 &&
191
aPicture.width <= PlanarYCbCrImage::MAX_DIMENSION &&
192
aPicture.x < PlanarYCbCrImage::MAX_DIMENSION &&
193
aPicture.x + aPicture.width < PlanarYCbCrImage::MAX_DIMENSION &&
194
aPicture.height <= PlanarYCbCrImage::MAX_DIMENSION &&
195
aPicture.y < PlanarYCbCrImage::MAX_DIMENSION &&
196
aPicture.y + aPicture.height < PlanarYCbCrImage::MAX_DIMENSION &&
197
aPicture.width * aPicture.height <=
198
MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
199
aPicture.width * aPicture.height != 0 &&
200
aDisplay.width <= PlanarYCbCrImage::MAX_DIMENSION &&
201
aDisplay.height <= PlanarYCbCrImage::MAX_DIMENSION &&
202
aDisplay.width * aDisplay.height <=
203
MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
204
aDisplay.width * aDisplay.height != 0;
205
}
206
207
already_AddRefed<SharedThreadPool> GetMediaThreadPool(MediaThreadType aType) {
208
const char* name;
209
uint32_t threads = 4;
210
switch (aType) {
211
case MediaThreadType::PLATFORM_DECODER:
212
name = "MediaPDecoder";
213
break;
214
case MediaThreadType::MSG_CONTROL:
215
name = "MSGControl";
216
break;
217
case MediaThreadType::WEBRTC_DECODER:
218
name = "WebRTCPD";
219
break;
220
case MediaThreadType::MDSM:
221
name = "MediaDecoderStateMachine";
222
threads = 1;
223
break;
224
default:
225
MOZ_FALLTHROUGH_ASSERT("Unexpected MediaThreadType");
226
case MediaThreadType::PLAYBACK:
227
name = "MediaPlayback";
228
break;
229
}
230
231
RefPtr<SharedThreadPool> pool =
232
SharedThreadPool::Get(nsDependentCString(name), threads);
233
234
// Ensure a larger stack for platform decoder threads
235
if (aType == MediaThreadType::PLATFORM_DECODER) {
236
const uint32_t minStackSize = 512 * 1024;
237
uint32_t stackSize;
238
MOZ_ALWAYS_SUCCEEDS(pool->GetThreadStackSize(&stackSize));
239
if (stackSize < minStackSize) {
240
MOZ_ALWAYS_SUCCEEDS(pool->SetThreadStackSize(minStackSize));
241
}
242
}
243
244
return pool.forget();
245
}
246
247
bool ExtractVPXCodecDetails(const nsAString& aCodec, uint8_t& aProfile,
248
uint8_t& aLevel, uint8_t& aBitDepth) {
249
uint8_t dummyChromaSubsampling = 1;
250
VideoColorSpace dummyColorspace;
251
return ExtractVPXCodecDetails(aCodec, aProfile, aLevel, aBitDepth,
252
dummyChromaSubsampling, dummyColorspace);
253
}
254
255
bool ExtractVPXCodecDetails(const nsAString& aCodec, uint8_t& aProfile,
256
uint8_t& aLevel, uint8_t& aBitDepth,
257
uint8_t& aChromaSubsampling,
258
VideoColorSpace& aColorSpace) {
259
// Assign default value.
260
aChromaSubsampling = 1;
261
auto splitter = aCodec.Split(u'.');
262
auto fieldsItr = splitter.begin();
263
auto fourCC = *fieldsItr;
264
265
if (!fourCC.EqualsLiteral("vp09") && !fourCC.EqualsLiteral("vp08")) {
266
// Invalid 4CC
267
return false;
268
}
269
++fieldsItr;
270
uint8_t* fields[] = {&aProfile,
271
&aLevel,
272
&aBitDepth,
273
&aChromaSubsampling,
274
&aColorSpace.mPrimaryId,
275
&aColorSpace.mTransferId,
276
&aColorSpace.mMatrixId,
277
&aColorSpace.mRangeId};
278
int fieldsCount = 0;
279
nsresult rv;
280
for (; fieldsItr != splitter.end(); ++fieldsItr, ++fieldsCount) {
281
if (fieldsCount > 7) {
282
// No more than 8 fields are expected.
283
return false;
284
}
285
*(fields[fieldsCount]) = static_cast<uint8_t>(
286
PromiseFlatString((*fieldsItr)).ToInteger(&rv, 10));
287
// We got invalid field value, parsing error.
288
NS_ENSURE_SUCCESS(rv, false);
289
}
290
// Mandatory Fields
291
// <sample entry 4CC>.<profile>.<level>.<bitDepth>.
292
// Optional Fields
293
// <chromaSubsampling>.<colourPrimaries>.<transferCharacteristics>.
294
// <matrixCoefficients>.<videoFullRangeFlag>
295
// First three fields are mandatory(we have parsed 4CC).
296
if (fieldsCount < 3) {
297
// Invalid number of fields.
298
return false;
299
}
300
// Start to validate the parsing value.
301
302
// profile should be 0,1,2 or 3.
304
// We don't support more than profile 2
305
if (aProfile > 2) {
306
// Invalid profile.
307
return false;
308
}
309
311
switch (aLevel) {
312
case 10:
313
case 11:
314
case 20:
315
case 21:
316
case 30:
317
case 31:
318
case 40:
319
case 41:
320
case 50:
321
case 51:
322
case 52:
323
case 60:
324
case 61:
325
case 62:
326
break;
327
default:
328
// Invalid level.
329
return false;
330
}
331
332
if (aBitDepth != 8 && aBitDepth != 10 && aBitDepth != 12) {
333
// Invalid bitDepth:
334
return false;
335
}
336
337
if (fieldsCount == 3) {
338
// No more options.
339
return true;
340
}
341
342
// chromaSubsampling should be 0,1,2,3...4~7 are reserved.
343
if (aChromaSubsampling > 3) {
344
return false;
345
}
346
347
if (fieldsCount == 4) {
348
// No more options.
349
return true;
350
}
351
352
// It is an integer that is defined by the "Colour primaries"
353
// section of ISO/IEC 23001-8:2016 Table 2.
354
// We treat reserved value as false case.
355
const auto& primaryId = aColorSpace.mPrimaryId;
356
if (primaryId == 0 || primaryId == 3 || primaryId > 22) {
357
// reserved value.
358
return false;
359
}
360
if (primaryId > 12 && primaryId < 22) {
361
// 13~21 are reserved values.
362
return false;
363
}
364
365
if (fieldsCount == 5) {
366
// No more options.
367
return true;
368
}
369
370
// It is an integer that is defined by the
371
// "Transfer characteristics" section of ISO/IEC 23001-8:2016 Table 3.
372
// We treat reserved value as false case.
373
const auto& transferId = aColorSpace.mTransferId;
374
if (transferId == 0 || transferId == 3 || transferId > 18) {
375
// reserved value.
376
return false;
377
}
378
379
if (fieldsCount == 6) {
380
// No more options.
381
return true;
382
}
383
384
// It is an integer that is defined by the
385
// "Matrix coefficients" section of ISO/IEC 23001-8:2016 Table 4.
386
// We treat reserved value as false case.
387
const auto& matrixId = aColorSpace.mMatrixId;
388
if (matrixId == 3 || matrixId > 11) {
389
return false;
390
}
391
392
// If matrixCoefficients is 0 (RGB), then chroma subsampling MUST be 3
393
// (4:4:4).
394
if (matrixId == 0 && aChromaSubsampling != 3) {
395
return false;
396
}
397
398
if (fieldsCount == 7) {
399
// No more options.
400
return true;
401
}
402
403
// videoFullRangeFlag indicates the black level and range of the luma and
404
// chroma signals. 0 = legal range (e.g. 16-235 for 8 bit sample depth);
405
// 1 = full range (e.g. 0-255 for 8-bit sample depth).
406
const auto& rangeId = aColorSpace.mRangeId;
407
return rangeId <= 1;
408
}
409
410
bool ExtractH264CodecDetails(const nsAString& aCodec, uint8_t& aProfile,
411
uint8_t& aConstraint, uint8_t& aLevel) {
412
// H.264 codecs parameters have a type defined as avcN.PPCCLL, where
413
// N = avc type. avc3 is avcc with SPS & PPS implicit (within stream)
414
// PP = profile_idc, CC = constraint_set flags, LL = level_idc.
415
// We ignore the constraint_set flags, as it's not clear from any
416
// documentation what constraints the platform decoders support.
417
// See
419
// for more details.
420
if (aCodec.Length() != strlen("avc1.PPCCLL")) {
421
return false;
422
}
423
424
// Verify the codec starts with "avc1." or "avc3.".
425
const nsAString& sample = Substring(aCodec, 0, 5);
426
if (!sample.EqualsASCII("avc1.") && !sample.EqualsASCII("avc3.")) {
427
return false;
428
}
429
430
// Extract the profile_idc, constraint_flags and level_idc.
431
nsresult rv = NS_OK;
432
aProfile = PromiseFlatString(Substring(aCodec, 5, 2)).ToInteger(&rv, 16);
433
NS_ENSURE_SUCCESS(rv, false);
434
435
// Constraint flags are stored on the 6 most significant bits, first two bits
436
// are reserved_zero_2bits.
437
aConstraint = PromiseFlatString(Substring(aCodec, 7, 2)).ToInteger(&rv, 16);
438
NS_ENSURE_SUCCESS(rv, false);
439
440
aLevel = PromiseFlatString(Substring(aCodec, 9, 2)).ToInteger(&rv, 16);
441
NS_ENSURE_SUCCESS(rv, false);
442
443
if (aLevel == 9) {
444
aLevel = H264_LEVEL_1_b;
445
} else if (aLevel <= 5) {
446
aLevel *= 10;
447
}
448
449
// We only make sure constraints is above 4 for collection perspective
450
// otherwise collect 0 for unknown.
451
Telemetry::Accumulate(Telemetry::VIDEO_CANPLAYTYPE_H264_CONSTRAINT_SET_FLAG,
452
aConstraint >= 4 ? aConstraint : 0);
453
// 244 is the highest meaningful profile value (High 4:4:4 Intra Profile)
454
// that can be represented as single hex byte, otherwise collect 0 for
455
// unknown.
456
Telemetry::Accumulate(Telemetry::VIDEO_CANPLAYTYPE_H264_PROFILE,
457
aProfile <= 244 ? aProfile : 0);
458
459
// Make sure aLevel represents a value between levels 1 and 5.2,
460
// otherwise collect 0 for unknown.
461
Telemetry::Accumulate(Telemetry::VIDEO_CANPLAYTYPE_H264_LEVEL,
462
(aLevel >= 10 && aLevel <= 52) ? aLevel : 0);
463
464
return true;
465
}
466
467
nsresult GenerateRandomName(nsCString& aOutSalt, uint32_t aLength) {
468
nsresult rv;
469
nsCOMPtr<nsIRandomGenerator> rg =
470
do_GetService("@mozilla.org/security/random-generator;1", &rv);
471
if (NS_FAILED(rv)) return rv;
472
473
// For each three bytes of random data we will get four bytes of ASCII.
474
const uint32_t requiredBytesLength =
475
static_cast<uint32_t>((aLength + 3) / 4 * 3);
476
477
uint8_t* buffer;
478
rv = rg->GenerateRandomBytes(requiredBytesLength, &buffer);
479
if (NS_FAILED(rv)) return rv;
480
481
nsAutoCString temp;
482
nsDependentCSubstring randomData(reinterpret_cast<const char*>(buffer),
483
requiredBytesLength);
484
rv = Base64Encode(randomData, temp);
485
free(buffer);
486
buffer = nullptr;
487
if (NS_FAILED(rv)) return rv;
488
489
aOutSalt = temp;
490
return NS_OK;
491
}
492
493
nsresult GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength) {
494
nsresult rv = GenerateRandomName(aOutSalt, aLength);
495
if (NS_FAILED(rv)) return rv;
496
497
// Base64 characters are alphanumeric (a-zA-Z0-9) and '+' and '/', so we need
498
// to replace illegal characters -- notably '/'
499
aOutSalt.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
500
return NS_OK;
501
}
502
503
already_AddRefed<TaskQueue> CreateMediaDecodeTaskQueue(const char* aName) {
504
RefPtr<TaskQueue> queue = new TaskQueue(
505
GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), aName);
506
return queue.forget();
507
}
508
509
void SimpleTimer::Cancel() {
510
if (mTimer) {
511
#ifdef DEBUG
512
nsCOMPtr<nsIEventTarget> target;
513
mTimer->GetTarget(getter_AddRefs(target));
514
bool onCurrent;
515
nsresult rv = target->IsOnCurrentThread(&onCurrent);
516
MOZ_ASSERT(NS_SUCCEEDED(rv) && onCurrent);
517
#endif
518
mTimer->Cancel();
519
mTimer = nullptr;
520
}
521
mTask = nullptr;
522
}
523
524
NS_IMETHODIMP
525
SimpleTimer::Notify(nsITimer* timer) {
526
RefPtr<SimpleTimer> deathGrip(this);
527
if (mTask) {
528
mTask->Run();
529
mTask = nullptr;
530
}
531
return NS_OK;
532
}
533
534
NS_IMETHODIMP
535
SimpleTimer::GetName(nsACString& aName) {
536
aName.AssignLiteral("SimpleTimer");
537
return NS_OK;
538
}
539
540
nsresult SimpleTimer::Init(nsIRunnable* aTask, uint32_t aTimeoutMs,
541
nsIEventTarget* aTarget) {
542
nsresult rv;
543
544
// Get target thread first, so we don't have to cancel the timer if it fails.
545
nsCOMPtr<nsIEventTarget> target;
546
if (aTarget) {
547
target = aTarget;
548
} else {
549
target = GetMainThreadEventTarget();
550
if (!target) {
551
return NS_ERROR_NOT_AVAILABLE;
552
}
553
}
554
555
rv = NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, aTimeoutMs,
556
nsITimer::TYPE_ONE_SHOT, target);
557
if (NS_FAILED(rv)) {
558
return rv;
559
}
560
561
mTask = aTask;
562
return NS_OK;
563
}
564
565
NS_IMPL_ISUPPORTS(SimpleTimer, nsITimerCallback, nsINamed)
566
567
already_AddRefed<SimpleTimer> SimpleTimer::Create(nsIRunnable* aTask,
568
uint32_t aTimeoutMs,
569
nsIEventTarget* aTarget) {
570
RefPtr<SimpleTimer> t(new SimpleTimer());
571
if (NS_FAILED(t->Init(aTask, aTimeoutMs, aTarget))) {
572
return nullptr;
573
}
574
return t.forget();
575
}
576
577
void LogToBrowserConsole(const nsAString& aMsg) {
578
if (!NS_IsMainThread()) {
579
nsString msg(aMsg);
580
nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
581
"LogToBrowserConsole", [msg]() { LogToBrowserConsole(msg); });
582
SystemGroup::Dispatch(TaskCategory::Other, task.forget());
583
return;
584
}
585
nsCOMPtr<nsIConsoleService> console(
586
do_GetService("@mozilla.org/consoleservice;1"));
587
if (!console) {
588
NS_WARNING("Failed to log message to console.");
589
return;
590
}
591
nsAutoString msg(aMsg);
592
console->LogStringMessage(msg.get());
593
}
594
595
bool ParseCodecsString(const nsAString& aCodecs,
596
nsTArray<nsString>& aOutCodecs) {
597
aOutCodecs.Clear();
598
bool expectMoreTokens = false;
599
nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
600
while (tokenizer.hasMoreTokens()) {
601
const nsAString& token = tokenizer.nextToken();
602
expectMoreTokens = tokenizer.separatorAfterCurrentToken();
603
aOutCodecs.AppendElement(token);
604
}
605
if (expectMoreTokens) {
606
// Last codec name was empty
607
return false;
608
}
609
return true;
610
}
611
612
bool ParseMIMETypeString(const nsAString& aMIMEType,
613
nsString& aOutContainerType,
614
nsTArray<nsString>& aOutCodecs) {
615
nsContentTypeParser parser(aMIMEType);
616
nsresult rv = parser.GetType(aOutContainerType);
617
if (NS_FAILED(rv)) {
618
return false;
619
}
620
621
nsString codecsStr;
622
parser.GetParameter("codecs", codecsStr);
623
return ParseCodecsString(codecsStr, aOutCodecs);
624
}
625
626
template <int N>
627
static bool StartsWith(const nsACString& string, const char (&prefix)[N]) {
628
if (N - 1 > string.Length()) {
629
return false;
630
}
631
return memcmp(string.Data(), prefix, N - 1) == 0;
632
}
633
634
bool IsH264CodecString(const nsAString& aCodec) {
635
uint8_t profile = 0;
636
uint8_t constraint = 0;
637
uint8_t level = 0;
638
return ExtractH264CodecDetails(aCodec, profile, constraint, level);
639
}
640
641
bool IsAACCodecString(const nsAString& aCodec) {
642
return aCodec.EqualsLiteral("mp4a.40.2") || // MPEG4 AAC-LC
643
aCodec.EqualsLiteral(
644
"mp4a.40.02") || // MPEG4 AAC-LC(for compatibility)
645
aCodec.EqualsLiteral("mp4a.40.5") || // MPEG4 HE-AAC
646
aCodec.EqualsLiteral(
647
"mp4a.40.05") || // MPEG4 HE-AAC(for compatibility)
648
aCodec.EqualsLiteral("mp4a.67") || // MPEG2 AAC-LC
649
aCodec.EqualsLiteral("mp4a.40.29"); // MPEG4 HE-AACv2
650
}
651
652
bool IsVP8CodecString(const nsAString& aCodec) {
653
uint8_t profile = 0;
654
uint8_t level = 0;
655
uint8_t bitDepth = 0;
656
return aCodec.EqualsLiteral("vp8") || aCodec.EqualsLiteral("vp8.0") ||
657
(StartsWith(NS_ConvertUTF16toUTF8(aCodec), "vp08") &&
658
ExtractVPXCodecDetails(aCodec, profile, level, bitDepth));
659
}
660
661
bool IsVP9CodecString(const nsAString& aCodec) {
662
uint8_t profile = 0;
663
uint8_t level = 0;
664
uint8_t bitDepth = 0;
665
return aCodec.EqualsLiteral("vp9") || aCodec.EqualsLiteral("vp9.0") ||
666
(StartsWith(NS_ConvertUTF16toUTF8(aCodec), "vp09") &&
667
ExtractVPXCodecDetails(aCodec, profile, level, bitDepth));
668
}
669
670
bool IsAV1CodecString(const nsAString& aCodec) {
671
return aCodec.EqualsLiteral("av1") ||
672
StartsWith(NS_ConvertUTF16toUTF8(aCodec), "av01");
673
}
674
675
UniquePtr<TrackInfo> CreateTrackInfoWithMIMEType(
676
const nsACString& aCodecMIMEType) {
677
UniquePtr<TrackInfo> trackInfo;
678
if (StartsWith(aCodecMIMEType, "audio/")) {
679
trackInfo.reset(new AudioInfo());
680
trackInfo->mMimeType = aCodecMIMEType;
681
} else if (StartsWith(aCodecMIMEType, "video/")) {
682
trackInfo.reset(new VideoInfo());
683
trackInfo->mMimeType = aCodecMIMEType;
684
}
685
return trackInfo;
686
}
687
688
UniquePtr<TrackInfo> CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(
689
const nsACString& aCodecMIMEType,
690
const MediaContainerType& aContainerType) {
691
UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aCodecMIMEType);
692
if (trackInfo) {
693
VideoInfo* videoInfo = trackInfo->GetAsVideoInfo();
694
if (videoInfo) {
695
Maybe<int32_t> maybeWidth = aContainerType.ExtendedType().GetWidth();
696
if (maybeWidth && *maybeWidth > 0) {
697
videoInfo->mImage.width = *maybeWidth;
698
videoInfo->mDisplay.width = *maybeWidth;
699
}
700
Maybe<int32_t> maybeHeight = aContainerType.ExtendedType().GetHeight();
701
if (maybeHeight && *maybeHeight > 0) {
702
videoInfo->mImage.height = *maybeHeight;
703
videoInfo->mDisplay.height = *maybeHeight;
704
}
705
} else if (trackInfo->GetAsAudioInfo()) {
706
AudioInfo* audioInfo = trackInfo->GetAsAudioInfo();
707
Maybe<int32_t> maybeChannels =
708
aContainerType.ExtendedType().GetChannels();
709
if (maybeChannels && *maybeChannels > 0) {
710
audioInfo->mChannels = *maybeChannels;
711
}
712
Maybe<int32_t> maybeSamplerate =
713
aContainerType.ExtendedType().GetSamplerate();
714
if (maybeSamplerate && *maybeSamplerate > 0) {
715
audioInfo->mRate = *maybeSamplerate;
716
}
717
}
718
}
719
return trackInfo;
720
}
721
722
bool OnCellularConnection() {
723
uint32_t linkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
724
if (XRE_IsContentProcess()) {
725
mozilla::dom::ContentChild* cpc =
726
mozilla::dom::ContentChild::GetSingleton();
727
if (!cpc) {
728
NS_WARNING("Can't get ContentChild singleton in content process!");
729
return false;
730
}
731
linkType = cpc->NetworkLinkType();
732
} else {
733
nsresult rv;
734
nsCOMPtr<nsINetworkLinkService> nls =
735
do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
736
if (NS_FAILED(rv)) {
737
NS_WARNING("Can't get nsINetworkLinkService.");
738
return false;
739
}
740
741
rv = nls->GetLinkType(&linkType);
742
if (NS_FAILED(rv)) {
743
NS_WARNING("Can't get network link type.");
744
return false;
745
}
746
}
747
748
switch (linkType) {
749
case nsINetworkLinkService::LINK_TYPE_UNKNOWN:
750
case nsINetworkLinkService::LINK_TYPE_ETHERNET:
751
case nsINetworkLinkService::LINK_TYPE_USB:
752
case nsINetworkLinkService::LINK_TYPE_WIFI:
753
return false;
754
case nsINetworkLinkService::LINK_TYPE_WIMAX:
755
case nsINetworkLinkService::LINK_TYPE_2G:
756
case nsINetworkLinkService::LINK_TYPE_3G:
757
case nsINetworkLinkService::LINK_TYPE_4G:
758
return true;
759
}
760
761
return false;
762
}
763
764
} // end namespace mozilla