tor-browser

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

MediaQueue.h (11088B)


      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 #if !defined(MediaQueue_h_)
      7 #  define MediaQueue_h_
      8 
      9 #  include <type_traits>
     10 
     11 #  include "MediaEventSource.h"
     12 #  include "TimeUnits.h"
     13 #  include "mozilla/RecursiveMutex.h"
     14 #  include "mozilla/TaskQueue.h"
     15 #  include "nsDeque.h"
     16 
     17 namespace mozilla {
     18 
     19 extern LazyLogModule gMediaDecoderLog;
     20 
     21 #  define QLOG(msg, ...)                       \
     22    MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, \
     23            ("MediaQueue=%p " msg, this, ##__VA_ARGS__))
     24 
     25 class AudioData;
     26 class VideoData;
     27 class EncodedFrame;
     28 
     29 template <typename T>
     30 struct TimestampAdjustmentTrait {
     31  static const bool mValue = false;
     32 };
     33 
     34 template <>
     35 struct TimestampAdjustmentTrait<AudioData> {
     36  static const bool mValue = true;
     37 };
     38 
     39 template <>
     40 struct TimestampAdjustmentTrait<VideoData> {
     41  static const bool mValue = true;
     42 };
     43 
     44 template <typename T>
     45 struct NonTimestampAdjustmentTrait {
     46  static const bool mValue = !TimestampAdjustmentTrait<T>::mValue;
     47 };
     48 
     49 template <typename T>
     50 struct DurationTypeTrait {
     51  using type = media::TimeUnit;
     52 };
     53 
     54 template <>
     55 struct DurationTypeTrait<EncodedFrame> {
     56  using type = uint64_t;
     57 };
     58 
     59 template <class T>
     60 class MediaQueue : private nsRefPtrDeque<T> {
     61 public:
     62  explicit MediaQueue(bool aEnablePreciseDuration = false)
     63      : nsRefPtrDeque<T>(),
     64        mRecursiveMutex("mediaqueue"),
     65        mEndOfStream(false),
     66        mEnablePreciseDuration(aEnablePreciseDuration) {}
     67 
     68  ~MediaQueue() { Reset(); }
     69 
     70  inline size_t GetSize() const {
     71    RecursiveMutexAutoLock lock(mRecursiveMutex);
     72    return nsRefPtrDeque<T>::GetSize();
     73  }
     74 
     75  template <typename U,
     76            std::enable_if_t<TimestampAdjustmentTrait<U>::mValue, bool> = true>
     77  inline void AdjustTimeStampIfNeeded(U* aItem) {
     78    static_assert(std::is_same_v<U, AudioData> || std::is_same_v<U, VideoData>);
     79    if (mOffset != media::TimeUnit::Zero()) {
     80      const auto prev = aItem->mTime, prevEndTime = aItem->GetEndTime();
     81      aItem->mTime += mOffset;
     82      if (!aItem->mTime.IsValid()) {
     83        NS_WARNING("Reverting timestamp adjustment due to sample overflow!");
     84        aItem->mTime = prev;
     85      } else {
     86        QLOG("adjusted %s sample [%" PRId64 ",%" PRId64 "] -> [%" PRId64
     87             ",%" PRId64 "]",
     88             std::is_same_v<U, AudioData> ? "audio" : "video",
     89             prev.ToMicroseconds(), prevEndTime.ToMicroseconds(),
     90             aItem->mTime.ToMicroseconds(),
     91             aItem->GetEndTime().ToMicroseconds());
     92      }
     93    }
     94  }
     95 
     96  template <typename U, std::enable_if_t<NonTimestampAdjustmentTrait<U>::mValue,
     97                                         bool> = true>
     98  inline void AdjustTimeStampIfNeeded(U* aItem) {}
     99 
    100  enum class TimestampAdjustment {
    101    Enable,
    102    Disable,
    103  };
    104  inline void PushFront(
    105      T* aItem, TimestampAdjustment aIsEnabled = TimestampAdjustment::Enable) {
    106    RecursiveMutexAutoLock lock(mRecursiveMutex);
    107    if (aIsEnabled == TimestampAdjustment::Enable) {
    108      AdjustTimeStampIfNeeded(aItem);
    109    }
    110    nsRefPtrDeque<T>::PushFront(aItem);
    111    AddDurationToPreciseDuration(aItem);
    112  }
    113 
    114  inline void Push(T* aItem) {
    115    MOZ_DIAGNOSTIC_ASSERT(aItem);
    116    Push(do_AddRef(aItem));
    117  }
    118 
    119  inline void Push(already_AddRefed<T> aItem) {
    120    RecursiveMutexAutoLock lock(mRecursiveMutex);
    121    T* item = aItem.take();
    122 
    123    MOZ_DIAGNOSTIC_ASSERT(item);
    124    MOZ_DIAGNOSTIC_ASSERT(item->GetEndTime() >= item->mTime);
    125    AdjustTimeStampIfNeeded(item);
    126    nsRefPtrDeque<T>::Push(dont_AddRef(item));
    127    AddDurationToPreciseDuration(item);
    128    mPushEvent.Notify(RefPtr<T>(item));
    129 
    130    // Pushing new data after queue has ended means that the stream is active
    131    // again, so we should not mark it as ended.
    132    if (mEndOfStream) {
    133      mEndOfStream = false;
    134    }
    135  }
    136 
    137  inline already_AddRefed<T> PopFront() {
    138    RecursiveMutexAutoLock lock(mRecursiveMutex);
    139    RefPtr<T> rv = nsRefPtrDeque<T>::PopFront();
    140    if (rv) {
    141      MOZ_DIAGNOSTIC_ASSERT(rv->GetEndTime() >= rv->mTime);
    142      SubtractDurationFromPreciseDuration(rv);
    143      mPopFrontEvent.Notify(RefPtr<T>(rv));
    144    }
    145    return rv.forget();
    146  }
    147 
    148  inline already_AddRefed<T> PopBack() {
    149    RecursiveMutexAutoLock lock(mRecursiveMutex);
    150    RefPtr<T> rv = nsRefPtrDeque<T>::Pop();
    151    if (rv) {
    152      MOZ_DIAGNOSTIC_ASSERT(rv->GetEndTime() >= rv->mTime);
    153      SubtractDurationFromPreciseDuration(rv);
    154    }
    155    return rv.forget();
    156  }
    157 
    158  inline RefPtr<T> PeekFront() const {
    159    RecursiveMutexAutoLock lock(mRecursiveMutex);
    160    return nsRefPtrDeque<T>::PeekFront();
    161  }
    162 
    163  inline RefPtr<T> PeekBack() const {
    164    RecursiveMutexAutoLock lock(mRecursiveMutex);
    165    return nsRefPtrDeque<T>::Peek();
    166  }
    167 
    168  void Reset() {
    169    RecursiveMutexAutoLock lock(mRecursiveMutex);
    170    nsRefPtrDeque<T>::Erase();
    171    SetOffset(media::TimeUnit::Zero());
    172    mEndOfStream = false;
    173    ResetPreciseDuration();
    174  }
    175 
    176  bool AtEndOfStream() const {
    177    RecursiveMutexAutoLock lock(mRecursiveMutex);
    178    return GetSize() == 0 && mEndOfStream;
    179  }
    180 
    181  // Returns true if the media queue has had its last item added to it.
    182  // This happens when the media stream has been completely decoded. Note this
    183  // does not mean that the corresponding stream has finished playback.
    184  bool IsFinished() const {
    185    RecursiveMutexAutoLock lock(mRecursiveMutex);
    186    return mEndOfStream;
    187  }
    188 
    189  // Informs the media queue that it won't be receiving any more items.
    190  void Finish() {
    191    RecursiveMutexAutoLock lock(mRecursiveMutex);
    192    if (!mEndOfStream) {
    193      mEndOfStream = true;
    194      mFinishEvent.Notify();
    195    }
    196  }
    197 
    198  // Returns the approximate number of microseconds of items in the queue.
    199  int64_t Duration() const {
    200    RecursiveMutexAutoLock lock(mRecursiveMutex);
    201    if (GetSize() == 0) {
    202      return 0;
    203    }
    204    T* last = nsRefPtrDeque<T>::Peek();
    205    T* first = nsRefPtrDeque<T>::PeekFront();
    206    return (last->GetEndTime() - first->mTime).ToMicroseconds();
    207  }
    208 
    209  // Return a precise duration if the feature is enabled. Otherwise, return -1.
    210  int64_t PreciseDuration() const {
    211    RecursiveMutexAutoLock lock(mRecursiveMutex);
    212    return GetPreciseDuration();
    213  }
    214 
    215  void LockedForEach(nsDequeFunctor<T>& aFunctor) const {
    216    RecursiveMutexAutoLock lock(mRecursiveMutex);
    217    nsRefPtrDeque<T>::ForEach(aFunctor);
    218  }
    219 
    220  // Fill aResult with the elements which end later than the given time aTime.
    221  void GetElementsAfter(const media::TimeUnit& aTime,
    222                        nsTArray<RefPtr<T>>* aResult) {
    223    GetElementsAfterStrict(aTime.ToMicroseconds(), aResult);
    224  }
    225 
    226  void GetFirstElements(uint32_t aMaxElements, nsTArray<RefPtr<T>>* aResult) {
    227    RecursiveMutexAutoLock lock(mRecursiveMutex);
    228    for (size_t i = 0; i < aMaxElements && i < GetSize(); ++i) {
    229      *aResult->AppendElement() = nsRefPtrDeque<T>::ObjectAt(i);
    230    }
    231  }
    232 
    233  uint32_t AudioFramesCount() {
    234    static_assert(std::is_same_v<T, AudioData>,
    235                  "Only usable with MediaQueue<AudioData>");
    236    RecursiveMutexAutoLock lock(mRecursiveMutex);
    237    uint32_t frames = 0;
    238    for (size_t i = 0; i < GetSize(); ++i) {
    239      T* v = nsRefPtrDeque<T>::ObjectAt(i);
    240      frames += v->Frames();
    241    }
    242    return frames;
    243  }
    244 
    245  bool SetOffset(const media::TimeUnit& aOffset) {
    246    if (!aOffset.IsValid()) {
    247      QLOG("Invalid offset!");
    248      return false;
    249    }
    250    RecursiveMutexAutoLock lock(mRecursiveMutex);
    251    mOffset = aOffset;
    252    QLOG("Set media queue offset %" PRId64, mOffset.ToMicroseconds());
    253    return true;
    254  }
    255 
    256  media::TimeUnit GetOffset() const {
    257    RecursiveMutexAutoLock lock(mRecursiveMutex);
    258    return mOffset;
    259  }
    260 
    261  MediaEventSource<RefPtr<T>>& PopFrontEvent() { return mPopFrontEvent; }
    262 
    263  MediaEventSource<RefPtr<T>>& PushEvent() { return mPushEvent; }
    264 
    265  MediaEventSource<void>& FinishEvent() { return mFinishEvent; }
    266 
    267 private:
    268  // Extracts elements from the queue into aResult, in order.
    269  // Elements whose end time is before or equal to aTime are ignored.
    270  void GetElementsAfterStrict(int64_t aTime, nsTArray<RefPtr<T>>* aResult) {
    271    RecursiveMutexAutoLock lock(mRecursiveMutex);
    272    if (GetSize() == 0) return;
    273    size_t i;
    274    for (i = GetSize() - 1; i > 0; --i) {
    275      T* v = nsRefPtrDeque<T>::ObjectAt(i);
    276      if (v->GetEndTime().ToMicroseconds() < aTime) break;
    277    }
    278    for (; i < GetSize(); ++i) {
    279      RefPtr<T> elem = nsRefPtrDeque<T>::ObjectAt(i);
    280      if (elem->GetEndTime().ToMicroseconds() > aTime) {
    281        aResult->AppendElement(elem);
    282      }
    283    }
    284  }
    285 
    286  mutable RecursiveMutex mRecursiveMutex MOZ_UNANNOTATED;
    287  MediaEventProducer<RefPtr<T>> mPopFrontEvent;
    288  MediaEventProducer<RefPtr<T>> mPushEvent;
    289  MediaEventProducer<void> mFinishEvent;
    290  // True when we've decoded the last frame of data in the
    291  // bitstream for which we're queueing frame data.
    292  bool mEndOfStream;
    293  // This offset will be added to any data pushed into the queue. We use it when
    294  // the media queue starts receiving looped data, which timestamp needs to be
    295  // modified.
    296  media::TimeUnit mOffset;
    297 
    298  inline void AddDurationToPreciseDuration(T* aItem) {
    299    if (!mEnablePreciseDuration) {
    300      return;
    301    }
    302    if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
    303                                 media::TimeUnit> ||
    304                  std::is_same_v<typename DurationTypeTrait<T>::type,
    305                                 uint64_t>) {
    306      mPreciseDuration += aItem->mDuration;
    307    }
    308  }
    309 
    310  inline void SubtractDurationFromPreciseDuration(T* aItem) {
    311    if (!mEnablePreciseDuration) {
    312      return;
    313    }
    314    if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
    315                                 media::TimeUnit> ||
    316                  std::is_same_v<typename DurationTypeTrait<T>::type,
    317                                 uint64_t>) {
    318      mPreciseDuration -= aItem->mDuration;
    319    }
    320  }
    321 
    322  inline void ResetPreciseDuration() {
    323    if (!mEnablePreciseDuration) {
    324      return;
    325    }
    326    if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
    327                                 media::TimeUnit>) {
    328      mPreciseDuration = media::TimeUnit::Zero();
    329    } else if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
    330                                        uint64_t>) {
    331      mPreciseDuration = 0;
    332    }
    333  }
    334 
    335  inline int64_t GetPreciseDuration() const {
    336    if (mEnablePreciseDuration) {
    337      if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
    338                                   media::TimeUnit>) {
    339        return mPreciseDuration.ToMicroseconds();
    340      } else if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
    341                                          uint64_t>) {
    342        return mPreciseDuration;
    343      }
    344    }
    345    return -1;
    346  }
    347 
    348  typename DurationTypeTrait<T>::type mPreciseDuration;
    349  const bool mEnablePreciseDuration = false;
    350 };
    351 
    352 }  // namespace mozilla
    353 
    354 #  undef QLOG
    355 
    356 #endif