gecko/content/media/MediaSegment.h
Robert O'Callahan d3b9aab0ce Bug 779715. Part 1: Add basic support for ProcessedMediaStreams. r=jesup
The main thing this patch does is to add ProcessedMediaStream objects and
MediaInputPorts connecting streams. ProcessedMediaStreams are an abstract
class that doesn't constrain what processing is actually performed, except
that for now we assume a stream's processing depends only on its inputs
window of data between mCurrentTime and mStateComputedTime.
This patch reorganizes the way the blocking status of each stream is computed.
The streams are partitioned into groups so that every stream which can affect
the blocking status of another stream is in the same group as that other stream.
We also add a pass to order the streams by dependency so we can process the streams
in order of dependency; this pass also identifies the streams that form part of a
cycle.

--HG--
extra : rebase_source : c45c931a264e73f295642a934500bbeaa6448774
2012-08-01 00:17:21 +12:00

310 lines
8.9 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_MEDIASEGMENT_H_
#define MOZILLA_MEDIASEGMENT_H_
#include "nsTArray.h"
namespace mozilla {
/**
* We represent media times in 64-bit fixed point. So 1 MediaTime is
* 1/(2^MEDIA_TIME_FRAC_BITS) seconds.
*/
typedef PRInt64 MediaTime;
const PRInt64 MEDIA_TIME_FRAC_BITS = 20;
const PRInt64 MEDIA_TIME_MAX = PR_INT64_MAX;
inline MediaTime MillisecondsToMediaTime(PRInt32 aMS)
{
return (MediaTime(aMS) << MEDIA_TIME_FRAC_BITS)/1000;
}
inline MediaTime SecondsToMediaTime(double aS)
{
NS_ASSERTION(aS <= (MEDIA_TIME_MAX >> MEDIA_TIME_FRAC_BITS),
"Out of range");
return MediaTime(aS * (1 << MEDIA_TIME_FRAC_BITS));
}
inline double MediaTimeToSeconds(MediaTime aTime)
{
return aTime*(1.0/(1 << MEDIA_TIME_FRAC_BITS));
}
/**
* A number of ticks at a rate determined by some underlying track (e.g.
* audio sample rate). We want to make sure that multiplying TrackTicks by
* 2^MEDIA_TIME_FRAC_BITS doesn't overflow, so we set its max accordingly.
*/
typedef PRInt64 TrackTicks;
const PRInt64 TRACK_TICKS_MAX = PR_INT64_MAX >> MEDIA_TIME_FRAC_BITS;
/**
* A MediaSegment is a chunk of media data sequential in time. Different
* types of data have different subclasses of MediaSegment, all inheriting
* from MediaSegmentBase.
* All MediaSegment data is timed using TrackTicks. The actual tick rate
* is defined on a per-track basis. For some track types, this can be
* a fixed constant for all tracks of that type (e.g. 1MHz for video).
*
* Each media segment defines a concept of "null media data" (e.g. silence
* for audio or "no video frame" for video), which can be efficiently
* represented. This is used for padding.
*/
class MediaSegment {
public:
virtual ~MediaSegment()
{
MOZ_COUNT_DTOR(MediaSegment);
}
enum Type {
AUDIO,
VIDEO,
TYPE_COUNT
};
/**
* Gets the total duration of the segment.
*/
TrackTicks GetDuration() const { return mDuration; }
Type GetType() const { return mType; }
/**
* Create a MediaSegment of the same type.
*/
virtual MediaSegment* CreateEmptyClone() const = 0;
/**
* Moves contents of aSource to the end of this segment.
*/
virtual void AppendFrom(MediaSegment* aSource) = 0;
/**
* Append a slice of aSource to this segment.
*/
virtual void AppendSlice(const MediaSegment& aSource,
TrackTicks aStart, TrackTicks aEnd) = 0;
/**
* Replace all contents up to aDuration with null data.
*/
virtual void ForgetUpTo(TrackTicks aDuration) = 0;
/**
* Insert aDuration of null data at the start of the segment.
*/
virtual void InsertNullDataAtStart(TrackTicks aDuration) = 0;
/**
* Insert aDuration of null data at the end of the segment.
*/
virtual void AppendNullData(TrackTicks aDuration) = 0;
protected:
MediaSegment(Type aType) : mDuration(0), mType(aType)
{
MOZ_COUNT_CTOR(MediaSegment);
}
TrackTicks mDuration; // total of mDurations of all chunks
Type mType;
};
/**
* C is the implementation class subclassed from MediaSegmentBase.
* C must contain a Chunk class.
*/
template <class C, class Chunk> class MediaSegmentBase : public MediaSegment {
public:
virtual MediaSegment* CreateEmptyClone() const
{
C* s = new C();
s->InitFrom(*static_cast<const C*>(this));
return s;
}
virtual void AppendFrom(MediaSegment* aSource)
{
NS_ASSERTION(aSource->GetType() == C::StaticType(), "Wrong type");
AppendFromInternal(static_cast<C*>(aSource));
}
void AppendFrom(C* aSource)
{
AppendFromInternal(aSource);
}
virtual void AppendSlice(const MediaSegment& aSource,
TrackTicks aStart, TrackTicks aEnd)
{
NS_ASSERTION(aSource.GetType() == C::StaticType(), "Wrong type");
AppendSliceInternal(static_cast<const C&>(aSource), aStart, aEnd);
}
void AppendSlice(const C& aOther, TrackTicks aStart, TrackTicks aEnd)
{
AppendSliceInternal(aOther, aStart, aEnd);
}
void InitToSlice(const C& aOther, TrackTicks aStart, TrackTicks aEnd)
{
static_cast<C*>(this)->InitFrom(aOther);
AppendSliceInternal(aOther, aStart, aEnd);
}
/**
* Replace the first aDuration ticks with null media data, because the data
* will not be required again.
*/
virtual void ForgetUpTo(TrackTicks aDuration)
{
if (mChunks.IsEmpty() || aDuration <= 0) {
return;
}
if (mChunks[0].IsNull()) {
TrackTicks extraToForget = NS_MIN(aDuration, mDuration) - mChunks[0].GetDuration();
if (extraToForget > 0) {
RemoveLeading(extraToForget, 1);
mChunks[0].mDuration += extraToForget;
mDuration += extraToForget;
}
return;
}
RemoveLeading(aDuration, 0);
mChunks.InsertElementAt(0)->SetNull(aDuration);
mDuration += aDuration;
}
virtual void InsertNullDataAtStart(TrackTicks aDuration)
{
if (aDuration <= 0) {
return;
}
if (!mChunks.IsEmpty() && mChunks[0].IsNull()) {
mChunks[0].mDuration += aDuration;
} else {
mChunks.InsertElementAt(0)->SetNull(aDuration);
}
mDuration += aDuration;
}
virtual void AppendNullData(TrackTicks aDuration)
{
if (aDuration <= 0) {
return;
}
if (!mChunks.IsEmpty() && mChunks[mChunks.Length() - 1].IsNull()) {
mChunks[mChunks.Length() - 1].mDuration += aDuration;
} else {
mChunks.AppendElement()->SetNull(aDuration);
}
mDuration += aDuration;
}
protected:
MediaSegmentBase(Type aType) : MediaSegment(aType) {}
/**
* Appends the contents of aSource to this segment, clearing aSource.
*/
void AppendFromInternal(MediaSegmentBase<C, Chunk>* aSource)
{
static_cast<C*>(this)->CheckCompatible(*static_cast<C*>(aSource));
mDuration += aSource->mDuration;
aSource->mDuration = 0;
if (!mChunks.IsEmpty() && !aSource->mChunks.IsEmpty() &&
mChunks[mChunks.Length() - 1].CanCombineWithFollowing(aSource->mChunks[0])) {
mChunks[mChunks.Length() - 1].mDuration += aSource->mChunks[0].mDuration;
aSource->mChunks.RemoveElementAt(0);
}
mChunks.MoveElementsFrom(aSource->mChunks);
}
void AppendSliceInternal(const MediaSegmentBase<C, Chunk>& aSource,
TrackTicks aStart, TrackTicks aEnd)
{
static_cast<C*>(this)->CheckCompatible(static_cast<const C&>(aSource));
NS_ASSERTION(aStart <= aEnd, "Endpoints inverted");
NS_ASSERTION(aStart >= 0 && aEnd <= aSource.mDuration,
"Slice out of range");
mDuration += aEnd - aStart;
TrackTicks offset = 0;
for (PRUint32 i = 0; i < aSource.mChunks.Length() && offset < aEnd; ++i) {
const Chunk& c = aSource.mChunks[i];
TrackTicks start = NS_MAX(aStart, offset);
TrackTicks nextOffset = offset + c.GetDuration();
TrackTicks end = NS_MIN(aEnd, nextOffset);
if (start < end) {
mChunks.AppendElement(c)->SliceTo(start - offset, end - offset);
}
offset = nextOffset;
}
}
Chunk* AppendChunk(TrackTicks aDuration)
{
Chunk* c = mChunks.AppendElement();
c->mDuration = aDuration;
mDuration += aDuration;
return c;
}
Chunk* FindChunkContaining(TrackTicks aOffset, TrackTicks* aStart = nullptr)
{
if (aOffset < 0) {
return nullptr;
}
TrackTicks offset = 0;
for (PRUint32 i = 0; i < mChunks.Length(); ++i) {
Chunk& c = mChunks[i];
TrackTicks nextOffset = offset + c.GetDuration();
if (aOffset < nextOffset) {
if (aStart) {
*aStart = offset;
}
return &c;
}
offset = nextOffset;
}
return nullptr;
}
Chunk* GetLastChunk()
{
if (mChunks.IsEmpty()) {
return nullptr;
}
return &mChunks[mChunks.Length() - 1];
}
class ChunkIterator {
public:
ChunkIterator(MediaSegmentBase<C, Chunk>& aSegment)
: mSegment(aSegment), mIndex(0) {}
bool IsEnded() { return mIndex >= mSegment.mChunks.Length(); }
void Next() { ++mIndex; }
Chunk& operator*() { return mSegment.mChunks[mIndex]; }
Chunk* operator->() { return &mSegment.mChunks[mIndex]; }
private:
MediaSegmentBase<C, Chunk>& mSegment;
PRUint32 mIndex;
};
void RemoveLeading(TrackTicks aDuration, PRUint32 aStartIndex)
{
NS_ASSERTION(aDuration >= 0, "Can't remove negative duration");
TrackTicks t = aDuration;
PRUint32 chunksToRemove = 0;
for (PRUint32 i = aStartIndex; i < mChunks.Length() && t > 0; ++i) {
Chunk* c = &mChunks[i];
if (c->GetDuration() > t) {
c->SliceTo(t, c->GetDuration());
t = 0;
break;
}
t -= c->GetDuration();
chunksToRemove = i + 1 - aStartIndex;
}
mChunks.RemoveElementsAt(aStartIndex, chunksToRemove);
mDuration -= aDuration - t;
}
nsTArray<Chunk> mChunks;
};
}
#endif /* MOZILLA_MEDIASEGMENT_H_ */