summaryrefslogtreecommitdiffstats
path: root/dom/media/MediaQueue.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/MediaQueue.h')
-rw-r--r--dom/media/MediaQueue.h277
1 files changed, 277 insertions, 0 deletions
diff --git a/dom/media/MediaQueue.h b/dom/media/MediaQueue.h
new file mode 100644
index 0000000000..4609493339
--- /dev/null
+++ b/dom/media/MediaQueue.h
@@ -0,0 +1,277 @@
+/* -*- 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/. */
+#if !defined(MediaQueue_h_)
+# define MediaQueue_h_
+
+# include <type_traits>
+
+# include "mozilla/RecursiveMutex.h"
+# include "mozilla/TaskQueue.h"
+
+# include "nsDeque.h"
+# include "MediaEventSource.h"
+# include "TimeUnits.h"
+
+namespace mozilla {
+
+extern LazyLogModule gMediaDecoderLog;
+
+# define QLOG(msg, ...) \
+ MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, \
+ ("MediaQueue=%p " msg, this, ##__VA_ARGS__))
+
+class AudioData;
+class VideoData;
+
+template <typename T>
+struct TimestampAdjustmentTrait {
+ static const bool mValue = false;
+};
+
+template <>
+struct TimestampAdjustmentTrait<AudioData> {
+ static const bool mValue = true;
+};
+
+template <>
+struct TimestampAdjustmentTrait<VideoData> {
+ static const bool mValue = true;
+};
+
+template <typename T>
+struct NonTimestampAdjustmentTrait {
+ static const bool mValue = !TimestampAdjustmentTrait<T>::mValue;
+};
+
+template <class T>
+class MediaQueue : private nsRefPtrDeque<T> {
+ public:
+ MediaQueue()
+ : nsRefPtrDeque<T>(),
+ mRecursiveMutex("mediaqueue"),
+ mEndOfStream(false) {}
+
+ ~MediaQueue() { Reset(); }
+
+ inline size_t GetSize() const {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ return nsRefPtrDeque<T>::GetSize();
+ }
+
+ template <typename U,
+ std::enable_if_t<TimestampAdjustmentTrait<U>::mValue, bool> = true>
+ inline void AdjustTimeStampIfNeeded(U* aItem) {
+ static_assert(std::is_same_v<U, AudioData> || std::is_same_v<U, VideoData>);
+ if (mOffset != media::TimeUnit::Zero()) {
+ const auto prev = aItem->mTime, prevEndTime = aItem->GetEndTime();
+ aItem->mTime += mOffset;
+ if (!aItem->mTime.IsValid()) {
+ NS_WARNING("Reverting timestamp adjustment due to sample overflow!");
+ aItem->mTime = prev;
+ } else {
+ QLOG("adjusted %s sample [%" PRId64 ",%" PRId64 "] -> [%" PRId64
+ ",%" PRId64 "]",
+ std::is_same_v<U, AudioData> ? "audio" : "video",
+ prev.ToMicroseconds(), prevEndTime.ToMicroseconds(),
+ aItem->mTime.ToMicroseconds(),
+ aItem->GetEndTime().ToMicroseconds());
+ }
+ }
+ }
+
+ template <typename U, std::enable_if_t<NonTimestampAdjustmentTrait<U>::mValue,
+ bool> = true>
+ inline void AdjustTimeStampIfNeeded(U* aItem) {}
+
+ enum class TimestampAdjustment {
+ Enable,
+ Disable,
+ };
+ inline void PushFront(
+ T* aItem, TimestampAdjustment aIsEnabled = TimestampAdjustment::Enable) {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ if (aIsEnabled == TimestampAdjustment::Enable) {
+ AdjustTimeStampIfNeeded(aItem);
+ }
+ nsRefPtrDeque<T>::PushFront(aItem);
+ }
+
+ inline void Push(T* aItem) {
+ MOZ_DIAGNOSTIC_ASSERT(aItem);
+ Push(do_AddRef(aItem));
+ }
+
+ inline void Push(already_AddRefed<T> aItem) {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ T* item = aItem.take();
+
+ MOZ_DIAGNOSTIC_ASSERT(item);
+ MOZ_DIAGNOSTIC_ASSERT(item->GetEndTime() >= item->mTime);
+ AdjustTimeStampIfNeeded(item);
+ nsRefPtrDeque<T>::Push(dont_AddRef(item));
+ mPushEvent.Notify(RefPtr<T>(item));
+
+ // Pushing new data after queue has ended means that the stream is active
+ // again, so we should not mark it as ended.
+ if (mEndOfStream) {
+ mEndOfStream = false;
+ }
+ }
+
+ inline already_AddRefed<T> PopFront() {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ RefPtr<T> rv = nsRefPtrDeque<T>::PopFront();
+ if (rv) {
+ MOZ_DIAGNOSTIC_ASSERT(rv->GetEndTime() >= rv->mTime);
+ mPopFrontEvent.Notify(RefPtr<T>(rv));
+ }
+ return rv.forget();
+ }
+
+ inline already_AddRefed<T> PopBack() {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ return nsRefPtrDeque<T>::Pop();
+ }
+
+ inline RefPtr<T> PeekFront() const {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ return nsRefPtrDeque<T>::PeekFront();
+ }
+
+ inline RefPtr<T> PeekBack() const {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ return nsRefPtrDeque<T>::Peek();
+ }
+
+ void Reset() {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ nsRefPtrDeque<T>::Erase();
+ SetOffset(media::TimeUnit::Zero());
+ mEndOfStream = false;
+ }
+
+ bool AtEndOfStream() const {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ return GetSize() == 0 && mEndOfStream;
+ }
+
+ // Returns true if the media queue has had its last item added to it.
+ // This happens when the media stream has been completely decoded. Note this
+ // does not mean that the corresponding stream has finished playback.
+ bool IsFinished() const {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ return mEndOfStream;
+ }
+
+ // Informs the media queue that it won't be receiving any more items.
+ void Finish() {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ if (!mEndOfStream) {
+ mEndOfStream = true;
+ mFinishEvent.Notify();
+ }
+ }
+
+ // Returns the approximate number of microseconds of items in the queue.
+ int64_t Duration() const {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ if (GetSize() == 0) {
+ return 0;
+ }
+ T* last = nsRefPtrDeque<T>::Peek();
+ T* first = nsRefPtrDeque<T>::PeekFront();
+ return (last->GetEndTime() - first->mTime).ToMicroseconds();
+ }
+
+ void LockedForEach(nsDequeFunctor<T>& aFunctor) const {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ nsRefPtrDeque<T>::ForEach(aFunctor);
+ }
+
+ // Fill aResult with the elements which end later than the given time aTime.
+ void GetElementsAfter(const media::TimeUnit& aTime,
+ nsTArray<RefPtr<T>>* aResult) {
+ GetElementsAfterStrict(aTime.ToMicroseconds(), aResult);
+ }
+
+ void GetFirstElements(uint32_t aMaxElements, nsTArray<RefPtr<T>>* aResult) {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ for (size_t i = 0; i < aMaxElements && i < GetSize(); ++i) {
+ *aResult->AppendElement() = nsRefPtrDeque<T>::ObjectAt(i);
+ }
+ }
+
+ uint32_t AudioFramesCount() {
+ static_assert(std::is_same_v<T, AudioData>,
+ "Only usable with MediaQueue<AudioData>");
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ uint32_t frames = 0;
+ for (size_t i = 0; i < GetSize(); ++i) {
+ T* v = nsRefPtrDeque<T>::ObjectAt(i);
+ frames += v->Frames();
+ }
+ return frames;
+ }
+
+ bool SetOffset(const media::TimeUnit& aOffset) {
+ if (!aOffset.IsValid()) {
+ QLOG("Invalid offset!");
+ return false;
+ }
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ mOffset = aOffset;
+ QLOG("Set media queue offset %" PRId64, mOffset.ToMicroseconds());
+ return true;
+ }
+
+ media::TimeUnit GetOffset() const {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ return mOffset;
+ }
+
+ MediaEventSource<RefPtr<T>>& PopFrontEvent() { return mPopFrontEvent; }
+
+ MediaEventSource<RefPtr<T>>& PushEvent() { return mPushEvent; }
+
+ MediaEventSource<void>& FinishEvent() { return mFinishEvent; }
+
+ private:
+ // Extracts elements from the queue into aResult, in order.
+ // Elements whose end time is before or equal to aTime are ignored.
+ void GetElementsAfterStrict(int64_t aTime, nsTArray<RefPtr<T>>* aResult) {
+ RecursiveMutexAutoLock lock(mRecursiveMutex);
+ if (GetSize() == 0) return;
+ size_t i;
+ for (i = GetSize() - 1; i > 0; --i) {
+ T* v = nsRefPtrDeque<T>::ObjectAt(i);
+ if (v->GetEndTime().ToMicroseconds() < aTime) break;
+ }
+ for (; i < GetSize(); ++i) {
+ RefPtr<T> elem = nsRefPtrDeque<T>::ObjectAt(i);
+ if (elem->GetEndTime().ToMicroseconds() > aTime) {
+ aResult->AppendElement(elem);
+ }
+ }
+ }
+
+ mutable RecursiveMutex mRecursiveMutex MOZ_UNANNOTATED;
+ MediaEventProducer<RefPtr<T>> mPopFrontEvent;
+ MediaEventProducer<RefPtr<T>> mPushEvent;
+ MediaEventProducer<void> mFinishEvent;
+ // True when we've decoded the last frame of data in the
+ // bitstream for which we're queueing frame data.
+ bool mEndOfStream;
+ // This offset will be added to any data pushed into the queue. We use it when
+ // the media queue starts receiving looped data, which timestamp needs to be
+ // modified.
+ media::TimeUnit mOffset;
+};
+
+} // namespace mozilla
+
+# undef QLOG
+
+#endif