Bug 816664: wallpaper patch for negative delta times for video frames r=roc,derf
[gecko.git] / content / media / MediaSegment.h
blob2106c9723c1f7e4c375515091adad9c59d2cbd08
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 "nsTArray.h"
11 namespace mozilla {
13 /**
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),
29 "Out of range");
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));
38 /**
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;
46 /**
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.
58 class MediaSegment {
59 public:
60 virtual ~MediaSegment()
62 MOZ_COUNT_DTOR(MediaSegment);
65 enum Type {
66 AUDIO,
67 VIDEO,
68 TYPE_COUNT
71 /**
72 * Gets the total duration of the segment.
74 TrackTicks GetDuration() const { return mDuration; }
75 Type GetType() const { return mType; }
77 /**
78 * Create a MediaSegment of the same type.
80 virtual MediaSegment* CreateEmptyClone() const = 0;
81 /**
82 * Moves contents of aSource to the end of this segment.
84 virtual void AppendFrom(MediaSegment* aSource) = 0;
85 /**
86 * Append a slice of aSource to this segment.
88 virtual void AppendSlice(const MediaSegment& aSource,
89 TrackTicks aStart, TrackTicks aEnd) = 0;
90 /**
91 * Replace all contents up to aDuration with null data.
93 virtual void ForgetUpTo(TrackTicks aDuration) = 0;
94 /**
95 * Insert aDuration of null data at the start of the segment.
97 virtual void InsertNullDataAtStart(TrackTicks aDuration) = 0;
98 /**
99 * Insert aDuration of null data at the end of the segment.
101 virtual void AppendNullData(TrackTicks aDuration) = 0;
103 protected:
104 MediaSegment(Type aType) : mDuration(0), mType(aType)
106 MOZ_COUNT_CTOR(MediaSegment);
109 TrackTicks mDuration; // total of mDurations of all chunks
110 Type mType;
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 {
118 public:
119 virtual MediaSegment* CreateEmptyClone() const
121 C* s = new C();
122 s->InitFrom(*static_cast<const C*>(this));
123 return s;
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) {
156 return;
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;
165 return;
167 RemoveLeading(aDuration, 0);
168 mChunks.InsertElementAt(0)->SetNull(aDuration);
169 mDuration += aDuration;
171 virtual void InsertNullDataAtStart(TrackTicks aDuration)
173 if (aDuration <= 0) {
174 return;
176 if (!mChunks.IsEmpty() && mChunks[0].IsNull()) {
177 mChunks[0].mDuration += aDuration;
178 } else {
179 mChunks.InsertElementAt(0)->SetNull(aDuration);
181 mDuration += aDuration;
183 virtual void AppendNullData(TrackTicks aDuration)
185 if (aDuration <= 0) {
186 return;
188 if (!mChunks.IsEmpty() && mChunks[mChunks.Length() - 1].IsNull()) {
189 mChunks[mChunks.Length() - 1].mDuration += aDuration;
190 } else {
191 mChunks.AppendElement()->SetNull(aDuration);
193 mDuration += aDuration;
196 class ChunkIterator {
197 public:
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]; }
204 private:
205 MediaSegmentBase<C, Chunk>& mSegment;
206 uint32_t mIndex;
209 protected:
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);
243 if (start < end) {
244 mChunks.AppendElement(c)->SliceTo(start - offset, end - offset);
246 offset = nextOffset;
250 Chunk* AppendChunk(TrackTicks aDuration)
252 MOZ_ASSERT(aDuration >= 0);
253 Chunk* c = mChunks.AppendElement();
254 c->mDuration = aDuration;
255 mDuration += aDuration;
256 return c;
259 Chunk* FindChunkContaining(TrackTicks aOffset, TrackTicks* aStart = nullptr)
261 if (aOffset < 0) {
262 return 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) {
269 if (aStart) {
270 *aStart = offset;
272 return &c;
274 offset = nextOffset;
276 return nullptr;
279 Chunk* GetLastChunk()
281 if (mChunks.IsEmpty()) {
282 return nullptr;
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());
296 t = 0;
297 break;
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_ */