LCOV - code coverage report
Current view: top level - dom/media - MediaSegment.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 196 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 78 0.0 %
Legend: Lines: hit not hit

          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_ */

Generated by: LCOV version 1.13