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