VideoUtils.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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. #ifndef VideoUtils_h
  7. #define VideoUtils_h
  8. #include "MediaInfo.h"
  9. #include "mozilla/Attributes.h"
  10. #include "mozilla/CheckedInt.h"
  11. #include "mozilla/MozPromise.h"
  12. #include "mozilla/ReentrantMonitor.h"
  13. #include "mozilla/RefPtr.h"
  14. #include "mozilla/UniquePtr.h"
  15. #include "nsAutoPtr.h"
  16. #include "nsIThread.h"
  17. #include "nsSize.h"
  18. #include "nsRect.h"
  19. #include "nsThreadUtils.h"
  20. #include "prtime.h"
  21. #include "AudioSampleFormat.h"
  22. #include "TimeUnits.h"
  23. #include "nsITimer.h"
  24. #include "nsCOMPtr.h"
  25. #include "VideoLimits.h"
  26. using mozilla::CheckedInt64;
  27. using mozilla::CheckedUint64;
  28. using mozilla::CheckedInt32;
  29. using mozilla::CheckedUint32;
  30. // This file contains stuff we'd rather put elsewhere, but which is
  31. // dependent on other changes which we don't want to wait for. We plan to
  32. // remove this file in the near future.
  33. // This belongs in xpcom/monitor/Monitor.h, once we've made
  34. // mozilla::Monitor non-reentrant.
  35. namespace mozilla {
  36. class MediaContentType;
  37. // EME Key System String.
  38. extern const nsLiteralCString kEMEKeySystemClearkey;
  39. extern const nsLiteralCString kEMEKeySystemWidevine;
  40. /**
  41. * ReentrantMonitorConditionallyEnter
  42. *
  43. * Enters the supplied monitor only if the conditional value |aEnter| is true.
  44. * E.g. Used to allow unmonitored read access on the decode thread,
  45. * and monitored access on all other threads.
  46. */
  47. class MOZ_STACK_CLASS ReentrantMonitorConditionallyEnter
  48. {
  49. public:
  50. ReentrantMonitorConditionallyEnter(bool aEnter,
  51. ReentrantMonitor &aReentrantMonitor) :
  52. mReentrantMonitor(nullptr)
  53. {
  54. MOZ_COUNT_CTOR(ReentrantMonitorConditionallyEnter);
  55. if (aEnter) {
  56. mReentrantMonitor = &aReentrantMonitor;
  57. NS_ASSERTION(mReentrantMonitor, "null monitor");
  58. mReentrantMonitor->Enter();
  59. }
  60. }
  61. ~ReentrantMonitorConditionallyEnter(void)
  62. {
  63. if (mReentrantMonitor) {
  64. mReentrantMonitor->Exit();
  65. }
  66. MOZ_COUNT_DTOR(ReentrantMonitorConditionallyEnter);
  67. }
  68. private:
  69. // Restrict to constructor and destructor defined above.
  70. ReentrantMonitorConditionallyEnter();
  71. ReentrantMonitorConditionallyEnter(const ReentrantMonitorConditionallyEnter&);
  72. ReentrantMonitorConditionallyEnter& operator =(const ReentrantMonitorConditionallyEnter&);
  73. static void* operator new(size_t) CPP_THROW_NEW;
  74. static void operator delete(void*);
  75. ReentrantMonitor* mReentrantMonitor;
  76. };
  77. // Shuts down a thread asynchronously.
  78. class ShutdownThreadEvent : public Runnable
  79. {
  80. public:
  81. explicit ShutdownThreadEvent(nsIThread* aThread) : mThread(aThread) {}
  82. ~ShutdownThreadEvent() {}
  83. NS_IMETHOD Run() override {
  84. mThread->Shutdown();
  85. mThread = nullptr;
  86. return NS_OK;
  87. }
  88. private:
  89. nsCOMPtr<nsIThread> mThread;
  90. };
  91. template<class T>
  92. class DeleteObjectTask: public Runnable {
  93. public:
  94. explicit DeleteObjectTask(nsAutoPtr<T>& aObject)
  95. : mObject(aObject)
  96. {
  97. }
  98. NS_IMETHOD Run() override {
  99. NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
  100. mObject = nullptr;
  101. return NS_OK;
  102. }
  103. private:
  104. nsAutoPtr<T> mObject;
  105. };
  106. template<class T>
  107. void DeleteOnMainThread(nsAutoPtr<T>& aObject) {
  108. NS_DispatchToMainThread(new DeleteObjectTask<T>(aObject));
  109. }
  110. class MediaResource;
  111. // Estimates the buffered ranges of a MediaResource using a simple
  112. // (byteOffset/length)*duration method. Probably inaccurate, but won't
  113. // do file I/O, and can be used when we don't have detailed knowledge
  114. // of the byte->time mapping of a resource. aDurationUsecs is the duration
  115. // of the media in microseconds. Estimated buffered ranges are stored in
  116. // aOutBuffered. Ranges are 0-normalized, i.e. in the range of (0,duration].
  117. media::TimeIntervals GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
  118. int64_t aDurationUsecs);
  119. // Converts from number of audio frames (aFrames) to microseconds, given
  120. // the specified audio rate (aRate).
  121. CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate);
  122. // Converts from number of audio frames (aFrames) TimeUnit, given
  123. // the specified audio rate (aRate).
  124. media::TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate);
  125. // Perform aValue * aMul / aDiv, reducing the possibility of overflow due to
  126. // aValue * aMul overflowing.
  127. CheckedInt64 SaferMultDiv(int64_t aValue, uint32_t aMul, uint32_t aDiv);
  128. // Converts from microseconds (aUsecs) to number of audio frames, given the
  129. // specified audio rate (aRate). Stores the result in aOutFrames. Returns
  130. // true if the operation succeeded, or false if there was an integer
  131. // overflow while calulating the conversion.
  132. CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate);
  133. // Format TimeUnit as number of frames at given rate.
  134. 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))
  137. // Converts seconds to milliseconds.
  138. #define SECONDS_TO_MS(s) ((int)((s) * (PR_MSEC_PER_SEC)))
  139. // Converts from seconds to microseconds. Returns failure if the resulting
  140. // integer is too big to fit in an int64_t.
  141. nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs);
  142. // Scales the display rect aDisplay by aspect ratio aAspectRatio.
  143. // Note that aDisplay must be validated by IsValidVideoRegion()
  144. // before being used!
  145. void ScaleDisplayByAspectRatio(nsIntSize& 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,
  149. uint32_t aFrames);
  150. bool IsVideoContentType(const nsCString& aContentType);
  151. // Returns true if it's safe to use aPicture as the picture to be
  152. // extracted inside a frame of size aFrame, and scaled up to and displayed
  153. // at a size of aDisplay. You should validate the frame, picture, and
  154. // display regions before using them to display video frames.
  155. bool IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture,
  156. const nsIntSize& aDisplay);
  157. // Template to automatically set a variable to a value on scope exit.
  158. // Useful for unsetting flags, etc.
  159. template<typename T>
  160. class AutoSetOnScopeExit {
  161. public:
  162. AutoSetOnScopeExit(T& aVar, T aValue)
  163. : mVar(aVar)
  164. , mValue(aValue)
  165. {}
  166. ~AutoSetOnScopeExit() {
  167. mVar = mValue;
  168. }
  169. private:
  170. T& mVar;
  171. const T mValue;
  172. };
  173. class SharedThreadPool;
  174. // The MediaDataDecoder API blocks, with implementations waiting on platform
  175. // decoder tasks. These platform decoder tasks are queued on a separate
  176. // thread pool to ensure they can run when the MediaDataDecoder clients'
  177. // thread pool is blocked. Tasks on the PLATFORM_DECODER thread pool must not
  178. // wait on tasks in the PLAYBACK thread pool.
  179. //
  180. // No new dependencies on this mechanism should be added, as methods are being
  181. // made async supported by MozPromise, making this unnecessary and
  182. // permitting unifying the pool.
  183. enum class MediaThreadType {
  184. PLAYBACK, // MediaDecoderStateMachine and MediaDecoderReader
  185. PLATFORM_DECODER
  186. };
  187. // Returns the thread pool that is shared amongst all decoder state machines
  188. // for decoding streams.
  189. already_AddRefed<SharedThreadPool> GetMediaThreadPool(MediaThreadType aType);
  190. enum H264_PROFILE {
  191. H264_PROFILE_UNKNOWN = 0,
  192. H264_PROFILE_BASE = 0x42,
  193. H264_PROFILE_MAIN = 0x4D,
  194. H264_PROFILE_EXTENDED = 0x58,
  195. H264_PROFILE_HIGH = 0x64,
  196. };
  197. enum H264_LEVEL {
  198. H264_LEVEL_1 = 10,
  199. H264_LEVEL_1_b = 11,
  200. H264_LEVEL_1_1 = 11,
  201. H264_LEVEL_1_2 = 12,
  202. H264_LEVEL_1_3 = 13,
  203. H264_LEVEL_2 = 20,
  204. H264_LEVEL_2_1 = 21,
  205. H264_LEVEL_2_2 = 22,
  206. H264_LEVEL_3 = 30,
  207. H264_LEVEL_3_1 = 31,
  208. H264_LEVEL_3_2 = 32,
  209. H264_LEVEL_4 = 40,
  210. H264_LEVEL_4_1 = 41,
  211. H264_LEVEL_4_2 = 42,
  212. H264_LEVEL_5 = 50,
  213. H264_LEVEL_5_1 = 51,
  214. H264_LEVEL_5_2 = 52
  215. };
  216. // Extracts the H.264/AVC profile and level from an H.264 codecs string.
  217. // H.264 codecs parameters have a type defined as avc1.PPCCLL, where
  218. // PP = profile_idc, CC = constraint_set flags, LL = level_idc.
  219. // See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
  220. // for more details.
  221. // Returns false on failure.
  222. bool
  223. ExtractH264CodecDetails(const nsAString& aCodecs,
  224. int16_t& aProfile,
  225. int16_t& aLevel);
  226. // Use a cryptographic quality PRNG to generate raw random bytes
  227. // and convert that to a base64 string.
  228. nsresult
  229. GenerateRandomName(nsCString& aOutSalt, uint32_t aLength);
  230. // This version returns a string suitable for use as a file or URL
  231. // path. This is based on code from nsExternalAppHandler::SetUpTempFile.
  232. nsresult
  233. GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength);
  234. already_AddRefed<TaskQueue>
  235. CreateMediaDecodeTaskQueue();
  236. // Iteratively invokes aWork until aCondition returns true, or aWork returns false.
  237. // Use this rather than a while loop to avoid bogarting the task queue.
  238. template<class Work, class Condition>
  239. RefPtr<GenericPromise> InvokeUntil(Work aWork, Condition aCondition) {
  240. RefPtr<GenericPromise::Private> p = new GenericPromise::Private(__func__);
  241. if (aCondition()) {
  242. p->Resolve(true, __func__);
  243. }
  244. struct Helper {
  245. static void Iteration(RefPtr<GenericPromise::Private> aPromise, Work aLocalWork, Condition aLocalCondition) {
  246. if (!aLocalWork()) {
  247. aPromise->Reject(NS_ERROR_FAILURE, __func__);
  248. } else if (aLocalCondition()) {
  249. aPromise->Resolve(true, __func__);
  250. } else {
  251. nsCOMPtr<nsIRunnable> r =
  252. NS_NewRunnableFunction([aPromise, aLocalWork, aLocalCondition] () { Iteration(aPromise, aLocalWork, aLocalCondition); });
  253. AbstractThread::GetCurrent()->Dispatch(r.forget());
  254. }
  255. }
  256. };
  257. Helper::Iteration(p, aWork, aCondition);
  258. return p.forget();
  259. }
  260. // Simple timer to run a runnable after a timeout.
  261. class SimpleTimer : public nsITimerCallback
  262. {
  263. public:
  264. NS_DECL_ISUPPORTS
  265. // Create a new timer to run aTask after aTimeoutMs milliseconds
  266. // on thread aTarget. If aTarget is null, task is run on the main thread.
  267. static already_AddRefed<SimpleTimer> Create(nsIRunnable* aTask,
  268. uint32_t aTimeoutMs,
  269. nsIThread* aTarget = nullptr);
  270. void Cancel();
  271. NS_IMETHOD Notify(nsITimer *timer) override;
  272. private:
  273. virtual ~SimpleTimer() {}
  274. nsresult Init(nsIRunnable* aTask, uint32_t aTimeoutMs, nsIThread* aTarget);
  275. RefPtr<nsIRunnable> mTask;
  276. nsCOMPtr<nsITimer> mTimer;
  277. };
  278. void
  279. LogToBrowserConsole(const nsAString& aMsg);
  280. bool
  281. ParseMIMETypeString(const nsAString& aMIMEType,
  282. nsString& aOutContainerType,
  283. nsTArray<nsString>& aOutCodecs);
  284. bool
  285. ParseCodecsString(const nsAString& aCodecs, nsTArray<nsString>& aOutCodecs);
  286. bool
  287. IsH264CodecString(const nsAString& aCodec);
  288. bool
  289. IsAACCodecString(const nsAString& aCodec);
  290. bool
  291. IsVP8CodecString(const nsAString& aCodec);
  292. bool
  293. IsVP9CodecString(const nsAString& aCodec);
  294. #ifdef MOZ_AV1
  295. bool
  296. IsAV1CodecString(const nsAString& aCodec);
  297. #endif
  298. // Try and create a TrackInfo with a given codec MIME type.
  299. UniquePtr<TrackInfo>
  300. CreateTrackInfoWithMIMEType(const nsACString& aCodecMIMEType);
  301. // Try and create a TrackInfo with a given codec MIME type, and optional extra
  302. // parameters from a content type (its MIME type and codecs are ignored).
  303. UniquePtr<TrackInfo>
  304. CreateTrackInfoWithMIMETypeAndContentTypeExtraParameters(
  305. const nsACString& aCodecMIMEType,
  306. const MediaContentType& aContentType);
  307. template <typename String>
  308. class StringListRange
  309. {
  310. typedef typename String::char_type CharType;
  311. typedef const CharType* Pointer;
  312. public:
  313. // Iterator into range, trims items and skips empty items.
  314. class Iterator
  315. {
  316. public:
  317. bool operator!=(const Iterator& a) const
  318. {
  319. return mStart != a.mStart || mEnd != a.mEnd;
  320. }
  321. Iterator& operator++()
  322. {
  323. SearchItemAt(mComma + 1);
  324. return *this;
  325. }
  326. typedef decltype(Substring(Pointer(), Pointer())) DereferencedType;
  327. DereferencedType operator*()
  328. {
  329. return Substring(mStart, mEnd);
  330. }
  331. private:
  332. friend class StringListRange;
  333. Iterator(const CharType* aRangeStart, uint32_t aLength)
  334. : mRangeEnd(aRangeStart + aLength)
  335. {
  336. SearchItemAt(aRangeStart);
  337. }
  338. void SearchItemAt(Pointer start)
  339. {
  340. // First, skip leading whitespace.
  341. for (Pointer p = start; ; ++p) {
  342. if (p >= mRangeEnd) {
  343. mStart = mEnd = mComma = mRangeEnd;
  344. return;
  345. }
  346. auto c = *p;
  347. if (c == CharType(',')) {
  348. // Comma -> Empty item -> Skip.
  349. } else if (c != CharType(' ')) {
  350. mStart = p;
  351. break;
  352. }
  353. }
  354. // Find comma, recording start of trailing space.
  355. Pointer trailingWhitespace = nullptr;
  356. for (Pointer p = mStart + 1; ; ++p) {
  357. if (p >= mRangeEnd) {
  358. mEnd = trailingWhitespace ? trailingWhitespace : p;
  359. mComma = p;
  360. return;
  361. }
  362. auto c = *p;
  363. if (c == CharType(',')) {
  364. mEnd = trailingWhitespace ? trailingWhitespace : p;
  365. mComma = p;
  366. return;
  367. }
  368. if (c == CharType(' ')) {
  369. // Found a whitespace -> Record as trailing if not first one.
  370. if (!trailingWhitespace) {
  371. trailingWhitespace = p;
  372. }
  373. } else {
  374. // Found a non-whitespace -> Reset trailing whitespace if needed.
  375. if (trailingWhitespace) {
  376. trailingWhitespace = nullptr;
  377. }
  378. }
  379. }
  380. }
  381. const Pointer mRangeEnd;
  382. Pointer mStart;
  383. Pointer mEnd;
  384. Pointer mComma;
  385. };
  386. explicit StringListRange(const String& aList) : mList(aList) {}
  387. Iterator begin()
  388. {
  389. return Iterator(mList.Data(), mList.Length());
  390. }
  391. Iterator end()
  392. {
  393. return Iterator(mList.Data() + mList.Length(), 0);
  394. }
  395. private:
  396. const String& mList;
  397. };
  398. template <typename String>
  399. StringListRange<String>
  400. MakeStringListRange(const String& aList)
  401. {
  402. return StringListRange<String>(aList);
  403. }
  404. template <typename ListString, typename ItemString>
  405. static bool
  406. StringListContains(const ListString& aList, const ItemString& aItem)
  407. {
  408. for (const auto& listItem : MakeStringListRange(aList)) {
  409. if (listItem.Equals(aItem)) {
  410. return true;
  411. }
  412. }
  413. return false;
  414. }
  415. } // end namespace mozilla
  416. #endif