Bug 1772053 - Enable dynamic code disable mitigations only on Windows 10 1703+ r...
[gecko.git] / dom / media / VideoUtils.h
blob05a8d7f583db5c5c397a219bbf51486c3c74f257
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 #ifndef VideoUtils_h
8 #define VideoUtils_h
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"
25 #include "nsCOMPtr.h"
26 #include "nsINamed.h"
27 #include "nsIThread.h"
28 #include "nsITimer.h"
29 #include "nsThreadUtils.h"
30 #include "prtime.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.
43 namespace mozilla {
45 class MediaContainerType;
47 /**
48 * ReentrantMonitorConditionallyEnter
50 * Enters the supplied monitor only if the conditional value |aEnter| is true.
51 * E.g. Used to allow unmonitored read access on the decode thread,
52 * and monitored access on all other threads.
54 class MOZ_STACK_CLASS ReentrantMonitorConditionallyEnter {
55 public:
56 ReentrantMonitorConditionallyEnter(bool aEnter,
57 ReentrantMonitor& aReentrantMonitor)
58 : mReentrantMonitor(nullptr) {
59 MOZ_COUNT_CTOR(ReentrantMonitorConditionallyEnter);
60 if (aEnter) {
61 mReentrantMonitor = &aReentrantMonitor;
62 NS_ASSERTION(mReentrantMonitor, "null monitor");
63 mReentrantMonitor->Enter();
66 ~ReentrantMonitorConditionallyEnter(void) {
67 if (mReentrantMonitor) {
68 mReentrantMonitor->Exit();
70 MOZ_COUNT_DTOR(ReentrantMonitorConditionallyEnter);
73 private:
74 // Restrict to constructor and destructor defined above.
75 ReentrantMonitorConditionallyEnter();
76 ReentrantMonitorConditionallyEnter(const ReentrantMonitorConditionallyEnter&);
77 ReentrantMonitorConditionallyEnter& operator=(
78 const ReentrantMonitorConditionallyEnter&);
79 static void* operator new(size_t) noexcept(true);
80 static void operator delete(void*);
82 ReentrantMonitor* mReentrantMonitor;
85 // Shuts down a thread asynchronously.
86 class ShutdownThreadEvent : public Runnable {
87 public:
88 explicit ShutdownThreadEvent(nsIThread* aThread)
89 : Runnable("ShutdownThreadEvent"), mThread(aThread) {}
90 ~ShutdownThreadEvent() = default;
91 NS_IMETHOD Run() override {
92 mThread->Shutdown();
93 mThread = nullptr;
94 return NS_OK;
97 private:
98 nsCOMPtr<nsIThread> mThread;
101 class MediaResource;
103 // Estimates the buffered ranges of a MediaResource using a simple
104 // (byteOffset/length)*duration method. Probably inaccurate, but won't
105 // do file I/O, and can be used when we don't have detailed knowledge
106 // of the byte->time mapping of a resource. aDurationUsecs is the duration
107 // of the media in microseconds. Estimated buffered ranges are stored in
108 // aOutBuffered. Ranges are 0-normalized, i.e. in the range of (0,duration].
109 media::TimeIntervals GetEstimatedBufferedTimeRanges(
110 mozilla::MediaResource* aStream, int64_t aDurationUsecs);
112 // Converts from number of audio frames (aFrames) to microseconds, given
113 // the specified audio rate (aRate).
114 CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate);
115 // Converts from number of audio frames (aFrames) TimeUnit, given
116 // the specified audio rate (aRate).
117 media::TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate);
118 // Perform aValue * aMul / aDiv, reducing the possibility of overflow due to
119 // aValue * aMul overflowing.
120 CheckedInt64 SaferMultDiv(int64_t aValue, uint64_t aMul, uint64_t aDiv);
122 // Converts from microseconds (aUsecs) to number of audio frames, given the
123 // specified audio rate (aRate). Stores the result in aOutFrames. Returns
124 // true if the operation succeeded, or false if there was an integer
125 // overflow while calulating the conversion.
126 CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate);
128 // Format TimeUnit as number of frames at given rate.
129 CheckedInt64 TimeUnitToFrames(const media::TimeUnit& aTime, uint32_t aRate);
131 // Converts milliseconds to seconds.
132 #define MS_TO_SECONDS(ms) ((double)(ms) / (PR_MSEC_PER_SEC))
134 // Converts seconds to milliseconds.
135 #define SECONDS_TO_MS(s) ((int)((s) * (PR_MSEC_PER_SEC)))
137 // Converts from seconds to microseconds. Returns failure if the resulting
138 // integer is too big to fit in an int64_t.
139 nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs);
141 // Scales the display rect aDisplay by aspect ratio aAspectRatio.
142 // Note that aDisplay must be validated by IsValidVideoRegion()
143 // before being used!
144 void ScaleDisplayByAspectRatio(gfx::IntSize& aDisplay, float aAspectRatio);
146 // Downmix Stereo audio samples to Mono.
147 // Input are the buffer contains stereo data and the number of frames.
148 void DownmixStereoToMono(mozilla::AudioDataValue* aBuffer, uint32_t aFrames);
150 // Decide the number of playback channels according to the
151 // given AudioInfo and the prefs that are being set.
152 uint32_t DecideAudioPlaybackChannels(const AudioInfo& info);
154 // Decide the sample-rate to use for audio output according to the
155 // given AudioInfo and the prefs that are being set.
156 uint32_t DecideAudioPlaybackSampleRate(const AudioInfo& info);
158 bool IsDefaultPlaybackDeviceMono();
160 bool IsVideoContentType(const nsCString& aContentType);
162 // Returns true if it's safe to use aPicture as the picture to be
163 // extracted inside a frame of size aFrame, and scaled up to and displayed
164 // at a size of aDisplay. You should validate the frame, picture, and
165 // display regions before using them to display video frames.
166 bool IsValidVideoRegion(const gfx::IntSize& aFrame,
167 const gfx::IntRect& aPicture,
168 const gfx::IntSize& aDisplay);
170 // Template to automatically set a variable to a value on scope exit.
171 // Useful for unsetting flags, etc.
172 template <typename T>
173 class AutoSetOnScopeExit {
174 public:
175 AutoSetOnScopeExit(T& aVar, T aValue) : mVar(aVar), mValue(aValue) {}
176 ~AutoSetOnScopeExit() { mVar = mValue; }
178 private:
179 T& mVar;
180 const T mValue;
183 enum class MediaThreadType {
184 SUPERVISOR, // MediaFormatReader, RemoteDecoderManager, MediaDecodeTask and
185 // others
186 PLATFORM_DECODER, // MediaDataDecoder
187 PLATFORM_ENCODER, // MediaDataEncoder
188 WEBRTC_CALL_THREAD,
189 WEBRTC_WORKER,
190 MDSM, // MediaDecoderStateMachine
192 // Returns the thread pool that is shared amongst all decoder state machines
193 // for decoding streams.
194 already_AddRefed<SharedThreadPool> GetMediaThreadPool(MediaThreadType aType);
196 enum H264_PROFILE {
197 H264_PROFILE_UNKNOWN = 0,
198 H264_PROFILE_BASE = 0x42,
199 H264_PROFILE_MAIN = 0x4D,
200 H264_PROFILE_EXTENDED = 0x58,
201 H264_PROFILE_HIGH = 0x64,
204 enum H264_LEVEL {
205 H264_LEVEL_1 = 10,
206 H264_LEVEL_1_b = 11,
207 H264_LEVEL_1_1 = 11,
208 H264_LEVEL_1_2 = 12,
209 H264_LEVEL_1_3 = 13,
210 H264_LEVEL_2 = 20,
211 H264_LEVEL_2_1 = 21,
212 H264_LEVEL_2_2 = 22,
213 H264_LEVEL_3 = 30,
214 H264_LEVEL_3_1 = 31,
215 H264_LEVEL_3_2 = 32,
216 H264_LEVEL_4 = 40,
217 H264_LEVEL_4_1 = 41,
218 H264_LEVEL_4_2 = 42,
219 H264_LEVEL_5 = 50,
220 H264_LEVEL_5_1 = 51,
221 H264_LEVEL_5_2 = 52
224 // Extracts the H.264/AVC profile and level from an H.264 codecs string.
225 // H.264 codecs parameters have a type defined as avc1.PPCCLL, where
226 // PP = profile_idc, CC = constraint_set flags, LL = level_idc.
227 // See
228 // http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
229 // for more details.
230 // Returns false on failure.
231 bool ExtractH264CodecDetails(const nsAString& aCodecs, uint8_t& aProfile,
232 uint8_t& aConstraint, uint8_t& aLevel);
234 struct VideoColorSpace {
235 // Default values are set according to
236 // https://www.webmproject.org/vp9/mp4/#optional-fields
237 // and https://aomediacodec.github.io/av1-isobmff/#codecsparam
238 gfx::CICP::ColourPrimaries mPrimaries = gfx::CICP::CP_BT709;
239 gfx::CICP::TransferCharacteristics mTransfer = gfx::CICP::TC_BT709;
240 gfx::CICP::MatrixCoefficients mMatrix = gfx::CICP::MC_BT709;
241 gfx::ColorRange mRange = gfx::ColorRange::LIMITED;
243 bool operator==(const VideoColorSpace& aOther) const {
244 return mPrimaries == aOther.mPrimaries && mTransfer == aOther.mTransfer &&
245 mMatrix == aOther.mMatrix && mRange == aOther.mRange;
247 bool operator!=(const VideoColorSpace& aOther) const {
248 return !(*this == aOther);
252 // Extracts the VPX codecs parameter string.
253 // See https://www.webmproject.org/vp9/mp4/#codecs-parameter-string
254 // for more details.
255 // Returns false on failure.
256 bool ExtractVPXCodecDetails(const nsAString& aCodec, uint8_t& aProfile,
257 uint8_t& aLevel, uint8_t& aBitDepth);
258 bool ExtractVPXCodecDetails(const nsAString& aCodec, uint8_t& aProfile,
259 uint8_t& aLevel, uint8_t& aBitDepth,
260 uint8_t& aChromaSubsampling,
261 VideoColorSpace& aColorSpace);
263 // Extracts AV1 codecs parameter string.
264 // See https://aomediacodec.github.io/av1-isobmff/#codecsparam
265 // Returns false if the codec is invalid.
266 bool ExtractAV1CodecDetails(const nsAString& aCodec, uint8_t& aProfile,
267 uint8_t& aLevel, uint8_t& aTier, uint8_t& aBitDepth,
268 bool& aMonochrome, bool& aSubsamplingX,
269 bool& aSubsamplingY, uint8_t& aChromaSamplePosition,
270 VideoColorSpace& aColorSpace);
272 // Use a cryptographic quality PRNG to generate raw random bytes
273 // and convert that to a base64 string.
274 nsresult GenerateRandomName(nsCString& aOutSalt, uint32_t aLength);
276 // This version returns a string suitable for use as a file or URL
277 // path. This is based on code from nsExternalAppHandler::SetUpTempFile.
278 nsresult GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength);
280 already_AddRefed<TaskQueue> CreateMediaDecodeTaskQueue(const char* aName);
282 // Iteratively invokes aWork until aCondition returns true, or aWork returns
283 // false. Use this rather than a while loop to avoid bogarting the task queue.
284 template <class Work, class Condition>
285 RefPtr<GenericPromise> InvokeUntil(Work aWork, Condition aCondition) {
286 RefPtr<GenericPromise::Private> p = new GenericPromise::Private(__func__);
288 if (aCondition()) {
289 p->Resolve(true, __func__);
292 struct Helper {
293 static void Iteration(const RefPtr<GenericPromise::Private>& aPromise,
294 Work aLocalWork, Condition aLocalCondition) {
295 if (!aLocalWork()) {
296 aPromise->Reject(NS_ERROR_FAILURE, __func__);
297 } else if (aLocalCondition()) {
298 aPromise->Resolve(true, __func__);
299 } else {
300 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
301 "InvokeUntil::Helper::Iteration",
302 [aPromise, aLocalWork, aLocalCondition]() {
303 Iteration(aPromise, aLocalWork, aLocalCondition);
305 AbstractThread::GetCurrent()->Dispatch(r.forget());
310 Helper::Iteration(p, aWork, aCondition);
311 return p;
314 // Simple timer to run a runnable after a timeout.
315 class SimpleTimer : public nsITimerCallback, public nsINamed {
316 public:
317 NS_DECL_ISUPPORTS
318 NS_DECL_NSINAMED
320 // Create a new timer to run aTask after aTimeoutMs milliseconds
321 // on thread aTarget. If aTarget is null, task is run on the main thread.
322 static already_AddRefed<SimpleTimer> Create(
323 nsIRunnable* aTask, uint32_t aTimeoutMs,
324 nsIEventTarget* aTarget = nullptr);
325 void Cancel();
327 NS_IMETHOD Notify(nsITimer* timer) override;
329 private:
330 virtual ~SimpleTimer() = default;
331 nsresult Init(nsIRunnable* aTask, uint32_t aTimeoutMs,
332 nsIEventTarget* aTarget);
334 RefPtr<nsIRunnable> mTask;
335 nsCOMPtr<nsITimer> mTimer;
338 void LogToBrowserConsole(const nsAString& aMsg);
340 bool ParseMIMETypeString(const nsAString& aMIMEType,
341 nsString& aOutContainerType,
342 nsTArray<nsString>& aOutCodecs);
344 bool ParseCodecsString(const nsAString& aCodecs,
345 nsTArray<nsString>& aOutCodecs);
347 bool IsH264CodecString(const nsAString& aCodec);
349 bool IsAACCodecString(const nsAString& aCodec);
351 bool IsVP8CodecString(const nsAString& aCodec);
353 bool IsVP9CodecString(const nsAString& aCodec);
355 bool IsAV1CodecString(const nsAString& aCodec);
357 // Try and create a TrackInfo with a given codec MIME type.
358 UniquePtr<TrackInfo> CreateTrackInfoWithMIMEType(
359 const nsACString& aCodecMIMEType);
361 // Try and create a TrackInfo with a given codec MIME type, and optional extra
362 // parameters from a container type (its MIME type and codecs are ignored).
363 UniquePtr<TrackInfo> CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(
364 const nsACString& aCodecMIMEType, const MediaContainerType& aContainerType);
366 namespace detail {
368 // aString should start with aMajor + '/'.
369 constexpr bool StartsWithMIMETypeMajor(const char* aString, const char* aMajor,
370 size_t aMajorRemaining) {
371 return (aMajorRemaining == 0 && *aString == '/') ||
372 (*aString == *aMajor &&
373 StartsWithMIMETypeMajor(aString + 1, aMajor + 1,
374 aMajorRemaining - 1));
377 // aString should only contain [a-z0-9\-\.] and a final '\0'.
378 constexpr bool EndsWithMIMESubtype(const char* aString, size_t aRemaining) {
379 return aRemaining == 0 || (((*aString >= 'a' && *aString <= 'z') ||
380 (*aString >= '0' && *aString <= '9') ||
381 *aString == '-' || *aString == '.') &&
382 EndsWithMIMESubtype(aString + 1, aRemaining - 1));
385 // Simple MIME-type literal string checker with a given (major) type.
386 // Only accepts "{aMajor}/[a-z0-9\-\.]+".
387 template <size_t MajorLengthPlus1>
388 constexpr bool IsMIMETypeWithMajor(const char* aString, size_t aLength,
389 const char (&aMajor)[MajorLengthPlus1]) {
390 return aLength > MajorLengthPlus1 && // Major + '/' + at least 1 char
391 StartsWithMIMETypeMajor(aString, aMajor, MajorLengthPlus1 - 1) &&
392 EndsWithMIMESubtype(aString + MajorLengthPlus1,
393 aLength - MajorLengthPlus1);
396 } // namespace detail
398 // Simple MIME-type string checker.
399 // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
400 // Add more if necessary.
401 constexpr bool IsMediaMIMEType(const char* aString, size_t aLength) {
402 return detail::IsMIMETypeWithMajor(aString, aLength, "application") ||
403 detail::IsMIMETypeWithMajor(aString, aLength, "audio") ||
404 detail::IsMIMETypeWithMajor(aString, aLength, "video");
407 // Simple MIME-type string literal checker.
408 // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
409 // Add more if necessary.
410 template <size_t LengthPlus1>
411 constexpr bool IsMediaMIMEType(const char (&aString)[LengthPlus1]) {
412 return IsMediaMIMEType(aString, LengthPlus1 - 1);
415 // Simple MIME-type string checker.
416 // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
417 // Add more if necessary.
418 inline bool IsMediaMIMEType(const nsACString& aString) {
419 return IsMediaMIMEType(aString.Data(), aString.Length());
422 enum class StringListRangeEmptyItems {
423 // Skip all empty items (empty string will process nothing)
424 // E.g.: "a,,b" -> ["a", "b"], "" -> nothing
425 Skip,
426 // Process all, except if string is empty
427 // E.g.: "a,,b" -> ["a", "", "b"], "" -> nothing
428 ProcessEmptyItems,
429 // Process all, including 1 empty item in an empty string
430 // E.g.: "a,,b" -> ["a", "", "b"], "" -> [""]
431 ProcessAll
434 template <typename String,
435 StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip>
436 class StringListRange {
437 typedef typename String::char_type CharType;
438 typedef const CharType* Pointer;
440 public:
441 // Iterator into range, trims items and optionally skips empty items.
442 class Iterator {
443 public:
444 bool operator!=(const Iterator& a) const {
445 return mStart != a.mStart || mEnd != a.mEnd;
447 Iterator& operator++() {
448 SearchItemAt(mComma + 1);
449 return *this;
451 // DereferencedType should be 'const nsDependent[C]String' pointing into
452 // mList (which is 'const ns[C]String&').
453 typedef decltype(Substring(Pointer(), Pointer())) DereferencedType;
454 DereferencedType operator*() { return Substring(mStart, mEnd); }
456 private:
457 friend class StringListRange;
458 Iterator(const CharType* aRangeStart, uint32_t aLength)
459 : mRangeEnd(aRangeStart + aLength),
460 mStart(nullptr),
461 mEnd(nullptr),
462 mComma(nullptr) {
463 SearchItemAt(aRangeStart);
465 void SearchItemAt(Pointer start) {
466 // First, skip leading whitespace.
467 for (Pointer p = start;; ++p) {
468 if (p >= mRangeEnd) {
469 if (p > mRangeEnd +
470 (empties != StringListRangeEmptyItems::Skip ? 1 : 0)) {
471 p = mRangeEnd +
472 (empties != StringListRangeEmptyItems::Skip ? 1 : 0);
474 mStart = mEnd = mComma = p;
475 return;
477 auto c = *p;
478 if (c == CharType(',')) {
479 // Comma -> Empty item -> Skip or process?
480 if (empties != StringListRangeEmptyItems::Skip) {
481 mStart = mEnd = mComma = p;
482 return;
484 } else if (c != CharType(' ')) {
485 mStart = p;
486 break;
489 // Find comma, recording start of trailing space.
490 Pointer trailingWhitespace = nullptr;
491 for (Pointer p = mStart + 1;; ++p) {
492 if (p >= mRangeEnd) {
493 mEnd = trailingWhitespace ? trailingWhitespace : p;
494 mComma = p;
495 return;
497 auto c = *p;
498 if (c == CharType(',')) {
499 mEnd = trailingWhitespace ? trailingWhitespace : p;
500 mComma = p;
501 return;
503 if (c == CharType(' ')) {
504 // Found a whitespace -> Record as trailing if not first one.
505 if (!trailingWhitespace) {
506 trailingWhitespace = p;
508 } else {
509 // Found a non-whitespace -> Reset trailing whitespace if needed.
510 if (trailingWhitespace) {
511 trailingWhitespace = nullptr;
516 const Pointer mRangeEnd;
517 Pointer mStart;
518 Pointer mEnd;
519 Pointer mComma;
522 explicit StringListRange(const String& aList) : mList(aList) {}
523 Iterator begin() const {
524 return Iterator(
525 mList.Data() +
526 ((empties == StringListRangeEmptyItems::ProcessEmptyItems &&
527 mList.Length() == 0)
529 : 0),
530 mList.Length());
532 Iterator end() const {
533 return Iterator(mList.Data() + mList.Length() +
534 (empties != StringListRangeEmptyItems::Skip ? 1 : 0),
538 private:
539 const String& mList;
542 template <StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip,
543 typename String>
544 StringListRange<String, empties> MakeStringListRange(const String& aList) {
545 return StringListRange<String, empties>(aList);
548 template <StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip,
549 typename ListString, typename ItemString>
550 static bool StringListContains(const ListString& aList,
551 const ItemString& aItem) {
552 for (const auto& listItem : MakeStringListRange<empties>(aList)) {
553 if (listItem.Equals(aItem)) {
554 return true;
557 return false;
560 inline void AppendStringIfNotEmpty(nsACString& aDest, nsACString&& aSrc) {
561 if (!aSrc.IsEmpty()) {
562 aDest.Append("\n"_ns);
563 aDest.Append(aSrc);
567 // Returns true if we're running on a cellular connection; 2G, 3G, etc.
568 // Main thread only.
569 bool OnCellularConnection();
571 inline gfx::YUVColorSpace DefaultColorSpace(const gfx::IntSize& aSize) {
572 return aSize.height < 720 ? gfx::YUVColorSpace::BT601
573 : gfx::YUVColorSpace::BT709;
576 } // end namespace mozilla
578 #endif