Bug 1132796: Evict data we likely previously read. r=cajbir

Also attempt to evict future data, the furthest away from playback position.
This commit is contained in:
Jean-Yves Avenard 2015-02-13 16:52:42 +11:00
parent ab2d3c3522
commit 43f860dc7e
6 changed files with 149 additions and 76 deletions

View File

@ -13,6 +13,7 @@
#include "MediaSourceReader.h" #include "MediaSourceReader.h"
#include "MediaSourceResource.h" #include "MediaSourceResource.h"
#include "MediaSourceUtils.h" #include "MediaSourceUtils.h"
#include "SourceBufferDecoder.h"
#include "VideoUtils.h" #include "VideoUtils.h"
#ifdef PR_LOGGING #ifdef PR_LOGGING
@ -320,6 +321,34 @@ MediaSourceDecoder::GetDuration()
return mMediaSourceDuration; return mMediaSourceDuration;
} }
already_AddRefed<SourceBufferDecoder>
MediaSourceDecoder::SelectDecoder(int64_t aTarget,
int64_t aTolerance,
const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders)
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
// Consider decoders in order of newest to oldest, as a newer decoder
// providing a given buffered range is expected to replace an older one.
for (int32_t i = aTrackDecoders.Length() - 1; i >= 0; --i) {
nsRefPtr<SourceBufferDecoder> newDecoder = aTrackDecoders[i];
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
newDecoder->GetBuffered(ranges);
if (ranges->Find(double(aTarget) / USECS_PER_S,
double(aTolerance) / USECS_PER_S) == dom::TimeRanges::NoIndex) {
MSE_DEBUGV("SelectDecoder(%lld fuzz:%lld) newDecoder=%p (%d/%d) target not in ranges=%s",
aTarget, aTolerance, newDecoder.get(), i+1,
aTrackDecoders.Length(), DumpTimeRanges(ranges).get());
continue;
}
return newDecoder.forget();
}
return nullptr;
}
#undef MSE_DEBUG #undef MSE_DEBUG
#undef MSE_DEBUGV #undef MSE_DEBUGV

View File

@ -82,6 +82,12 @@ public:
// reader in this decoders MediaSourceReader. // reader in this decoders MediaSourceReader.
bool IsActiveReader(MediaDecoderReader* aReader); bool IsActiveReader(MediaDecoderReader* aReader);
// Return a decoder from the set available in aTrackDecoders that has data
// available in the range requested by aTarget.
already_AddRefed<SourceBufferDecoder> SelectDecoder(int64_t aTarget /* microseconds */,
int64_t aTolerance /* microseconds */,
const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
// Returns a string describing the state of the MediaSource internal // Returns a string describing the state of the MediaSource internal
// buffered data. Used for debugging purposes. // buffered data. Used for debugging purposes.
void GetMozDebugReaderData(nsAString& aString); void GetMozDebugReaderData(nsAString& aString);

View File

@ -463,27 +463,8 @@ MediaSourceReader::SelectDecoder(int64_t aTarget,
int64_t aTolerance, int64_t aTolerance,
const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders) const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders)
{ {
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); return static_cast<MediaSourceDecoder*>(mDecoder)
->SelectDecoder(aTarget, aTolerance, aTrackDecoders);
// Consider decoders in order of newest to oldest, as a newer decoder
// providing a given buffered range is expected to replace an older one.
for (int32_t i = aTrackDecoders.Length() - 1; i >= 0; --i) {
nsRefPtr<SourceBufferDecoder> newDecoder = aTrackDecoders[i];
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
newDecoder->GetBuffered(ranges);
if (ranges->Find(double(aTarget) / USECS_PER_S,
double(aTolerance) / USECS_PER_S) == dom::TimeRanges::NoIndex) {
MSE_DEBUGV("SelectDecoder(%lld fuzz:%lld) newDecoder=%p (%d/%d) target not in ranges=%s",
aTarget, aTolerance, newDecoder.get(), i+1,
aTrackDecoders.Length(), DumpTimeRanges(ranges).get());
continue;
}
return newDecoder.forget();
}
return nullptr;
} }
bool bool

View File

@ -206,8 +206,8 @@ private:
// Return a decoder from the set available in aTrackDecoders that has data // Return a decoder from the set available in aTrackDecoders that has data
// available in the range requested by aTarget. // available in the range requested by aTarget.
already_AddRefed<SourceBufferDecoder> SelectDecoder(int64_t aTarget, already_AddRefed<SourceBufferDecoder> SelectDecoder(int64_t aTarget /* microseconds */,
int64_t aTolerance, int64_t aTolerance /* microseconds */,
const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders); const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
bool HaveData(int64_t aTarget, MediaData::Type aType); bool HaveData(int64_t aTarget, MediaData::Type aType);

View File

@ -37,6 +37,8 @@ extern PRLogModuleInfo* GetMediaSourceLog();
// Time in microsecond under which a timestamp will be considered to be 0. // Time in microsecond under which a timestamp will be considered to be 0.
#define FUZZ_TIMESTAMP_OFFSET 100000 #define FUZZ_TIMESTAMP_OFFSET 100000
#define EOS_FUZZ_US 125000
namespace mozilla { namespace mozilla {
TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType) TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType)
@ -314,76 +316,101 @@ TrackBuffer::EvictData(double aPlaybackTime,
return false; return false;
} }
// Get a list of initialized decoders // Get a list of initialized decoders.
nsTArray<SourceBufferDecoder*> decoders; nsTArray<SourceBufferDecoder*> decoders;
decoders.AppendElements(mInitializedDecoders); decoders.AppendElements(mInitializedDecoders);
// First try to evict data before the current play position, starting // First try to evict data before the current play position, starting
// with the earliest time. // with the oldest decoder.
uint32_t i = 0; for (uint32_t i = 0; i < decoders.Length() && toEvict > 0; ++i) {
bool pastCurrentDecoder = true;
for (; i < decoders.Length() && toEvict > 0; ++i) {
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges(); nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
decoders[i]->GetBuffered(buffered); decoders[i]->GetBuffered(buffered);
bool onCurrent = decoders[i] == mCurrentDecoder;
if (onCurrent) {
pastCurrentDecoder = false;
}
MSE_DEBUG("decoder=%u/%u threshold=%u " MSE_DEBUG("decoder=%u/%u threshold=%u toEvict=%lld",
"toEvict=%lld current=%s pastCurrent=%s", i, decoders.Length(), aThreshold, toEvict);
i, decoders.Length(), aThreshold, toEvict,
onCurrent ? "true" : "false",
pastCurrentDecoder ? "true" : "false");
if (pastCurrentDecoder && // To ensure we don't evict data past the current playback position
!mParentDecoder->IsActiveReader(decoders[i]->GetReader())) { // we apply a threshold of a few seconds back and evict data up to
// Remove data from older decoders than the current one. // that point.
// Don't remove data if it is currently active. if (aPlaybackTime > MSE_EVICT_THRESHOLD_TIME) {
MSE_DEBUG("evicting all before start " double time = aPlaybackTime - MSE_EVICT_THRESHOLD_TIME;
"bufferedStart=%f bufferedEnd=%f aPlaybackTime=%f size=%lld", int64_t playbackOffset = decoders[i]->ConvertToByteOffset(time);
buffered->GetStartTime(), buffered->GetEndTime(), MSE_DEBUG("evicting some bufferedEnd=%f"
aPlaybackTime, decoders[i]->GetResource()->GetSize()); "aPlaybackTime=%f time=%f, playbackOffset=%lld size=%lld",
toEvict -= decoders[i]->GetResource()->EvictAll(); buffered->GetEndTime(), aPlaybackTime, time,
} else { playbackOffset, decoders[i]->GetResource()->GetSize());
// To ensure we don't evict data past the current playback position if (playbackOffset > 0) {
// we apply a threshold of a few seconds back and evict data up to toEvict -= decoders[i]->GetResource()->EvictData(playbackOffset,
// that point. toEvict);
if (aPlaybackTime > MSE_EVICT_THRESHOLD_TIME) {
double time = aPlaybackTime - MSE_EVICT_THRESHOLD_TIME;
int64_t playbackOffset = decoders[i]->ConvertToByteOffset(time);
MSE_DEBUG("evicting some bufferedEnd=%f"
"aPlaybackTime=%f time=%f, playbackOffset=%lld size=%lld",
buffered->GetEndTime(), aPlaybackTime, time,
playbackOffset, decoders[i]->GetResource()->GetSize());
if (playbackOffset > 0) {
toEvict -= decoders[i]->GetResource()->EvictData(playbackOffset,
playbackOffset);
}
} }
} }
} }
// Remove decoders that have no data in them // Evict all data from decoders we've likely already read from.
for (i = 0; i < decoders.Length(); ++i) { for (uint32_t i = 0; i < decoders.Length() && toEvict > 0; ++i) {
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges(); if (mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
decoders[i]->GetBuffered(buffered); break;
MSE_DEBUG("maybe remove empty decoders=%d " }
"size=%lld start=%f end=%f", if (decoders[i] == mCurrentDecoder) {
i, decoders[i]->GetResource()->GetSize(),
buffered->GetStartTime(), buffered->GetEndTime());
if (decoders[i] == mCurrentDecoder
|| mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
continue; continue;
} }
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
decoders[i]->GetBuffered(buffered);
if (decoders[i]->GetResource()->GetSize() == 0 || // Remove data from older decoders than the current one.
buffered->GetStartTime() < 0.0 || MSE_DEBUG("evicting all "
buffered->GetEndTime() < 0.0) { "bufferedStart=%f bufferedEnd=%f aPlaybackTime=%f size=%lld",
MSE_DEBUG("remove empty decoders=%d", i); buffered->GetStartTime(), buffered->GetEndTime(),
RemoveDecoder(decoders[i]); aPlaybackTime, decoders[i]->GetResource()->GetSize());
toEvict -= decoders[i]->GetResource()->EvictAll();
}
// Evict all data from future decoders, starting furthest away from
// current playback position.
// We will ignore the currently playing decoder and the one playing after that
// in order to ensure we give enough time to the DASH player to re-buffer
// as necessary.
// TODO: This step should be done using RangeRemoval:
// Something like: RangeRemoval(aPlaybackTime + 60s, End);
// Find the reader currently being played with.
SourceBufferDecoder* playingDecoder = nullptr;
for (uint32_t i = 0; i < decoders.Length() && toEvict > 0; ++i) {
if (mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
playingDecoder = decoders[i];
break;
} }
} }
// Find the next decoder we're likely going to play with.
nsRefPtr<SourceBufferDecoder> nextPlayingDecoder = nullptr;
if (playingDecoder) {
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
playingDecoder->GetBuffered(buffered);
nextPlayingDecoder =
mParentDecoder->SelectDecoder(buffered->GetEndTime() * USECS_PER_S + 1,
EOS_FUZZ_US,
mInitializedDecoders);
}
// Sort decoders by their start times.
decoders.Sort(DecoderSorter());
for (int32_t i = int32_t(decoders.Length()) - 1; i >= 0 && toEvict > 0; --i) {
if (decoders[i] == playingDecoder || decoders[i] == nextPlayingDecoder ||
decoders[i] == mCurrentDecoder) {
continue;
}
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
decoders[i]->GetBuffered(buffered);
MSE_DEBUG("evicting all "
"bufferedStart=%f bufferedEnd=%f aPlaybackTime=%f size=%lld",
buffered->GetStartTime(), buffered->GetEndTime(),
aPlaybackTime, decoders[i]->GetResource()->GetSize());
toEvict -= decoders[i]->GetResource()->EvictAll();
}
RemoveEmptyDecoders(decoders);
bool evicted = toEvict < (totalSize - aThreshold); bool evicted = toEvict < (totalSize - aThreshold);
if (evicted) { if (evicted) {
@ -395,6 +422,33 @@ TrackBuffer::EvictData(double aPlaybackTime,
return evicted; return evicted;
} }
void
TrackBuffer::RemoveEmptyDecoders(nsTArray<mozilla::SourceBufferDecoder*>& aDecoders)
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
// Remove decoders that have no data in them
for (uint32_t i = 0; i < aDecoders.Length(); ++i) {
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
aDecoders[i]->GetBuffered(buffered);
MSE_DEBUG("maybe remove empty decoders=%d "
"size=%lld start=%f end=%f",
i, aDecoders[i]->GetResource()->GetSize(),
buffered->GetStartTime(), buffered->GetEndTime());
if (aDecoders[i] == mCurrentDecoder ||
mParentDecoder->IsActiveReader(aDecoders[i]->GetReader())) {
continue;
}
if (aDecoders[i]->GetResource()->GetSize() == 0 ||
buffered->GetStartTime() < 0.0 ||
buffered->GetEndTime() < 0.0) {
MSE_DEBUG("remove empty decoders=%d", i);
RemoveDecoder(aDecoders[i]);
}
}
}
void void
TrackBuffer::EvictBefore(double aTime) TrackBuffer::EvictBefore(double aTime)
{ {

View File

@ -156,6 +156,9 @@ private:
// function. // function.
void RemoveDecoder(SourceBufferDecoder* aDecoder); void RemoveDecoder(SourceBufferDecoder* aDecoder);
// Remove all empty decoders from the provided list;
void RemoveEmptyDecoders(nsTArray<SourceBufferDecoder*>& aDecoders);
nsAutoPtr<ContainerParser> mParser; nsAutoPtr<ContainerParser> mParser;
// A task queue using the shared media thread pool. Used exclusively to // A task queue using the shared media thread pool. Used exclusively to