mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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:
parent
ab2d3c3522
commit
43f860dc7e
@ -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
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user