mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1044498 - Handle overlapped SourceBuffer appends by spinning up a new decoder. Only implemented for WebM so far. r=cajbir
This commit is contained in:
parent
ff3baa6107
commit
bc89b03a62
@ -210,7 +210,7 @@ MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
nsRefPtr<SourceBuffer> sourceBuffer = SourceBuffer::Create(this, NS_ConvertUTF16toUTF8(mimeType));
|
||||
nsRefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(this, NS_ConvertUTF16toUTF8(mimeType));
|
||||
if (!sourceBuffer) {
|
||||
aRv.Throw(NS_ERROR_FAILURE); // XXX need a better error here
|
||||
return nullptr;
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "SourceBufferDecoder.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
#include "WebMBufferedParser.h"
|
||||
|
||||
struct JSContext;
|
||||
class JSObject;
|
||||
|
||||
@ -59,11 +61,21 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
|
||||
double& aStart, double& aEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static ContainerParser* CreateForMIMEType(const nsACString& aType);
|
||||
};
|
||||
|
||||
class WebMContainerParser : public ContainerParser {
|
||||
public:
|
||||
WebMContainerParser()
|
||||
: mTimecodeScale(0)
|
||||
{}
|
||||
|
||||
bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
ContainerParser::IsInitSegmentPresent(aData, aLength);
|
||||
@ -83,6 +95,37 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
|
||||
double& aStart, double& aEnd)
|
||||
{
|
||||
// XXX: This is overly primitive, needs to collect data as it's appended
|
||||
// to the SB and handle, rather than assuming everything is present in a
|
||||
// single aData segment.
|
||||
|
||||
WebMBufferedParser parser(0);
|
||||
if (mTimecodeScale != 0) {
|
||||
parser.SetTimecodeScale(mTimecodeScale);
|
||||
}
|
||||
|
||||
nsTArray<WebMTimeDataOffset> mapping;
|
||||
ReentrantMonitor dummy("dummy");
|
||||
parser.Append(aData, aLength, mapping, dummy);
|
||||
|
||||
mTimecodeScale = parser.GetTimecodeScale();
|
||||
|
||||
if (mapping.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const double NS_PER_S = 1e9;
|
||||
aStart = mapping[0].mTimecode / NS_PER_S;
|
||||
aEnd = mapping.LastElement().mTimecode / NS_PER_S;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t mTimecodeScale;
|
||||
};
|
||||
|
||||
class MP4ContainerParser : public ContainerParser {
|
||||
@ -352,6 +395,7 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
|
||||
: DOMEventTargetHelper(aMediaSource->GetParentObject())
|
||||
, mMediaSource(aMediaSource)
|
||||
, mType(aType)
|
||||
, mLastParsedTimestamp(UnspecifiedNaN<double>())
|
||||
, mAppendWindowStart(0)
|
||||
, mAppendWindowEnd(PositiveInfinity<double>())
|
||||
, mTimestampOffset(0)
|
||||
@ -366,13 +410,6 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
|
||||
InitNewDecoder();
|
||||
}
|
||||
|
||||
already_AddRefed<SourceBuffer>
|
||||
SourceBuffer::Create(MediaSource* aMediaSource, const nsACString& aType)
|
||||
{
|
||||
nsRefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(aMediaSource, aType);
|
||||
return sourceBuffer.forget();
|
||||
}
|
||||
|
||||
SourceBuffer::~SourceBuffer()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@ -504,6 +541,26 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
double start, end;
|
||||
if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
|
||||
if (start <= mLastParsedTimestamp) {
|
||||
// This data is earlier in the timeline than data we have already
|
||||
// processed, so we must create a new decoder to handle the decoding.
|
||||
DiscardDecoder();
|
||||
|
||||
// If we've got a decoder here, it's not initialized, so we can use it
|
||||
// rather than creating a new one.
|
||||
if (!InitNewDecoder()) {
|
||||
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
|
||||
return;
|
||||
}
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized (%f, %f).",
|
||||
this, start, end);
|
||||
mDecoderInitialized = true;
|
||||
}
|
||||
mLastParsedTimestamp = end;
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData: Segment start=%f end=%f", this, start, end);
|
||||
}
|
||||
// XXX: For future reference: NDA call must run on the main thread.
|
||||
mDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
|
||||
aLength,
|
||||
@ -520,7 +577,7 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
const uint32_t evict_threshold = 75 * (1 << 20);
|
||||
bool evicted = mDecoder->GetResource()->EvictData(evict_threshold);
|
||||
if (evicted) {
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendBuffer Evict; current buffered start=%f",
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData Evict; current buffered start=%f",
|
||||
this, GetBufferedStart());
|
||||
|
||||
// We notify that we've evicted from the time range 0 through to
|
||||
|
@ -89,7 +89,7 @@ public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SourceBuffer, DOMEventTargetHelper)
|
||||
|
||||
static already_AddRefed<SourceBuffer> Create(MediaSource* aMediaSource, const nsACString& aType);
|
||||
SourceBuffer(MediaSource* aMediaSource, const nsACString& aType);
|
||||
|
||||
MediaSource* GetParentObject() const;
|
||||
|
||||
@ -114,8 +114,6 @@ public:
|
||||
private:
|
||||
~SourceBuffer();
|
||||
|
||||
SourceBuffer(MediaSource* aMediaSource, const nsACString& aType);
|
||||
|
||||
friend class AsyncEventRunner<SourceBuffer>;
|
||||
void DispatchSimpleEvent(const char* aName);
|
||||
void QueueAsyncSimpleEvent(const char* aName);
|
||||
@ -141,6 +139,8 @@ private:
|
||||
|
||||
nsAutoPtr<ContainerParser> mParser;
|
||||
|
||||
double mLastParsedTimestamp;
|
||||
|
||||
nsRefPtr<SourceBufferDecoder> mDecoder;
|
||||
nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
|
||||
|
||||
|
@ -1026,52 +1026,45 @@ nsresult WebMReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime,
|
||||
|
||||
nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
|
||||
{
|
||||
MediaResource* resource = mDecoder->GetResource();
|
||||
|
||||
uint64_t timecodeScale;
|
||||
if (!mContext || nestegg_tstamp_scale(mContext, &timecodeScale) == -1) {
|
||||
return NS_OK;
|
||||
if (aBuffered->Length() != 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
MediaResource* resource = mDecoder->GetResource();
|
||||
|
||||
// Special case completely cached files. This also handles local files.
|
||||
bool isFullyCached = resource->IsDataCachedToEndOfResource(0);
|
||||
if (isFullyCached) {
|
||||
if (mContext && resource->IsDataCachedToEndOfResource(0)) {
|
||||
uint64_t duration = 0;
|
||||
if (nestegg_duration(mContext, &duration) == 0) {
|
||||
aBuffered->Add(0, duration / NS_PER_S);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t bufferedLength = 0;
|
||||
aBuffered->GetLength(&bufferedLength);
|
||||
|
||||
// Either we the file is not fully cached, or we couldn't find a duration in
|
||||
// the WebM bitstream.
|
||||
if (!isFullyCached || !bufferedLength) {
|
||||
MediaResource* resource = mDecoder->GetResource();
|
||||
nsTArray<MediaByteRange> ranges;
|
||||
nsresult res = resource->GetCachedRanges(ranges);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
nsTArray<MediaByteRange> ranges;
|
||||
nsresult res = resource->GetCachedRanges(ranges);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
for (uint32_t index = 0; index < ranges.Length(); index++) {
|
||||
uint64_t start, end;
|
||||
bool rv = mBufferedState->CalculateBufferedForRange(ranges[index].mStart,
|
||||
ranges[index].mEnd,
|
||||
&start, &end);
|
||||
if (rv) {
|
||||
double startTime = start * timecodeScale / NS_PER_S - aStartTime;
|
||||
double endTime = end * timecodeScale / NS_PER_S - aStartTime;
|
||||
// If this range extends to the end of the file, the true end time
|
||||
// is the file's duration.
|
||||
if (resource->IsDataCachedToEndOfResource(ranges[index].mStart)) {
|
||||
uint64_t duration = 0;
|
||||
if (nestegg_duration(mContext, &duration) == 0) {
|
||||
endTime = duration / NS_PER_S;
|
||||
}
|
||||
for (uint32_t index = 0; index < ranges.Length(); index++) {
|
||||
uint64_t start, end;
|
||||
bool rv = mBufferedState->CalculateBufferedForRange(ranges[index].mStart,
|
||||
ranges[index].mEnd,
|
||||
&start, &end);
|
||||
if (rv) {
|
||||
double startTime = start / NS_PER_S - aStartTime;
|
||||
double endTime = end / NS_PER_S - aStartTime;
|
||||
// If this range extends to the end of the file, the true end time
|
||||
// is the file's duration.
|
||||
if (mContext && resource->IsDataCachedToEndOfResource(ranges[index].mStart)) {
|
||||
uint64_t duration = 0;
|
||||
if (nestegg_duration(mContext, &duration) == 0) {
|
||||
endTime = duration / NS_PER_S;
|
||||
}
|
||||
|
||||
aBuffered->Add(startTime, endTime);
|
||||
}
|
||||
|
||||
aBuffered->Add(startTime, endTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS += [
|
||||
'WebMBufferedParser.h',
|
||||
'WebMDecoder.h',
|
||||
'WebMReader.h',
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user