michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #if !defined(MediaQueue_h_) michael@0: #define MediaQueue_h_ michael@0: michael@0: #include "nsDeque.h" michael@0: #include "nsTArray.h" michael@0: #include "mozilla/ReentrantMonitor.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "MediaTaskQueue.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: // Thread and type safe wrapper around nsDeque. michael@0: template michael@0: class MediaQueueDeallocator : public nsDequeFunctor { michael@0: virtual void* operator() (void* anObject) { michael@0: delete static_cast(anObject); michael@0: return nullptr; michael@0: } michael@0: }; michael@0: michael@0: template class MediaQueue : private nsDeque { michael@0: public: michael@0: michael@0: MediaQueue() michael@0: : nsDeque(new MediaQueueDeallocator()), michael@0: mReentrantMonitor("mediaqueue"), michael@0: mEndOfStream(false) michael@0: {} michael@0: michael@0: ~MediaQueue() { michael@0: Reset(); michael@0: } michael@0: michael@0: inline int32_t GetSize() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: return nsDeque::GetSize(); michael@0: } michael@0: michael@0: inline void Push(T* aItem) { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: nsDeque::Push(aItem); michael@0: } michael@0: michael@0: inline void PushFront(T* aItem) { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: nsDeque::PushFront(aItem); michael@0: } michael@0: michael@0: inline T* PopFront() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: T* rv = static_cast(nsDeque::PopFront()); michael@0: if (rv) { michael@0: NotifyPopListeners(); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: inline T* Peek() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: return static_cast(nsDeque::Peek()); michael@0: } michael@0: michael@0: inline T* PeekFront() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: return static_cast(nsDeque::PeekFront()); michael@0: } michael@0: michael@0: inline void Empty() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: nsDeque::Empty(); michael@0: } michael@0: michael@0: inline void Erase() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: nsDeque::Erase(); michael@0: } michael@0: michael@0: void Reset() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: while (GetSize() > 0) { michael@0: T* x = PopFront(); michael@0: delete x; michael@0: } michael@0: mEndOfStream = false; michael@0: } michael@0: michael@0: bool AtEndOfStream() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: return GetSize() == 0 && mEndOfStream; michael@0: } michael@0: michael@0: // Returns true if the media queue has had its last item added to it. michael@0: // This happens when the media stream has been completely decoded. Note this michael@0: // does not mean that the corresponding stream has finished playback. michael@0: bool IsFinished() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: return mEndOfStream; michael@0: } michael@0: michael@0: // Informs the media queue that it won't be receiving any more items. michael@0: void Finish() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: mEndOfStream = true; michael@0: } michael@0: michael@0: // Returns the approximate number of microseconds of items in the queue. michael@0: int64_t Duration() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: if (GetSize() < 2) { michael@0: return 0; michael@0: } michael@0: T* last = Peek(); michael@0: T* first = PeekFront(); michael@0: return last->mTime - first->mTime; michael@0: } michael@0: michael@0: void LockedForEach(nsDequeFunctor& aFunctor) const { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: ForEach(aFunctor); michael@0: } michael@0: michael@0: // Extracts elements from the queue into aResult, in order. michael@0: // Elements whose start time is before aTime are ignored. michael@0: void GetElementsAfter(int64_t aTime, nsTArray* aResult) { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: if (!GetSize()) michael@0: return; michael@0: int32_t i; michael@0: for (i = GetSize() - 1; i > 0; --i) { michael@0: T* v = static_cast(ObjectAt(i)); michael@0: if (v->GetEndTime() < aTime) michael@0: break; michael@0: } michael@0: // Elements less than i have a end time before aTime. It's also possible michael@0: // that the element at i has a end time before aTime, but that's OK. michael@0: for (; i < GetSize(); ++i) { michael@0: aResult->AppendElement(static_cast(ObjectAt(i))); michael@0: } michael@0: } michael@0: michael@0: uint32_t FrameCount() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: uint32_t frames = 0; michael@0: for (int32_t i = 0; i < GetSize(); ++i) { michael@0: T* v = static_cast(ObjectAt(i)); michael@0: frames += v->mFrames; michael@0: } michael@0: return frames; michael@0: } michael@0: michael@0: void ClearListeners() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: mPopListeners.Clear(); michael@0: } michael@0: michael@0: void AddPopListener(nsIRunnable* aRunnable, MediaTaskQueue* aTaskQueue) { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: mPopListeners.AppendElement(Listener(aRunnable, aTaskQueue)); michael@0: } michael@0: michael@0: private: michael@0: mutable ReentrantMonitor mReentrantMonitor; michael@0: michael@0: struct Listener { michael@0: Listener(nsIRunnable* aRunnable, MediaTaskQueue* aTaskQueue) michael@0: : mRunnable(aRunnable) michael@0: , mTarget(aTaskQueue) michael@0: { michael@0: } michael@0: Listener(const Listener& aOther) michael@0: : mRunnable(aOther.mRunnable) michael@0: , mTarget(aOther.mTarget) michael@0: { michael@0: } michael@0: RefPtr mRunnable; michael@0: RefPtr mTarget; michael@0: }; michael@0: michael@0: nsTArray mPopListeners; michael@0: michael@0: void NotifyPopListeners() { michael@0: for (uint32_t i = 0; i < mPopListeners.Length(); i++) { michael@0: Listener& l = mPopListeners[i]; michael@0: l.mTarget->Dispatch(l.mRunnable); michael@0: } michael@0: } michael@0: michael@0: // True when we've decoded the last frame of data in the michael@0: // bitstream for which we're queueing frame data. michael@0: bool mEndOfStream; michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif