tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

VideoUtils.h (20728B)


      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 
      7 #ifndef VideoUtils_h
      8 #define VideoUtils_h
      9 
     10 #include "AudioSampleFormat.h"
     11 #include "MediaCodecsSupport.h"
     12 #include "MediaInfo.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"
     31 
     32 using mozilla::CheckedInt32;
     33 using mozilla::CheckedInt64;
     34 using mozilla::CheckedUint32;
     35 using mozilla::CheckedUint64;
     36 
     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.
     40 
     41 // This belongs in xpcom/monitor/Monitor.h, once we've made
     42 // mozilla::Monitor non-reentrant.
     43 namespace mozilla {
     44 
     45 class MediaContainerType;
     46 
     47 /**
     48 * ReentrantMonitorConditionallyEnter
     49 *
     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.
     53 */
     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();
     64    }
     65  }
     66  ~ReentrantMonitorConditionallyEnter(void) {
     67    if (mReentrantMonitor) {
     68      mReentrantMonitor->Exit();
     69    }
     70    MOZ_COUNT_DTOR(ReentrantMonitorConditionallyEnter);
     71  }
     72 
     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*);
     81 
     82  ReentrantMonitor* mReentrantMonitor;
     83 };
     84 
     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;
     95  }
     96 
     97 private:
     98  nsCOMPtr<nsIThread> mThread;
     99 };
    100 
    101 class MediaResource;
    102 
    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);
    111 
    112 double ToMicrosecondResolution(double aSeconds);
    113 // Converts from number of audio frames (aFrames) to microseconds, given
    114 // the specified audio rate (aRate).
    115 CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate);
    116 // Converts from number of audio frames (aFrames) TimeUnit, given
    117 // the specified audio rate (aRate).
    118 media::TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate);
    119 // Perform aValue * aMul / aDiv, reducing the possibility of overflow due to
    120 // aValue * aMul overflowing.
    121 CheckedInt64 SaferMultDiv(int64_t aValue, uint64_t aMul, uint64_t aDiv);
    122 
    123 // Converts from microseconds (aUsecs) to number of audio frames, given the
    124 // specified audio rate (aRate). Stores the result in aOutFrames. Returns
    125 // true if the operation succeeded, or false if there was an integer
    126 // overflow while calulating the conversion.
    127 CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate);
    128 
    129 // Format TimeUnit as number of frames at given rate.
    130 CheckedInt64 TimeUnitToFrames(const media::TimeUnit& aTime, uint32_t aRate);
    131 
    132 // Converts from seconds to microseconds. Returns failure if the resulting
    133 // integer is too big to fit in an int64_t.
    134 nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs);
    135 
    136 // Scales the display rect aDisplay by aspect ratio aAspectRatio.
    137 // Note that aDisplay must be validated by IsValidVideoRegion()
    138 // before being used!
    139 void ScaleDisplayByAspectRatio(gfx::IntSize& aDisplay, float aAspectRatio);
    140 
    141 // Downmix Stereo audio samples to Mono.
    142 // Input are the buffer contains stereo data and the number of frames.
    143 void DownmixStereoToMono(mozilla::AudioDataValue* aBuffer, uint32_t aFrames);
    144 
    145 // Decide the number of playback channels according to the
    146 // given AudioInfo and the prefs that are being set.
    147 uint32_t DecideAudioPlaybackChannels(const AudioInfo& info);
    148 
    149 // Decide the sample-rate to use for audio output according to the
    150 // given AudioInfo and the prefs that are being set.
    151 uint32_t DecideAudioPlaybackSampleRate(const AudioInfo& info,
    152                                       bool aShouldResistFingerprinting);
    153 
    154 bool IsDefaultPlaybackDeviceMono();
    155 
    156 bool IsVideoContentType(const nsCString& aContentType);
    157 
    158 // Returns true if it's safe to use aPicture as the picture to be
    159 // extracted inside a frame of size aFrame, and scaled up to and displayed
    160 // at a size of aDisplay. You should validate the frame, picture, and
    161 // display regions before using them to display video frames.
    162 bool IsValidVideoRegion(const gfx::IntSize& aFrame,
    163                        const gfx::IntRect& aPicture,
    164                        const gfx::IntSize& aDisplay);
    165 
    166 // Template to automatically set a variable to a value on scope exit.
    167 // Useful for unsetting flags, etc.
    168 template <typename T>
    169 class AutoSetOnScopeExit {
    170 public:
    171  AutoSetOnScopeExit(T& aVar, T aValue) : mVar(aVar), mValue(aValue) {}
    172  ~AutoSetOnScopeExit() { mVar = mValue; }
    173 
    174 private:
    175  T& mVar;
    176  const T mValue;
    177 };
    178 
    179 enum class MediaThreadType {
    180  SUPERVISOR,  // MediaFormatReader, RemoteDecoderManager, MediaDecodeTask and
    181               // others
    182  PLATFORM_DECODER,  // MediaDataDecoder
    183  PLATFORM_ENCODER,  // MediaDataEncoder
    184  WEBRTC_CALL_THREAD,
    185  WEBRTC_WORKER,
    186  MDSM,  // MediaDecoderStateMachine
    187 };
    188 // Returns the thread pool that is shared amongst all decoder state machines
    189 // for decoding streams.
    190 already_AddRefed<SharedThreadPool> GetMediaThreadPool(MediaThreadType aType);
    191 
    192 // Extracts the H.264/AVC profile and level from an H.264 codecs string.
    193 // H.264 codecs parameters have a type defined as avc1.PPCCLL, where
    194 // PP = profile_idc, CC = constraint_set flags, LL = level_idc.
    195 // See
    196 // http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
    197 // for more details.
    198 // Returns false on failure.
    199 enum class H264CodecStringStrictness {
    200  Lenient,  // Allow and returns invalid profile, constraint and level values.
    201            // It is necessary to accept those strings for Web Compat reasons.
    202  Strict    // Rejects invalid profile, constraint and level values.
    203 };
    204 bool ExtractH264CodecDetails(const nsAString& aCodecs, uint8_t& aProfile,
    205                             uint8_t& aConstraint, H264_LEVEL& aLevel,
    206                             H264CodecStringStrictness aStrictness);
    207 
    208 struct VideoColorSpace {
    209  // Default values are set according to
    210  // https://www.webmproject.org/vp9/mp4/#optional-fields
    211  // and https://aomediacodec.github.io/av1-isobmff/#codecsparam
    212  gfx::CICP::ColourPrimaries mPrimaries = gfx::CICP::CP_BT709;
    213  gfx::CICP::TransferCharacteristics mTransfer = gfx::CICP::TC_BT709;
    214  gfx::CICP::MatrixCoefficients mMatrix = gfx::CICP::MC_BT709;
    215  gfx::ColorRange mRange = gfx::ColorRange::LIMITED;
    216 
    217  bool operator==(const VideoColorSpace& aOther) const {
    218    return mPrimaries == aOther.mPrimaries && mTransfer == aOther.mTransfer &&
    219           mMatrix == aOther.mMatrix && mRange == aOther.mRange;
    220  }
    221  bool operator!=(const VideoColorSpace& aOther) const {
    222    return !(*this == aOther);
    223  }
    224 };
    225 
    226 // Extracts the VPX codecs parameter string.
    227 // See https://www.webmproject.org/vp9/mp4/#codecs-parameter-string
    228 // for more details.
    229 // Returns false on failure.
    230 bool ExtractVPXCodecDetails(const nsAString& aCodec, uint8_t& aProfile,
    231                            uint8_t& aLevel, uint8_t& aBitDepth);
    232 bool ExtractVPXCodecDetails(const nsAString& aCodec, uint8_t& aProfile,
    233                            uint8_t& aLevel, uint8_t& aBitDepth,
    234                            uint8_t& aChromaSubsampling,
    235                            mozilla::VideoColorSpace& aColorSpace);
    236 
    237 // Extracts AV1 codecs parameter string.
    238 // See https://aomediacodec.github.io/av1-isobmff/#codecsparam
    239 // Returns false if the codec is invalid.
    240 bool ExtractAV1CodecDetails(const nsAString& aCodec, uint8_t& aProfile,
    241                            uint8_t& aLevel, uint8_t& aTier, uint8_t& aBitDepth,
    242                            bool& aMonochrome, bool& aSubsamplingX,
    243                            bool& aSubsamplingY, uint8_t& aChromaSamplePosition,
    244                            mozilla::VideoColorSpace& aColorSpace);
    245 
    246 // Use a cryptographic quality PRNG to generate raw random bytes
    247 // and convert that to a base64 string.
    248 nsresult GenerateRandomName(nsCString& aOutSalt, uint32_t aLength);
    249 
    250 // This version returns a string suitable for use as a file or URL
    251 // path. This is based on code from nsExternalAppHandler::SetUpTempFile.
    252 nsresult GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength);
    253 
    254 already_AddRefed<TaskQueue> CreateMediaDecodeTaskQueue(const char* aName);
    255 
    256 // Iteratively invokes aWork until aCondition returns true, or aWork returns
    257 // false. Use this rather than a while loop to avoid bogarting the task queue.
    258 template <class Work, class Condition>
    259 RefPtr<GenericPromise> InvokeUntil(Work aWork, Condition aCondition) {
    260  RefPtr<GenericPromise::Private> p = new GenericPromise::Private(__func__);
    261 
    262  if (aCondition()) {
    263    p->Resolve(true, __func__);
    264  }
    265 
    266  struct Helper {
    267    static void Iteration(const RefPtr<GenericPromise::Private>& aPromise,
    268                          Work aLocalWork, Condition aLocalCondition) {
    269      if (!aLocalWork()) {
    270        aPromise->Reject(NS_ERROR_FAILURE, __func__);
    271      } else if (aLocalCondition()) {
    272        aPromise->Resolve(true, __func__);
    273      } else {
    274        nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    275            "InvokeUntil::Helper::Iteration",
    276            [aPromise, aLocalWork, aLocalCondition]() {
    277              Iteration(aPromise, aLocalWork, aLocalCondition);
    278            });
    279        AbstractThread::GetCurrent()->Dispatch(r.forget());
    280      }
    281    }
    282  };
    283 
    284  Helper::Iteration(p, aWork, aCondition);
    285  return p;
    286 }
    287 
    288 // Simple timer to run a runnable after a timeout.
    289 class SimpleTimer : public nsITimerCallback, public nsINamed {
    290 public:
    291  NS_DECL_ISUPPORTS
    292  NS_DECL_NSINAMED
    293 
    294  // Create a new timer to run aTask after aTimeoutMs milliseconds
    295  // on thread aTarget. If aTarget is null, task is run on the main thread.
    296  static already_AddRefed<SimpleTimer> Create(
    297      nsIRunnable* aTask, uint32_t aTimeoutMs,
    298      nsIEventTarget* aTarget = nullptr);
    299  void Cancel();
    300 
    301  NS_IMETHOD Notify(nsITimer* timer) override;
    302 
    303 private:
    304  virtual ~SimpleTimer() = default;
    305  nsresult Init(nsIRunnable* aTask, uint32_t aTimeoutMs,
    306                nsIEventTarget* aTarget);
    307 
    308  RefPtr<nsIRunnable> mTask;
    309  nsCOMPtr<nsITimer> mTimer;
    310 };
    311 
    312 void LogToBrowserConsole(const nsAString& aMsg);
    313 
    314 bool ParseMIMETypeString(const nsAString& aMIMEType,
    315                         nsString& aOutContainerType,
    316                         nsTArray<nsString>& aOutCodecs);
    317 
    318 bool ParseCodecsString(const nsAString& aCodecs,
    319                       nsTArray<nsString>& aOutCodecs);
    320 
    321 bool IsH264CodecString(const nsAString& aCodec);
    322 bool IsAllowedH264Codec(const nsAString& aCodec);
    323 
    324 bool IsH265CodecString(const nsAString& aCodec);
    325 
    326 bool IsAACCodecString(const nsAString& aCodec);
    327 
    328 bool IsVP8CodecString(const nsAString& aCodec);
    329 
    330 bool IsVP9CodecString(const nsAString& aCodec);
    331 
    332 bool IsAV1CodecString(const nsAString& aCodec);
    333 
    334 // Try and create a TrackInfo with a given codec MIME type.
    335 UniquePtr<TrackInfo> CreateTrackInfoWithMIMEType(
    336    const nsACString& aCodecMIMEType);
    337 
    338 // Try and create a TrackInfo with a given codec MIME type, and optional extra
    339 // parameters from a container type (its MIME type and codecs are ignored).
    340 UniquePtr<TrackInfo> CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(
    341    const nsACString& aCodecMIMEType, const MediaContainerType& aContainerType);
    342 
    343 namespace detail {
    344 
    345 // aString should start with aMajor + '/'.
    346 constexpr bool StartsWithMIMETypeMajor(const char* aString, const char* aMajor,
    347                                       size_t aMajorRemaining) {
    348  return (aMajorRemaining == 0 && *aString == '/') ||
    349         (*aString == *aMajor &&
    350          StartsWithMIMETypeMajor(aString + 1, aMajor + 1,
    351                                  aMajorRemaining - 1));
    352 }
    353 
    354 // aString should only contain [a-z0-9\-\.] and a final '\0'.
    355 constexpr bool EndsWithMIMESubtype(const char* aString, size_t aRemaining) {
    356  return aRemaining == 0 || (((*aString >= 'a' && *aString <= 'z') ||
    357                              (*aString >= '0' && *aString <= '9') ||
    358                              *aString == '-' || *aString == '.') &&
    359                             EndsWithMIMESubtype(aString + 1, aRemaining - 1));
    360 }
    361 
    362 // Simple MIME-type literal string checker with a given (major) type.
    363 // Only accepts "{aMajor}/[a-z0-9\-\.]+".
    364 template <size_t MajorLengthPlus1>
    365 constexpr bool IsMIMETypeWithMajor(const char* aString, size_t aLength,
    366                                   const char (&aMajor)[MajorLengthPlus1]) {
    367  return aLength > MajorLengthPlus1 &&  // Major + '/' + at least 1 char
    368         StartsWithMIMETypeMajor(aString, aMajor, MajorLengthPlus1 - 1) &&
    369         EndsWithMIMESubtype(aString + MajorLengthPlus1,
    370                             aLength - MajorLengthPlus1);
    371 }
    372 
    373 }  // namespace detail
    374 
    375 // Simple MIME-type string checker.
    376 // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
    377 // Add more if necessary.
    378 constexpr bool IsMediaMIMEType(const char* aString, size_t aLength) {
    379  return detail::IsMIMETypeWithMajor(aString, aLength, "application") ||
    380         detail::IsMIMETypeWithMajor(aString, aLength, "audio") ||
    381         detail::IsMIMETypeWithMajor(aString, aLength, "video");
    382 }
    383 
    384 // Simple MIME-type string literal checker.
    385 // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
    386 // Add more if necessary.
    387 template <size_t LengthPlus1>
    388 constexpr bool IsMediaMIMEType(const char (&aString)[LengthPlus1]) {
    389  return IsMediaMIMEType(aString, LengthPlus1 - 1);
    390 }
    391 
    392 // Simple MIME-type string checker.
    393 // Only accepts lowercase "{application,audio,video}/[a-z0-9\-\.]+".
    394 // Add more if necessary.
    395 inline bool IsMediaMIMEType(const nsACString& aString) {
    396  return IsMediaMIMEType(aString.Data(), aString.Length());
    397 }
    398 
    399 enum class StringListRangeEmptyItems {
    400  // Skip all empty items (empty string will process nothing)
    401  // E.g.: "a,,b" -> ["a", "b"], "" -> nothing
    402  Skip,
    403  // Process all, except if string is empty
    404  // E.g.: "a,,b" -> ["a", "", "b"], "" -> nothing
    405  ProcessEmptyItems,
    406  // Process all, including 1 empty item in an empty string
    407  // E.g.: "a,,b" -> ["a", "", "b"], "" -> [""]
    408  ProcessAll
    409 };
    410 
    411 template <typename String,
    412          StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip>
    413 class StringListRange {
    414  using CharType = typename String::char_type;
    415  using Pointer = const CharType*;
    416 
    417 public:
    418  // Iterator into range, trims items and optionally skips empty items.
    419  class Iterator {
    420   public:
    421    bool operator!=(const Iterator& a) const {
    422      return mStart != a.mStart || mEnd != a.mEnd;
    423    }
    424    Iterator& operator++() {
    425      SearchItemAt(mComma + 1);
    426      return *this;
    427    }
    428    // DereferencedType should be 'const nsDependent[C]String' pointing into
    429    // mList (which is 'const ns[C]String&').
    430    using DereferencedType = decltype(Substring(Pointer(), Pointer()));
    431    DereferencedType operator*() { return Substring(mStart, mEnd); }
    432 
    433   private:
    434    friend class StringListRange;
    435    Iterator(const CharType* aRangeStart, uint32_t aLength)
    436        : mRangeEnd(aRangeStart + aLength),
    437          mStart(nullptr),
    438          mEnd(nullptr),
    439          mComma(nullptr) {
    440      SearchItemAt(aRangeStart);
    441    }
    442    void SearchItemAt(Pointer start) {
    443      // First, skip leading whitespace.
    444      for (Pointer p = start;; ++p) {
    445        if (p >= mRangeEnd) {
    446          if (p > mRangeEnd +
    447                      (empties != StringListRangeEmptyItems::Skip ? 1 : 0)) {
    448            p = mRangeEnd +
    449                (empties != StringListRangeEmptyItems::Skip ? 1 : 0);
    450          }
    451          mStart = mEnd = mComma = p;
    452          return;
    453        }
    454        auto c = *p;
    455        if (c == CharType(',')) {
    456          // Comma -> Empty item -> Skip or process?
    457          if (empties != StringListRangeEmptyItems::Skip) {
    458            mStart = mEnd = mComma = p;
    459            return;
    460          }
    461        } else if (c != CharType(' ')) {
    462          mStart = p;
    463          break;
    464        }
    465      }
    466      // Find comma, recording start of trailing space.
    467      Pointer trailingWhitespace = nullptr;
    468      for (Pointer p = mStart + 1;; ++p) {
    469        if (p >= mRangeEnd) {
    470          mEnd = trailingWhitespace ? trailingWhitespace : p;
    471          mComma = p;
    472          return;
    473        }
    474        auto c = *p;
    475        if (c == CharType(',')) {
    476          mEnd = trailingWhitespace ? trailingWhitespace : p;
    477          mComma = p;
    478          return;
    479        }
    480        if (c == CharType(' ')) {
    481          // Found a whitespace -> Record as trailing if not first one.
    482          if (!trailingWhitespace) {
    483            trailingWhitespace = p;
    484          }
    485        } else {
    486          // Found a non-whitespace -> Reset trailing whitespace if needed.
    487          if (trailingWhitespace) {
    488            trailingWhitespace = nullptr;
    489          }
    490        }
    491      }
    492    }
    493    const Pointer mRangeEnd;
    494    Pointer mStart;
    495    Pointer mEnd;
    496    Pointer mComma;
    497  };
    498 
    499  explicit StringListRange(const String& aList) : mList(aList) {}
    500  Iterator begin() const {
    501    return Iterator(
    502        mList.Data() +
    503            ((empties == StringListRangeEmptyItems::ProcessEmptyItems &&
    504              mList.Length() == 0)
    505                 ? 1
    506                 : 0),
    507        mList.Length());
    508  }
    509  Iterator end() const {
    510    return Iterator(mList.Data() + mList.Length() +
    511                        (empties != StringListRangeEmptyItems::Skip ? 1 : 0),
    512                    0);
    513  }
    514 
    515 private:
    516  const String& mList;
    517 };
    518 
    519 template <StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip,
    520          typename String>
    521 StringListRange<String, empties> MakeStringListRange(const String& aList) {
    522  return StringListRange<String, empties>(aList);
    523 }
    524 
    525 template <StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip,
    526          typename ListString, typename ItemString>
    527 static bool StringListContains(const ListString& aList,
    528                               const ItemString& aItem) {
    529  for (const auto& listItem : MakeStringListRange<empties>(aList)) {
    530    if (listItem.Equals(aItem)) {
    531      return true;
    532    }
    533  }
    534  return false;
    535 }
    536 
    537 inline void AppendStringIfNotEmpty(nsACString& aDest, nsACString&& aSrc) {
    538  if (!aSrc.IsEmpty()) {
    539    aDest.Append("\n"_ns);
    540    aDest.Append(aSrc);
    541  }
    542 }
    543 
    544 // Returns true if we're running on a cellular connection; 2G, 3G, etc.
    545 // Main thread only.
    546 bool OnCellularConnection();
    547 
    548 inline gfx::YUVColorSpace DefaultColorSpace(const gfx::IntSize& aSize) {
    549  return aSize.height < 720 ? gfx::YUVColorSpace::BT601
    550                            : gfx::YUVColorSpace::BT709;
    551 }
    552 
    553 bool IsWaveMimetype(const nsACString& aMimeType);
    554 
    555 void DetermineResolutionForTelemetry(const MediaInfo& aInfo,
    556                                     nsCString& aResolutionOut);
    557 
    558 // True if given MediaCodecsSupported contains any hardware decoding support.
    559 bool ContainHardwareCodecsSupported(
    560    const media::MediaCodecsSupported& aSupport);
    561 
    562 }  // end namespace mozilla
    563 
    564 #endif