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"
11 #ifdef MOZILLA_INTERNAL_API
12 # include "mozilla/TimeStamp.h"
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
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
;
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
35 typedef int64_t TrackTicks
;
36 const int64_t TRACK_TICKS_MAX
= INT64_MAX
>> TRACK_RATE_MAX_BITS
;
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
;
46 * Media time relative to the start of a MediaTrack.
48 typedef MediaTime TrackTime
;
49 const TrackTime TRACK_TIME_MAX
= MEDIA_TIME_MAX
;
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
,
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
,
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
;
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)
83 const size_t DEFAULT_SEGMENT_CAPACITY
= 16;
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.
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
,
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 {
182 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const {
183 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf
);
187 explicit MediaSegment(Type 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
204 // The latest principal handle that the MediaTrackGraph has processed for
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
{
216 bool IsNull() const override
{
217 for (typename
C::ConstChunkIterator
iter(*this); !iter
.IsEnded();
219 if (!iter
->IsNull()) {
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) {
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
;
258 RemoveLeading(aDuration
, 0);
259 mChunks
.InsertElementAt(0)->SetNull(aDuration
);
260 mDuration
+= aDuration
;
262 void FlushAfter(TrackTime aNewEnd
) override
{
263 if (mChunks
.IsEmpty()) {
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
);
276 RemoveTrailing(extraToKeep
, 1);
278 if (aNewEnd
> mDuration
) {
279 NS_ASSERTION(aNewEnd
<= mDuration
, "can't add data in FlushAfter");
282 RemoveTrailing(aNewEnd
, 0);
286 void InsertNullDataAtStart(TrackTime aDuration
) override
{
287 if (aDuration
<= 0) {
290 if (!mChunks
.IsEmpty() && mChunks
[0].IsNull()) {
291 mChunks
[0].mDuration
+= aDuration
;
293 mChunks
.InsertElementAt(0)->SetNull(aDuration
);
295 mDuration
+= aDuration
;
297 void AppendNullData(TrackTime aDuration
) override
{
298 if (aDuration
<= 0) {
301 if (!mChunks
.IsEmpty() && mChunks
[mChunks
.Length() - 1].IsNull()) {
302 mChunks
[mChunks
.Length() - 1].mDuration
+= aDuration
;
304 mChunks
.AppendElement()->SetNull(aDuration
);
306 mDuration
+= aDuration
;
308 void ReplaceWithDisabled() override
{
309 if (GetType() != AUDIO
) {
310 MOZ_CRASH("Disabling unknown segment type");
314 void ReplaceWithNull() override
{
315 TrackTime duration
= GetDuration();
317 AppendNullData(duration
);
319 void Clear() override
{
321 mChunks
.ClearAndRetainStorage();
322 mChunks
.SetCapacity(DEFAULT_SEGMENT_CAPACITY
);
325 class ChunkIterator
{
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
]; }
335 MediaSegmentBase
<C
, Chunk
>& mSegment
;
338 class ConstChunkIterator
{
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
]; }
348 const MediaSegmentBase
<C
, Chunk
>& mSegment
;
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
);
362 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const override
{
363 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf
);
366 Chunk
* GetLastChunk() {
367 if (mChunks
.IsEmpty()) {
370 return &mChunks
[mChunks
.Length() - 1];
373 const Chunk
* GetLastChunk() const {
374 if (mChunks
.IsEmpty()) {
377 return &mChunks
[mChunks
.Length() - 1];
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;
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
;
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
);
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
;
435 mChunks
.AppendElement(c
)->SliceTo(start
- offset
, end
- offset
);
442 Chunk
* AppendChunk(TrackTime aDuration
) {
443 MOZ_ASSERT(aDuration
>= 0);
444 Chunk
* c
= mChunks
.AppendElement();
445 c
->mDuration
= aDuration
;
446 mDuration
+= aDuration
;
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());
461 t
-= c
->GetDuration();
462 chunksToRemove
= i
+ 1 - aStartIndex
;
464 if (aStartIndex
== 0 && chunksToRemove
== mChunks
.Length()) {
465 mChunks
.ClearAndRetainStorage();
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");
479 for (i
= aStartIndex
; i
< mChunks
.Length() && t
; ++i
) {
480 Chunk
* c
= &mChunks
[i
];
481 if (c
->GetDuration() > t
) {
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_ */