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/. */
10 #include "AudioSampleFormat.h"
11 #include "MediaInfo.h"
12 #include "TimeUnits.h"
13 #include "VideoLimits.h"
14 #include "mozilla/AbstractThread.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/CheckedInt.h"
17 #include "mozilla/MozPromise.h"
18 #include "mozilla/ReentrantMonitor.h"
19 #include "mozilla/RefPtr.h"
20 #include "mozilla/SharedThreadPool.h"
21 #include "mozilla/TaskQueue.h"
22 #include "mozilla/UniquePtr.h"
23 #include "mozilla/gfx/Point.h" // for gfx::IntSize
24 #include "mozilla/gfx/Types.h"
27 #include "nsIThread.h"
29 #include "nsThreadUtils.h"
32 using mozilla::CheckedInt32
;
33 using mozilla::CheckedInt64
;
34 using mozilla::CheckedUint32
;
35 using mozilla::CheckedUint64
;
37 // This file contains stuff we'd rather put elsewhere, but which is
38 // dependent on other changes which we don't want to wait for. We plan to
39 // remove this file in the near future.
41 // This belongs in xpcom/monitor/Monitor.h, once we've made
42 // mozilla::Monitor non-reentrant.
45 class MediaContainerType
;
47 // EME Key System String.
48 #define EME_KEY_SYSTEM_CLEARKEY "org.w3.clearkey"
49 #define EME_KEY_SYSTEM_WIDEVINE "com.widevine.alpha"
52 * ReentrantMonitorConditionallyEnter
54 * Enters the supplied monitor only if the conditional value |aEnter| is true.
55 * E.g. Used to allow unmonitored read access on the decode thread,
56 * and monitored access on all other threads.
58 class MOZ_STACK_CLASS ReentrantMonitorConditionallyEnter
{
60 ReentrantMonitorConditionallyEnter(bool aEnter
,
61 ReentrantMonitor
& aReentrantMonitor
)
62 : mReentrantMonitor(nullptr) {
63 MOZ_COUNT_CTOR(ReentrantMonitorConditionallyEnter
);
65 mReentrantMonitor
= &aReentrantMonitor
;
66 NS_ASSERTION(mReentrantMonitor
, "null monitor");
67 mReentrantMonitor
->Enter();
70 ~ReentrantMonitorConditionallyEnter(void) {
71 if (mReentrantMonitor
) {
72 mReentrantMonitor
->Exit();
74 MOZ_COUNT_DTOR(ReentrantMonitorConditionallyEnter
);
78 // Restrict to constructor and destructor defined above.
79 ReentrantMonitorConditionallyEnter();
80 ReentrantMonitorConditionallyEnter(const ReentrantMonitorConditionallyEnter
&);
81 ReentrantMonitorConditionallyEnter
& operator=(
82 const ReentrantMonitorConditionallyEnter
&);
83 static void* operator new(size_t) noexcept(true);
84 static void operator delete(void*);
86 ReentrantMonitor
* mReentrantMonitor
;
89 // Shuts down a thread asynchronously.
90 class ShutdownThreadEvent
: public Runnable
{
92 explicit ShutdownThreadEvent(nsIThread
* aThread
)
93 : Runnable("ShutdownThreadEvent"), mThread(aThread
) {}
94 ~ShutdownThreadEvent() = default;
95 NS_IMETHOD
Run() override
{
102 nsCOMPtr
<nsIThread
> mThread
;
107 // Estimates the buffered ranges of a MediaResource using a simple
108 // (byteOffset/length)*duration method. Probably inaccurate, but won't
109 // do file I/O, and can be used when we don't have detailed knowledge
110 // of the byte->time mapping of a resource. aDurationUsecs is the duration
111 // of the media in microseconds. Estimated buffered ranges are stored in
112 // aOutBuffered. Ranges are 0-normalized, i.e. in the range of (0,duration].
113 media::TimeIntervals
GetEstimatedBufferedTimeRanges(
114 mozilla::MediaResource
* aStream
, int64_t aDurationUsecs
);
116 // Converts from number of audio frames (aFrames) to microseconds, given
117 // the specified audio rate (aRate).
118 CheckedInt64
FramesToUsecs(int64_t aFrames
, uint32_t aRate
);
119 // Converts from number of audio frames (aFrames) TimeUnit, given
120 // the specified audio rate (aRate).
121 media::TimeUnit
FramesToTimeUnit(int64_t aFrames
, uint32_t aRate
);
122 // Perform aValue * aMul / aDiv, reducing the possibility of overflow due to
123 // aValue * aMul overflowing.
124 CheckedInt64
SaferMultDiv(int64_t aValue
, uint64_t aMul
, uint64_t aDiv
);
126 // Converts from microseconds (aUsecs) to number of audio frames, given the
127 // specified audio rate (aRate). Stores the result in aOutFrames. Returns
128 // true if the operation succeeded, or false if there was an integer
129 // overflow while calulating the conversion.
130 CheckedInt64
UsecsToFrames(int64_t aUsecs
, uint32_t aRate
);
132 // Format TimeUnit as number of frames at given rate.
133 CheckedInt64
TimeUnitToFrames(const media::TimeUnit
& aTime
, uint32_t aRate
);
135 // Converts milliseconds to seconds.
136 #define MS_TO_SECONDS(ms) ((double)(ms) / (PR_MSEC_PER_SEC))
138 // Converts seconds to milliseconds.
139 #define SECONDS_TO_MS(s) ((int)((s) * (PR_MSEC_PER_SEC)))
141 // Converts from seconds to microseconds. Returns failure if the resulting
142 // integer is too big to fit in an int64_t.
143 nsresult
SecondsToUsecs(double aSeconds
, int64_t& aOutUsecs
);
145 // Scales the display rect aDisplay by aspect ratio aAspectRatio.
146 // Note that aDisplay must be validated by IsValidVideoRegion()
147 // before being used!
148 void ScaleDisplayByAspectRatio(gfx::IntSize
& aDisplay
, float aAspectRatio
);
150 // Downmix Stereo audio samples to Mono.
151 // Input are the buffer contains stereo data and the number of frames.
152 void DownmixStereoToMono(mozilla::AudioDataValue
* aBuffer
, uint32_t aFrames
);
154 // Decide the number of playback channels according to the
155 // given AudioInfo and the prefs that are being set.
156 uint32_t DecideAudioPlaybackChannels(const AudioInfo
& info
);
158 // Decide the sample-rate to use for audio output according to the
159 // given AudioInfo and the prefs that are being set.
160 uint32_t DecideAudioPlaybackSampleRate(const AudioInfo
& info
);
162 bool IsDefaultPlaybackDeviceMono();
164 bool IsVideoContentType(const nsCString
& aContentType
);
166 // Returns true if it's safe to use aPicture as the picture to be
167 // extracted inside a frame of size aFrame, and scaled up to and displayed
168 // at a size of aDisplay. You should validate the frame, picture, and
169 // display regions before using them to display video frames.
170 bool IsValidVideoRegion(const gfx::IntSize
& aFrame
,
171 const gfx::IntRect
& aPicture
,
172 const gfx::IntSize
& aDisplay
);
174 // Template to automatically set a variable to a value on scope exit.
175 // Useful for unsetting flags, etc.
176 template <typename T
>
177 class AutoSetOnScopeExit
{
179 AutoSetOnScopeExit(T
& aVar
, T aValue
) : mVar(aVar
), mValue(aValue
) {}
180 ~AutoSetOnScopeExit() { mVar
= mValue
; }
187 enum class MediaThreadType
{
188 SUPERVISOR
, // MediaFormatReader, RemoteDecoderManager, MediaDecodeTask and
190 PLATFORM_DECODER
, // MediaDataDecoder
191 PLATFORM_ENCODER
, // MediaDataEncoder
193 MDSM
, // MediaDecoderStateMachine
195 // Returns the thread pool that is shared amongst all decoder state machines
196 // for decoding streams.
197 already_AddRefed
<SharedThreadPool
> GetMediaThreadPool(MediaThreadType aType
);
200 H264_PROFILE_UNKNOWN
= 0,
201 H264_PROFILE_BASE
= 0x42,
202 H264_PROFILE_MAIN
= 0x4D,
203 H264_PROFILE_EXTENDED
= 0x58,
204 H264_PROFILE_HIGH
= 0x64,
227 // Extracts the H.264/AVC profile and level from an H.264 codecs string.
228 // H.264 codecs parameters have a type defined as avc1.PPCCLL, where
229 // PP = profile_idc, CC = constraint_set flags, LL = level_idc.
231 // http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
233 // Returns false on failure.
234 bool ExtractH264CodecDetails(const nsAString
& aCodecs
, uint8_t& aProfile
,
235 uint8_t& aConstraint
, uint8_t& aLevel
);
237 struct VideoColorSpace
{
238 // TODO: Define the value type as strong type enum
239 // to better know the exact meaning corresponding to ISO/IEC 23001-8:2016.
240 // Default value is listed
241 // https://www.webmproject.org/vp9/mp4/#optional-fields
242 uint8_t mPrimaryId
= 1; // Table 2
243 uint8_t mTransferId
= 1; // Table 3
244 uint8_t mMatrixId
= 1; // Table 4
245 uint8_t mRangeId
= 0;
248 // Extracts the VPX codecs parameter string.
249 // See https://www.webmproject.org/vp9/mp4/#codecs-parameter-string
251 // Returns false on failure.
252 bool ExtractVPXCodecDetails(const nsAString
& aCodec
, uint8_t& aProfile
,
253 uint8_t& aLevel
, uint8_t& aBitDepth
);
254 bool ExtractVPXCodecDetails(const nsAString
& aCodec
, uint8_t& aProfile
,
255 uint8_t& aLevel
, uint8_t& aBitDepth
,
256 uint8_t& aChromaSubsampling
,
257 VideoColorSpace
& aColorSpace
);
259 // Use a cryptographic quality PRNG to generate raw random bytes
260 // and convert that to a base64 string.
261 nsresult
GenerateRandomName(nsCString
& aOutSalt
, uint32_t aLength
);
263 // This version returns a string suitable for use as a file or URL
264 // path. This is based on code from nsExternalAppHandler::SetUpTempFile.
265 nsresult
GenerateRandomPathName(nsCString
& aOutSalt
, uint32_t aLength
);
267 already_AddRefed
<TaskQueue
> CreateMediaDecodeTaskQueue(const char* aName
);
269 // Iteratively invokes aWork until aCondition returns true, or aWork returns
270 // false. Use this rather than a while loop to avoid bogarting the task queue.
271 template <class Work
, class Condition
>
272 RefPtr
<GenericPromise
> InvokeUntil(Work aWork
, Condition aCondition
) {
273 RefPtr
<GenericPromise::Private
> p
= new GenericPromise::Private(__func__
);
276 p
->Resolve(true, __func__
);
280 static void Iteration(const RefPtr
<GenericPromise::Private
>& aPromise
,
281 Work aLocalWork
, Condition aLocalCondition
) {
283 aPromise
->Reject(NS_ERROR_FAILURE
, __func__
);
284 } else if (aLocalCondition()) {
285 aPromise
->Resolve(true, __func__
);
287 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
288 "InvokeUntil::Helper::Iteration",
289 [aPromise
, aLocalWork
, aLocalCondition
]() {
290 Iteration(aPromise
, aLocalWork
, aLocalCondition
);
292 AbstractThread::GetCurrent()->Dispatch(r
.forget());
297 Helper::Iteration(p
, aWork
, aCondition
);
301 // Simple timer to run a runnable after a timeout.
302 class SimpleTimer
: public nsITimerCallback
, public nsINamed
{
307 // Create a new timer to run aTask after aTimeoutMs milliseconds
308 // on thread aTarget. If aTarget is null, task is run on the main thread.
309 static already_AddRefed
<SimpleTimer
> Create(
310 nsIRunnable
* aTask
, uint32_t aTimeoutMs
,
311 nsIEventTarget
* aTarget
= nullptr);
314 NS_IMETHOD
Notify(nsITimer
* timer
) override
;
317 virtual ~SimpleTimer() = default;
318 nsresult
Init(nsIRunnable
* aTask
, uint32_t aTimeoutMs
,
319 nsIEventTarget
* aTarget
);
321 RefPtr
<nsIRunnable
> mTask
;
322 nsCOMPtr
<nsITimer
> mTimer
;
325 void LogToBrowserConsole(const nsAString
& aMsg
);
327 bool ParseMIMETypeString(const nsAString
& aMIMEType
,
328 nsString
& aOutContainerType
,
329 nsTArray
<nsString
>& aOutCodecs
);
331 bool ParseCodecsString(const nsAString
& aCodecs
,
332 nsTArray
<nsString
>& aOutCodecs
);
334 bool IsH264CodecString(const nsAString
& aCodec
);
336 bool IsAACCodecString(const nsAString
& aCodec
);
338 bool IsVP8CodecString(const nsAString
& aCodec
);
340 bool IsVP9CodecString(const nsAString
& aCodec
);
342 bool IsAV1CodecString(const nsAString
& aCodec
);
344 // Try and create a TrackInfo with a given codec MIME type.
345 UniquePtr
<TrackInfo
> CreateTrackInfoWithMIMEType(
346 const nsACString
& aCodecMIMEType
);
348 // Try and create a TrackInfo with a given codec MIME type, and optional extra
349 // parameters from a container type (its MIME type and codecs are ignored).
350 UniquePtr
<TrackInfo
> CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(
351 const nsACString
& aCodecMIMEType
, const MediaContainerType
& aContainerType
);
355 // aString should start with aMajor + '/'.
356 constexpr bool StartsWithMIMETypeMajor(const char* aString
, const char* aMajor
,
357 size_t aMajorRemaining
) {
358 return (aMajorRemaining
== 0 && *aString
== '/') ||
359 (*aString
== *aMajor
&&
360 StartsWithMIMETypeMajor(aString
+ 1, aMajor
+ 1,
361 aMajorRemaining
- 1));
364 // aString should only contain [a-z0-9\-\.] and a final '\0'.
365 constexpr bool EndsWithMIMESubtype(const char* aString
, size_t aRemaining
) {
366 return aRemaining
== 0 || (((*aString
>= 'a' && *aString
<= 'z') ||
367 (*aString
>= '0' && *aString
<= '9') ||
368 *aString
== '-' || *aString
== '.') &&
369 EndsWithMIMESubtype(aString
+ 1, aRemaining
- 1));
372 // Simple MIME-type literal string checker with a given (major) type.
373 // Only accepts "{aMajor}/[a-z0-9\-\.]+".
374 template <size_t MajorLengthPlus1
>
375 constexpr bool IsMIMETypeWithMajor(const char* aString
, size_t aLength
,
376 const char (&aMajor
)[MajorLengthPlus1
]) {
377 return aLength
> MajorLengthPlus1
&& // Major + '/' + at least 1 char
378 StartsWithMIMETypeMajor(aString
, aMajor
, MajorLengthPlus1
- 1) &&
379 EndsWithMIMESubtype(aString
+ MajorLengthPlus1
,
380 aLength
- MajorLengthPlus1
);
383 } // namespace detail
385 // Simple MIME-type string checker.
386 // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
387 // Add more if necessary.
388 constexpr bool IsMediaMIMEType(const char* aString
, size_t aLength
) {
389 return detail::IsMIMETypeWithMajor(aString
, aLength
, "application") ||
390 detail::IsMIMETypeWithMajor(aString
, aLength
, "audio") ||
391 detail::IsMIMETypeWithMajor(aString
, aLength
, "video");
394 // Simple MIME-type string literal checker.
395 // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
396 // Add more if necessary.
397 template <size_t LengthPlus1
>
398 constexpr bool IsMediaMIMEType(const char (&aString
)[LengthPlus1
]) {
399 return IsMediaMIMEType(aString
, LengthPlus1
- 1);
402 // Simple MIME-type string checker.
403 // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
404 // Add more if necessary.
405 inline bool IsMediaMIMEType(const nsACString
& aString
) {
406 return IsMediaMIMEType(aString
.Data(), aString
.Length());
409 enum class StringListRangeEmptyItems
{
410 // Skip all empty items (empty string will process nothing)
411 // E.g.: "a,,b" -> ["a", "b"], "" -> nothing
413 // Process all, except if string is empty
414 // E.g.: "a,,b" -> ["a", "", "b"], "" -> nothing
416 // Process all, including 1 empty item in an empty string
417 // E.g.: "a,,b" -> ["a", "", "b"], "" -> [""]
421 template <typename String
,
422 StringListRangeEmptyItems empties
= StringListRangeEmptyItems::Skip
>
423 class StringListRange
{
424 typedef typename
String::char_type CharType
;
425 typedef const CharType
* Pointer
;
428 // Iterator into range, trims items and optionally skips empty items.
431 bool operator!=(const Iterator
& a
) const {
432 return mStart
!= a
.mStart
|| mEnd
!= a
.mEnd
;
434 Iterator
& operator++() {
435 SearchItemAt(mComma
+ 1);
438 // DereferencedType should be 'const nsDependent[C]String' pointing into
439 // mList (which is 'const ns[C]String&').
440 typedef decltype(Substring(Pointer(), Pointer())) DereferencedType
;
441 DereferencedType
operator*() { return Substring(mStart
, mEnd
); }
444 friend class StringListRange
;
445 Iterator(const CharType
* aRangeStart
, uint32_t aLength
)
446 : mRangeEnd(aRangeStart
+ aLength
),
450 SearchItemAt(aRangeStart
);
452 void SearchItemAt(Pointer start
) {
453 // First, skip leading whitespace.
454 for (Pointer p
= start
;; ++p
) {
455 if (p
>= mRangeEnd
) {
457 (empties
!= StringListRangeEmptyItems::Skip
? 1 : 0)) {
459 (empties
!= StringListRangeEmptyItems::Skip
? 1 : 0);
461 mStart
= mEnd
= mComma
= p
;
465 if (c
== CharType(',')) {
466 // Comma -> Empty item -> Skip or process?
467 if (empties
!= StringListRangeEmptyItems::Skip
) {
468 mStart
= mEnd
= mComma
= p
;
471 } else if (c
!= CharType(' ')) {
476 // Find comma, recording start of trailing space.
477 Pointer trailingWhitespace
= nullptr;
478 for (Pointer p
= mStart
+ 1;; ++p
) {
479 if (p
>= mRangeEnd
) {
480 mEnd
= trailingWhitespace
? trailingWhitespace
: p
;
485 if (c
== CharType(',')) {
486 mEnd
= trailingWhitespace
? trailingWhitespace
: p
;
490 if (c
== CharType(' ')) {
491 // Found a whitespace -> Record as trailing if not first one.
492 if (!trailingWhitespace
) {
493 trailingWhitespace
= p
;
496 // Found a non-whitespace -> Reset trailing whitespace if needed.
497 if (trailingWhitespace
) {
498 trailingWhitespace
= nullptr;
503 const Pointer mRangeEnd
;
509 explicit StringListRange(const String
& aList
) : mList(aList
) {}
510 Iterator
begin() const {
513 ((empties
== StringListRangeEmptyItems::ProcessEmptyItems
&&
519 Iterator
end() const {
520 return Iterator(mList
.Data() + mList
.Length() +
521 (empties
!= StringListRangeEmptyItems::Skip
? 1 : 0),
529 template <StringListRangeEmptyItems empties
= StringListRangeEmptyItems::Skip
,
531 StringListRange
<String
, empties
> MakeStringListRange(const String
& aList
) {
532 return StringListRange
<String
, empties
>(aList
);
535 template <StringListRangeEmptyItems empties
= StringListRangeEmptyItems::Skip
,
536 typename ListString
, typename ItemString
>
537 static bool StringListContains(const ListString
& aList
,
538 const ItemString
& aItem
) {
539 for (const auto& listItem
: MakeStringListRange
<empties
>(aList
)) {
540 if (listItem
.Equals(aItem
)) {
547 inline void AppendStringIfNotEmpty(nsACString
& aDest
, nsACString
&& aSrc
) {
548 if (!aSrc
.IsEmpty()) {
549 aDest
.Append("\n"_ns
);
554 // Returns true if we're running on a cellular connection; 2G, 3G, etc.
556 bool OnCellularConnection();
558 inline gfx::YUVColorSpace
DefaultColorSpace(const gfx::IntSize
& aSize
) {
559 return aSize
.height
< 720 ? gfx::YUVColorSpace::BT601
560 : gfx::YUVColorSpace::BT709
;
563 } // end namespace mozilla