Line data Source code
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 : * We pass the principal through the MediaStreamGraph by wrapping it in a thread
61 : * safe nsMainThreadPtrHandle, since it cannot be used directly off the main
62 : * thread. We can compare two PrincipalHandles to each other on any thread, but
63 : * they can only be created and converted back to nsIPrincipal* on main thread.
64 : */
65 : typedef nsMainThreadPtrHandle<nsIPrincipal> PrincipalHandle;
66 :
67 0 : inline PrincipalHandle MakePrincipalHandle(nsIPrincipal* aPrincipal)
68 : {
69 : RefPtr<nsMainThreadPtrHolder<nsIPrincipal>> holder =
70 : new nsMainThreadPtrHolder<nsIPrincipal>(
71 0 : "MakePrincipalHandle::nsIPrincipal", aPrincipal);
72 0 : return PrincipalHandle(holder);
73 : }
74 :
75 : #define PRINCIPAL_HANDLE_NONE nullptr
76 :
77 0 : inline nsIPrincipal* GetPrincipalFromHandle(PrincipalHandle& aPrincipalHandle)
78 : {
79 0 : MOZ_ASSERT(NS_IsMainThread());
80 0 : return aPrincipalHandle.get();
81 : }
82 :
83 0 : inline bool PrincipalHandleMatches(PrincipalHandle& aPrincipalHandle,
84 : nsIPrincipal* aOther)
85 : {
86 0 : if (!aOther) {
87 0 : return false;
88 : }
89 :
90 0 : nsIPrincipal* principal = GetPrincipalFromHandle(aPrincipalHandle);
91 0 : if (!principal) {
92 0 : return false;
93 : }
94 :
95 : bool result;
96 0 : if (NS_FAILED(principal->Equals(aOther, &result))) {
97 0 : NS_ERROR("Principal check failed");
98 0 : return false;
99 : }
100 :
101 0 : return result;
102 : }
103 :
104 : /**
105 : * A MediaSegment is a chunk of media data sequential in time. Different
106 : * types of data have different subclasses of MediaSegment, all inheriting
107 : * from MediaSegmentBase.
108 : * All MediaSegment data is timed using StreamTime. The actual tick rate
109 : * is defined on a per-track basis. For some track types, this can be
110 : * a fixed constant for all tracks of that type (e.g. 1MHz for video).
111 : *
112 : * Each media segment defines a concept of "null media data" (e.g. silence
113 : * for audio or "no video frame" for video), which can be efficiently
114 : * represented. This is used for padding.
115 : */
116 : class MediaSegment {
117 : public:
118 0 : virtual ~MediaSegment()
119 0 : {
120 0 : MOZ_COUNT_DTOR(MediaSegment);
121 0 : }
122 :
123 : enum Type {
124 : AUDIO,
125 : VIDEO,
126 : TYPE_COUNT
127 : };
128 :
129 : /**
130 : * Gets the total duration of the segment.
131 : */
132 0 : StreamTime GetDuration() const { return mDuration; }
133 0 : Type GetType() const { return mType; }
134 :
135 : /**
136 : * Gets the last principal id that was appended to this segment.
137 : */
138 0 : PrincipalHandle GetLastPrincipalHandle() const { return mLastPrincipalHandle; }
139 : /**
140 : * Called by the MediaStreamGraph as it appends a chunk with a different
141 : * principal id than the current one.
142 : */
143 0 : void SetLastPrincipalHandle(const PrincipalHandle& aLastPrincipalHandle)
144 : {
145 0 : mLastPrincipalHandle = aLastPrincipalHandle;
146 0 : }
147 :
148 : /**
149 : * Create a MediaSegment of the same type.
150 : */
151 : virtual MediaSegment* CreateEmptyClone() const = 0;
152 : /**
153 : * Moves contents of aSource to the end of this segment.
154 : */
155 : virtual void AppendFrom(MediaSegment* aSource) = 0;
156 : /**
157 : * Append a slice of aSource to this segment.
158 : */
159 : virtual void AppendSlice(const MediaSegment& aSource,
160 : StreamTime aStart, StreamTime aEnd) = 0;
161 : /**
162 : * Replace all contents up to aDuration with null data.
163 : */
164 : virtual void ForgetUpTo(StreamTime aDuration) = 0;
165 : /**
166 : * Forget all data buffered after a given point
167 : */
168 : virtual void FlushAfter(StreamTime aNewEnd) = 0;
169 : /**
170 : * Insert aDuration of null data at the start of the segment.
171 : */
172 : virtual void InsertNullDataAtStart(StreamTime aDuration) = 0;
173 : /**
174 : * Insert aDuration of null data at the end of the segment.
175 : */
176 : virtual void AppendNullData(StreamTime aDuration) = 0;
177 : /**
178 : * Replace contents with disabled (silence/black) data of the same duration
179 : */
180 : virtual void ReplaceWithDisabled() = 0;
181 : /**
182 : * Replace contents with null data of the same duration
183 : */
184 : virtual void ReplaceWithNull() = 0;
185 : /**
186 : * Remove all contents, setting duration to 0.
187 : */
188 : virtual void Clear() = 0;
189 :
190 0 : virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
191 : {
192 0 : return 0;
193 : }
194 :
195 0 : virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
196 : {
197 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
198 : }
199 :
200 : protected:
201 0 : explicit MediaSegment(Type aType)
202 0 : : mDuration(0), mType(aType), mLastPrincipalHandle(PRINCIPAL_HANDLE_NONE)
203 : {
204 0 : MOZ_COUNT_CTOR(MediaSegment);
205 0 : }
206 :
207 : StreamTime mDuration; // total of mDurations of all chunks
208 : Type mType;
209 :
210 : // The latest principal handle that the MediaStreamGraph has processed for
211 : // this segment.
212 : PrincipalHandle mLastPrincipalHandle;
213 : };
214 :
215 : /**
216 : * C is the implementation class subclassed from MediaSegmentBase.
217 : * C must contain a Chunk class.
218 : */
219 0 : template <class C, class Chunk> class MediaSegmentBase : public MediaSegment {
220 : public:
221 0 : MediaSegment* CreateEmptyClone() const override
222 : {
223 0 : return new C();
224 : }
225 0 : void AppendFrom(MediaSegment* aSource) override
226 : {
227 0 : NS_ASSERTION(aSource->GetType() == C::StaticType(), "Wrong type");
228 0 : AppendFromInternal(static_cast<C*>(aSource));
229 0 : }
230 0 : void AppendFrom(C* aSource)
231 : {
232 0 : AppendFromInternal(aSource);
233 0 : }
234 0 : void AppendSlice(const MediaSegment& aSource,
235 : StreamTime aStart, StreamTime aEnd) override
236 : {
237 0 : NS_ASSERTION(aSource.GetType() == C::StaticType(), "Wrong type");
238 0 : AppendSliceInternal(static_cast<const C&>(aSource), aStart, aEnd);
239 0 : }
240 0 : void AppendSlice(const C& aOther, StreamTime aStart, StreamTime aEnd)
241 : {
242 0 : AppendSliceInternal(aOther, aStart, aEnd);
243 0 : }
244 : /**
245 : * Replace the first aDuration ticks with null media data, because the data
246 : * will not be required again.
247 : */
248 0 : void ForgetUpTo(StreamTime aDuration) override
249 : {
250 0 : if (mChunks.IsEmpty() || aDuration <= 0) {
251 0 : return;
252 : }
253 0 : if (mChunks[0].IsNull()) {
254 0 : StreamTime extraToForget = std::min(aDuration, mDuration) - mChunks[0].GetDuration();
255 0 : if (extraToForget > 0) {
256 0 : RemoveLeading(extraToForget, 1);
257 0 : mChunks[0].mDuration += extraToForget;
258 0 : mDuration += extraToForget;
259 : }
260 0 : return;
261 : }
262 0 : RemoveLeading(aDuration, 0);
263 0 : mChunks.InsertElementAt(0)->SetNull(aDuration);
264 0 : mDuration += aDuration;
265 : }
266 0 : void FlushAfter(StreamTime aNewEnd) override
267 : {
268 0 : if (mChunks.IsEmpty()) {
269 0 : return;
270 : }
271 :
272 0 : if (mChunks[0].IsNull()) {
273 0 : StreamTime extraToKeep = aNewEnd - mChunks[0].GetDuration();
274 0 : if (extraToKeep < 0) {
275 : // reduce the size of the Null, get rid of everthing else
276 0 : mChunks[0].SetNull(aNewEnd);
277 0 : extraToKeep = 0;
278 : }
279 0 : RemoveTrailing(extraToKeep, 1);
280 : } else {
281 0 : if (aNewEnd > mDuration) {
282 0 : NS_ASSERTION(aNewEnd <= mDuration, "can't add data in FlushAfter");
283 0 : return;
284 : }
285 0 : RemoveTrailing(aNewEnd, 0);
286 : }
287 0 : mDuration = aNewEnd;
288 : }
289 0 : void InsertNullDataAtStart(StreamTime aDuration) override
290 : {
291 0 : if (aDuration <= 0) {
292 0 : return;
293 : }
294 0 : if (!mChunks.IsEmpty() && mChunks[0].IsNull()) {
295 0 : mChunks[0].mDuration += aDuration;
296 : } else {
297 0 : mChunks.InsertElementAt(0)->SetNull(aDuration);
298 : }
299 : #ifdef MOZILLA_INTERNAL_API
300 0 : mChunks[0].mTimeStamp = mozilla::TimeStamp::Now();
301 : #endif
302 0 : mDuration += aDuration;
303 : }
304 0 : void AppendNullData(StreamTime aDuration) override
305 : {
306 0 : if (aDuration <= 0) {
307 0 : return;
308 : }
309 0 : if (!mChunks.IsEmpty() && mChunks[mChunks.Length() - 1].IsNull()) {
310 0 : mChunks[mChunks.Length() - 1].mDuration += aDuration;
311 : } else {
312 0 : mChunks.AppendElement()->SetNull(aDuration);
313 : }
314 0 : mDuration += aDuration;
315 : }
316 0 : void ReplaceWithDisabled() override
317 : {
318 0 : if (GetType() != AUDIO) {
319 0 : MOZ_CRASH("Disabling unknown segment type");
320 : }
321 0 : ReplaceWithNull();
322 0 : }
323 0 : void ReplaceWithNull() override
324 : {
325 0 : StreamTime duration = GetDuration();
326 0 : Clear();
327 0 : AppendNullData(duration);
328 0 : }
329 0 : void Clear() override
330 : {
331 0 : mDuration = 0;
332 0 : mChunks.Clear();
333 0 : }
334 :
335 : class ChunkIterator {
336 : public:
337 0 : explicit ChunkIterator(MediaSegmentBase<C, Chunk>& aSegment)
338 0 : : mSegment(aSegment), mIndex(0) {}
339 0 : bool IsEnded() { return mIndex >= mSegment.mChunks.Length(); }
340 0 : void Next() { ++mIndex; }
341 0 : Chunk& operator*() { return mSegment.mChunks[mIndex]; }
342 0 : Chunk* operator->() { return &mSegment.mChunks[mIndex]; }
343 : private:
344 : MediaSegmentBase<C, Chunk>& mSegment;
345 : uint32_t mIndex;
346 : };
347 : class ConstChunkIterator {
348 : public:
349 0 : explicit ConstChunkIterator(const MediaSegmentBase<C, Chunk>& aSegment)
350 0 : : mSegment(aSegment), mIndex(0) {}
351 0 : bool IsEnded() { return mIndex >= mSegment.mChunks.Length(); }
352 0 : void Next() { ++mIndex; }
353 0 : const Chunk& operator*() { return mSegment.mChunks[mIndex]; }
354 0 : const Chunk* operator->() { return &mSegment.mChunks[mIndex]; }
355 : private:
356 : const MediaSegmentBase<C, Chunk>& mSegment;
357 : uint32_t mIndex;
358 : };
359 :
360 : Chunk* FindChunkContaining(StreamTime aOffset, StreamTime* aStart = nullptr)
361 : {
362 : if (aOffset < 0) {
363 : return nullptr;
364 : }
365 : StreamTime offset = 0;
366 : for (uint32_t i = 0; i < mChunks.Length(); ++i) {
367 : Chunk& c = mChunks[i];
368 : StreamTime nextOffset = offset + c.GetDuration();
369 : if (aOffset < nextOffset) {
370 : if (aStart) {
371 : *aStart = offset;
372 : }
373 : return &c;
374 : }
375 : offset = nextOffset;
376 : }
377 : return nullptr;
378 : }
379 :
380 0 : void RemoveLeading(StreamTime aDuration)
381 : {
382 0 : RemoveLeading(aDuration, 0);
383 0 : }
384 :
385 : #ifdef MOZILLA_INTERNAL_API
386 0 : void GetStartTime(TimeStamp &aTime) {
387 0 : aTime = mChunks[0].mTimeStamp;
388 0 : }
389 : #endif
390 :
391 0 : size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
392 : {
393 0 : size_t amount = mChunks.ShallowSizeOfExcludingThis(aMallocSizeOf);
394 0 : for (size_t i = 0; i < mChunks.Length(); i++) {
395 0 : amount += mChunks[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
396 : }
397 0 : return amount;
398 : }
399 :
400 0 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
401 : {
402 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
403 : }
404 :
405 0 : Chunk* GetLastChunk()
406 : {
407 0 : if (mChunks.IsEmpty()) {
408 0 : return nullptr;
409 : }
410 0 : return &mChunks[mChunks.Length() - 1];
411 : }
412 :
413 : protected:
414 0 : explicit MediaSegmentBase(Type aType) : MediaSegment(aType) {}
415 :
416 : /**
417 : * Appends the contents of aSource to this segment, clearing aSource.
418 : */
419 0 : void AppendFromInternal(MediaSegmentBase<C, Chunk>* aSource)
420 : {
421 0 : MOZ_ASSERT(aSource->mDuration >= 0);
422 0 : mDuration += aSource->mDuration;
423 0 : aSource->mDuration = 0;
424 0 : if (!mChunks.IsEmpty() && !aSource->mChunks.IsEmpty() &&
425 0 : mChunks[mChunks.Length() - 1].CanCombineWithFollowing(aSource->mChunks[0])) {
426 0 : mChunks[mChunks.Length() - 1].mDuration += aSource->mChunks[0].mDuration;
427 0 : aSource->mChunks.RemoveElementAt(0);
428 : }
429 0 : mChunks.AppendElements(Move(aSource->mChunks));
430 0 : }
431 :
432 0 : void AppendSliceInternal(const MediaSegmentBase<C, Chunk>& aSource,
433 : StreamTime aStart, StreamTime aEnd)
434 : {
435 0 : MOZ_ASSERT(aStart <= aEnd, "Endpoints inverted");
436 0 : NS_ASSERTION(aStart >= 0 && aEnd <= aSource.mDuration, "Slice out of range");
437 0 : mDuration += aEnd - aStart;
438 0 : StreamTime offset = 0;
439 0 : for (uint32_t i = 0; i < aSource.mChunks.Length() && offset < aEnd; ++i) {
440 0 : const Chunk& c = aSource.mChunks[i];
441 0 : StreamTime start = std::max(aStart, offset);
442 0 : StreamTime nextOffset = offset + c.GetDuration();
443 0 : StreamTime end = std::min(aEnd, nextOffset);
444 0 : if (start < end) {
445 0 : mChunks.AppendElement(c)->SliceTo(start - offset, end - offset);
446 : }
447 0 : offset = nextOffset;
448 : }
449 0 : }
450 :
451 0 : Chunk* AppendChunk(StreamTime aDuration)
452 : {
453 0 : MOZ_ASSERT(aDuration >= 0);
454 0 : Chunk* c = mChunks.AppendElement();
455 0 : c->mDuration = aDuration;
456 0 : mDuration += aDuration;
457 0 : return c;
458 : }
459 :
460 0 : void RemoveLeading(StreamTime aDuration, uint32_t aStartIndex)
461 : {
462 0 : NS_ASSERTION(aDuration >= 0, "Can't remove negative duration");
463 0 : StreamTime t = aDuration;
464 0 : uint32_t chunksToRemove = 0;
465 0 : for (uint32_t i = aStartIndex; i < mChunks.Length() && t > 0; ++i) {
466 0 : Chunk* c = &mChunks[i];
467 0 : if (c->GetDuration() > t) {
468 0 : c->SliceTo(t, c->GetDuration());
469 0 : t = 0;
470 0 : break;
471 : }
472 0 : t -= c->GetDuration();
473 0 : chunksToRemove = i + 1 - aStartIndex;
474 : }
475 0 : mChunks.RemoveElementsAt(aStartIndex, chunksToRemove);
476 0 : mDuration -= aDuration - t;
477 0 : }
478 :
479 0 : void RemoveTrailing(StreamTime aKeep, uint32_t aStartIndex)
480 : {
481 0 : NS_ASSERTION(aKeep >= 0, "Can't keep negative duration");
482 0 : StreamTime t = aKeep;
483 : uint32_t i;
484 0 : for (i = aStartIndex; i < mChunks.Length(); ++i) {
485 0 : Chunk* c = &mChunks[i];
486 0 : if (c->GetDuration() > t) {
487 0 : c->SliceTo(0, t);
488 0 : break;
489 : }
490 0 : t -= c->GetDuration();
491 0 : if (t == 0) {
492 0 : break;
493 : }
494 : }
495 0 : if (i+1 < mChunks.Length()) {
496 0 : mChunks.RemoveElementsAt(i+1, mChunks.Length() - (i+1));
497 : }
498 : // Caller must adjust mDuration
499 0 : }
500 :
501 : nsTArray<Chunk> mChunks;
502 : #ifdef MOZILLA_INTERNAL_API
503 : mozilla::TimeStamp mTimeStamp;
504 : #endif
505 : };
506 :
507 : } // namespace mozilla
508 :
509 : #endif /* MOZILLA_MEDIASEGMENT_H_ */
|