/* -*- 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 "mozilla/RecursiveMutex.h" #include "mozilla/TaskQueue.h" #include "nsDeque.h" #include "MediaEventSource.h" #include "TimeUnits.h" namespace mozilla { // Thread and type safe wrapper around nsDeque. template class MediaQueueDeallocator : public nsDequeFunctor { virtual void operator()(void* aObject) { RefPtr releaseMe = dont_AddRef(static_cast(aObject)); } }; template class MediaQueue : private nsDeque { public: MediaQueue() : nsDeque(new MediaQueueDeallocator()), mRecursiveMutex("mediaqueue"), mEndOfStream(false) {} ~MediaQueue() { Reset(); } inline size_t GetSize() const { RecursiveMutexAutoLock lock(mRecursiveMutex); return nsDeque::GetSize(); } inline void Push(T* aItem) { RecursiveMutexAutoLock lock(mRecursiveMutex); MOZ_ASSERT(!mEndOfStream); MOZ_ASSERT(aItem); NS_ADDREF(aItem); MOZ_ASSERT(aItem->GetEndTime() >= aItem->mTime); nsDeque::Push(aItem); mPushEvent.Notify(RefPtr(aItem)); } inline already_AddRefed PopFront() { RecursiveMutexAutoLock lock(mRecursiveMutex); RefPtr rv = dont_AddRef(static_cast(nsDeque::PopFront())); if (rv) { mPopEvent.Notify(rv); } return rv.forget(); } inline RefPtr PeekFront() const { RecursiveMutexAutoLock lock(mRecursiveMutex); return static_cast(nsDeque::PeekFront()); } void Reset() { RecursiveMutexAutoLock lock(mRecursiveMutex); while (GetSize() > 0) { RefPtr x = dont_AddRef(static_cast(nsDeque::PopFront())); } 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() { RecursiveMutexAutoLock lock(mRecursiveMutex); if (GetSize() == 0) { return 0; } T* last = static_cast(nsDeque::Peek()); T* first = static_cast(nsDeque::PeekFront()); return (last->GetEndTime() - first->mTime).ToMicroseconds(); } void LockedForEach(nsDequeFunctor& aFunctor) const { RecursiveMutexAutoLock lock(mRecursiveMutex); ForEach(aFunctor); } // Extracts elements from the queue into aResult, in order. // Elements whose start time is before aTime are ignored. void GetElementsAfter(int64_t aTime, nsTArray>* aResult) { RecursiveMutexAutoLock lock(mRecursiveMutex); if (GetSize() == 0) return; size_t i; for (i = GetSize() - 1; i > 0; --i) { T* v = static_cast(ObjectAt(i)); if (v->GetEndTime().ToMicroseconds() < aTime) break; } // Elements less than i have a end time before aTime. It's also possible // that the element at i has a end time before aTime, but that's OK. for (; i < GetSize(); ++i) { RefPtr elem = static_cast(ObjectAt(static_cast(i))); aResult->AppendElement(elem); } } void GetElementsAfter(const media::TimeUnit& aTime, nsTArray>* aResult) { GetElementsAfter(aTime.ToMicroseconds(), aResult); } void GetFirstElements(uint32_t aMaxElements, nsTArray>* aResult) { RecursiveMutexAutoLock lock(mRecursiveMutex); for (size_t i = 0; i < aMaxElements && i < GetSize(); ++i) { *aResult->AppendElement() = static_cast(ObjectAt(i)); } } uint32_t FrameCount() { RecursiveMutexAutoLock lock(mRecursiveMutex); uint32_t frames = 0; for (size_t i = 0; i < GetSize(); ++i) { T* v = static_cast(ObjectAt(i)); frames += v->mFrames; } return frames; } MediaEventSource>& PopEvent() { return mPopEvent; } MediaEventSource>& PushEvent() { return mPushEvent; } MediaEventSource& FinishEvent() { return mFinishEvent; } private: mutable RecursiveMutex mRecursiveMutex; MediaEventProducer> mPopEvent; MediaEventProducer> mPushEvent; MediaEventProducer mFinishEvent; // True when we've decoded the last frame of data in the // bitstream for which we're queueing frame data. bool mEndOfStream; }; } // namespace mozilla #endif