Bug 1772053 - Enable dynamic code disable mitigations only on Windows 10 1703+ r...
[gecko.git] / dom / media / MediaSegment.h
blob181a726c741104f538d9370db97220a81c267d49
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/. */
6 #ifndef MOZILLA_MEDIASEGMENT_H_
7 #define MOZILLA_MEDIASEGMENT_H_
9 #include "PrincipalHandle.h"
10 #include "nsTArray.h"
11 #ifdef MOZILLA_INTERNAL_API
12 # include "mozilla/TimeStamp.h"
13 #endif
14 #include <algorithm>
16 namespace mozilla {
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.
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;
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.
35 typedef int64_t TrackTicks;
36 const int64_t TRACK_TICKS_MAX = INT64_MAX >> TRACK_RATE_MAX_BITS;
38 /**
39 * We represent media times in 64-bit audio frame counts or ticks.
40 * All tracks in a MediaTrackGraph have the same rate.
42 typedef int64_t MediaTime;
43 const int64_t MEDIA_TIME_MAX = TRACK_TICKS_MAX;
45 /**
46 * Media time relative to the start of a MediaTrack.
48 typedef MediaTime TrackTime;
49 const TrackTime TRACK_TIME_MAX = MEDIA_TIME_MAX;
51 /**
52 * Media time relative to the start of the graph timeline.
54 typedef MediaTime GraphTime;
55 const GraphTime GRAPH_TIME_MAX = MEDIA_TIME_MAX;
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;
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;
75 /**
76 * The number of chunks allocated by default for a MediaSegment.
77 * Appending more chunks than this will cause further allocations.
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.
83 const size_t DEFAULT_SEGMENT_CAPACITY = 16;
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).
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.
97 class MediaSegment {
98 public:
99 MediaSegment(const MediaSegment&) = delete;
100 MediaSegment& operator=(const MediaSegment&) = delete;
102 MOZ_COUNTED_DTOR_VIRTUAL(MediaSegment)
104 enum Type { AUDIO, VIDEO, TYPE_COUNT };
107 * Gets the total duration of the segment.
109 TrackTime GetDuration() const { return mDuration; }
110 Type GetType() const { return mType; }
113 * Gets the last principal id that was appended to this segment.
115 const PrincipalHandle& GetLastPrincipalHandle() const {
116 return mLastPrincipalHandle;
119 * Called by the MediaTrackGraph as it appends a chunk with a different
120 * principal id than the current one.
122 void SetLastPrincipalHandle(PrincipalHandle aLastPrincipalHandle) {
123 mLastPrincipalHandle = std::forward<PrincipalHandle>(aLastPrincipalHandle);
127 * Returns true if all chunks in this segment are null.
129 virtual bool IsNull() const = 0;
132 * Returns true if this segment contains no chunks.
134 virtual bool IsEmpty() const = 0;
137 * Create a MediaSegment of the same type.
139 virtual MediaSegment* CreateEmptyClone() const = 0;
141 * Moves contents of aSource to the end of this segment.
143 virtual void AppendFrom(MediaSegment* aSource) = 0;
145 * Append a slice of aSource to this segment.
147 virtual void AppendSlice(const MediaSegment& aSource, TrackTime aStart,
148 TrackTime aEnd) = 0;
150 * Replace all contents up to aDuration with null data.
152 virtual void ForgetUpTo(TrackTime aDuration) = 0;
154 * Forget all data buffered after a given point
156 virtual void FlushAfter(TrackTime aNewEnd) = 0;
158 * Insert aDuration of null data at the start of the segment.
160 virtual void InsertNullDataAtStart(TrackTime aDuration) = 0;
162 * Insert aDuration of null data at the end of the segment.
164 virtual void AppendNullData(TrackTime aDuration) = 0;
166 * Replace contents with disabled (silence/black) data of the same duration
168 virtual void ReplaceWithDisabled() = 0;
170 * Replace contents with null data of the same duration
172 virtual void ReplaceWithNull() = 0;
174 * Remove all contents, setting duration to 0.
176 virtual void Clear() = 0;
178 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
179 return 0;
182 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
183 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
186 protected:
187 explicit MediaSegment(Type aType)
188 : mDuration(0),
189 mType(aType),
190 mLastPrincipalHandle(PRINCIPAL_HANDLE_NONE) {
191 MOZ_COUNT_CTOR(MediaSegment);
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);
201 TrackTime mDuration; // total of mDurations of all chunks
202 Type mType;
204 // The latest principal handle that the MediaTrackGraph has processed for
205 // this segment.
206 PrincipalHandle mLastPrincipalHandle;
210 * C is the implementation class subclassed from MediaSegmentBase.
211 * C must contain a Chunk class.
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;
223 return true;
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));
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);
237 void AppendSlice(const C& aOther, TrackTime aStart, TrackTime aEnd) {
238 AppendSliceInternal(aOther, aStart, aEnd);
241 * Replace the first aDuration ticks with null media data, because the data
242 * will not be required again.
244 void ForgetUpTo(TrackTime aDuration) override {
245 if (mChunks.IsEmpty() || aDuration <= 0) {
246 return;
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;
256 return;
258 RemoveLeading(aDuration, 0);
259 mChunks.InsertElementAt(0)->SetNull(aDuration);
260 mDuration += aDuration;
262 void FlushAfter(TrackTime aNewEnd) override {
263 if (mChunks.IsEmpty()) {
264 return;
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;
276 RemoveTrailing(extraToKeep, 1);
277 } else {
278 if (aNewEnd > mDuration) {
279 NS_ASSERTION(aNewEnd <= mDuration, "can't add data in FlushAfter");
280 return;
282 RemoveTrailing(aNewEnd, 0);
284 mDuration = aNewEnd;
286 void InsertNullDataAtStart(TrackTime aDuration) override {
287 if (aDuration <= 0) {
288 return;
290 if (!mChunks.IsEmpty() && mChunks[0].IsNull()) {
291 mChunks[0].mDuration += aDuration;
292 } else {
293 mChunks.InsertElementAt(0)->SetNull(aDuration);
295 mDuration += aDuration;
297 void AppendNullData(TrackTime aDuration) override {
298 if (aDuration <= 0) {
299 return;
301 if (!mChunks.IsEmpty() && mChunks[mChunks.Length() - 1].IsNull()) {
302 mChunks[mChunks.Length() - 1].mDuration += aDuration;
303 } else {
304 mChunks.AppendElement()->SetNull(aDuration);
306 mDuration += aDuration;
308 void ReplaceWithDisabled() override {
309 if (GetType() != AUDIO) {
310 MOZ_CRASH("Disabling unknown segment type");
312 ReplaceWithNull();
314 void ReplaceWithNull() override {
315 TrackTime duration = GetDuration();
316 Clear();
317 AppendNullData(duration);
319 void Clear() override {
320 mDuration = 0;
321 mChunks.ClearAndRetainStorage();
322 mChunks.SetCapacity(DEFAULT_SEGMENT_CAPACITY);
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]; }
334 private:
335 MediaSegmentBase<C, Chunk>& mSegment;
336 uint32_t mIndex;
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]; }
347 private:
348 const MediaSegmentBase<C, Chunk>& mSegment;
349 uint32_t mIndex;
352 void RemoveLeading(TrackTime aDuration) { RemoveLeading(aDuration, 0); }
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);
359 return amount;
362 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
363 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
366 Chunk* GetLastChunk() {
367 if (mChunks.IsEmpty()) {
368 return nullptr;
370 return &mChunks[mChunks.Length() - 1];
373 const Chunk* GetLastChunk() const {
374 if (mChunks.IsEmpty()) {
375 return nullptr;
377 return &mChunks[mChunks.Length() - 1];
380 protected:
381 explicit MediaSegmentBase(Type aType) : MediaSegment(aType), mChunks() {}
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");
393 * Appends the contents of aSource to this segment, clearing aSource.
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;
407 for (; offset < aSource->mChunks.Length(); ++offset) {
408 mChunks.AppendElement(std::move(aSource->mChunks[offset]));
411 aSource->mChunks.ClearAndRetainStorage();
412 MOZ_ASSERT(aSource->mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY,
413 "Capacity must be retained after appending from aSource");
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);
438 offset = nextOffset;
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;
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;
461 t -= c->GetDuration();
462 chunksToRemove = i + 1 - aStartIndex;
464 if (aStartIndex == 0 && chunksToRemove == mChunks.Length()) {
465 mChunks.ClearAndRetainStorage();
466 } else {
467 mChunks.RemoveElementsAt(aStartIndex, chunksToRemove);
469 mDuration -= aDuration - t;
471 MOZ_ASSERT(mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY,
472 "Capacity must be retained after removing chunks");
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;
485 t -= c->GetDuration();
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);
491 MOZ_ASSERT(mChunks.Capacity() >= DEFAULT_SEGMENT_CAPACITY,
492 "Capacity must be retained after removing chunks");
493 // Caller must adjust mDuration
496 AutoTArray<Chunk, DEFAULT_SEGMENT_CAPACITY> mChunks;
499 } // namespace mozilla
501 #endif /* MOZILLA_MEDIASEGMENT_H_ */