Bug 1165819: Abstract TrackBuffer interface. r=kentuckyfriedtakahe

This commit is contained in:
Jean-Yves Avenard 2015-06-05 11:52:57 +10:00
parent 1ea063ca2c
commit 156f5fdac0
7 changed files with 182 additions and 85 deletions

View File

@ -140,10 +140,7 @@ SourceBuffer::GetBuffered(ErrorResult& aRv)
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
// We only manage a single trackbuffer in our source buffer.
// As such, there's no need to adjust the end of the trackbuffers as per
// Step 4: http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
media::TimeIntervals ranges = mTrackBuffer->Buffered();
media::TimeIntervals ranges = mContentManager->Buffered();
MSE_DEBUGV("ranges=%s", DumpTimeRanges(ranges).get());
nsRefPtr<dom::TimeRanges> tr = new dom::TimeRanges();
ranges.ToTimeRanges(tr);
@ -215,12 +212,9 @@ SourceBuffer::Abort(ErrorResult& aRv)
return;
}
AbortBufferAppend();
mTrackBuffer->ResetParserState();
mContentManager->ResetParserState();
mAppendWindowStart = 0;
mAppendWindowEnd = PositiveInfinity<double>();
// Discard the current decoder so no new data will be added to it.
MSE_DEBUG("Discarding decoder");
mTrackBuffer->DiscardCurrentDecoder();
}
void
@ -230,7 +224,7 @@ SourceBuffer::AbortBufferAppend()
mPendingAppend.DisconnectIfExists();
// TODO: Abort segment parser loop, and stream append loop algorithms.
// cancel any pending buffer append.
mTrackBuffer->AbortAppendData();
mContentManager->AbortAppendData();
AbortUpdating();
}
}
@ -277,8 +271,8 @@ void
SourceBuffer::DoRangeRemoval(double aStart, double aEnd)
{
MSE_DEBUG("DoRangeRemoval(%f, %f)", aStart, aEnd);
if (mTrackBuffer && !IsInfinite(aStart)) {
mTrackBuffer->RangeRemoval(media::TimeUnit::FromSeconds(aStart),
if (mContentManager && !IsInfinite(aStart)) {
mContentManager->RangeRemoval(media::TimeUnit::FromSeconds(aStart),
media::TimeUnit::FromSeconds(aEnd));
}
}
@ -289,10 +283,10 @@ SourceBuffer::Detach()
MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG("Detach");
AbortBufferAppend();
if (mTrackBuffer) {
mTrackBuffer->Detach();
if (mContentManager) {
mContentManager->Detach();
}
mTrackBuffer = nullptr;
mContentManager = nullptr;
mMediaSource = nullptr;
}
@ -302,7 +296,7 @@ SourceBuffer::Ended()
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(IsAttached());
MSE_DEBUG("Ended");
mTrackBuffer->EndCurrentDecoder();
mContentManager->Ended();
}
SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
@ -321,9 +315,9 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
MOZ_ASSERT(aMediaSource);
mEvictionThreshold = Preferences::GetUint("media.mediasource.eviction_threshold",
75 * (1 << 20));
mTrackBuffer = new TrackBuffer(aMediaSource->GetDecoder(), aType);
MSE_DEBUG("Create mTrackBuffer=%p",
mTrackBuffer.get());
mContentManager = SourceBufferContentManager::CreateManager(aMediaSource->GetDecoder(), aType);
MSE_DEBUG("Create mContentManager=%p",
mContentManager.get());
}
SourceBuffer::~SourceBuffer()
@ -451,14 +445,14 @@ SourceBuffer::AppendData(MediaLargeByteBuffer* aData, double aTimestampOffset,
return;
}
mPendingAppend.Begin(mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)
mPendingAppend.Begin(mContentManager->AppendData(aData, aTimestampOffset * USECS_PER_S)
->Then(AbstractThread::MainThread(), __func__, this,
&SourceBuffer::AppendDataCompletedWithSuccess,
&SourceBuffer::AppendDataErrored));
}
void
SourceBuffer::AppendDataCompletedWithSuccess(bool aGotMedia)
SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks)
{
mPendingAppend.Complete();
if (!mUpdating) {
@ -466,7 +460,7 @@ SourceBuffer::AppendDataCompletedWithSuccess(bool aGotMedia)
return;
}
if (mTrackBuffer->HasInitSegment()) {
if (aHasActiveTracks) {
if (!mActive) {
mActive = true;
mMediaSource->SourceBufferIsActive(this);
@ -474,9 +468,7 @@ SourceBuffer::AppendDataCompletedWithSuccess(bool aGotMedia)
}
}
if (aGotMedia) {
CheckEndTime();
}
CheckEndTime();
StopUpdating();
}
@ -504,7 +496,7 @@ SourceBuffer::AppendError(bool aDecoderError)
// The buffer append algorithm has been interrupted by abort().
return;
}
mTrackBuffer->ResetParserState();
mContentManager->ResetParserState();
mUpdating = false;
@ -522,6 +514,8 @@ SourceBuffer::AppendError(bool aDecoderError)
already_AddRefed<MediaLargeByteBuffer>
SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
{
typedef SourceBufferContentManager::EvictDataResult Result;
if (!IsAttached() || mUpdating) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
@ -543,10 +537,10 @@ SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult&
// threshold.
uint32_t toEvict =
(mEvictionThreshold > aLength) ? mEvictionThreshold - aLength : aLength;
bool evicted =
mTrackBuffer->EvictData(mMediaSource->GetDecoder()->GetCurrentTime(),
toEvict, &newBufferStartTime);
if (evicted) {
Result evicted =
mContentManager->EvictData(mMediaSource->GetDecoder()->GetCurrentTime(),
toEvict, &newBufferStartTime);
if (evicted == Result::DATA_EVICTED) {
MSE_DEBUG("AppendData Evict; current buffered start=%f",
GetBufferedStart());
@ -559,8 +553,8 @@ SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult&
// As we can only evict once we have playable data, we must give a chance
// to the DASH player to provide a complete media segment.
if (aLength > mEvictionThreshold ||
((mTrackBuffer->GetSize() > mEvictionThreshold - aLength) &&
!mTrackBuffer->HasOnlyIncompleteMedia())) {
((mContentManager->GetSize() > mEvictionThreshold - aLength) &&
evicted != Result::CANT_EVICT)) {
aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
return nullptr;
}
@ -603,15 +597,15 @@ SourceBuffer::Evict(double aStart, double aEnd)
if (currentTime + safety_threshold >= evictTime) {
evictTime -= safety_threshold;
}
mTrackBuffer->EvictBefore(evictTime);
mContentManager->EvictBefore(evictTime);
}
#if defined(DEBUG)
void
SourceBuffer::Dump(const char* aPath)
{
if (mTrackBuffer) {
mTrackBuffer->Dump(aPath);
if (mContentManager) {
mContentManager->Dump(aPath);
}
}
#endif
@ -620,9 +614,9 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer)
// Tell the TrackBuffer to end its current SourceBufferResource.
TrackBuffer* track = tmp->mTrackBuffer;
if (track) {
track->Detach();
SourceBufferContentManager* manager = tmp->mContentManager;
if (manager) {
manager->Detach();
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaSource)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DOMEventTargetHelper)

View File

@ -23,6 +23,7 @@
#include "nsISupports.h"
#include "nsString.h"
#include "nscore.h"
#include "SourceBufferContentManager.h"
class JSObject;
struct JSContext;
@ -33,7 +34,6 @@ class ErrorResult;
class MediaLargeByteBuffer;
class TrackBuffer;
template <typename T> class AsyncEventRunner;
typedef MediaPromise<bool, nsresult, /* IsExclusive = */ true> TrackBufferAppendPromise;
namespace dom {
@ -162,14 +162,14 @@ private:
uint32_t aLength,
ErrorResult& aRv);
void AppendDataCompletedWithSuccess(bool aValue);
void AppendDataCompletedWithSuccess(bool aHasActiveTracks);
void AppendDataErrored(nsresult aError);
nsRefPtr<MediaSource> mMediaSource;
uint32_t mEvictionThreshold;
nsRefPtr<TrackBuffer> mTrackBuffer;
nsRefPtr<SourceBufferContentManager> mContentManager;
double mAppendWindowStart;
double mAppendWindowEnd;
@ -186,7 +186,7 @@ private:
// aborted and another AppendData queued.
uint32_t mUpdateID;
MediaPromiseRequestHolder<TrackBufferAppendPromise> mPendingAppend;
MediaPromiseRequestHolder<SourceBufferContentManager::AppendPromise> mPendingAppend;
const nsCString mType;
};

View File

@ -0,0 +1,20 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "SourceBufferContentManager.h"
namespace mozilla {
already_AddRefed<SourceBufferContentManager>
SourceBufferContentManager::CreateManager(MediaSourceDecoder* aParentDecoder,
const nsACString &aType)
{
nsRefPtr<SourceBufferContentManager> manager;
manager = new TrackBuffer(aParentDecoder, aType);
return manager.forget();
}
}

View File

@ -0,0 +1,87 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_SOURCEBUFFERCONTENTMANAGER_H_
#define MOZILLA_SOURCEBUFFERCONTENTMANAGER_H_
#include "MediaData.h"
#include "MediaPromise.h"
#include "MediaSourceDecoder.h"
#include "SourceBuffer.h"
#include "TimeUnits.h"
#include "nsString.h"
namespace mozilla {
class SourceBufferContentManager {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SourceBufferContentManager);
typedef MediaPromise<bool, nsresult, /* IsExclusive = */ true> AppendPromise;
static already_AddRefed<SourceBufferContentManager>
CreateManager(MediaSourceDecoder* aParentDecoder, const nsACString& aType);
// Append data to the current decoder. Also responsible for calling
// NotifyDataArrived on the decoder to keep buffered range computation up
// to date. Returns false if the append failed.
virtual nsRefPtr<AppendPromise>
AppendData(MediaLargeByteBuffer* aData, int64_t aTimestampOffset /* microseconds */) = 0;
// Abort any pending AppendData.
virtual void AbortAppendData() = 0;
// Run MSE Reset Parser State Algorithm.
// 3.5.2 Reset Parser State
// http://w3c.github.io/media-source/#sourcebuffer-reset-parser-state
virtual void ResetParserState() = 0;
// Runs MSE range removal algorithm.
// http://w3c.github.io/media-source/#sourcebuffer-coded-frame-removal
virtual bool RangeRemoval(mozilla::media::TimeUnit aStart,
mozilla::media::TimeUnit aEnd) = 0;
enum class EvictDataResult : int8_t
{
NO_DATA_EVICTED,
DATA_EVICTED,
CANT_EVICT,
};
// Evicts data up to aPlaybackTime. aThreshold is used to
// bound the data being evicted. It will not evict more than aThreshold
// bytes. aBufferStartTime contains the new start time of the data after the
// eviction.
virtual EvictDataResult
EvictData(double aPlaybackTime, uint32_t aThreshold, double* aBufferStartTime) = 0;
// Evicts data up to aTime.
virtual void EvictBefore(double aTime) = 0;
// Returns the buffered range currently managed.
// This may be called on any thread.
// Buffered must conform to http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
virtual media::TimeIntervals Buffered() = 0;
// Return the size of the data managed by this SourceBufferContentManager.
virtual int64_t GetSize() = 0;
// Indicate that the MediaSource parent object got into "ended" state.
virtual void Ended() = 0;
// The parent SourceBuffer is about to be destroyed.
virtual void Detach() = 0;
#if defined(DEBUG)
virtual void Dump(const char* aPath) { }
#endif
protected:
virtual ~SourceBufferContentManager() { }
};
} // namespace mozilla
#endif /* MOZILLA_SOURCEBUFFERCONTENTMANAGER_H_ */

View File

@ -140,14 +140,14 @@ TrackBuffer::ContinueShutdown()
mShutdownPromise.Resolve(true, __func__);
}
nsRefPtr<TrackBufferAppendPromise>
nsRefPtr<TrackBuffer::AppendPromise>
TrackBuffer::AppendData(MediaLargeByteBuffer* aData, int64_t aTimestampOffset)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInitializationPromise.IsEmpty());
DecodersToInitialize decoders(this);
nsRefPtr<TrackBufferAppendPromise> p = mInitializationPromise.Ensure(__func__);
nsRefPtr<AppendPromise> p = mInitializationPromise.Ensure(__func__);
bool hadInitData = mParser->HasInitData();
bool hadCompleteInitData = mParser->HasCompleteInitData();
nsRefPtr<MediaLargeByteBuffer> oldInit = mParser->InitData();
@ -243,7 +243,7 @@ TrackBuffer::AppendData(MediaLargeByteBuffer* aData, int64_t aTimestampOffset)
// required when data is appended.
NotifyTimeRangesChanged();
mInitializationPromise.Resolve(gotMedia, __func__);
mInitializationPromise.Resolve(HasInitSegment(), __func__);
return p;
}
@ -297,7 +297,7 @@ public:
}
};
bool
TrackBuffer::EvictDataResult
TrackBuffer::EvictData(double aPlaybackTime,
uint32_t aThreshold,
double* aBufferStartTime)
@ -305,15 +305,15 @@ TrackBuffer::EvictData(double aPlaybackTime,
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
if (!mCurrentDecoder) {
return false;
if (!mCurrentDecoder || mInitializedDecoders.IsEmpty()) {
return EvictDataResult::CANT_EVICT;
}
int64_t totalSize = GetSize();
int64_t toEvict = totalSize - aThreshold;
if (toEvict <= 0 || mInitializedDecoders.IsEmpty()) {
return false;
if (toEvict <= 0) {
return EvictDataResult::NO_DATA_EVICTED;
}
// Get a list of initialized decoders.
@ -358,7 +358,7 @@ TrackBuffer::EvictData(double aPlaybackTime,
rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return false;
return EvictDataResult::CANT_EVICT;
}
}
}
@ -451,7 +451,9 @@ TrackBuffer::EvictData(double aPlaybackTime,
NotifyTimeRangesChanged();
}
return evicted;
return evicted ?
EvictDataResult::DATA_EVICTED :
(HasOnlyIncompleteMedia() ? EvictDataResult::CANT_EVICT : EvictDataResult::NO_DATA_EVICTED);
}
void
@ -820,7 +822,7 @@ TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
MSE_DEBUG("Reader %p activated",
aDecoder->GetReader());
mInitializationPromise.ResolveIfExists(aDecoder->GetRealMediaDuration() > 0, __func__);
mInitializationPromise.ResolveIfExists(true, __func__);
}
bool

View File

@ -24,10 +24,8 @@ class ContainerParser;
class MediaSourceDecoder;
class MediaLargeByteBuffer;
class TrackBuffer final {
class TrackBuffer final : public SourceBufferContentManager {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackBuffer);
TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType);
nsRefPtr<ShutdownPromise> Shutdown();
@ -35,24 +33,39 @@ public:
// Append data to the current decoder. Also responsible for calling
// NotifyDataArrived on the decoder to keep buffered range computation up
// to date. Returns false if the append failed.
nsRefPtr<TrackBufferAppendPromise> AppendData(MediaLargeByteBuffer* aData,
int64_t aTimestampOffset /* microseconds */);
nsRefPtr<AppendPromise> AppendData(MediaLargeByteBuffer* aData,
int64_t aTimestampOffset /* microseconds */) override;
// Evicts data held in the current decoders SourceBufferResource from the
// start of the buffer through to aPlaybackTime. aThreshold is used to
// bound the data being evicted. It will not evict more than aThreshold
// bytes. aBufferStartTime contains the new start time of the current
// decoders buffered data after the eviction. Returns true if data was
// evicted.
bool EvictData(double aPlaybackTime, uint32_t aThreshold, double* aBufferStartTime);
// decoders buffered data after the eviction.
EvictDataResult EvictData(double aPlaybackTime, uint32_t aThreshold, double* aBufferStartTime) override;
// Evicts data held in all the decoders SourceBufferResource from the start
// of the buffer through to aTime.
void EvictBefore(double aTime);
void EvictBefore(double aTime) override;
bool RangeRemoval(mozilla::media::TimeUnit aStart,
mozilla::media::TimeUnit aEnd) override;
void AbortAppendData() override;
int64_t GetSize() override;
void ResetParserState() override;
// Returns the union of the decoders buffered ranges in aRanges.
// This may be called on any thread.
media::TimeIntervals Buffered();
media::TimeIntervals Buffered() override;
void Ended() override
{
EndCurrentDecoder();
}
void Detach() override;
// Mark the current decoder's resource as ended, clear mCurrentDecoder and
// reset mLast{Start,End}Timestamp. Main thread only.
@ -60,8 +73,6 @@ public:
// Mark the current decoder's resource as ended.
void EndCurrentDecoder();
void Detach();
// Returns true if an init segment has been appended.
bool HasInitSegment();
@ -77,30 +88,11 @@ public:
void BreakCycles();
// Run MSE Reset Parser State Algorithm.
// 3.5.2 Reset Parser State
// http://w3c.github.io/media-source/#sourcebuffer-reset-parser-state
void ResetParserState();
// Returns a reference to mInitializedDecoders, used by MediaSourceReader
// to select decoders.
// TODO: Refactor to a cleaner interface between TrackBuffer and MediaSourceReader.
const nsTArray<nsRefPtr<SourceBufferDecoder>>& Decoders();
// Runs MSE range removal algorithm.
// http://w3c.github.io/media-source/#sourcebuffer-coded-frame-removal
// Implementation is only partial, we can only trim a buffer.
// Returns true if data was evicted.
// Times are in microseconds.
bool RangeRemoval(mozilla::media::TimeUnit aStart,
mozilla::media::TimeUnit aEnd);
// Abort any pending appendBuffer by rejecting any pending promises.
void AbortAppendData();
// Return the size used by all decoders managed by this TrackBuffer.
int64_t GetSize();
// Return true if we have a partial media segment being appended that is
// currently not playable.
bool HasOnlyIncompleteMedia();
@ -116,7 +108,7 @@ public:
private:
friend class DecodersToInitialize;
friend class MetadataRecipient;
~TrackBuffer();
virtual ~TrackBuffer();
// Create a new decoder, set mCurrentDecoder to the new decoder and
// returns it. The new decoder must be queued using QueueInitializeDecoder
@ -219,7 +211,7 @@ private:
bool mDecoderPerSegment;
bool mShutdown;
MediaPromiseHolder<TrackBufferAppendPromise> mInitializationPromise;
MediaPromiseHolder<AppendPromise> mInitializationPromise;
// Track our request for metadata from the reader.
MediaPromiseRequestHolder<MediaDecoderReader::MetadataPromise> mMetadataRequest;
};

View File

@ -9,6 +9,7 @@ EXPORTS += [
'AsyncEventRunner.h',
'MediaSourceDecoder.h',
'MediaSourceReader.h',
'SourceBufferContentManager.h',
]
EXPORTS.mozilla.dom += [
@ -25,6 +26,7 @@ UNIFIED_SOURCES += [
'MediaSourceUtils.cpp',
'ResourceQueue.cpp',
'SourceBuffer.cpp',
'SourceBufferContentManager.cpp',
'SourceBufferDecoder.cpp',
'SourceBufferList.cpp',
'SourceBufferResource.cpp',