summaryrefslogtreecommitdiffstats
path: root/dom/media/VideoUtils.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/VideoUtils.h')
-rw-r--r--dom/media/VideoUtils.h474
1 files changed, 474 insertions, 0 deletions
diff --git a/dom/media/VideoUtils.h b/dom/media/VideoUtils.h
new file mode 100644
index 000000000..441b63792
--- /dev/null
+++ b/dom/media/VideoUtils.h
@@ -0,0 +1,474 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef VideoUtils_h
+#define VideoUtils_h
+
+#include "MediaInfo.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+
+#include "nsAutoPtr.h"
+#include "nsIThread.h"
+#include "nsSize.h"
+#include "nsRect.h"
+
+#include "nsThreadUtils.h"
+#include "prtime.h"
+#include "AudioSampleFormat.h"
+#include "TimeUnits.h"
+#include "nsITimer.h"
+#include "nsCOMPtr.h"
+#include "VideoLimits.h"
+
+using mozilla::CheckedInt64;
+using mozilla::CheckedUint64;
+using mozilla::CheckedInt32;
+using mozilla::CheckedUint32;
+
+// This file contains stuff we'd rather put elsewhere, but which is
+// dependent on other changes which we don't want to wait for. We plan to
+// remove this file in the near future.
+
+
+// This belongs in xpcom/monitor/Monitor.h, once we've made
+// mozilla::Monitor non-reentrant.
+namespace mozilla {
+
+class MediaContentType;
+
+// EME Key System String.
+extern const nsLiteralCString kEMEKeySystemClearkey;
+extern const nsLiteralCString kEMEKeySystemWidevine;
+extern const nsLiteralCString kEMEKeySystemPrimetime;
+
+/**
+ * ReentrantMonitorConditionallyEnter
+ *
+ * Enters the supplied monitor only if the conditional value |aEnter| is true.
+ * E.g. Used to allow unmonitored read access on the decode thread,
+ * and monitored access on all other threads.
+ */
+class MOZ_STACK_CLASS ReentrantMonitorConditionallyEnter
+{
+public:
+ ReentrantMonitorConditionallyEnter(bool aEnter,
+ ReentrantMonitor &aReentrantMonitor) :
+ mReentrantMonitor(nullptr)
+ {
+ MOZ_COUNT_CTOR(ReentrantMonitorConditionallyEnter);
+ if (aEnter) {
+ mReentrantMonitor = &aReentrantMonitor;
+ NS_ASSERTION(mReentrantMonitor, "null monitor");
+ mReentrantMonitor->Enter();
+ }
+ }
+ ~ReentrantMonitorConditionallyEnter(void)
+ {
+ if (mReentrantMonitor) {
+ mReentrantMonitor->Exit();
+ }
+ MOZ_COUNT_DTOR(ReentrantMonitorConditionallyEnter);
+ }
+private:
+ // Restrict to constructor and destructor defined above.
+ ReentrantMonitorConditionallyEnter();
+ ReentrantMonitorConditionallyEnter(const ReentrantMonitorConditionallyEnter&);
+ ReentrantMonitorConditionallyEnter& operator =(const ReentrantMonitorConditionallyEnter&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+ static void operator delete(void*);
+
+ ReentrantMonitor* mReentrantMonitor;
+};
+
+// Shuts down a thread asynchronously.
+class ShutdownThreadEvent : public Runnable
+{
+public:
+ explicit ShutdownThreadEvent(nsIThread* aThread) : mThread(aThread) {}
+ ~ShutdownThreadEvent() {}
+ NS_IMETHOD Run() override {
+ mThread->Shutdown();
+ mThread = nullptr;
+ return NS_OK;
+ }
+private:
+ nsCOMPtr<nsIThread> mThread;
+};
+
+template<class T>
+class DeleteObjectTask: public Runnable {
+public:
+ explicit DeleteObjectTask(nsAutoPtr<T>& aObject)
+ : mObject(aObject)
+ {
+ }
+ NS_IMETHOD Run() override {
+ NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+ mObject = nullptr;
+ return NS_OK;
+ }
+private:
+ nsAutoPtr<T> mObject;
+};
+
+template<class T>
+void DeleteOnMainThread(nsAutoPtr<T>& aObject) {
+ NS_DispatchToMainThread(new DeleteObjectTask<T>(aObject));
+}
+
+class MediaResource;
+
+// Estimates the buffered ranges of a MediaResource using a simple
+// (byteOffset/length)*duration method. Probably inaccurate, but won't
+// do file I/O, and can be used when we don't have detailed knowledge
+// of the byte->time mapping of a resource. aDurationUsecs is the duration
+// of the media in microseconds. Estimated buffered ranges are stored in
+// aOutBuffered. Ranges are 0-normalized, i.e. in the range of (0,duration].
+media::TimeIntervals GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
+ int64_t aDurationUsecs);
+
+// Converts from number of audio frames (aFrames) to microseconds, given
+// the specified audio rate (aRate).
+CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate);
+// Converts from number of audio frames (aFrames) TimeUnit, given
+// the specified audio rate (aRate).
+media::TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate);
+// Perform aValue * aMul / aDiv, reducing the possibility of overflow due to
+// aValue * aMul overflowing.
+CheckedInt64 SaferMultDiv(int64_t aValue, uint32_t aMul, uint32_t aDiv);
+
+// Converts from microseconds (aUsecs) to number of audio frames, given the
+// specified audio rate (aRate). Stores the result in aOutFrames. Returns
+// true if the operation succeeded, or false if there was an integer
+// overflow while calulating the conversion.
+CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate);
+
+// Format TimeUnit as number of frames at given rate.
+CheckedInt64 TimeUnitToFrames(const media::TimeUnit& aTime, uint32_t aRate);
+
+// Converts milliseconds to seconds.
+#define MS_TO_SECONDS(ms) ((double)(ms) / (PR_MSEC_PER_SEC))
+
+// Converts seconds to milliseconds.
+#define SECONDS_TO_MS(s) ((int)((s) * (PR_MSEC_PER_SEC)))
+
+// Converts from seconds to microseconds. Returns failure if the resulting
+// integer is too big to fit in an int64_t.
+nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs);
+
+// Scales the display rect aDisplay by aspect ratio aAspectRatio.
+// Note that aDisplay must be validated by IsValidVideoRegion()
+// before being used!
+void ScaleDisplayByAspectRatio(nsIntSize& aDisplay, float aAspectRatio);
+
+// Downmix Stereo audio samples to Mono.
+// Input are the buffer contains stereo data and the number of frames.
+void DownmixStereoToMono(mozilla::AudioDataValue* aBuffer,
+ uint32_t aFrames);
+
+bool IsVideoContentType(const nsCString& aContentType);
+
+// Returns true if it's safe to use aPicture as the picture to be
+// extracted inside a frame of size aFrame, and scaled up to and displayed
+// at a size of aDisplay. You should validate the frame, picture, and
+// display regions before using them to display video frames.
+bool IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture,
+ const nsIntSize& aDisplay);
+
+// Template to automatically set a variable to a value on scope exit.
+// Useful for unsetting flags, etc.
+template<typename T>
+class AutoSetOnScopeExit {
+public:
+ AutoSetOnScopeExit(T& aVar, T aValue)
+ : mVar(aVar)
+ , mValue(aValue)
+ {}
+ ~AutoSetOnScopeExit() {
+ mVar = mValue;
+ }
+private:
+ T& mVar;
+ const T mValue;
+};
+
+class SharedThreadPool;
+
+// The MediaDataDecoder API blocks, with implementations waiting on platform
+// decoder tasks. These platform decoder tasks are queued on a separate
+// thread pool to ensure they can run when the MediaDataDecoder clients'
+// thread pool is blocked. Tasks on the PLATFORM_DECODER thread pool must not
+// wait on tasks in the PLAYBACK thread pool.
+//
+// No new dependencies on this mechanism should be added, as methods are being
+// made async supported by MozPromise, making this unnecessary and
+// permitting unifying the pool.
+enum class MediaThreadType {
+ PLAYBACK, // MediaDecoderStateMachine and MediaDecoderReader
+ PLATFORM_DECODER
+};
+// Returns the thread pool that is shared amongst all decoder state machines
+// for decoding streams.
+already_AddRefed<SharedThreadPool> GetMediaThreadPool(MediaThreadType aType);
+
+enum H264_PROFILE {
+ H264_PROFILE_UNKNOWN = 0,
+ H264_PROFILE_BASE = 0x42,
+ H264_PROFILE_MAIN = 0x4D,
+ H264_PROFILE_EXTENDED = 0x58,
+ H264_PROFILE_HIGH = 0x64,
+};
+
+enum H264_LEVEL {
+ H264_LEVEL_1 = 10,
+ H264_LEVEL_1_b = 11,
+ H264_LEVEL_1_1 = 11,
+ H264_LEVEL_1_2 = 12,
+ H264_LEVEL_1_3 = 13,
+ H264_LEVEL_2 = 20,
+ H264_LEVEL_2_1 = 21,
+ H264_LEVEL_2_2 = 22,
+ H264_LEVEL_3 = 30,
+ H264_LEVEL_3_1 = 31,
+ H264_LEVEL_3_2 = 32,
+ H264_LEVEL_4 = 40,
+ H264_LEVEL_4_1 = 41,
+ H264_LEVEL_4_2 = 42,
+ H264_LEVEL_5 = 50,
+ H264_LEVEL_5_1 = 51,
+ H264_LEVEL_5_2 = 52
+};
+
+// Extracts the H.264/AVC profile and level from an H.264 codecs string.
+// H.264 codecs parameters have a type defined as avc1.PPCCLL, where
+// PP = profile_idc, CC = constraint_set flags, LL = level_idc.
+// See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
+// for more details.
+// Returns false on failure.
+bool
+ExtractH264CodecDetails(const nsAString& aCodecs,
+ int16_t& aProfile,
+ int16_t& aLevel);
+
+// Use a cryptographic quality PRNG to generate raw random bytes
+// and convert that to a base64 string.
+nsresult
+GenerateRandomName(nsCString& aOutSalt, uint32_t aLength);
+
+// This version returns a string suitable for use as a file or URL
+// path. This is based on code from nsExternalAppHandler::SetUpTempFile.
+nsresult
+GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength);
+
+already_AddRefed<TaskQueue>
+CreateMediaDecodeTaskQueue();
+
+// Iteratively invokes aWork until aCondition returns true, or aWork returns false.
+// Use this rather than a while loop to avoid bogarting the task queue.
+template<class Work, class Condition>
+RefPtr<GenericPromise> InvokeUntil(Work aWork, Condition aCondition) {
+ RefPtr<GenericPromise::Private> p = new GenericPromise::Private(__func__);
+
+ if (aCondition()) {
+ p->Resolve(true, __func__);
+ }
+
+ struct Helper {
+ static void Iteration(RefPtr<GenericPromise::Private> aPromise, Work aLocalWork, Condition aLocalCondition) {
+ if (!aLocalWork()) {
+ aPromise->Reject(NS_ERROR_FAILURE, __func__);
+ } else if (aLocalCondition()) {
+ aPromise->Resolve(true, __func__);
+ } else {
+ nsCOMPtr<nsIRunnable> r =
+ NS_NewRunnableFunction([aPromise, aLocalWork, aLocalCondition] () { Iteration(aPromise, aLocalWork, aLocalCondition); });
+ AbstractThread::GetCurrent()->Dispatch(r.forget());
+ }
+ }
+ };
+
+ Helper::Iteration(p, aWork, aCondition);
+ return p.forget();
+}
+
+// Simple timer to run a runnable after a timeout.
+class SimpleTimer : public nsITimerCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // Create a new timer to run aTask after aTimeoutMs milliseconds
+ // on thread aTarget. If aTarget is null, task is run on the main thread.
+ static already_AddRefed<SimpleTimer> Create(nsIRunnable* aTask,
+ uint32_t aTimeoutMs,
+ nsIThread* aTarget = nullptr);
+ void Cancel();
+
+ NS_IMETHOD Notify(nsITimer *timer) override;
+
+private:
+ virtual ~SimpleTimer() {}
+ nsresult Init(nsIRunnable* aTask, uint32_t aTimeoutMs, nsIThread* aTarget);
+
+ RefPtr<nsIRunnable> mTask;
+ nsCOMPtr<nsITimer> mTimer;
+};
+
+void
+LogToBrowserConsole(const nsAString& aMsg);
+
+bool
+ParseMIMETypeString(const nsAString& aMIMEType,
+ nsString& aOutContainerType,
+ nsTArray<nsString>& aOutCodecs);
+
+bool
+ParseCodecsString(const nsAString& aCodecs, nsTArray<nsString>& aOutCodecs);
+
+bool
+IsH264CodecString(const nsAString& aCodec);
+
+bool
+IsAACCodecString(const nsAString& aCodec);
+
+bool
+IsVP8CodecString(const nsAString& aCodec);
+
+bool
+IsVP9CodecString(const nsAString& aCodec);
+
+// Try and create a TrackInfo with a given codec MIME type.
+UniquePtr<TrackInfo>
+CreateTrackInfoWithMIMEType(const nsACString& aCodecMIMEType);
+
+// Try and create a TrackInfo with a given codec MIME type, and optional extra
+// parameters from a content type (its MIME type and codecs are ignored).
+UniquePtr<TrackInfo>
+CreateTrackInfoWithMIMETypeAndContentTypeExtraParameters(
+ const nsACString& aCodecMIMEType,
+ const MediaContentType& aContentType);
+
+template <typename String>
+class StringListRange
+{
+ typedef typename String::char_type CharType;
+ typedef const CharType* Pointer;
+
+public:
+ // Iterator into range, trims items and skips empty items.
+ class Iterator
+ {
+ public:
+ bool operator!=(const Iterator& a) const
+ {
+ return mStart != a.mStart || mEnd != a.mEnd;
+ }
+ Iterator& operator++()
+ {
+ SearchItemAt(mComma + 1);
+ return *this;
+ }
+ typedef decltype(Substring(Pointer(), Pointer())) DereferencedType;
+ DereferencedType operator*()
+ {
+ return Substring(mStart, mEnd);
+ }
+ private:
+ friend class StringListRange;
+ Iterator(const CharType* aRangeStart, uint32_t aLength)
+ : mRangeEnd(aRangeStart + aLength)
+ {
+ SearchItemAt(aRangeStart);
+ }
+ void SearchItemAt(Pointer start)
+ {
+ // First, skip leading whitespace.
+ for (Pointer p = start; ; ++p) {
+ if (p >= mRangeEnd) {
+ mStart = mEnd = mComma = mRangeEnd;
+ return;
+ }
+ auto c = *p;
+ if (c == CharType(',')) {
+ // Comma -> Empty item -> Skip.
+ } else if (c != CharType(' ')) {
+ mStart = p;
+ break;
+ }
+ }
+ // Find comma, recording start of trailing space.
+ Pointer trailingWhitespace = nullptr;
+ for (Pointer p = mStart + 1; ; ++p) {
+ if (p >= mRangeEnd) {
+ mEnd = trailingWhitespace ? trailingWhitespace : p;
+ mComma = p;
+ return;
+ }
+ auto c = *p;
+ if (c == CharType(',')) {
+ mEnd = trailingWhitespace ? trailingWhitespace : p;
+ mComma = p;
+ return;
+ }
+ if (c == CharType(' ')) {
+ // Found a whitespace -> Record as trailing if not first one.
+ if (!trailingWhitespace) {
+ trailingWhitespace = p;
+ }
+ } else {
+ // Found a non-whitespace -> Reset trailing whitespace if needed.
+ if (trailingWhitespace) {
+ trailingWhitespace = nullptr;
+ }
+ }
+ }
+ }
+ const Pointer mRangeEnd;
+ Pointer mStart;
+ Pointer mEnd;
+ Pointer mComma;
+ };
+
+ explicit StringListRange(const String& aList) : mList(aList) {}
+ Iterator begin()
+ {
+ return Iterator(mList.Data(), mList.Length());
+ }
+ Iterator end()
+ {
+ return Iterator(mList.Data() + mList.Length(), 0);
+ }
+private:
+ const String& mList;
+};
+
+template <typename String>
+StringListRange<String>
+MakeStringListRange(const String& aList)
+{
+ return StringListRange<String>(aList);
+}
+
+template <typename ListString, typename ItemString>
+static bool
+StringListContains(const ListString& aList, const ItemString& aItem)
+{
+ for (const auto& listItem : MakeStringListRange(aList)) {
+ if (listItem.Equals(aItem)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // end namespace mozilla
+
+#endif