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_
14 * We represent media times in 64-bit fixed point. So 1 MediaTime is
15 * 1/(2^MEDIA_TIME_FRAC_BITS) seconds.
17 typedef int64_t MediaTime
;
18 const int64_t MEDIA_TIME_FRAC_BITS
= 20;
19 const int64_t MEDIA_TIME_MAX
= INT64_MAX
;
21 inline MediaTime
MillisecondsToMediaTime(int32_t aMS
)
23 return (MediaTime(aMS
) << MEDIA_TIME_FRAC_BITS
)/1000;
26 inline MediaTime
SecondsToMediaTime(double aS
)
28 NS_ASSERTION(aS
<= (MEDIA_TIME_MAX
>> MEDIA_TIME_FRAC_BITS
),
30 return MediaTime(aS
* (1 << MEDIA_TIME_FRAC_BITS
));
33 inline double MediaTimeToSeconds(MediaTime aTime
)
35 return aTime
*(1.0/(1 << MEDIA_TIME_FRAC_BITS
));
39 * A number of ticks at a rate determined by some underlying track (e.g.
40 * audio sample rate). We want to make sure that multiplying TrackTicks by
41 * 2^MEDIA_TIME_FRAC_BITS doesn't overflow, so we set its max accordingly.
43 typedef int64_t TrackTicks
;
44 const int64_t TRACK_TICKS_MAX
= INT64_MAX
>> MEDIA_TIME_FRAC_BITS
;
47 * A MediaSegment is a chunk of media data sequential in time. Different
48 * types of data have different subclasses of MediaSegment, all inheriting
49 * from MediaSegmentBase.
50 * All MediaSegment data is timed using TrackTicks. The actual tick rate
51 * is defined on a per-track basis. For some track types, this can be
52 * a fixed constant for all tracks of that type (e.g. 1MHz for video).
54 * Each media segment defines a concept of "null media data" (e.g. silence
55 * for audio or "no video frame" for video), which can be efficiently
56 * represented. This is used for padding.
60 virtual ~MediaSegment()
62 MOZ_COUNT_DTOR(MediaSegment
);
72 * Gets the total duration of the segment.
74 TrackTicks
GetDuration() const { return mDuration
; }
75 Type
GetType() const { return mType
; }
78 * Create a MediaSegment of the same type.
80 virtual MediaSegment
* CreateEmptyClone() const = 0;
82 * Moves contents of aSource to the end of this segment.
84 virtual void AppendFrom(MediaSegment
* aSource
) = 0;
86 * Append a slice of aSource to this segment.
88 virtual void AppendSlice(const MediaSegment
& aSource
,
89 TrackTicks aStart
, TrackTicks aEnd
) = 0;
91 * Replace all contents up to aDuration with null data.
93 virtual void ForgetUpTo(TrackTicks aDuration
) = 0;
95 * Insert aDuration of null data at the start of the segment.
97 virtual void InsertNullDataAtStart(TrackTicks aDuration
) = 0;
99 * Insert aDuration of null data at the end of the segment.
101 virtual void AppendNullData(TrackTicks aDuration
) = 0;
104 MediaSegment(Type aType
) : mDuration(0), mType(aType
)
106 MOZ_COUNT_CTOR(MediaSegment
);
109 TrackTicks mDuration
; // total of mDurations of all chunks
114 * C is the implementation class subclassed from MediaSegmentBase.
115 * C must contain a Chunk class.
117 template <class C
, class Chunk
> class MediaSegmentBase
: public MediaSegment
{
119 virtual MediaSegment
* CreateEmptyClone() const
122 s
->InitFrom(*static_cast<const C
*>(this));
125 virtual void AppendFrom(MediaSegment
* aSource
)
127 NS_ASSERTION(aSource
->GetType() == C::StaticType(), "Wrong type");
128 AppendFromInternal(static_cast<C
*>(aSource
));
130 void AppendFrom(C
* aSource
)
132 AppendFromInternal(aSource
);
134 virtual void AppendSlice(const MediaSegment
& aSource
,
135 TrackTicks aStart
, TrackTicks aEnd
)
137 NS_ASSERTION(aSource
.GetType() == C::StaticType(), "Wrong type");
138 AppendSliceInternal(static_cast<const C
&>(aSource
), aStart
, aEnd
);
140 void AppendSlice(const C
& aOther
, TrackTicks aStart
, TrackTicks aEnd
)
142 AppendSliceInternal(aOther
, aStart
, aEnd
);
144 void InitToSlice(const C
& aOther
, TrackTicks aStart
, TrackTicks aEnd
)
146 static_cast<C
*>(this)->InitFrom(aOther
);
147 AppendSliceInternal(aOther
, aStart
, aEnd
);
150 * Replace the first aDuration ticks with null media data, because the data
151 * will not be required again.
153 virtual void ForgetUpTo(TrackTicks aDuration
)
155 if (mChunks
.IsEmpty() || aDuration
<= 0) {
158 if (mChunks
[0].IsNull()) {
159 TrackTicks extraToForget
= NS_MIN(aDuration
, mDuration
) - mChunks
[0].GetDuration();
160 if (extraToForget
> 0) {
161 RemoveLeading(extraToForget
, 1);
162 mChunks
[0].mDuration
+= extraToForget
;
163 mDuration
+= extraToForget
;
167 RemoveLeading(aDuration
, 0);
168 mChunks
.InsertElementAt(0)->SetNull(aDuration
);
169 mDuration
+= aDuration
;
171 virtual void InsertNullDataAtStart(TrackTicks aDuration
)
173 if (aDuration
<= 0) {
176 if (!mChunks
.IsEmpty() && mChunks
[0].IsNull()) {
177 mChunks
[0].mDuration
+= aDuration
;
179 mChunks
.InsertElementAt(0)->SetNull(aDuration
);
181 mDuration
+= aDuration
;
183 virtual void AppendNullData(TrackTicks aDuration
)
185 if (aDuration
<= 0) {
188 if (!mChunks
.IsEmpty() && mChunks
[mChunks
.Length() - 1].IsNull()) {
189 mChunks
[mChunks
.Length() - 1].mDuration
+= aDuration
;
191 mChunks
.AppendElement()->SetNull(aDuration
);
193 mDuration
+= aDuration
;
196 class ChunkIterator
{
198 ChunkIterator(MediaSegmentBase
<C
, Chunk
>& aSegment
)
199 : mSegment(aSegment
), mIndex(0) {}
200 bool IsEnded() { return mIndex
>= mSegment
.mChunks
.Length(); }
201 void Next() { ++mIndex
; }
202 Chunk
& operator*() { return mSegment
.mChunks
[mIndex
]; }
203 Chunk
* operator->() { return &mSegment
.mChunks
[mIndex
]; }
205 MediaSegmentBase
<C
, Chunk
>& mSegment
;
210 MediaSegmentBase(Type aType
) : MediaSegment(aType
) {}
213 * Appends the contents of aSource to this segment, clearing aSource.
215 void AppendFromInternal(MediaSegmentBase
<C
, Chunk
>* aSource
)
217 static_cast<C
*>(this)->CheckCompatible(*static_cast<C
*>(aSource
));
218 MOZ_ASSERT(aSource
->mDuration
>= 0);
219 mDuration
+= aSource
->mDuration
;
220 aSource
->mDuration
= 0;
221 if (!mChunks
.IsEmpty() && !aSource
->mChunks
.IsEmpty() &&
222 mChunks
[mChunks
.Length() - 1].CanCombineWithFollowing(aSource
->mChunks
[0])) {
223 mChunks
[mChunks
.Length() - 1].mDuration
+= aSource
->mChunks
[0].mDuration
;
224 aSource
->mChunks
.RemoveElementAt(0);
226 mChunks
.MoveElementsFrom(aSource
->mChunks
);
229 void AppendSliceInternal(const MediaSegmentBase
<C
, Chunk
>& aSource
,
230 TrackTicks aStart
, TrackTicks aEnd
)
232 static_cast<C
*>(this)->CheckCompatible(static_cast<const C
&>(aSource
));
233 NS_ASSERTION(aStart
<= aEnd
, "Endpoints inverted");
234 NS_ASSERTION(aStart
>= 0 && aEnd
<= aSource
.mDuration
,
235 "Slice out of range");
236 mDuration
+= aEnd
- aStart
;
237 TrackTicks offset
= 0;
238 for (uint32_t i
= 0; i
< aSource
.mChunks
.Length() && offset
< aEnd
; ++i
) {
239 const Chunk
& c
= aSource
.mChunks
[i
];
240 TrackTicks start
= NS_MAX(aStart
, offset
);
241 TrackTicks nextOffset
= offset
+ c
.GetDuration();
242 TrackTicks end
= NS_MIN(aEnd
, nextOffset
);
244 mChunks
.AppendElement(c
)->SliceTo(start
- offset
, end
- offset
);
250 Chunk
* AppendChunk(TrackTicks aDuration
)
252 MOZ_ASSERT(aDuration
>= 0);
253 Chunk
* c
= mChunks
.AppendElement();
254 c
->mDuration
= aDuration
;
255 mDuration
+= aDuration
;
259 Chunk
* FindChunkContaining(TrackTicks aOffset
, TrackTicks
* aStart
= nullptr)
264 TrackTicks offset
= 0;
265 for (uint32_t i
= 0; i
< mChunks
.Length(); ++i
) {
266 Chunk
& c
= mChunks
[i
];
267 TrackTicks nextOffset
= offset
+ c
.GetDuration();
268 if (aOffset
< nextOffset
) {
279 Chunk
* GetLastChunk()
281 if (mChunks
.IsEmpty()) {
284 return &mChunks
[mChunks
.Length() - 1];
287 void RemoveLeading(TrackTicks aDuration
, uint32_t aStartIndex
)
289 NS_ASSERTION(aDuration
>= 0, "Can't remove negative duration");
290 TrackTicks t
= aDuration
;
291 uint32_t chunksToRemove
= 0;
292 for (uint32_t i
= aStartIndex
; i
< mChunks
.Length() && t
> 0; ++i
) {
293 Chunk
* c
= &mChunks
[i
];
294 if (c
->GetDuration() > t
) {
295 c
->SliceTo(t
, c
->GetDuration());
299 t
-= c
->GetDuration();
300 chunksToRemove
= i
+ 1 - aStartIndex
;
302 mChunks
.RemoveElementsAt(aStartIndex
, chunksToRemove
);
303 mDuration
-= aDuration
- t
;
306 nsTArray
<Chunk
> mChunks
;
311 #endif /* MOZILLA_MEDIASEGMENT_H_ */