2010-04-02 07:03:07 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* 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/. */
|
2010-04-02 07:03:07 +04:00
|
|
|
|
2010-04-27 12:53:44 +04:00
|
|
|
#ifndef VideoUtils_h
|
|
|
|
#define VideoUtils_h
|
|
|
|
|
2017-06-20 18:56:55 +03:00
|
|
|
#include "AudioSampleFormat.h"
|
2016-10-07 09:39:32 +03:00
|
|
|
#include "MediaInfo.h"
|
2017-06-20 18:56:55 +03:00
|
|
|
#include "VideoLimits.h"
|
2017-06-09 21:45:30 +03:00
|
|
|
#include "mozilla/AbstractThread.h"
|
2013-05-30 00:43:41 +04:00
|
|
|
#include "mozilla/Attributes.h"
|
2012-05-14 23:50:20 +04:00
|
|
|
#include "mozilla/CheckedInt.h"
|
2015-07-16 21:52:43 +03:00
|
|
|
#include "mozilla/MozPromise.h"
|
|
|
|
#include "mozilla/ReentrantMonitor.h"
|
2015-10-18 08:24:48 +03:00
|
|
|
#include "mozilla/RefPtr.h"
|
2017-12-15 20:14:02 +03:00
|
|
|
#include "mozilla/SharedThreadPool.h"
|
2020-06-22 16:55:21 +03:00
|
|
|
#include "mozilla/TaskQueue.h"
|
2016-10-07 09:39:32 +03:00
|
|
|
#include "mozilla/UniquePtr.h"
|
2020-06-22 16:55:21 +03:00
|
|
|
#include "mozilla/gfx/Point.h" // for gfx::IntSize
|
|
|
|
#include "mozilla/gfx/Types.h"
|
2017-06-20 18:56:55 +03:00
|
|
|
#include "nsCOMPtr.h"
|
2017-07-26 21:18:20 +03:00
|
|
|
#include "nsINamed.h"
|
2014-06-11 02:53:21 +04:00
|
|
|
#include "nsIThread.h"
|
2017-06-20 18:56:55 +03:00
|
|
|
#include "nsITimer.h"
|
2012-03-28 04:04:20 +04:00
|
|
|
#include "nsThreadUtils.h"
|
2013-06-10 19:30:00 +04:00
|
|
|
#include "prtime.h"
|
2011-06-24 02:08:54 +04:00
|
|
|
|
2012-02-22 16:28:06 +04:00
|
|
|
using mozilla::CheckedInt32;
|
|
|
|
using mozilla::CheckedInt64;
|
|
|
|
using mozilla::CheckedUint32;
|
|
|
|
using mozilla::CheckedUint64;
|
|
|
|
|
2010-04-02 07:03:07 +04:00
|
|
|
// 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 {
|
|
|
|
|
2017-01-18 03:59:03 +03:00
|
|
|
class MediaContainerType;
|
2016-10-07 09:39:32 +03:00
|
|
|
|
2012-12-07 03:27:08 +04:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-04-12 07:20:09 +04:00
|
|
|
class MOZ_STACK_CLASS ReentrantMonitorConditionallyEnter {
|
2012-12-07 03:27:08 +04:00
|
|
|
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);
|
|
|
|
}
|
2018-11-19 16:25:37 +03:00
|
|
|
|
2012-12-07 03:27:08 +04:00
|
|
|
private:
|
|
|
|
// Restrict to constructor and destructor defined above.
|
|
|
|
ReentrantMonitorConditionallyEnter();
|
|
|
|
ReentrantMonitorConditionallyEnter(const ReentrantMonitorConditionallyEnter&);
|
|
|
|
ReentrantMonitorConditionallyEnter& operator=(
|
|
|
|
const ReentrantMonitorConditionallyEnter&);
|
2019-08-14 04:32:41 +03:00
|
|
|
static void* operator new(size_t) noexcept(true);
|
2012-12-07 03:27:08 +04:00
|
|
|
static void operator delete(void*);
|
|
|
|
|
|
|
|
ReentrantMonitor* mReentrantMonitor;
|
|
|
|
};
|
|
|
|
|
2012-03-28 04:04:20 +04:00
|
|
|
// Shuts down a thread asynchronously.
|
2016-04-26 03:23:21 +03:00
|
|
|
class ShutdownThreadEvent : public Runnable {
|
2012-03-28 04:04:20 +04:00
|
|
|
public:
|
2017-06-12 22:34:10 +03:00
|
|
|
explicit ShutdownThreadEvent(nsIThread* aThread)
|
|
|
|
: Runnable("ShutdownThreadEvent"), mThread(aThread) {}
|
2020-03-04 18:39:20 +03:00
|
|
|
~ShutdownThreadEvent() = default;
|
2015-03-21 19:28:04 +03:00
|
|
|
NS_IMETHOD Run() override {
|
2012-03-28 04:04:20 +04:00
|
|
|
mThread->Shutdown();
|
2012-07-30 18:20:58 +04:00
|
|
|
mThread = nullptr;
|
2012-03-28 04:04:20 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2018-11-19 16:25:37 +03:00
|
|
|
|
2012-03-28 04:04:20 +04:00
|
|
|
private:
|
|
|
|
nsCOMPtr<nsIThread> mThread;
|
|
|
|
};
|
|
|
|
|
2012-09-03 02:56:29 +04:00
|
|
|
class MediaResource;
|
2010-08-13 06:28:15 +04:00
|
|
|
|
2012-09-03 02:56:29 +04:00
|
|
|
// 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].
|
2015-05-18 09:15:47 +03:00
|
|
|
media::TimeIntervals GetEstimatedBufferedTimeRanges(
|
|
|
|
mozilla::MediaResource* aStream, int64_t aDurationUsecs);
|
2012-09-03 02:56:29 +04:00
|
|
|
|
2023-05-24 16:18:42 +03:00
|
|
|
double ToMicrosecondResolution(double aSeconds);
|
2011-09-27 07:31:18 +04:00
|
|
|
// Converts from number of audio frames (aFrames) to microseconds, given
|
2015-07-01 09:50:27 +03:00
|
|
|
// the specified audio rate (aRate).
|
2012-08-22 19:56:38 +04:00
|
|
|
CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate);
|
2015-07-01 09:50:27 +03:00
|
|
|
// Converts from number of audio frames (aFrames) TimeUnit, given
|
|
|
|
// the specified audio rate (aRate).
|
|
|
|
media::TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate);
|
2016-04-26 04:16:23 +03:00
|
|
|
// Perform aValue * aMul / aDiv, reducing the possibility of overflow due to
|
|
|
|
// aValue * aMul overflowing.
|
2018-03-27 12:27:08 +03:00
|
|
|
CheckedInt64 SaferMultDiv(int64_t aValue, uint64_t aMul, uint64_t aDiv);
|
2010-08-13 06:28:15 +04:00
|
|
|
|
2011-09-27 07:31:18 +04:00
|
|
|
// Converts from microseconds (aUsecs) to number of audio frames, given the
|
|
|
|
// specified audio rate (aRate). Stores the result in aOutFrames. Returns
|
2011-09-30 03:34:37 +04:00
|
|
|
// true if the operation succeeded, or false if there was an integer
|
2010-08-13 06:28:15 +04:00
|
|
|
// overflow while calulating the conversion.
|
2012-08-22 19:56:38 +04:00
|
|
|
CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate);
|
2011-04-14 02:12:23 +04:00
|
|
|
|
2015-06-18 17:45:05 +03:00
|
|
|
// Format TimeUnit as number of frames at given rate.
|
|
|
|
CheckedInt64 TimeUnitToFrames(const media::TimeUnit& aTime, uint32_t aRate);
|
|
|
|
|
2015-02-24 06:11:43 +03:00
|
|
|
// Converts milliseconds to seconds.
|
|
|
|
#define MS_TO_SECONDS(ms) ((double)(ms) / (PR_MSEC_PER_SEC))
|
|
|
|
|
2013-06-10 19:30:00 +04:00
|
|
|
// Converts seconds to milliseconds.
|
2015-02-24 06:11:43 +03:00
|
|
|
#define SECONDS_TO_MS(s) ((int)((s) * (PR_MSEC_PER_SEC)))
|
2013-06-10 19:30:00 +04:00
|
|
|
|
2014-04-01 07:39:04 +04:00
|
|
|
// 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);
|
|
|
|
|
2011-06-24 02:08:54 +04:00
|
|
|
// Scales the display rect aDisplay by aspect ratio aAspectRatio.
|
2014-02-04 05:49:21 +04:00
|
|
|
// Note that aDisplay must be validated by IsValidVideoRegion()
|
2011-06-24 02:08:54 +04:00
|
|
|
// before being used!
|
2017-08-29 05:00:33 +03:00
|
|
|
void ScaleDisplayByAspectRatio(gfx::IntSize& aDisplay, float aAspectRatio);
|
2011-06-24 02:08:54 +04:00
|
|
|
|
2015-08-06 09:30:22 +03:00
|
|
|
// 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);
|
|
|
|
|
2018-03-15 19:28:14 +03:00
|
|
|
// Decide the number of playback channels according to the
|
|
|
|
// given AudioInfo and the prefs that are being set.
|
|
|
|
uint32_t DecideAudioPlaybackChannels(const AudioInfo& info);
|
|
|
|
|
2020-09-15 12:00:33 +03:00
|
|
|
// Decide the sample-rate to use for audio output according to the
|
|
|
|
// given AudioInfo and the prefs that are being set.
|
2023-02-15 13:55:31 +03:00
|
|
|
uint32_t DecideAudioPlaybackSampleRate(const AudioInfo& info,
|
|
|
|
bool aShouldResistFingerprinting);
|
2020-09-15 12:00:33 +03:00
|
|
|
|
2018-04-09 13:07:58 +03:00
|
|
|
bool IsDefaultPlaybackDeviceMono();
|
|
|
|
|
2013-11-04 02:45:19 +04:00
|
|
|
bool IsVideoContentType(const nsCString& aContentType);
|
|
|
|
|
2014-02-04 05:49:21 +04:00
|
|
|
// 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.
|
2017-08-29 05:00:33 +03:00
|
|
|
bool IsValidVideoRegion(const gfx::IntSize& aFrame,
|
|
|
|
const gfx::IntRect& aPicture,
|
|
|
|
const gfx::IntSize& aDisplay);
|
2014-02-04 05:49:21 +04:00
|
|
|
|
2014-05-06 04:12:05 +04:00
|
|
|
// 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; }
|
2018-11-19 16:25:37 +03:00
|
|
|
|
2014-05-06 04:12:05 +04:00
|
|
|
private:
|
|
|
|
T& mVar;
|
|
|
|
const T mValue;
|
|
|
|
};
|
|
|
|
|
2015-05-07 07:01:43 +03:00
|
|
|
enum class MediaThreadType {
|
2020-10-26 18:13:37 +03:00
|
|
|
SUPERVISOR, // MediaFormatReader, RemoteDecoderManager, MediaDecodeTask and
|
2020-08-03 11:01:07 +03:00
|
|
|
// others
|
2017-12-15 20:39:33 +03:00
|
|
|
PLATFORM_DECODER, // MediaDataDecoder
|
2019-08-09 23:58:06 +03:00
|
|
|
PLATFORM_ENCODER, // MediaDataEncoder
|
2021-10-01 13:20:13 +03:00
|
|
|
WEBRTC_CALL_THREAD,
|
|
|
|
WEBRTC_WORKER,
|
2020-08-03 11:01:07 +03:00
|
|
|
MDSM, // MediaDecoderStateMachine
|
2015-05-07 07:01:43 +03:00
|
|
|
};
|
2014-06-18 09:07:02 +04:00
|
|
|
// Returns the thread pool that is shared amongst all decoder state machines
|
|
|
|
// for decoding streams.
|
2015-06-17 17:00:52 +03:00
|
|
|
already_AddRefed<SharedThreadPool> GetMediaThreadPool(MediaThreadType aType);
|
2014-06-18 09:07:02 +04:00
|
|
|
|
2014-08-28 07:32:34 +04:00
|
|
|
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, uint8_t& aProfile,
|
2018-06-20 03:23:05 +03:00
|
|
|
uint8_t& aConstraint, uint8_t& aLevel);
|
2018-11-19 16:25:37 +03:00
|
|
|
|
2017-10-13 08:55:05 +03:00
|
|
|
struct VideoColorSpace {
|
2022-04-11 05:38:03 +03:00
|
|
|
// Default values are set according to
|
2017-10-13 08:55:05 +03:00
|
|
|
// https://www.webmproject.org/vp9/mp4/#optional-fields
|
2022-04-11 05:38:03 +03:00
|
|
|
// and https://aomediacodec.github.io/av1-isobmff/#codecsparam
|
|
|
|
gfx::CICP::ColourPrimaries mPrimaries = gfx::CICP::CP_BT709;
|
|
|
|
gfx::CICP::TransferCharacteristics mTransfer = gfx::CICP::TC_BT709;
|
|
|
|
gfx::CICP::MatrixCoefficients mMatrix = gfx::CICP::MC_BT709;
|
|
|
|
gfx::ColorRange mRange = gfx::ColorRange::LIMITED;
|
|
|
|
|
|
|
|
bool operator==(const VideoColorSpace& aOther) const {
|
|
|
|
return mPrimaries == aOther.mPrimaries && mTransfer == aOther.mTransfer &&
|
|
|
|
mMatrix == aOther.mMatrix && mRange == aOther.mRange;
|
|
|
|
}
|
2022-04-11 05:38:04 +03:00
|
|
|
bool operator!=(const VideoColorSpace& aOther) const {
|
|
|
|
return !(*this == aOther);
|
|
|
|
}
|
2017-10-13 08:55:05 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
// Extracts the VPX codecs parameter string.
|
|
|
|
// See https://www.webmproject.org/vp9/mp4/#codecs-parameter-string
|
|
|
|
// for more details.
|
|
|
|
// Returns false on failure.
|
|
|
|
bool ExtractVPXCodecDetails(const nsAString& aCodec, uint8_t& aProfile,
|
|
|
|
uint8_t& aLevel, uint8_t& aBitDepth);
|
|
|
|
bool ExtractVPXCodecDetails(const nsAString& aCodec, uint8_t& aProfile,
|
|
|
|
uint8_t& aLevel, uint8_t& aBitDepth,
|
|
|
|
uint8_t& aChromaSubsampling,
|
2023-12-23 00:39:13 +03:00
|
|
|
mozilla::VideoColorSpace& aColorSpace);
|
2017-10-13 08:55:05 +03:00
|
|
|
|
2022-04-11 05:38:04 +03:00
|
|
|
// Extracts AV1 codecs parameter string.
|
|
|
|
// See https://aomediacodec.github.io/av1-isobmff/#codecsparam
|
|
|
|
// Returns false if the codec is invalid.
|
|
|
|
bool ExtractAV1CodecDetails(const nsAString& aCodec, uint8_t& aProfile,
|
|
|
|
uint8_t& aLevel, uint8_t& aTier, uint8_t& aBitDepth,
|
|
|
|
bool& aMonochrome, bool& aSubsamplingX,
|
|
|
|
bool& aSubsamplingY, uint8_t& aChromaSamplePosition,
|
2023-12-23 00:39:13 +03:00
|
|
|
mozilla::VideoColorSpace& aColorSpace);
|
2022-04-11 05:38:04 +03:00
|
|
|
|
2014-10-13 02:53:43 +04:00
|
|
|
// Use a cryptographic quality PRNG to generate raw random bytes
|
2015-03-03 17:51:05 +03:00
|
|
|
// 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
|
2014-10-13 02:53:43 +04:00
|
|
|
// path. This is based on code from nsExternalAppHandler::SetUpTempFile.
|
|
|
|
nsresult GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength);
|
|
|
|
|
2017-04-25 09:57:55 +03:00
|
|
|
already_AddRefed<TaskQueue> CreateMediaDecodeTaskQueue(const char* aName);
|
2014-12-09 22:34:00 +03:00
|
|
|
|
2015-06-30 02:49:47 +03:00
|
|
|
// 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>
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<GenericPromise> InvokeUntil(Work aWork, Condition aCondition) {
|
|
|
|
RefPtr<GenericPromise::Private> p = new GenericPromise::Private(__func__);
|
2015-06-30 02:49:47 +03:00
|
|
|
|
|
|
|
if (aCondition()) {
|
|
|
|
p->Resolve(true, __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Helper {
|
2016-11-15 01:15:53 +03:00
|
|
|
static void Iteration(const RefPtr<GenericPromise::Private>& aPromise,
|
|
|
|
Work aLocalWork, Condition aLocalCondition) {
|
2015-09-30 08:32:32 +03:00
|
|
|
if (!aLocalWork()) {
|
2015-06-30 02:49:47 +03:00
|
|
|
aPromise->Reject(NS_ERROR_FAILURE, __func__);
|
2015-09-30 08:32:32 +03:00
|
|
|
} else if (aLocalCondition()) {
|
2015-06-30 02:49:47 +03:00
|
|
|
aPromise->Resolve(true, __func__);
|
|
|
|
} else {
|
2017-06-12 22:34:10 +03:00
|
|
|
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
|
|
|
"InvokeUntil::Helper::Iteration",
|
|
|
|
[aPromise, aLocalWork, aLocalCondition]() {
|
|
|
|
Iteration(aPromise, aLocalWork, aLocalCondition);
|
|
|
|
});
|
2015-06-30 02:49:47 +03:00
|
|
|
AbstractThread::GetCurrent()->Dispatch(r.forget());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Helper::Iteration(p, aWork, aCondition);
|
2020-02-13 17:38:48 +03:00
|
|
|
return p;
|
2015-06-30 02:49:47 +03:00
|
|
|
}
|
|
|
|
|
2015-09-29 03:06:11 +03:00
|
|
|
// Simple timer to run a runnable after a timeout.
|
2017-07-26 21:18:20 +03:00
|
|
|
class SimpleTimer : public nsITimerCallback, public nsINamed {
|
2015-09-29 03:06:11 +03:00
|
|
|
public:
|
|
|
|
NS_DECL_ISUPPORTS
|
2017-07-26 21:18:20 +03:00
|
|
|
NS_DECL_NSINAMED
|
2015-09-29 03:06:11 +03:00
|
|
|
|
|
|
|
// 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,
|
2017-06-01 23:41:18 +03:00
|
|
|
nsIEventTarget* aTarget = nullptr);
|
2015-09-29 03:06:11 +03:00
|
|
|
void Cancel();
|
|
|
|
|
|
|
|
NS_IMETHOD Notify(nsITimer* timer) override;
|
|
|
|
|
|
|
|
private:
|
2020-03-04 18:39:20 +03:00
|
|
|
virtual ~SimpleTimer() = default;
|
2017-06-01 23:41:18 +03:00
|
|
|
nsresult Init(nsIRunnable* aTask, uint32_t aTimeoutMs,
|
|
|
|
nsIEventTarget* aTarget);
|
2015-09-29 03:06:11 +03:00
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<nsIRunnable> mTask;
|
2015-09-29 03:06:11 +03:00
|
|
|
nsCOMPtr<nsITimer> mTimer;
|
|
|
|
};
|
|
|
|
|
2015-09-29 03:06:14 +03:00
|
|
|
void LogToBrowserConsole(const nsAString& aMsg);
|
|
|
|
|
2016-07-01 04:36:57 +03:00
|
|
|
bool ParseMIMETypeString(const nsAString& aMIMEType,
|
|
|
|
nsString& aOutContainerType,
|
|
|
|
nsTArray<nsString>& aOutCodecs);
|
2015-10-05 05:03:48 +03:00
|
|
|
|
2016-07-01 04:36:57 +03:00
|
|
|
bool ParseCodecsString(const nsAString& aCodecs,
|
|
|
|
nsTArray<nsString>& aOutCodecs);
|
2015-10-05 05:03:48 +03:00
|
|
|
|
2016-07-01 04:36:57 +03:00
|
|
|
bool IsH264CodecString(const nsAString& aCodec);
|
2015-10-05 05:03:48 +03:00
|
|
|
|
2023-07-20 21:24:52 +03:00
|
|
|
bool IsH265CodecString(const nsAString& aCodec);
|
|
|
|
|
2015-10-06 06:52:58 +03:00
|
|
|
bool IsAACCodecString(const nsAString& aCodec);
|
|
|
|
|
2016-07-01 04:36:57 +03:00
|
|
|
bool IsVP8CodecString(const nsAString& aCodec);
|
|
|
|
|
|
|
|
bool IsVP9CodecString(const nsAString& aCodec);
|
|
|
|
|
2018-07-20 11:24:56 +03:00
|
|
|
bool IsAV1CodecString(const nsAString& aCodec);
|
|
|
|
|
2016-10-07 09:39:32 +03:00
|
|
|
// 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
|
2017-01-18 03:59:03 +03:00
|
|
|
// parameters from a container type (its MIME type and codecs are ignored).
|
|
|
|
UniquePtr<TrackInfo> CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(
|
|
|
|
const nsACString& aCodecMIMEType, const MediaContainerType& aContainerType);
|
2016-10-07 09:39:32 +03:00
|
|
|
|
2017-01-01 00:46:34 +03:00
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
// aString should start with aMajor + '/'.
|
|
|
|
constexpr bool StartsWithMIMETypeMajor(const char* aString, const char* aMajor,
|
|
|
|
size_t aMajorRemaining) {
|
2017-09-04 12:27:43 +03:00
|
|
|
return (aMajorRemaining == 0 && *aString == '/') ||
|
|
|
|
(*aString == *aMajor &&
|
|
|
|
StartsWithMIMETypeMajor(aString + 1, aMajor + 1,
|
|
|
|
aMajorRemaining - 1));
|
2017-01-01 00:46:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// aString should only contain [a-z0-9\-\.] and a final '\0'.
|
|
|
|
constexpr bool EndsWithMIMESubtype(const char* aString, size_t aRemaining) {
|
2017-09-04 12:27:43 +03:00
|
|
|
return aRemaining == 0 || (((*aString >= 'a' && *aString <= 'z') ||
|
|
|
|
(*aString >= '0' && *aString <= '9') ||
|
|
|
|
*aString == '-' || *aString == '.') &&
|
|
|
|
EndsWithMIMESubtype(aString + 1, aRemaining - 1));
|
2017-01-01 00:46:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Simple MIME-type literal string checker with a given (major) type.
|
|
|
|
// Only accepts "{aMajor}/[a-z0-9\-\.]+".
|
|
|
|
template <size_t MajorLengthPlus1>
|
|
|
|
constexpr bool IsMIMETypeWithMajor(const char* aString, size_t aLength,
|
|
|
|
const char (&aMajor)[MajorLengthPlus1]) {
|
2017-09-04 12:27:43 +03:00
|
|
|
return aLength > MajorLengthPlus1 && // Major + '/' + at least 1 char
|
|
|
|
StartsWithMIMETypeMajor(aString, aMajor, MajorLengthPlus1 - 1) &&
|
|
|
|
EndsWithMIMESubtype(aString + MajorLengthPlus1,
|
|
|
|
aLength - MajorLengthPlus1);
|
2017-01-01 00:46:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
// Simple MIME-type string checker.
|
|
|
|
// Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
|
|
|
|
// Add more if necessary.
|
|
|
|
constexpr bool IsMediaMIMEType(const char* aString, size_t aLength) {
|
2017-09-04 12:27:43 +03:00
|
|
|
return detail::IsMIMETypeWithMajor(aString, aLength, "application") ||
|
|
|
|
detail::IsMIMETypeWithMajor(aString, aLength, "audio") ||
|
|
|
|
detail::IsMIMETypeWithMajor(aString, aLength, "video");
|
2017-01-01 00:46:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Simple MIME-type string literal checker.
|
|
|
|
// Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
|
|
|
|
// Add more if necessary.
|
|
|
|
template <size_t LengthPlus1>
|
|
|
|
constexpr bool IsMediaMIMEType(const char (&aString)[LengthPlus1]) {
|
|
|
|
return IsMediaMIMEType(aString, LengthPlus1 - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simple MIME-type string checker.
|
|
|
|
// Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
|
|
|
|
// Add more if necessary.
|
|
|
|
inline bool IsMediaMIMEType(const nsACString& aString) {
|
|
|
|
return IsMediaMIMEType(aString.Data(), aString.Length());
|
|
|
|
}
|
|
|
|
|
2016-11-28 14:45:58 +03:00
|
|
|
enum class StringListRangeEmptyItems {
|
|
|
|
// Skip all empty items (empty string will process nothing)
|
|
|
|
// E.g.: "a,,b" -> ["a", "b"], "" -> nothing
|
|
|
|
Skip,
|
|
|
|
// Process all, except if string is empty
|
|
|
|
// E.g.: "a,,b" -> ["a", "", "b"], "" -> nothing
|
|
|
|
ProcessEmptyItems,
|
|
|
|
// Process all, including 1 empty item in an empty string
|
|
|
|
// E.g.: "a,,b" -> ["a", "", "b"], "" -> [""]
|
|
|
|
ProcessAll
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename String,
|
|
|
|
StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip>
|
2016-05-26 12:24:35 +03:00
|
|
|
class StringListRange {
|
2023-08-08 15:12:52 +03:00
|
|
|
using CharType = typename String::char_type;
|
|
|
|
using Pointer = const CharType*;
|
2016-05-26 12:24:35 +03:00
|
|
|
|
|
|
|
public:
|
2016-11-28 14:45:58 +03:00
|
|
|
// Iterator into range, trims items and optionally skips empty items.
|
2016-05-26 12:24:35 +03:00
|
|
|
class Iterator {
|
|
|
|
public:
|
|
|
|
bool operator!=(const Iterator& a) const {
|
|
|
|
return mStart != a.mStart || mEnd != a.mEnd;
|
|
|
|
}
|
|
|
|
Iterator& operator++() {
|
|
|
|
SearchItemAt(mComma + 1);
|
|
|
|
return *this;
|
|
|
|
}
|
2016-12-16 11:03:20 +03:00
|
|
|
// DereferencedType should be 'const nsDependent[C]String' pointing into
|
|
|
|
// mList (which is 'const ns[C]String&').
|
2023-08-08 15:12:52 +03:00
|
|
|
using DereferencedType = decltype(Substring(Pointer(), Pointer()));
|
2016-05-26 12:24:35 +03:00
|
|
|
DereferencedType operator*() { return Substring(mStart, mEnd); }
|
2018-11-19 16:25:37 +03:00
|
|
|
|
2016-05-26 12:24:35 +03:00
|
|
|
private:
|
|
|
|
friend class StringListRange;
|
|
|
|
Iterator(const CharType* aRangeStart, uint32_t aLength)
|
|
|
|
: mRangeEnd(aRangeStart + aLength),
|
2018-06-15 10:25:02 +03:00
|
|
|
mStart(nullptr),
|
|
|
|
mEnd(nullptr),
|
|
|
|
mComma(nullptr) {
|
2016-05-26 12:24:35 +03:00
|
|
|
SearchItemAt(aRangeStart);
|
|
|
|
}
|
|
|
|
void SearchItemAt(Pointer start) {
|
|
|
|
// First, skip leading whitespace.
|
|
|
|
for (Pointer p = start;; ++p) {
|
|
|
|
if (p >= mRangeEnd) {
|
2016-11-28 14:45:58 +03:00
|
|
|
if (p > mRangeEnd +
|
|
|
|
(empties != StringListRangeEmptyItems::Skip ? 1 : 0)) {
|
|
|
|
p = mRangeEnd +
|
|
|
|
(empties != StringListRangeEmptyItems::Skip ? 1 : 0);
|
|
|
|
}
|
|
|
|
mStart = mEnd = mComma = p;
|
2016-05-26 12:24:35 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto c = *p;
|
|
|
|
if (c == CharType(',')) {
|
2016-11-28 14:45:58 +03:00
|
|
|
// Comma -> Empty item -> Skip or process?
|
|
|
|
if (empties != StringListRangeEmptyItems::Skip) {
|
|
|
|
mStart = mEnd = mComma = p;
|
|
|
|
return;
|
|
|
|
}
|
2016-05-26 12:24:35 +03:00
|
|
|
} 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) {}
|
2016-12-16 11:03:20 +03:00
|
|
|
Iterator begin() const {
|
2017-09-04 12:27:43 +03:00
|
|
|
return Iterator(
|
|
|
|
mList.Data() +
|
|
|
|
((empties == StringListRangeEmptyItems::ProcessEmptyItems &&
|
|
|
|
mList.Length() == 0)
|
|
|
|
? 1
|
|
|
|
: 0),
|
|
|
|
mList.Length());
|
2016-05-26 12:24:35 +03:00
|
|
|
}
|
2016-12-16 11:03:20 +03:00
|
|
|
Iterator end() const {
|
2016-11-28 14:45:58 +03:00
|
|
|
return Iterator(mList.Data() + mList.Length() +
|
|
|
|
(empties != StringListRangeEmptyItems::Skip ? 1 : 0),
|
|
|
|
0);
|
2016-05-26 12:24:35 +03:00
|
|
|
}
|
2018-11-19 16:25:37 +03:00
|
|
|
|
2016-05-26 12:24:35 +03:00
|
|
|
private:
|
|
|
|
const String& mList;
|
|
|
|
};
|
|
|
|
|
2016-11-28 14:45:58 +03:00
|
|
|
template <StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip,
|
|
|
|
typename String>
|
2016-05-26 12:24:35 +03:00
|
|
|
StringListRange<String, empties> MakeStringListRange(const String& aList) {
|
2016-11-28 14:45:58 +03:00
|
|
|
return StringListRange<String, empties>(aList);
|
2016-05-26 12:24:35 +03:00
|
|
|
}
|
|
|
|
|
2016-11-28 14:45:58 +03:00
|
|
|
template <StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip,
|
|
|
|
typename ListString, typename ItemString>
|
2016-05-26 12:24:35 +03:00
|
|
|
static bool StringListContains(const ListString& aList,
|
|
|
|
const ItemString& aItem) {
|
2016-11-28 14:45:58 +03:00
|
|
|
for (const auto& listItem : MakeStringListRange<empties>(aList)) {
|
2016-05-26 12:24:35 +03:00
|
|
|
if (listItem.Equals(aItem)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-10-25 08:13:30 +03:00
|
|
|
inline void AppendStringIfNotEmpty(nsACString& aDest, nsACString&& aSrc) {
|
|
|
|
if (!aSrc.IsEmpty()) {
|
|
|
|
aDest.Append("\n"_ns);
|
|
|
|
aDest.Append(aSrc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-07 01:41:25 +03:00
|
|
|
// Returns true if we're running on a cellular connection; 2G, 3G, etc.
|
|
|
|
// Main thread only.
|
|
|
|
bool OnCellularConnection();
|
|
|
|
|
2019-07-26 11:45:39 +03:00
|
|
|
inline gfx::YUVColorSpace DefaultColorSpace(const gfx::IntSize& aSize) {
|
|
|
|
return aSize.height < 720 ? gfx::YUVColorSpace::BT601
|
|
|
|
: gfx::YUVColorSpace::BT709;
|
|
|
|
}
|
|
|
|
|
2013-11-04 02:45:19 +04:00
|
|
|
} // end namespace mozilla
|
|
|
|
|
2010-04-02 07:03:07 +04:00
|
|
|
#endif
|