content/media/MediaSegment.h

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #ifndef MOZILLA_MEDIASEGMENT_H_
michael@0 7 #define MOZILLA_MEDIASEGMENT_H_
michael@0 8
michael@0 9 #include "nsTArray.h"
michael@0 10 #ifdef MOZILLA_INTERNAL_API
michael@0 11 #include "mozilla/TimeStamp.h"
michael@0 12 #endif
michael@0 13 #include <algorithm>
michael@0 14 #include "Latency.h"
michael@0 15
michael@0 16 namespace mozilla {
michael@0 17
michael@0 18 /**
michael@0 19 * We represent media times in 64-bit fixed point. So 1 MediaTime is
michael@0 20 * 1/(2^MEDIA_TIME_FRAC_BITS) seconds.
michael@0 21 */
michael@0 22 typedef int64_t MediaTime;
michael@0 23 const int64_t MEDIA_TIME_FRAC_BITS = 20;
michael@0 24 const int64_t MEDIA_TIME_MAX = INT64_MAX;
michael@0 25
michael@0 26 inline MediaTime MillisecondsToMediaTime(int32_t aMS)
michael@0 27 {
michael@0 28 return (MediaTime(aMS) << MEDIA_TIME_FRAC_BITS)/1000;
michael@0 29 }
michael@0 30
michael@0 31 inline MediaTime SecondsToMediaTime(double aS)
michael@0 32 {
michael@0 33 NS_ASSERTION(aS <= (MEDIA_TIME_MAX >> MEDIA_TIME_FRAC_BITS),
michael@0 34 "Out of range");
michael@0 35 return MediaTime(aS * (1 << MEDIA_TIME_FRAC_BITS));
michael@0 36 }
michael@0 37
michael@0 38 inline double MediaTimeToSeconds(MediaTime aTime)
michael@0 39 {
michael@0 40 return aTime*(1.0/(1 << MEDIA_TIME_FRAC_BITS));
michael@0 41 }
michael@0 42
michael@0 43 inline int64_t MediaTimeToMicroseconds(MediaTime aTime)
michael@0 44 {
michael@0 45 return aTime*(1000000.0/(1 << MEDIA_TIME_FRAC_BITS));
michael@0 46 }
michael@0 47
michael@0 48 /**
michael@0 49 * A number of ticks at a rate determined by some underlying track (e.g.
michael@0 50 * audio sample rate). We want to make sure that multiplying TrackTicks by
michael@0 51 * 2^MEDIA_TIME_FRAC_BITS doesn't overflow, so we set its max accordingly.
michael@0 52 */
michael@0 53 typedef int64_t TrackTicks;
michael@0 54 const int64_t TRACK_TICKS_MAX = INT64_MAX >> MEDIA_TIME_FRAC_BITS;
michael@0 55
michael@0 56 /**
michael@0 57 * A MediaSegment is a chunk of media data sequential in time. Different
michael@0 58 * types of data have different subclasses of MediaSegment, all inheriting
michael@0 59 * from MediaSegmentBase.
michael@0 60 * All MediaSegment data is timed using TrackTicks. The actual tick rate
michael@0 61 * is defined on a per-track basis. For some track types, this can be
michael@0 62 * a fixed constant for all tracks of that type (e.g. 1MHz for video).
michael@0 63 *
michael@0 64 * Each media segment defines a concept of "null media data" (e.g. silence
michael@0 65 * for audio or "no video frame" for video), which can be efficiently
michael@0 66 * represented. This is used for padding.
michael@0 67 */
michael@0 68 class MediaSegment {
michael@0 69 public:
michael@0 70 virtual ~MediaSegment()
michael@0 71 {
michael@0 72 MOZ_COUNT_DTOR(MediaSegment);
michael@0 73 }
michael@0 74
michael@0 75 enum Type {
michael@0 76 AUDIO,
michael@0 77 VIDEO,
michael@0 78 TYPE_COUNT
michael@0 79 };
michael@0 80
michael@0 81 /**
michael@0 82 * Gets the total duration of the segment.
michael@0 83 */
michael@0 84 TrackTicks GetDuration() const { return mDuration; }
michael@0 85 Type GetType() const { return mType; }
michael@0 86
michael@0 87 /**
michael@0 88 * Create a MediaSegment of the same type.
michael@0 89 */
michael@0 90 virtual MediaSegment* CreateEmptyClone() const = 0;
michael@0 91 /**
michael@0 92 * Moves contents of aSource to the end of this segment.
michael@0 93 */
michael@0 94 virtual void AppendFrom(MediaSegment* aSource) = 0;
michael@0 95 /**
michael@0 96 * Append a slice of aSource to this segment.
michael@0 97 */
michael@0 98 virtual void AppendSlice(const MediaSegment& aSource,
michael@0 99 TrackTicks aStart, TrackTicks aEnd) = 0;
michael@0 100 /**
michael@0 101 * Replace all contents up to aDuration with null data.
michael@0 102 */
michael@0 103 virtual void ForgetUpTo(TrackTicks aDuration) = 0;
michael@0 104 /**
michael@0 105 * Insert aDuration of null data at the start of the segment.
michael@0 106 */
michael@0 107 virtual void InsertNullDataAtStart(TrackTicks aDuration) = 0;
michael@0 108 /**
michael@0 109 * Insert aDuration of null data at the end of the segment.
michael@0 110 */
michael@0 111 virtual void AppendNullData(TrackTicks aDuration) = 0;
michael@0 112 /**
michael@0 113 * Replace contents with disabled data of the same duration
michael@0 114 */
michael@0 115 virtual void ReplaceWithDisabled() = 0;
michael@0 116 /**
michael@0 117 * Remove all contents, setting duration to 0.
michael@0 118 */
michael@0 119 virtual void Clear() = 0;
michael@0 120
michael@0 121 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 122 {
michael@0 123 return 0;
michael@0 124 }
michael@0 125
michael@0 126 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 127 {
michael@0 128 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 129 }
michael@0 130
michael@0 131 protected:
michael@0 132 MediaSegment(Type aType) : mDuration(0), mType(aType)
michael@0 133 {
michael@0 134 MOZ_COUNT_CTOR(MediaSegment);
michael@0 135 }
michael@0 136
michael@0 137 TrackTicks mDuration; // total of mDurations of all chunks
michael@0 138 Type mType;
michael@0 139 };
michael@0 140
michael@0 141 /**
michael@0 142 * C is the implementation class subclassed from MediaSegmentBase.
michael@0 143 * C must contain a Chunk class.
michael@0 144 */
michael@0 145 template <class C, class Chunk> class MediaSegmentBase : public MediaSegment {
michael@0 146 public:
michael@0 147 virtual MediaSegment* CreateEmptyClone() const
michael@0 148 {
michael@0 149 return new C();
michael@0 150 }
michael@0 151 virtual void AppendFrom(MediaSegment* aSource)
michael@0 152 {
michael@0 153 NS_ASSERTION(aSource->GetType() == C::StaticType(), "Wrong type");
michael@0 154 AppendFromInternal(static_cast<C*>(aSource));
michael@0 155 }
michael@0 156 void AppendFrom(C* aSource)
michael@0 157 {
michael@0 158 AppendFromInternal(aSource);
michael@0 159 }
michael@0 160 virtual void AppendSlice(const MediaSegment& aSource,
michael@0 161 TrackTicks aStart, TrackTicks aEnd)
michael@0 162 {
michael@0 163 NS_ASSERTION(aSource.GetType() == C::StaticType(), "Wrong type");
michael@0 164 AppendSliceInternal(static_cast<const C&>(aSource), aStart, aEnd);
michael@0 165 }
michael@0 166 void AppendSlice(const C& aOther, TrackTicks aStart, TrackTicks aEnd)
michael@0 167 {
michael@0 168 AppendSliceInternal(aOther, aStart, aEnd);
michael@0 169 }
michael@0 170 /**
michael@0 171 * Replace the first aDuration ticks with null media data, because the data
michael@0 172 * will not be required again.
michael@0 173 */
michael@0 174 virtual void ForgetUpTo(TrackTicks aDuration)
michael@0 175 {
michael@0 176 if (mChunks.IsEmpty() || aDuration <= 0) {
michael@0 177 return;
michael@0 178 }
michael@0 179 if (mChunks[0].IsNull()) {
michael@0 180 TrackTicks extraToForget = std::min(aDuration, mDuration) - mChunks[0].GetDuration();
michael@0 181 if (extraToForget > 0) {
michael@0 182 RemoveLeading(extraToForget, 1);
michael@0 183 mChunks[0].mDuration += extraToForget;
michael@0 184 mDuration += extraToForget;
michael@0 185 }
michael@0 186 return;
michael@0 187 }
michael@0 188 RemoveLeading(aDuration, 0);
michael@0 189 mChunks.InsertElementAt(0)->SetNull(aDuration);
michael@0 190 mDuration += aDuration;
michael@0 191 }
michael@0 192 virtual void InsertNullDataAtStart(TrackTicks aDuration)
michael@0 193 {
michael@0 194 if (aDuration <= 0) {
michael@0 195 return;
michael@0 196 }
michael@0 197 if (!mChunks.IsEmpty() && mChunks[0].IsNull()) {
michael@0 198 mChunks[0].mDuration += aDuration;
michael@0 199 } else {
michael@0 200 mChunks.InsertElementAt(0)->SetNull(aDuration);
michael@0 201 }
michael@0 202 #ifdef MOZILLA_INTERNAL_API
michael@0 203 mChunks[0].mTimeStamp = mozilla::TimeStamp::Now();
michael@0 204 #endif
michael@0 205 mDuration += aDuration;
michael@0 206 }
michael@0 207 virtual void AppendNullData(TrackTicks aDuration)
michael@0 208 {
michael@0 209 if (aDuration <= 0) {
michael@0 210 return;
michael@0 211 }
michael@0 212 if (!mChunks.IsEmpty() && mChunks[mChunks.Length() - 1].IsNull()) {
michael@0 213 mChunks[mChunks.Length() - 1].mDuration += aDuration;
michael@0 214 } else {
michael@0 215 mChunks.AppendElement()->SetNull(aDuration);
michael@0 216 }
michael@0 217 mDuration += aDuration;
michael@0 218 }
michael@0 219 virtual void ReplaceWithDisabled()
michael@0 220 {
michael@0 221 if (GetType() != AUDIO) {
michael@0 222 MOZ_CRASH("Disabling unknown segment type");
michael@0 223 }
michael@0 224 TrackTicks duration = GetDuration();
michael@0 225 Clear();
michael@0 226 AppendNullData(duration);
michael@0 227 }
michael@0 228 virtual void Clear()
michael@0 229 {
michael@0 230 mDuration = 0;
michael@0 231 mChunks.Clear();
michael@0 232 }
michael@0 233
michael@0 234 class ChunkIterator {
michael@0 235 public:
michael@0 236 ChunkIterator(MediaSegmentBase<C, Chunk>& aSegment)
michael@0 237 : mSegment(aSegment), mIndex(0) {}
michael@0 238 bool IsEnded() { return mIndex >= mSegment.mChunks.Length(); }
michael@0 239 void Next() { ++mIndex; }
michael@0 240 Chunk& operator*() { return mSegment.mChunks[mIndex]; }
michael@0 241 Chunk* operator->() { return &mSegment.mChunks[mIndex]; }
michael@0 242 private:
michael@0 243 MediaSegmentBase<C, Chunk>& mSegment;
michael@0 244 uint32_t mIndex;
michael@0 245 };
michael@0 246
michael@0 247 void RemoveLeading(TrackTicks aDuration)
michael@0 248 {
michael@0 249 RemoveLeading(aDuration, 0);
michael@0 250 }
michael@0 251
michael@0 252 #ifdef MOZILLA_INTERNAL_API
michael@0 253 void GetStartTime(TimeStamp &aTime) {
michael@0 254 aTime = mChunks[0].mTimeStamp;
michael@0 255 }
michael@0 256 #endif
michael@0 257
michael@0 258 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
michael@0 259 {
michael@0 260 size_t amount = mChunks.SizeOfExcludingThis(aMallocSizeOf);
michael@0 261 for (size_t i = 0; i < mChunks.Length(); i++) {
michael@0 262 amount += mChunks[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
michael@0 263 }
michael@0 264 return amount;
michael@0 265 }
michael@0 266
michael@0 267 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
michael@0 268 {
michael@0 269 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 270 }
michael@0 271
michael@0 272 protected:
michael@0 273 MediaSegmentBase(Type aType) : MediaSegment(aType) {}
michael@0 274
michael@0 275 /**
michael@0 276 * Appends the contents of aSource to this segment, clearing aSource.
michael@0 277 */
michael@0 278 void AppendFromInternal(MediaSegmentBase<C, Chunk>* aSource)
michael@0 279 {
michael@0 280 MOZ_ASSERT(aSource->mDuration >= 0);
michael@0 281 mDuration += aSource->mDuration;
michael@0 282 aSource->mDuration = 0;
michael@0 283 if (!mChunks.IsEmpty() && !aSource->mChunks.IsEmpty() &&
michael@0 284 mChunks[mChunks.Length() - 1].CanCombineWithFollowing(aSource->mChunks[0])) {
michael@0 285 mChunks[mChunks.Length() - 1].mDuration += aSource->mChunks[0].mDuration;
michael@0 286 aSource->mChunks.RemoveElementAt(0);
michael@0 287 }
michael@0 288 mChunks.MoveElementsFrom(aSource->mChunks);
michael@0 289 }
michael@0 290
michael@0 291 void AppendSliceInternal(const MediaSegmentBase<C, Chunk>& aSource,
michael@0 292 TrackTicks aStart, TrackTicks aEnd)
michael@0 293 {
michael@0 294 MOZ_ASSERT(aStart <= aEnd, "Endpoints inverted");
michael@0 295 NS_WARN_IF_FALSE(aStart >= 0 && aEnd <= aSource.mDuration, "Slice out of range");
michael@0 296 mDuration += aEnd - aStart;
michael@0 297 TrackTicks offset = 0;
michael@0 298 for (uint32_t i = 0; i < aSource.mChunks.Length() && offset < aEnd; ++i) {
michael@0 299 const Chunk& c = aSource.mChunks[i];
michael@0 300 TrackTicks start = std::max(aStart, offset);
michael@0 301 TrackTicks nextOffset = offset + c.GetDuration();
michael@0 302 TrackTicks end = std::min(aEnd, nextOffset);
michael@0 303 if (start < end) {
michael@0 304 mChunks.AppendElement(c)->SliceTo(start - offset, end - offset);
michael@0 305 }
michael@0 306 offset = nextOffset;
michael@0 307 }
michael@0 308 }
michael@0 309
michael@0 310 Chunk* AppendChunk(TrackTicks aDuration)
michael@0 311 {
michael@0 312 MOZ_ASSERT(aDuration >= 0);
michael@0 313 Chunk* c = mChunks.AppendElement();
michael@0 314 c->mDuration = aDuration;
michael@0 315 mDuration += aDuration;
michael@0 316 return c;
michael@0 317 }
michael@0 318
michael@0 319 Chunk* FindChunkContaining(TrackTicks aOffset, TrackTicks* aStart = nullptr)
michael@0 320 {
michael@0 321 if (aOffset < 0) {
michael@0 322 return nullptr;
michael@0 323 }
michael@0 324 TrackTicks offset = 0;
michael@0 325 for (uint32_t i = 0; i < mChunks.Length(); ++i) {
michael@0 326 Chunk& c = mChunks[i];
michael@0 327 TrackTicks nextOffset = offset + c.GetDuration();
michael@0 328 if (aOffset < nextOffset) {
michael@0 329 if (aStart) {
michael@0 330 *aStart = offset;
michael@0 331 }
michael@0 332 return &c;
michael@0 333 }
michael@0 334 offset = nextOffset;
michael@0 335 }
michael@0 336 return nullptr;
michael@0 337 }
michael@0 338
michael@0 339 Chunk* GetLastChunk()
michael@0 340 {
michael@0 341 if (mChunks.IsEmpty()) {
michael@0 342 return nullptr;
michael@0 343 }
michael@0 344 return &mChunks[mChunks.Length() - 1];
michael@0 345 }
michael@0 346
michael@0 347 void RemoveLeading(TrackTicks aDuration, uint32_t aStartIndex)
michael@0 348 {
michael@0 349 NS_ASSERTION(aDuration >= 0, "Can't remove negative duration");
michael@0 350 TrackTicks t = aDuration;
michael@0 351 uint32_t chunksToRemove = 0;
michael@0 352 for (uint32_t i = aStartIndex; i < mChunks.Length() && t > 0; ++i) {
michael@0 353 Chunk* c = &mChunks[i];
michael@0 354 if (c->GetDuration() > t) {
michael@0 355 c->SliceTo(t, c->GetDuration());
michael@0 356 t = 0;
michael@0 357 break;
michael@0 358 }
michael@0 359 t -= c->GetDuration();
michael@0 360 chunksToRemove = i + 1 - aStartIndex;
michael@0 361 }
michael@0 362 mChunks.RemoveElementsAt(aStartIndex, chunksToRemove);
michael@0 363 mDuration -= aDuration - t;
michael@0 364 }
michael@0 365
michael@0 366 nsTArray<Chunk> mChunks;
michael@0 367 #ifdef MOZILLA_INTERNAL_API
michael@0 368 mozilla::TimeStamp mTimeStamp;
michael@0 369 #endif
michael@0 370 };
michael@0 371
michael@0 372 }
michael@0 373
michael@0 374 #endif /* MOZILLA_MEDIASEGMENT_H_ */

mercurial