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