tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MediaSegment.h (16410B)


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