MediaSegment.h (16410B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #ifndef MOZILLA_MEDIASEGMENT_H_ 7 #define MOZILLA_MEDIASEGMENT_H_ 8 9 #include "PrincipalHandle.h" 10 #include "nsTArray.h" 11 #ifdef MOZILLA_INTERNAL_API 12 # include "mozilla/TimeStamp.h" 13 #endif 14 #include <algorithm> 15 16 namespace mozilla { 17 18 /** 19 * Track or graph rate in Hz. Maximum 1 << TRACK_RATE_MAX_BITS Hz. This 20 * maximum avoids overflow in conversions between track rates and conversions 21 * from seconds. 22 */ 23 typedef int32_t TrackRate; 24 const int64_t TRACK_RATE_MAX_BITS = 20; 25 const TrackRate TRACK_RATE_MAX = 1 << TRACK_RATE_MAX_BITS; 26 27 /** 28 * A number of ticks at a rate determined by some underlying track (e.g., audio 29 * sample rate). We want to make sure that multiplying TrackTicks by a TrackRate 30 * doesn't overflow, so we set its max accordingly. 31 * TrackTime should be used instead when we're working with MediaTrackGraph's 32 * rate, but TrackTicks can be used outside MediaTracks when we have data at a 33 * different rate. 34 */ 35 typedef int64_t TrackTicks; 36 const int64_t TRACK_TICKS_MAX = INT64_MAX >> TRACK_RATE_MAX_BITS; 37 38 /** 39 * We represent media times in 64-bit audio frame counts or ticks. 40 * All tracks in a MediaTrackGraph have the same rate. 41 */ 42 typedef int64_t MediaTime; 43 const int64_t MEDIA_TIME_MAX = TRACK_TICKS_MAX; 44 45 /** 46 * Media time relative to the start of a MediaTrack. 47 */ 48 typedef MediaTime TrackTime; 49 const TrackTime TRACK_TIME_MAX = MEDIA_TIME_MAX; 50 51 /** 52 * Media time relative to the start of the graph timeline. 53 */ 54 typedef MediaTime GraphTime; 55 const GraphTime GRAPH_TIME_MAX = MEDIA_TIME_MAX; 56 57 /* Time conversion helper functions */ 58 inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate, 59 TrackRate aInRate, 60 TrackTicks aTicks) { 61 MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate"); 62 MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate"); 63 MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); 64 return (aTicks * aOutRate) / aInRate; 65 } 66 67 inline TrackTicks RateConvertTicksRoundUp(TrackRate aOutRate, TrackRate aInRate, 68 TrackTicks aTicks) { 69 MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate"); 70 MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate"); 71 MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); 72 return (aTicks * aOutRate + aInRate - 1) / aInRate; 73 } 74 75 /** 76 * The number of chunks allocated by default for a MediaSegment. 77 * Appending more chunks than this will cause further allocations. 78 * 79 * 16 is an arbitrary number intended to cover the most common cases in the 80 * MediaTrackGraph (1 with silence and 1-2 with data for a realtime track) 81 * with some margin. 82 */ 83 const size_t DEFAULT_SEGMENT_CAPACITY = 16; 84 85 /** 86 * A MediaSegment is a chunk of media data sequential in time. Different 87 * types of data have different subclasses of MediaSegment, all inheriting 88 * from MediaSegmentBase. 89 * All MediaSegment data is timed using TrackTime. The actual tick rate 90 * is defined on a per-track basis. For some track types, this can be 91 * a fixed constant for all tracks of that type (e.g. 1MHz for video). 92 * 93 * Each media segment defines a concept of "null media data" (e.g. silence 94 * for audio or "no video frame" for video), which can be efficiently 95 * represented. This is used for padding. 96 */ 97 class MediaSegment { 98 public: 99 MediaSegment(const MediaSegment&) = delete; 100 MediaSegment& operator=(const MediaSegment&) = delete; 101 102 MOZ_COUNTED_DTOR_VIRTUAL(MediaSegment) 103 104 enum Type { AUDIO, VIDEO, TYPE_COUNT }; 105 106 /** 107 * Gets the total duration of the segment. 108 */ 109 TrackTime GetDuration() const { return mDuration; } 110 Type GetType() const { return mType; } 111 112 /** 113 * Gets the last principal id that was appended to this segment. 114 */ 115 const PrincipalHandle& GetLastPrincipalHandle() const { 116 return mLastPrincipalHandle; 117 } 118 /** 119 * Called by the MediaTrackGraph as it appends a chunk with a different 120 * principal id than the current one. 121 */ 122 void SetLastPrincipalHandle(PrincipalHandle aLastPrincipalHandle) { 123 mLastPrincipalHandle = std::forward<PrincipalHandle>(aLastPrincipalHandle); 124 } 125 126 /** 127 * Returns true if all chunks in this segment are null. 128 */ 129 virtual bool IsNull() const = 0; 130 131 /** 132 * Returns true if this segment contains no chunks. 133 */ 134 virtual bool IsEmpty() const = 0; 135 136 /** 137 * Create a MediaSegment of the same type. 138 */ 139 virtual MediaSegment* CreateEmptyClone() const = 0; 140 /** 141 * Moves contents of aSource to the end of this segment. 142 */ 143 virtual void AppendFrom(MediaSegment* aSource) = 0; 144 /** 145 * Append a slice of aSource to this segment. 146 */ 147 virtual void AppendSlice(const MediaSegment& aSource, TrackTime aStart, 148 TrackTime aEnd) = 0; 149 /** 150 * Replace all contents up to aDuration with null data. 151 */ 152 virtual void ForgetUpTo(TrackTime aDuration) = 0; 153 /** 154 * Forget all data buffered after a given point 155 */ 156 virtual void FlushAfter(TrackTime aNewEnd) = 0; 157 /** 158 * Insert aDuration of null data at the start of the segment. 159 */ 160 virtual void InsertNullDataAtStart(TrackTime aDuration) = 0; 161 /** 162 * Insert aDuration of null data at the end of the segment. 163 */ 164 virtual void AppendNullData(TrackTime aDuration) = 0; 165 /** 166 * Replace contents with disabled (silence/black) data of the same duration 167 */ 168 virtual void ReplaceWithDisabled() = 0; 169 /** 170 * Replace contents with null data of the same duration 171 */ 172 virtual void ReplaceWithNull() = 0; 173 /** 174 * Remove all contents, setting duration to 0. 175 */ 176 virtual void Clear() = 0; 177 178 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { 179 return 0; 180 } 181 182 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { 183 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 184 } 185 186 protected: 187 explicit MediaSegment(Type aType) 188 : mDuration(0), 189 mType(aType), 190 mLastPrincipalHandle(PRINCIPAL_HANDLE_NONE) { 191 MOZ_COUNT_CTOR(MediaSegment); 192 } 193 194 MediaSegment(MediaSegment&& aSegment) 195 : mDuration(std::move(aSegment.mDuration)), 196 mType(std::move(aSegment.mType)), 197 mLastPrincipalHandle(std::move(aSegment.mLastPrincipalHandle)) { 198 MOZ_COUNT_CTOR(MediaSegment); 199 } 200 201 TrackTime mDuration; // total of mDurations of all chunks 202 Type mType; 203 204 // The latest principal handle that the MediaTrackGraph has processed for 205 // this segment. 206 PrincipalHandle mLastPrincipalHandle; 207 }; 208 209 /** 210 * C is the implementation class subclassed from MediaSegmentBase. 211 * C must contain a Chunk class. 212 */ 213 template <class C, class Chunk> 214 class MediaSegmentBase : public MediaSegment { 215 public: 216 bool IsNull() const override { 217 for (typename C::ConstChunkIterator iter(*this); !iter.IsEnded(); 218 iter.Next()) { 219 if (!iter->IsNull()) { 220 return false; 221 } 222 } 223 return true; 224 } 225 bool IsEmpty() const override { return mChunks.IsEmpty(); } 226 MediaSegment* CreateEmptyClone() const override { return new C(); } 227 void AppendFrom(MediaSegment* aSource) override { 228 NS_ASSERTION(aSource->GetType() == C::StaticType(), "Wrong type"); 229 AppendFromInternal(static_cast<C*>(aSource)); 230 } 231 void AppendFrom(C* aSource) { AppendFromInternal(aSource); } 232 void AppendSlice(const MediaSegment& aSource, TrackTime aStart, 233 TrackTime aEnd) override { 234 NS_ASSERTION(aSource.GetType() == C::StaticType(), "Wrong type"); 235 AppendSliceInternal(static_cast<const C&>(aSource), aStart, aEnd); 236 } 237 void AppendSlice(const C& aOther, TrackTime aStart, TrackTime aEnd) { 238 AppendSliceInternal(aOther, aStart, aEnd); 239 } 240 /** 241 * Replace the first aDuration ticks with null media data, because the data 242 * will not be required again. 243 */ 244 void ForgetUpTo(TrackTime aDuration) override { 245 if (mChunks.IsEmpty() || aDuration <= 0) { 246 return; 247 } 248 if (mChunks[0].IsNull()) { 249 TrackTime extraToForget = 250 std::min(aDuration, mDuration) - mChunks[0].GetDuration(); 251 if (extraToForget > 0) { 252 RemoveLeading(extraToForget, 1); 253 mChunks[0].mDuration += extraToForget; 254 mDuration += extraToForget; 255 } 256 return; 257 } 258 RemoveLeading(aDuration, 0); 259 mChunks.InsertElementAt(0)->SetNull(aDuration); 260 mDuration += aDuration; 261 } 262 void FlushAfter(TrackTime aNewEnd) override { 263 if (mChunks.IsEmpty()) { 264 return; 265 } 266 267 if (!aNewEnd) { 268 Clear(); 269 } else if (mChunks[0].IsNull()) { 270 TrackTime extraToKeep = aNewEnd - mChunks[0].GetDuration(); 271 if (extraToKeep < 0) { 272 // reduce the size of the Null, get rid of everthing else 273 mChunks[0].SetNull(aNewEnd); 274 extraToKeep = 0; 275 } 276 RemoveTrailing(extraToKeep, 1); 277 } else { 278 if (aNewEnd > mDuration) { 279 NS_ASSERTION(aNewEnd <= mDuration, "can't add data in FlushAfter"); 280 return; 281 } 282 RemoveTrailing(aNewEnd, 0); 283 } 284 mDuration = aNewEnd; 285 } 286 void InsertNullDataAtStart(TrackTime aDuration) override { 287 if (aDuration <= 0) { 288 return; 289 } 290 if (!mChunks.IsEmpty() && mChunks[0].IsNull()) { 291 mChunks[0].mDuration += aDuration; 292 } else { 293 mChunks.InsertElementAt(0)->SetNull(aDuration); 294 } 295 mDuration += aDuration; 296 } 297 void AppendNullData(TrackTime aDuration) override { 298 if (aDuration <= 0) { 299 return; 300 } 301 if (!mChunks.IsEmpty() && mChunks[mChunks.Length() - 1].IsNull()) { 302 mChunks[mChunks.Length() - 1].mDuration += aDuration; 303 } else { 304 mChunks.AppendElement()->SetNull(aDuration); 305 } 306 mDuration += aDuration; 307 } 308 void ReplaceWithDisabled() override { 309 if (GetType() != AUDIO) { 310 MOZ_CRASH("Disabling unknown segment type"); 311 } 312 ReplaceWithNull(); 313 } 314 void ReplaceWithNull() override { 315 TrackTime duration = GetDuration(); 316 Clear(); 317 AppendNullData(duration); 318 } 319 void Clear() override { 320 mDuration = 0; 321 mChunks.ClearAndRetainStorage(); 322 mChunks.SetCapacity(DEFAULT_SEGMENT_CAPACITY); 323 } 324 325 class ChunkIterator { 326 public: 327 explicit ChunkIterator(MediaSegmentBase<C, Chunk>& aSegment) 328 : mSegment(aSegment), mIndex(0) {} 329 bool IsEnded() { return mIndex >= mSegment.mChunks.Length(); } 330 void Next() { ++mIndex; } 331 Chunk& operator*() { return mSegment.mChunks[mIndex]; } 332 Chunk* operator->() { return &mSegment.mChunks[mIndex]; } 333 334 private: 335 MediaSegmentBase<C, Chunk>& mSegment; 336 uint32_t mIndex; 337 }; 338 class ConstChunkIterator { 339 public: 340 explicit ConstChunkIterator(const MediaSegmentBase<C, Chunk>& aSegment) 341 : mSegment(aSegment), mIndex(0) {} 342 bool IsEnded() { return mIndex >= mSegment.mChunks.Length(); } 343 void Next() { ++mIndex; } 344 const Chunk& operator*() { return mSegment.mChunks[mIndex]; } 345 const Chunk* operator->() { return &mSegment.mChunks[mIndex]; } 346 347 private: 348 const MediaSegmentBase<C, Chunk>& mSegment; 349 uint32_t mIndex; 350 }; 351 352 void RemoveLeading(TrackTime aDuration) { RemoveLeading(aDuration, 0); } 353 354 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override { 355 size_t amount = mChunks.ShallowSizeOfExcludingThis(aMallocSizeOf); 356 for (size_t i = 0; i < mChunks.Length(); i++) { 357 amount += mChunks[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf); 358 } 359 return amount; 360 } 361 362 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { 363 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 364 } 365 366 Chunk* GetLastChunk() { 367 if (mChunks.IsEmpty()) { 368 return nullptr; 369 } 370 return &mChunks[mChunks.Length() - 1]; 371 } 372 373 const Chunk* GetLastChunk() const { 374 if (mChunks.IsEmpty()) { 375 return nullptr; 376 } 377 return &mChunks[mChunks.Length() - 1]; 378 } 379 380 protected: 381 explicit MediaSegmentBase(Type aType) : MediaSegment(aType), mChunks() {} 382 383 MediaSegmentBase(MediaSegmentBase&& aSegment) 384 : MediaSegment(std::move(aSegment)), 385 mChunks(std::move(aSegment.mChunks)) { 386 MOZ_ASSERT(mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY, 387 "Capacity must be retained in self after swap"); 388 MOZ_ASSERT(aSegment.mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY, 389 "Capacity must be retained in other after swap"); 390 } 391 392 /** 393 * Appends the contents of aSource to this segment, clearing aSource. 394 */ 395 void AppendFromInternal(MediaSegmentBase<C, Chunk>* aSource) { 396 MOZ_ASSERT(aSource->mDuration >= 0); 397 mDuration += aSource->mDuration; 398 aSource->mDuration = 0; 399 size_t offset = 0; 400 if (!mChunks.IsEmpty() && !aSource->mChunks.IsEmpty() && 401 mChunks[mChunks.Length() - 1].CanCombineWithFollowing( 402 aSource->mChunks[0])) { 403 mChunks[mChunks.Length() - 1].mDuration += aSource->mChunks[0].mDuration; 404 offset = 1; 405 } 406 407 for (; offset < aSource->mChunks.Length(); ++offset) { 408 mChunks.AppendElement(std::move(aSource->mChunks[offset])); 409 } 410 411 aSource->mChunks.ClearAndRetainStorage(); 412 MOZ_ASSERT(aSource->mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY, 413 "Capacity must be retained after appending from aSource"); 414 } 415 416 void AppendSliceInternal(const MediaSegmentBase<C, Chunk>& aSource, 417 TrackTime aStart, TrackTime aEnd) { 418 MOZ_ASSERT(aStart <= aEnd, "Endpoints inverted"); 419 NS_ASSERTION(aStart >= 0 && aEnd <= aSource.mDuration, 420 "Slice out of range"); 421 mDuration += aEnd - aStart; 422 TrackTime offset = 0; 423 for (uint32_t i = 0; i < aSource.mChunks.Length() && offset < aEnd; ++i) { 424 const Chunk& c = aSource.mChunks[i]; 425 TrackTime start = std::max(aStart, offset); 426 TrackTime nextOffset = offset + c.GetDuration(); 427 TrackTime end = std::min(aEnd, nextOffset); 428 if (start < end) { 429 if (!mChunks.IsEmpty() && 430 mChunks[mChunks.Length() - 1].CanCombineWithFollowing(c)) { 431 MOZ_ASSERT(start - offset >= 0 && end - offset <= aSource.mDuration, 432 "Slice out of bounds"); 433 mChunks[mChunks.Length() - 1].mDuration += end - start; 434 } else { 435 mChunks.AppendElement(c)->SliceTo(start - offset, end - offset); 436 } 437 } 438 offset = nextOffset; 439 } 440 } 441 442 Chunk* AppendChunk(TrackTime aDuration) { 443 MOZ_ASSERT(aDuration >= 0); 444 Chunk* c = mChunks.AppendElement(); 445 c->mDuration = aDuration; 446 mDuration += aDuration; 447 return c; 448 } 449 450 void RemoveLeading(TrackTime aDuration, uint32_t aStartIndex) { 451 NS_ASSERTION(aDuration >= 0, "Can't remove negative duration"); 452 TrackTime t = aDuration; 453 uint32_t chunksToRemove = 0; 454 for (uint32_t i = aStartIndex; i < mChunks.Length() && t > 0; ++i) { 455 Chunk* c = &mChunks[i]; 456 if (c->GetDuration() > t) { 457 c->SliceTo(t, c->GetDuration()); 458 t = 0; 459 break; 460 } 461 t -= c->GetDuration(); 462 chunksToRemove = i + 1 - aStartIndex; 463 } 464 if (aStartIndex == 0 && chunksToRemove == mChunks.Length()) { 465 mChunks.ClearAndRetainStorage(); 466 } else { 467 mChunks.RemoveElementsAt(aStartIndex, chunksToRemove); 468 } 469 mDuration -= aDuration - t; 470 471 MOZ_ASSERT(mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY, 472 "Capacity must be retained after removing chunks"); 473 } 474 475 void RemoveTrailing(TrackTime aKeep, uint32_t aStartIndex) { 476 NS_ASSERTION(aKeep >= 0, "Can't keep negative duration"); 477 TrackTime t = aKeep; 478 uint32_t i; 479 for (i = aStartIndex; i < mChunks.Length() && t; ++i) { 480 Chunk* c = &mChunks[i]; 481 if (c->GetDuration() > t) { 482 c->SliceTo(0, t); 483 break; 484 } 485 t -= c->GetDuration(); 486 } 487 // At this point `i` is already advanced due to last check in the loop. 488 if (i < mChunks.Length()) { 489 mChunks.RemoveLastElements(mChunks.Length() - i); 490 } 491 MOZ_ASSERT(mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY, 492 "Capacity must be retained after removing chunks"); 493 // Caller must adjust mDuration 494 } 495 496 AutoTArray<Chunk, DEFAULT_SEGMENT_CAPACITY> mChunks; 497 }; 498 499 } // namespace mozilla 500 501 #endif /* MOZILLA_MEDIASEGMENT_H_ */