зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1558364 - Add MediaSpan and use it for TrackBuffersManager::mInputBuffer. r=jya
As seen in this profile of a Twitch replay: https://perfht.ml/2K9Ydb3 we can often end up spending time in TrackBuffersManager::CodedFrameProcessing() shaving off bytes from the front off TrackBuffersManager::mInputBuffer. This requires all the remaining bytes to be memmove'd down to the start of this array. Sometimes we have close to 1MB in that buffer, and when we're just trying to consume a few hundred bytes, that becomes high overhead. So intead of using this "slice off, shuffle down" approach change TrackBuffersManager::mInputBuffer to be a new type MediaSpan, which maintains a RefPtr to a MediaByteBuffer and a span defining the subregion of the buffer we care about. This means the RemoveElementsAt(0,N) operation becomes basically free, and we can eliminate a few other copies we were doing as well. Differential Revision: https://phabricator.services.mozilla.com/D34661 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
f404754f4b
Коммит
d5f83d4740
|
@ -9,6 +9,7 @@
|
|||
#include "nscore.h"
|
||||
#include "nsTArray.h"
|
||||
#include "MediaData.h"
|
||||
#include "MediaSpan.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Result.h"
|
||||
|
||||
|
@ -34,6 +35,10 @@ class MOZ_RAII BufferReader {
|
|||
: mPtr(aData->Elements()),
|
||||
mRemaining(aData->Length()),
|
||||
mLength(aData->Length()) {}
|
||||
explicit BufferReader(const mozilla::MediaSpan& aData)
|
||||
: mPtr(aData.Elements()),
|
||||
mRemaining(aData.Length()),
|
||||
mLength(aData.Length()) {}
|
||||
|
||||
void SetData(const nsTArray<uint8_t>& aData) {
|
||||
MOZ_ASSERT(!mPtr && !mRemaining);
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
/* -*- 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/. */
|
||||
#if !defined(MediaSpan_h)
|
||||
# define MediaSpan_h
|
||||
|
||||
# include "MediaData.h"
|
||||
# include "mozilla/RefPtr.h"
|
||||
# include "mozilla/Span.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// A MediaSpan wraps a MediaByteBuffer and exposes a slice/span, or subregion,
|
||||
// of the buffer. This allows you to slice off a logical subregion without
|
||||
// needing to reallocate a new buffer to hold it. You can also append to a
|
||||
// MediaSpan without affecting other MediaSpans referencing the same buffer
|
||||
// (the MediaSpan receiving the append will allocate a new buffer to store its
|
||||
// result if necessary, to ensure other MediaSpans referencing its original
|
||||
// buffer are unaffected). Note there are no protections here that something
|
||||
// other than MediaSpans doesn't modify the underlying MediaByteBuffer while
|
||||
// a MediaSpan is alive.
|
||||
class MediaSpan {
|
||||
public:
|
||||
~MediaSpan() = default;
|
||||
|
||||
explicit MediaSpan(const MediaSpan& aOther) = default;
|
||||
|
||||
MediaSpan(MediaSpan&& aOther) noexcept = default;
|
||||
|
||||
explicit MediaSpan(const RefPtr<MediaByteBuffer>& aBuffer)
|
||||
: mBuffer(aBuffer), mStart(0), mLength(aBuffer ? aBuffer->Length() : 0) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mBuffer);
|
||||
}
|
||||
|
||||
explicit MediaSpan(MediaByteBuffer* aBuffer)
|
||||
: mBuffer(aBuffer), mStart(0), mLength(aBuffer ? aBuffer->Length() : 0) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mBuffer);
|
||||
}
|
||||
|
||||
MediaSpan& operator=(const MediaSpan& aOther) = default;
|
||||
|
||||
static MediaSpan WithCopyOf(const RefPtr<MediaByteBuffer>& aBuffer) {
|
||||
RefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(aBuffer->Length());
|
||||
buffer->AppendElements(*aBuffer);
|
||||
return MediaSpan(buffer);
|
||||
}
|
||||
|
||||
bool IsEmpty() const { return Length() == 0; }
|
||||
|
||||
// Note: It's unsafe to store the pointer returned by this function, as an
|
||||
// append operation could cause the wrapped MediaByteBuffer to be
|
||||
// reallocated, invalidating pointers previously returned by this function.
|
||||
const uint8_t* Elements() const {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mStart < mBuffer->Length());
|
||||
return mBuffer->Elements() + mStart;
|
||||
}
|
||||
|
||||
size_t Length() const { return mLength; }
|
||||
|
||||
uint8_t operator[](size_t aIndex) const {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aIndex < Length());
|
||||
return (*mBuffer)[mStart + aIndex];
|
||||
}
|
||||
|
||||
bool Append(MediaByteBuffer* aBuffer) {
|
||||
if (!aBuffer) {
|
||||
return true;
|
||||
}
|
||||
if (mStart + mLength < mBuffer->Length()) {
|
||||
// This MediaSpan finishes before the end of its buffer. The buffer
|
||||
// could be shared with another MediaSpan. So we can't just append to
|
||||
// the underlying buffer without risking damaging other MediaSpans' data.
|
||||
// So we must reallocate a new buffer, copy our old data into it, and
|
||||
// append the new data into it.
|
||||
RefPtr<MediaByteBuffer> buffer =
|
||||
new MediaByteBuffer(mLength + aBuffer->Length());
|
||||
if (!buffer->AppendElements(Elements(), Length()) ||
|
||||
!buffer->AppendElements(*aBuffer)) {
|
||||
return false;
|
||||
}
|
||||
mBuffer = buffer;
|
||||
mLength += aBuffer->Length();
|
||||
return true;
|
||||
}
|
||||
if (!mBuffer->AppendElements(*aBuffer)) {
|
||||
return false;
|
||||
}
|
||||
mLength += aBuffer->Length();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns a new MediaSpan, spanning from the start of this span,
|
||||
// up until aEnd.
|
||||
MediaSpan To(size_t aEnd) const {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aEnd <= Length());
|
||||
return MediaSpan(mBuffer, mStart, aEnd);
|
||||
}
|
||||
|
||||
// Returns a new MediaSpan, spanning from aStart bytes offset from
|
||||
// the start of this span, until the end of this span.
|
||||
MediaSpan From(size_t aStart) const {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aStart <= Length());
|
||||
return MediaSpan(mBuffer, mStart + aStart, Length() - aStart);
|
||||
}
|
||||
|
||||
void RemoveFront(size_t aNumBytes) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aNumBytes <= Length());
|
||||
mStart += aNumBytes;
|
||||
mLength -= aNumBytes;
|
||||
}
|
||||
|
||||
MediaByteBuffer* Buffer() const { return mBuffer; }
|
||||
|
||||
private:
|
||||
MediaSpan(MediaByteBuffer* aBuffer, size_t aStart, size_t aLength)
|
||||
: mBuffer(aBuffer), mStart(aStart), mLength(aLength) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mStart + mLength <= mBuffer->Length());
|
||||
}
|
||||
|
||||
RefPtr<MediaByteBuffer> mBuffer;
|
||||
size_t mStart = 0;
|
||||
size_t mLength = 0;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MediaSpan_h
|
|
@ -0,0 +1,111 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "MediaSpan.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
already_AddRefed<MediaByteBuffer> makeBuffer(uint8_t aStart, uint8_t aEnd) {
|
||||
RefPtr<MediaByteBuffer> buffer(new MediaByteBuffer);
|
||||
for (uint8_t i = aStart; i <= aEnd; i++) {
|
||||
buffer->AppendElement(i);
|
||||
}
|
||||
return buffer.forget();
|
||||
}
|
||||
|
||||
bool IsRangeAt(const MediaSpan& aSpan, uint8_t aStart, uint8_t aEnd,
|
||||
size_t aAt) {
|
||||
size_t length = size_t(aEnd) - size_t(aStart) + 1;
|
||||
if (aAt + length > aSpan.Length()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (aSpan[aAt + i] != uint8_t(aStart + i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsRange(const MediaSpan& aSpan, uint8_t aStart, uint8_t aEnd) {
|
||||
return IsRangeAt(aSpan, aStart, aEnd, 0);
|
||||
}
|
||||
|
||||
TEST(MediaSpan, AppendToFromSpan)
|
||||
{
|
||||
RefPtr<MediaByteBuffer> buffer1 = makeBuffer(0, 9);
|
||||
MediaSpan span1 = MediaSpan(buffer1);
|
||||
EXPECT_EQ(span1.Length(), size_t(10));
|
||||
EXPECT_TRUE(IsRange(span1, 0, 9));
|
||||
|
||||
MediaSpan span2 = span1.From(5);
|
||||
|
||||
EXPECT_EQ(span2.Length(), size_t(5));
|
||||
EXPECT_TRUE(IsRange(span2, 5, 9));
|
||||
RefPtr<MediaByteBuffer> buffer2 = makeBuffer(10, 19);
|
||||
EXPECT_EQ(buffer2->Length(), size_t(10));
|
||||
span2.Append(buffer2);
|
||||
|
||||
// Span2 should be: [5...19]
|
||||
EXPECT_EQ(span2.Length(), size_t(15));
|
||||
EXPECT_TRUE(IsRange(span2, 5, 19));
|
||||
|
||||
// Span1 should not be modified by the append to span2.
|
||||
EXPECT_EQ(span1.Length(), size_t(10));
|
||||
EXPECT_TRUE(IsRange(span1, 0, 9));
|
||||
}
|
||||
|
||||
TEST(MediaSpan, AppendToToSpan)
|
||||
{
|
||||
RefPtr<MediaByteBuffer> buffer1 = makeBuffer(0, 9);
|
||||
MediaSpan span1 = MediaSpan(buffer1);
|
||||
EXPECT_EQ(span1.Length(), size_t(10));
|
||||
EXPECT_TRUE(IsRange(span1, 0, 9));
|
||||
|
||||
MediaSpan span2 = span1.To(5);
|
||||
|
||||
// Span2 should be [0...4]
|
||||
EXPECT_EQ(span2.Length(), size_t(5));
|
||||
EXPECT_TRUE(IsRange(span2, 0, 4));
|
||||
RefPtr<MediaByteBuffer> buffer2 = makeBuffer(10, 19);
|
||||
EXPECT_EQ(buffer2->Length(), size_t(10));
|
||||
span2.Append(buffer2);
|
||||
|
||||
// Span2 should be: [0...4][10...19]
|
||||
EXPECT_EQ(span2.Length(), size_t(15));
|
||||
EXPECT_TRUE(IsRangeAt(span2, 0, 4, 0));
|
||||
EXPECT_TRUE(IsRangeAt(span2, 10, 19, 5));
|
||||
|
||||
// Span1 should not be modified by the append to span2.
|
||||
EXPECT_EQ(span1.Length(), size_t(10));
|
||||
EXPECT_TRUE(IsRange(span1, 0, 9));
|
||||
}
|
||||
|
||||
TEST(MediaSpan, RemoveFront)
|
||||
{
|
||||
RefPtr<MediaByteBuffer> buffer1 = makeBuffer(0, 9);
|
||||
MediaSpan span1 = MediaSpan(buffer1);
|
||||
EXPECT_EQ(span1.Length(), size_t(10));
|
||||
EXPECT_TRUE(IsRange(span1, 0, 9));
|
||||
|
||||
MediaSpan span2(span1);
|
||||
EXPECT_EQ(span2.Length(), size_t(10));
|
||||
|
||||
span2.RemoveFront(5);
|
||||
|
||||
// Span2 should now be [5...9]
|
||||
EXPECT_EQ(span2.Length(), size_t(5));
|
||||
EXPECT_TRUE(IsRange(span2, 5, 9));
|
||||
|
||||
// Span1 should be unaffected.
|
||||
EXPECT_EQ(span1.Length(), size_t(10));
|
||||
EXPECT_TRUE(IsRange(span1, 0, 9));
|
||||
}
|
|
@ -34,6 +34,7 @@ UNIFIED_SOURCES += [
|
|||
'TestMediaDataEncoder.cpp',
|
||||
'TestMediaEventSource.cpp',
|
||||
'TestMediaMIMETypes.cpp',
|
||||
'TestMediaSpan.cpp',
|
||||
'TestMP3Demuxer.cpp',
|
||||
'TestMP4Demuxer.cpp',
|
||||
'TestOpusParser.cpp',
|
||||
|
|
|
@ -51,25 +51,23 @@ ContainerParser::ContainerParser(const MediaContainerType& aType)
|
|||
|
||||
ContainerParser::~ContainerParser() = default;
|
||||
|
||||
MediaResult ContainerParser::IsInitSegmentPresent(MediaByteBuffer* aData) {
|
||||
MSE_DEBUG("aLength=%zu [%x%x%x%x]", aData->Length(),
|
||||
aData->Length() > 0 ? (*aData)[0] : 0,
|
||||
aData->Length() > 1 ? (*aData)[1] : 0,
|
||||
aData->Length() > 2 ? (*aData)[2] : 0,
|
||||
aData->Length() > 3 ? (*aData)[3] : 0);
|
||||
MediaResult ContainerParser::IsInitSegmentPresent(const MediaSpan& aData) {
|
||||
MSE_DEBUG(
|
||||
"aLength=%zu [%x%x%x%x]", aData.Length(),
|
||||
aData.Length() > 0 ? aData[0] : 0, aData.Length() > 1 ? aData[1] : 0,
|
||||
aData.Length() > 2 ? aData[2] : 0, aData.Length() > 3 ? aData[3] : 0);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
MediaResult ContainerParser::IsMediaSegmentPresent(MediaByteBuffer* aData) {
|
||||
MSE_DEBUG("aLength=%zu [%x%x%x%x]", aData->Length(),
|
||||
aData->Length() > 0 ? (*aData)[0] : 0,
|
||||
aData->Length() > 1 ? (*aData)[1] : 0,
|
||||
aData->Length() > 2 ? (*aData)[2] : 0,
|
||||
aData->Length() > 3 ? (*aData)[3] : 0);
|
||||
MediaResult ContainerParser::IsMediaSegmentPresent(const MediaSpan& aData) {
|
||||
MSE_DEBUG(
|
||||
"aLength=%zu [%x%x%x%x]", aData.Length(),
|
||||
aData.Length() > 0 ? aData[0] : 0, aData.Length() > 1 ? aData[1] : 0,
|
||||
aData.Length() > 2 ? aData[2] : 0, aData.Length() > 3 ? aData[3] : 0);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
MediaResult ContainerParser::ParseStartAndEndTimestamps(MediaByteBuffer* aData,
|
||||
MediaResult ContainerParser::ParseStartAndEndTimestamps(const MediaSpan& aData,
|
||||
int64_t& aStart,
|
||||
int64_t& aEnd) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
@ -113,9 +111,9 @@ class WebMContainerParser
|
|||
|
||||
static const unsigned NS_PER_USEC = 1000;
|
||||
|
||||
MediaResult IsInitSegmentPresent(MediaByteBuffer* aData) override {
|
||||
MediaResult IsInitSegmentPresent(const MediaSpan& aData) override {
|
||||
ContainerParser::IsInitSegmentPresent(aData);
|
||||
if (aData->Length() < 4) {
|
||||
if (aData.Length() < 4) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
|
@ -123,7 +121,7 @@ class WebMContainerParser
|
|||
nsTArray<WebMTimeDataOffset> mapping;
|
||||
ReentrantMonitor dummy("dummy");
|
||||
bool result =
|
||||
parser.Append(aData->Elements(), aData->Length(), mapping, dummy);
|
||||
parser.Append(aData.Elements(), aData.Length(), mapping, dummy);
|
||||
if (!result) {
|
||||
return MediaResult(NS_ERROR_FAILURE,
|
||||
RESULT_DETAIL("Invalid webm content"));
|
||||
|
@ -131,9 +129,9 @@ class WebMContainerParser
|
|||
return parser.mInitEndOffset > 0 ? NS_OK : NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
MediaResult IsMediaSegmentPresent(MediaByteBuffer* aData) override {
|
||||
MediaResult IsMediaSegmentPresent(const MediaSpan& aData) override {
|
||||
ContainerParser::IsMediaSegmentPresent(aData);
|
||||
if (aData->Length() < 4) {
|
||||
if (aData.Length() < 4) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
|
@ -142,7 +140,7 @@ class WebMContainerParser
|
|||
ReentrantMonitor dummy("dummy");
|
||||
parser.AppendMediaSegmentOnly();
|
||||
bool result =
|
||||
parser.Append(aData->Elements(), aData->Length(), mapping, dummy);
|
||||
parser.Append(aData.Elements(), aData.Length(), mapping, dummy);
|
||||
if (!result) {
|
||||
return MediaResult(NS_ERROR_FAILURE,
|
||||
RESULT_DETAIL("Invalid webm content"));
|
||||
|
@ -150,7 +148,7 @@ class WebMContainerParser
|
|||
return parser.GetClusterOffset() >= 0 ? NS_OK : NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
MediaResult ParseStartAndEndTimestamps(MediaByteBuffer* aData,
|
||||
MediaResult ParseStartAndEndTimestamps(const MediaSpan& aData,
|
||||
int64_t& aStart,
|
||||
int64_t& aEnd) override {
|
||||
bool initSegment = NS_SUCCEEDED(IsInitSegmentPresent(aData));
|
||||
|
@ -188,7 +186,7 @@ class WebMContainerParser
|
|||
mapping.AppendElements(mOverlappedMapping);
|
||||
mOverlappedMapping.Clear();
|
||||
ReentrantMonitor dummy("dummy");
|
||||
mParser.Append(aData->Elements(), aData->Length(), mapping, dummy);
|
||||
mParser.Append(aData.Elements(), aData.Length(), mapping, dummy);
|
||||
if (mResource) {
|
||||
mResource->AppendData(aData);
|
||||
}
|
||||
|
@ -214,8 +212,8 @@ class WebMContainerParser
|
|||
}
|
||||
mHasInitData = true;
|
||||
}
|
||||
mOffset += aData->Length();
|
||||
mTotalParsed += aData->Length();
|
||||
mOffset += aData.Length();
|
||||
mTotalParsed += aData.Length();
|
||||
|
||||
if (mapping.IsEmpty()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
@ -378,12 +376,12 @@ class MP4ContainerParser : public ContainerParser,
|
|||
explicit MP4ContainerParser(const MediaContainerType& aType)
|
||||
: ContainerParser(aType) {}
|
||||
|
||||
MediaResult IsInitSegmentPresent(MediaByteBuffer* aData) override {
|
||||
MediaResult IsInitSegmentPresent(const MediaSpan& aData) override {
|
||||
ContainerParser::IsInitSegmentPresent(aData);
|
||||
// Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
|
||||
// file is the 'ftyp' atom followed by a file type. We just check for a
|
||||
// vaguely valid 'ftyp' atom.
|
||||
if (aData->Length() < 8) {
|
||||
if (aData.Length() < 8) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
AtomParser parser(*this, aData, AtomParser::StopAt::eInitSegment);
|
||||
|
@ -395,8 +393,8 @@ class MP4ContainerParser : public ContainerParser,
|
|||
return parser.StartWithInitSegment() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
MediaResult IsMediaSegmentPresent(MediaByteBuffer* aData) override {
|
||||
if (aData->Length() < 8) {
|
||||
MediaResult IsMediaSegmentPresent(const MediaSpan& aData) override {
|
||||
if (aData.Length() < 8) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
AtomParser parser(*this, aData, AtomParser::StopAt::eMediaSegment);
|
||||
|
@ -413,13 +411,13 @@ class MP4ContainerParser : public ContainerParser,
|
|||
public:
|
||||
enum class StopAt { eInitSegment, eMediaSegment, eEnd };
|
||||
|
||||
AtomParser(const MP4ContainerParser& aParser, const MediaByteBuffer* aData,
|
||||
AtomParser(const MP4ContainerParser& aParser, const MediaSpan& aData,
|
||||
StopAt aStop = StopAt::eEnd) {
|
||||
mValid = Init(aParser, aData, aStop).isOk();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> Init(const MP4ContainerParser& aParser,
|
||||
const MediaByteBuffer* aData, StopAt aStop) {
|
||||
const MediaSpan& aData, StopAt aStop) {
|
||||
const MediaContainerType mType(
|
||||
aParser.ContainerType()); // for logging macro.
|
||||
BufferReader reader(aData);
|
||||
|
@ -519,7 +517,7 @@ class MP4ContainerParser : public ContainerParser,
|
|||
};
|
||||
|
||||
public:
|
||||
MediaResult ParseStartAndEndTimestamps(MediaByteBuffer* aData,
|
||||
MediaResult ParseStartAndEndTimestamps(const MediaSpan& aData,
|
||||
int64_t& aStart,
|
||||
int64_t& aEnd) override {
|
||||
bool initSegment = NS_SUCCEEDED(IsInitSegmentPresent(aData));
|
||||
|
@ -540,7 +538,7 @@ class MP4ContainerParser : public ContainerParser,
|
|||
mCompleteMediaSegmentRange = MediaByteRange();
|
||||
mGlobalOffset = mTotalParsed;
|
||||
} else if (!mStream || !mParser) {
|
||||
mTotalParsed += aData->Length();
|
||||
mTotalParsed += aData.Length();
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
|
@ -566,7 +564,7 @@ class MP4ContainerParser : public ContainerParser,
|
|||
}
|
||||
mHasInitData = true;
|
||||
}
|
||||
mTotalParsed += aData->Length();
|
||||
mTotalParsed += aData.Length();
|
||||
|
||||
MP4Interval<Microseconds> compositionRange =
|
||||
mParser->GetCompositionRange(byteRanges);
|
||||
|
@ -576,13 +574,8 @@ class MP4ContainerParser : public ContainerParser,
|
|||
mCompleteMediaSegmentRange =
|
||||
mParser->FirstCompleteMediaSegment() + mGlobalOffset;
|
||||
|
||||
ErrorResult rv;
|
||||
if (HasCompleteInitData()) {
|
||||
mResource->EvictData(mParser->mOffset, mParser->mOffset, rv);
|
||||
}
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
mResource->EvictData(mParser->mOffset, mParser->mOffset);
|
||||
}
|
||||
|
||||
if (compositionRange.IsNull()) {
|
||||
|
@ -625,35 +618,32 @@ class ADTSContainerParser
|
|||
/// Returns true if the header is parsed successfully.
|
||||
/// Returns false if the header is invalid or incomplete,
|
||||
/// without modifying the passed-in Header object.
|
||||
bool Parse(MediaByteBuffer* aData, Header& header) {
|
||||
MOZ_ASSERT(aData);
|
||||
|
||||
bool Parse(const MediaSpan& aData, Header& header) {
|
||||
// ADTS initialization segments are just the packet header.
|
||||
if (aData->Length() < 7) {
|
||||
if (aData.Length() < 7) {
|
||||
MSE_DEBUG("buffer too short for header.");
|
||||
return false;
|
||||
}
|
||||
// Check 0xfffx sync word plus layer 0.
|
||||
if (((*aData)[0] != 0xff) || (((*aData)[1] & 0xf6) != 0xf0)) {
|
||||
if ((aData[0] != 0xff) || ((aData[1] & 0xf6) != 0xf0)) {
|
||||
MSE_DEBUG("no syncword.");
|
||||
return false;
|
||||
}
|
||||
bool have_crc = !((*aData)[1] & 0x01);
|
||||
if (have_crc && aData->Length() < 9) {
|
||||
bool have_crc = !(aData[1] & 0x01);
|
||||
if (have_crc && aData.Length() < 9) {
|
||||
MSE_DEBUG("buffer too short for header with crc.");
|
||||
return false;
|
||||
}
|
||||
uint8_t frequency_index = ((*aData)[2] & 0x3c) >> 2;
|
||||
uint8_t frequency_index = (aData[2] & 0x3c) >> 2;
|
||||
MOZ_ASSERT(frequency_index < 16);
|
||||
if (frequency_index == 15) {
|
||||
MSE_DEBUG("explicit frequency disallowed.");
|
||||
return false;
|
||||
}
|
||||
size_t header_length = have_crc ? 9 : 7;
|
||||
size_t data_length = (((*aData)[3] & 0x03) << 11) |
|
||||
(((*aData)[4] & 0xff) << 3) |
|
||||
(((*aData)[5] & 0xe0) >> 5);
|
||||
uint8_t frames = ((*aData)[6] & 0x03) + 1;
|
||||
size_t data_length = ((aData[3] & 0x03) << 11) | ((aData[4] & 0xff) << 3) |
|
||||
((aData[5] & 0xe0) >> 5);
|
||||
uint8_t frames = (aData[6] & 0x03) + 1;
|
||||
MOZ_ASSERT(frames > 0);
|
||||
MOZ_ASSERT(frames < 4);
|
||||
|
||||
|
@ -665,7 +655,7 @@ class ADTSContainerParser
|
|||
return true;
|
||||
}
|
||||
|
||||
MediaResult IsInitSegmentPresent(MediaByteBuffer* aData) override {
|
||||
MediaResult IsInitSegmentPresent(const MediaSpan& aData) override {
|
||||
// Call superclass for logging.
|
||||
ContainerParser::IsInitSegmentPresent(aData);
|
||||
|
||||
|
@ -681,7 +671,7 @@ class ADTSContainerParser
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
MediaResult IsMediaSegmentPresent(MediaByteBuffer* aData) override {
|
||||
MediaResult IsMediaSegmentPresent(const MediaSpan& aData) override {
|
||||
// Call superclass for logging.
|
||||
ContainerParser::IsMediaSegmentPresent(aData);
|
||||
|
||||
|
@ -697,7 +687,7 @@ class ADTSContainerParser
|
|||
// We're supposed to return true as long as aData contains the
|
||||
// start of a media segment, whether or not it's complete. So
|
||||
// return true if we have any data beyond the header.
|
||||
if (aData->Length() <= header.header_length) {
|
||||
if (aData.Length() <= header.header_length) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
|
@ -705,7 +695,7 @@ class ADTSContainerParser
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
MediaResult ParseStartAndEndTimestamps(MediaByteBuffer* aData,
|
||||
MediaResult ParseStartAndEndTimestamps(const MediaSpan& aData,
|
||||
int64_t& aStart,
|
||||
int64_t& aEnd) override {
|
||||
// ADTS header.
|
||||
|
@ -719,15 +709,15 @@ class ADTSContainerParser
|
|||
|
||||
// Cache raw header in case the caller wants a copy.
|
||||
mInitData = new MediaByteBuffer(header.header_length);
|
||||
mInitData->AppendElements(aData->Elements(), header.header_length);
|
||||
mInitData->AppendElements(aData.Elements(), header.header_length);
|
||||
|
||||
// Check that we have enough data for the frame body.
|
||||
if (aData->Length() < header.frame_length) {
|
||||
if (aData.Length() < header.frame_length) {
|
||||
MSE_DEBUGV(
|
||||
"Not enough data for %llu byte frame"
|
||||
" in %llu byte buffer.",
|
||||
(unsigned long long)header.frame_length,
|
||||
(unsigned long long)(aData->Length()));
|
||||
(unsigned long long)(aData.Length()));
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
mCompleteMediaSegmentRange =
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define MOZILLA_CONTAINERPARSER_H_
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "MediaSpan.h"
|
||||
#include "MediaContainerType.h"
|
||||
#include "MediaResource.h"
|
||||
#include "MediaResult.h"
|
||||
|
@ -30,7 +31,7 @@ class ContainerParser : public DecoderDoctorLifeLogger<ContainerParser> {
|
|||
// Return NS_OK if segment is present, NS_ERROR_NOT_AVAILABLE if no sufficient
|
||||
// data is currently available to make a determination. Any other value
|
||||
// indicates an error.
|
||||
virtual MediaResult IsInitSegmentPresent(MediaByteBuffer* aData);
|
||||
virtual MediaResult IsInitSegmentPresent(const MediaSpan& aData);
|
||||
|
||||
// Return true if aData starts with a media segment.
|
||||
// The base implementation exists only for debug logging and is expected
|
||||
|
@ -38,13 +39,13 @@ class ContainerParser : public DecoderDoctorLifeLogger<ContainerParser> {
|
|||
// Return NS_OK if segment is present, NS_ERROR_NOT_AVAILABLE if no sufficient
|
||||
// data is currently available to make a determination. Any other value
|
||||
// indicates an error.
|
||||
virtual MediaResult IsMediaSegmentPresent(MediaByteBuffer* aData);
|
||||
virtual MediaResult IsMediaSegmentPresent(const MediaSpan& aData);
|
||||
|
||||
// Parse aData to extract the start and end frame times from the media
|
||||
// segment. aData may not start on a parser sync boundary. Return NS_OK
|
||||
// if aStart and aEnd have been updated and NS_ERROR_NOT_AVAILABLE otherwise
|
||||
// when no error were encountered.
|
||||
virtual MediaResult ParseStartAndEndTimestamps(MediaByteBuffer* aData,
|
||||
virtual MediaResult ParseStartAndEndTimestamps(const MediaSpan& aData,
|
||||
int64_t& aStart,
|
||||
int64_t& aEnd);
|
||||
|
||||
|
|
|
@ -24,17 +24,11 @@ extern mozilla::LogModule* GetSourceBufferResourceLog();
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
ResourceItem::ResourceItem(MediaByteBuffer* aData, uint64_t aOffset)
|
||||
ResourceItem::ResourceItem(const MediaSpan& aData, uint64_t aOffset)
|
||||
: mData(aData), mOffset(aOffset) {}
|
||||
|
||||
size_t ResourceItem::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
|
||||
// size including this
|
||||
size_t size = aMallocSizeOf(this);
|
||||
|
||||
// size excluding this
|
||||
size += mData->ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
|
||||
return size;
|
||||
return aMallocSizeOf(this);
|
||||
}
|
||||
|
||||
class ResourceQueueDeallocator : public nsDequeFunctor {
|
||||
|
@ -58,10 +52,10 @@ const uint8_t* ResourceQueue::GetContiguousAccess(int64_t aOffset,
|
|||
return nullptr;
|
||||
}
|
||||
ResourceItem* item = ResourceAt(start);
|
||||
if (offset + aSize > item->mData->Length()) {
|
||||
if (offset + aSize > item->mData.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
return item->mData->Elements() + offset;
|
||||
return item->mData.Elements() + offset;
|
||||
}
|
||||
|
||||
void ResourceQueue::CopyData(uint64_t aOffset, uint32_t aCount, char* aDest) {
|
||||
|
@ -70,9 +64,9 @@ void ResourceQueue::CopyData(uint64_t aOffset, uint32_t aCount, char* aDest) {
|
|||
size_t i = start;
|
||||
while (i < uint32_t(GetSize()) && aCount > 0) {
|
||||
ResourceItem* item = ResourceAt(i++);
|
||||
uint32_t bytes = std::min(aCount, uint32_t(item->mData->Length() - offset));
|
||||
uint32_t bytes = std::min(aCount, uint32_t(item->mData.Length() - offset));
|
||||
if (bytes != 0) {
|
||||
memcpy(aDest, &(*item->mData)[offset], bytes);
|
||||
memcpy(aDest, item->mData.Elements() + offset, bytes);
|
||||
offset = 0;
|
||||
aCount -= bytes;
|
||||
aDest += bytes;
|
||||
|
@ -80,44 +74,37 @@ void ResourceQueue::CopyData(uint64_t aOffset, uint32_t aCount, char* aDest) {
|
|||
}
|
||||
}
|
||||
|
||||
void ResourceQueue::AppendItem(MediaByteBuffer* aData) {
|
||||
void ResourceQueue::AppendItem(const MediaSpan& aData) {
|
||||
uint64_t offset = mLogicalLength;
|
||||
mLogicalLength += aData->Length();
|
||||
mLogicalLength += aData.Length();
|
||||
Push(new ResourceItem(aData, offset));
|
||||
}
|
||||
|
||||
uint32_t ResourceQueue::Evict(uint64_t aOffset, uint32_t aSizeToEvict,
|
||||
ErrorResult& aRv) {
|
||||
uint32_t ResourceQueue::Evict(uint64_t aOffset, uint32_t aSizeToEvict) {
|
||||
SBR_DEBUG("Evict(aOffset=%" PRIu64 ", aSizeToEvict=%u)", aOffset,
|
||||
aSizeToEvict);
|
||||
return EvictBefore(std::min(aOffset, mOffset + (uint64_t)aSizeToEvict), aRv);
|
||||
return EvictBefore(std::min(aOffset, mOffset + (uint64_t)aSizeToEvict));
|
||||
}
|
||||
|
||||
uint32_t ResourceQueue::EvictBefore(uint64_t aOffset, ErrorResult& aRv) {
|
||||
uint32_t ResourceQueue::EvictBefore(uint64_t aOffset) {
|
||||
SBR_DEBUG("EvictBefore(%" PRIu64 ")", aOffset);
|
||||
uint32_t evicted = 0;
|
||||
while (ResourceItem* item = ResourceAt(0)) {
|
||||
SBR_DEBUG("item=%p length=%zu offset=%" PRIu64, item, item->mData->Length(),
|
||||
SBR_DEBUG("item=%p length=%zu offset=%" PRIu64, item, item->mData.Length(),
|
||||
mOffset);
|
||||
if (item->mData->Length() + mOffset >= aOffset) {
|
||||
if (item->mData.Length() + mOffset >= aOffset) {
|
||||
if (aOffset <= mOffset) {
|
||||
break;
|
||||
}
|
||||
uint32_t offset = aOffset - mOffset;
|
||||
mOffset += offset;
|
||||
evicted += offset;
|
||||
RefPtr<MediaByteBuffer> data = new MediaByteBuffer;
|
||||
if (!data->AppendElements(item->mData->Elements() + offset,
|
||||
item->mData->Length() - offset, fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return 0;
|
||||
}
|
||||
item->mData.RemoveFront(offset);
|
||||
item->mOffset += offset;
|
||||
item->mData = data;
|
||||
break;
|
||||
}
|
||||
mOffset += item->mData->Length();
|
||||
evicted += item->mData->Length();
|
||||
mOffset += item->mData.Length();
|
||||
evicted += item->mData.Length();
|
||||
delete PopFront();
|
||||
}
|
||||
return evicted;
|
||||
|
@ -127,10 +114,10 @@ uint32_t ResourceQueue::EvictAll() {
|
|||
SBR_DEBUG("EvictAll()");
|
||||
uint32_t evicted = 0;
|
||||
while (ResourceItem* item = ResourceAt(0)) {
|
||||
SBR_DEBUG("item=%p length=%zu offset=%" PRIu64, item, item->mData->Length(),
|
||||
SBR_DEBUG("item=%p length=%zu offset=%" PRIu64, item, item->mData.Length(),
|
||||
mOffset);
|
||||
mOffset += item->mData->Length();
|
||||
evicted += item->mData->Length();
|
||||
mOffset += item->mData.Length();
|
||||
evicted += item->mData.Length();
|
||||
delete PopFront();
|
||||
}
|
||||
return evicted;
|
||||
|
@ -140,10 +127,20 @@ size_t ResourceQueue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
|
|||
// Calculate the size of the internal deque.
|
||||
size_t size = nsDeque::SizeOfExcludingThis(aMallocSizeOf);
|
||||
|
||||
// Sum the ResourceItems.
|
||||
// Sum the ResourceItems. The ResourceItems's MediaSpans may share the
|
||||
// same underlying MediaByteBuffers, so we need to de-dupe the buffers
|
||||
// in order to report an accurate size.
|
||||
nsTArray<MediaByteBuffer*> buffers;
|
||||
for (uint32_t i = 0; i < uint32_t(GetSize()); ++i) {
|
||||
const ResourceItem* item = ResourceAt(i);
|
||||
size += item->SizeOfIncludingThis(aMallocSizeOf);
|
||||
if (!buffers.Contains(item->mData.Buffer())) {
|
||||
buffers.AppendElement(item->mData.Buffer());
|
||||
}
|
||||
}
|
||||
|
||||
for (MediaByteBuffer* buffer : buffers) {
|
||||
size += buffer->ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
return size;
|
||||
|
@ -160,7 +157,7 @@ void ResourceQueue::Dump(const char* aPath) {
|
|||
if (!fp) {
|
||||
return;
|
||||
}
|
||||
Unused << fwrite(item->mData->Elements(), item->mData->Length(), 1, fp);
|
||||
Unused << fwrite(item->mData.Elements(), item->mData.Length(), 1, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
@ -180,13 +177,13 @@ uint32_t ResourceQueue::GetAtOffset(uint64_t aOffset,
|
|||
size_t mid = lo + (hi - lo) / 2;
|
||||
const ResourceItem* resource = ResourceAt(mid);
|
||||
if (resource->mOffset <= aOffset &&
|
||||
aOffset < resource->mOffset + resource->mData->Length()) {
|
||||
aOffset < resource->mOffset + resource->mData.Length()) {
|
||||
if (aResourceOffset) {
|
||||
*aResourceOffset = aOffset - resource->mOffset;
|
||||
}
|
||||
return uint32_t(mid);
|
||||
}
|
||||
if (resource->mOffset + resource->mData->Length() <= aOffset) {
|
||||
if (resource->mOffset + resource->mData.Length() <= aOffset) {
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hi = mid;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#define MOZILLA_RESOURCEQUEUE_H_
|
||||
|
||||
#include "nsDeque.h"
|
||||
#include "MediaData.h"
|
||||
#include "MediaSpan.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -26,9 +26,9 @@ class ErrorResult;
|
|||
// timepoint.
|
||||
|
||||
struct ResourceItem {
|
||||
ResourceItem(MediaByteBuffer* aData, uint64_t aOffset);
|
||||
ResourceItem(const MediaSpan& aData, uint64_t aOffset);
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
|
||||
RefPtr<MediaByteBuffer> mData;
|
||||
MediaSpan mData;
|
||||
uint64_t mOffset;
|
||||
};
|
||||
|
||||
|
@ -46,13 +46,13 @@ class ResourceQueue : private nsDeque {
|
|||
// Copies aCount bytes from aOffset in the queue into aDest.
|
||||
void CopyData(uint64_t aOffset, uint32_t aCount, char* aDest);
|
||||
|
||||
void AppendItem(MediaByteBuffer* aData);
|
||||
void AppendItem(const MediaSpan& aData);
|
||||
|
||||
// Tries to evict at least aSizeToEvict from the queue up until
|
||||
// aOffset. Returns amount evicted.
|
||||
uint32_t Evict(uint64_t aOffset, uint32_t aSizeToEvict, ErrorResult& aRv);
|
||||
uint32_t Evict(uint64_t aOffset, uint32_t aSizeToEvict);
|
||||
|
||||
uint32_t EvictBefore(uint64_t aOffset, ErrorResult& aRv);
|
||||
uint32_t EvictBefore(uint64_t aOffset);
|
||||
|
||||
uint32_t EvictAll();
|
||||
|
||||
|
|
|
@ -79,21 +79,21 @@ nsresult SourceBufferResource::ReadFromCache(char* aBuffer, int64_t aOffset,
|
|||
}
|
||||
|
||||
uint32_t SourceBufferResource::EvictData(uint64_t aPlaybackOffset,
|
||||
int64_t aThreshold, ErrorResult& aRv) {
|
||||
int64_t aThreshold) {
|
||||
MOZ_ASSERT(OnThread());
|
||||
SBR_DEBUG("EvictData(aPlaybackOffset=%" PRIu64
|
||||
","
|
||||
"aThreshold=%" PRId64 ")",
|
||||
aPlaybackOffset, aThreshold);
|
||||
uint32_t result = mInputBuffer.Evict(aPlaybackOffset, aThreshold, aRv);
|
||||
uint32_t result = mInputBuffer.Evict(aPlaybackOffset, aThreshold);
|
||||
return result;
|
||||
}
|
||||
|
||||
void SourceBufferResource::EvictBefore(uint64_t aOffset, ErrorResult& aRv) {
|
||||
void SourceBufferResource::EvictBefore(uint64_t aOffset) {
|
||||
MOZ_ASSERT(OnThread());
|
||||
SBR_DEBUG("EvictBefore(aOffset=%" PRIu64 ")", aOffset);
|
||||
|
||||
mInputBuffer.EvictBefore(aOffset, aRv);
|
||||
mInputBuffer.EvictBefore(aOffset);
|
||||
}
|
||||
|
||||
uint32_t SourceBufferResource::EvictAll() {
|
||||
|
@ -103,9 +103,13 @@ uint32_t SourceBufferResource::EvictAll() {
|
|||
}
|
||||
|
||||
void SourceBufferResource::AppendData(MediaByteBuffer* aData) {
|
||||
AppendData(MediaSpan(aData));
|
||||
}
|
||||
|
||||
void SourceBufferResource::AppendData(const MediaSpan& aData) {
|
||||
MOZ_ASSERT(OnThread());
|
||||
SBR_DEBUG("AppendData(aData=%p, aLength=%zu)", aData->Elements(),
|
||||
aData->Length());
|
||||
SBR_DEBUG("AppendData(aData=%p, aLength=%zu)", aData.Elements(),
|
||||
aData.Length());
|
||||
mInputBuffer.AppendItem(aData);
|
||||
mEnded = false;
|
||||
}
|
||||
|
|
|
@ -88,6 +88,7 @@ class SourceBufferResource final
|
|||
|
||||
// Used by SourceBuffer.
|
||||
void AppendData(MediaByteBuffer* aData);
|
||||
void AppendData(const MediaSpan& aData);
|
||||
void Ended();
|
||||
bool IsEnded() {
|
||||
MOZ_ASSERT(OnThread());
|
||||
|
@ -95,11 +96,10 @@ class SourceBufferResource final
|
|||
}
|
||||
// Remove data from resource if it holds more than the threshold reduced by
|
||||
// the given number of bytes. Returns amount evicted.
|
||||
uint32_t EvictData(uint64_t aPlaybackOffset, int64_t aThresholdReduct,
|
||||
ErrorResult& aRv);
|
||||
uint32_t EvictData(uint64_t aPlaybackOffset, int64_t aThresholdReduct);
|
||||
|
||||
// Remove data from resource before the given offset.
|
||||
void EvictBefore(uint64_t aOffset, ErrorResult& aRv);
|
||||
void EvictBefore(uint64_t aOffset);
|
||||
|
||||
// Remove all data from the resource
|
||||
uint32_t EvictAll();
|
||||
|
|
|
@ -93,8 +93,7 @@ class DispatchKeyNeededEvent : public Runnable {
|
|||
|
||||
TrackBuffersManager::TrackBuffersManager(MediaSourceDecoder* aParentDecoder,
|
||||
const MediaContainerType& aType)
|
||||
: mInputBuffer(new MediaByteBuffer),
|
||||
mBufferFull(false),
|
||||
: mBufferFull(false),
|
||||
mFirstInitializationSegmentReceived(false),
|
||||
mChangeTypeReceived(false),
|
||||
mNewMediaSegmentStarted(false),
|
||||
|
@ -211,10 +210,11 @@ void TrackBuffersManager::ProcessTasks() {
|
|||
switch (task->GetType()) {
|
||||
case Type::AppendBuffer:
|
||||
mCurrentTask = task;
|
||||
if (!mInputBuffer) {
|
||||
mInputBuffer = task->As<AppendBufferTask>()->mBuffer;
|
||||
} else if (!mInputBuffer->AppendElements(
|
||||
*task->As<AppendBufferTask>()->mBuffer, fallible)) {
|
||||
if (!mInputBuffer || mInputBuffer->IsEmpty()) {
|
||||
// Note: we reset mInputBuffer here to ensure it doesn't grow unbounded.
|
||||
mInputBuffer.reset();
|
||||
mInputBuffer = Some(MediaSpan(task->As<AppendBufferTask>()->mBuffer));
|
||||
} else if (!mInputBuffer->Append(task->As<AppendBufferTask>()->mBuffer)) {
|
||||
RejectAppend(NS_ERROR_OUT_OF_MEMORY, __func__);
|
||||
return;
|
||||
}
|
||||
|
@ -448,7 +448,7 @@ void TrackBuffersManager::CompleteResetParserState() {
|
|||
|
||||
// 7. Remove all bytes from the input buffer.
|
||||
mPendingInputBuffer = nullptr;
|
||||
mInputBuffer = nullptr;
|
||||
mInputBuffer.reset();
|
||||
if (mCurrentInputBuffer) {
|
||||
mCurrentInputBuffer->EvictAll();
|
||||
// The demuxer will be recreated during the next run of SegmentParserLoop.
|
||||
|
@ -470,8 +470,7 @@ void TrackBuffersManager::CompleteResetParserState() {
|
|||
CreateDemuxerforMIMEType();
|
||||
// Recreate our input buffer. We can't directly assign the initData buffer
|
||||
// to mInputBuffer as it will get modified in the Segment Parser Loop.
|
||||
mInputBuffer = new MediaByteBuffer;
|
||||
mInputBuffer->AppendElements(*mInitData);
|
||||
mInputBuffer = Some(MediaSpan::WithCopyOf(mInitData));
|
||||
RecreateParser(true);
|
||||
} else {
|
||||
RecreateParser(false);
|
||||
|
@ -705,7 +704,8 @@ void TrackBuffersManager::SegmentParserLoop() {
|
|||
// steps:
|
||||
if (mSourceBufferAttributes->GetAppendState() ==
|
||||
AppendState::WAITING_FOR_SEGMENT) {
|
||||
MediaResult haveInitSegment = mParser->IsInitSegmentPresent(mInputBuffer);
|
||||
MediaResult haveInitSegment =
|
||||
mParser->IsInitSegmentPresent(*mInputBuffer);
|
||||
if (NS_SUCCEEDED(haveInitSegment)) {
|
||||
SetAppendState(AppendState::PARSING_INIT_SEGMENT);
|
||||
if (mFirstInitializationSegmentReceived && !mChangeTypeReceived) {
|
||||
|
@ -715,7 +715,7 @@ void TrackBuffersManager::SegmentParserLoop() {
|
|||
continue;
|
||||
}
|
||||
MediaResult haveMediaSegment =
|
||||
mParser->IsMediaSegmentPresent(mInputBuffer);
|
||||
mParser->IsMediaSegmentPresent(*mInputBuffer);
|
||||
if (NS_SUCCEEDED(haveMediaSegment)) {
|
||||
SetAppendState(AppendState::PARSING_MEDIA_SEGMENT);
|
||||
mNewMediaSegmentStarted = true;
|
||||
|
@ -740,7 +740,7 @@ void TrackBuffersManager::SegmentParserLoop() {
|
|||
|
||||
int64_t start, end;
|
||||
MediaResult newData =
|
||||
mParser->ParseStartAndEndTimestamps(mInputBuffer, start, end);
|
||||
mParser->ParseStartAndEndTimestamps(*mInputBuffer, start, end);
|
||||
if (!NS_SUCCEEDED(newData) && newData.Code() != NS_ERROR_NOT_AVAILABLE) {
|
||||
RejectAppend(newData, __func__);
|
||||
return;
|
||||
|
@ -752,7 +752,7 @@ void TrackBuffersManager::SegmentParserLoop() {
|
|||
if (mSourceBufferAttributes->GetAppendState() ==
|
||||
AppendState::PARSING_INIT_SEGMENT) {
|
||||
if (mParser->InitSegmentRange().IsEmpty()) {
|
||||
mInputBuffer = nullptr;
|
||||
mInputBuffer.reset();
|
||||
NeedMoreData();
|
||||
return;
|
||||
}
|
||||
|
@ -797,11 +797,12 @@ void TrackBuffersManager::SegmentParserLoop() {
|
|||
// 2. If the input buffer does not contain a complete media segment
|
||||
// header yet, then jump to the need more data step below.
|
||||
if (!mPendingInputBuffer) {
|
||||
mPendingInputBuffer = mInputBuffer;
|
||||
} else {
|
||||
mPendingInputBuffer->AppendElements(*mInputBuffer);
|
||||
mPendingInputBuffer = new MediaByteBuffer();
|
||||
}
|
||||
mInputBuffer = nullptr;
|
||||
mPendingInputBuffer->AppendElements(mInputBuffer->Elements(),
|
||||
mInputBuffer->Length());
|
||||
|
||||
mInputBuffer.reset();
|
||||
NeedMoreData();
|
||||
return;
|
||||
}
|
||||
|
@ -973,7 +974,8 @@ void TrackBuffersManager::OnDemuxerResetDone(const MediaResult& aResult) {
|
|||
// We had a partial media segment header stashed aside.
|
||||
// Reparse its content so we can continue parsing the current input buffer.
|
||||
int64_t start, end;
|
||||
mParser->ParseStartAndEndTimestamps(mPendingInputBuffer, start, end);
|
||||
mParser->ParseStartAndEndTimestamps(MediaSpan(mPendingInputBuffer), start,
|
||||
end);
|
||||
mProcessedInput += mPendingInputBuffer->Length();
|
||||
}
|
||||
|
||||
|
@ -981,12 +983,19 @@ void TrackBuffersManager::OnDemuxerResetDone(const MediaResult& aResult) {
|
|||
}
|
||||
|
||||
void TrackBuffersManager::AppendDataToCurrentInputBuffer(
|
||||
MediaByteBuffer* aData) {
|
||||
const MediaSpan& aData) {
|
||||
MOZ_ASSERT(mCurrentInputBuffer);
|
||||
mCurrentInputBuffer->AppendData(aData);
|
||||
mInputDemuxer->NotifyDataArrived();
|
||||
}
|
||||
|
||||
void TrackBuffersManager::AppendDataToCurrentInputBuffer(
|
||||
MediaByteBuffer* aData) {
|
||||
MOZ_ASSERT(mCurrentInputBuffer);
|
||||
mCurrentInputBuffer->AppendData(MediaSpan(aData));
|
||||
mInputDemuxer->NotifyDataArrived();
|
||||
}
|
||||
|
||||
void TrackBuffersManager::InitializationSegmentReceived() {
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(mParser->HasCompleteInitData());
|
||||
|
@ -1007,12 +1016,8 @@ void TrackBuffersManager::InitializationSegmentReceived() {
|
|||
// to the resource.
|
||||
mCurrentInputBuffer->AppendData(mParser->InitData());
|
||||
uint32_t length = endInit - (mProcessedInput - mInputBuffer->Length());
|
||||
if (mInputBuffer->Length() == length) {
|
||||
mInputBuffer = nullptr;
|
||||
} else {
|
||||
MOZ_RELEASE_ASSERT(length <= mInputBuffer->Length());
|
||||
mInputBuffer->RemoveElementsAt(0, length);
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(length <= mInputBuffer->Length());
|
||||
mInputBuffer->RemoveFront(length);
|
||||
CreateDemuxerforMIMEType();
|
||||
if (!mInputDemuxer) {
|
||||
NS_WARNING("TODO type not supported");
|
||||
|
@ -1337,8 +1342,8 @@ TrackBuffersManager::CodedFrameProcessing() {
|
|||
|
||||
MediaByteRange mediaRange = mParser->MediaSegmentRange();
|
||||
if (mediaRange.IsEmpty()) {
|
||||
AppendDataToCurrentInputBuffer(mInputBuffer);
|
||||
mInputBuffer = nullptr;
|
||||
AppendDataToCurrentInputBuffer(*mInputBuffer);
|
||||
mInputBuffer.reset();
|
||||
} else {
|
||||
MOZ_ASSERT(mProcessedInput >= mInputBuffer->Length());
|
||||
if (int64_t(mProcessedInput - mInputBuffer->Length()) > mediaRange.mEnd) {
|
||||
|
@ -1360,13 +1365,8 @@ TrackBuffersManager::CodedFrameProcessing() {
|
|||
CompleteCodedFrameProcessing();
|
||||
return p;
|
||||
}
|
||||
RefPtr<MediaByteBuffer> segment = new MediaByteBuffer;
|
||||
if (!segment->AppendElements(mInputBuffer->Elements(), length, fallible)) {
|
||||
return CodedFrameProcessingPromise::CreateAndReject(
|
||||
NS_ERROR_OUT_OF_MEMORY, __func__);
|
||||
}
|
||||
AppendDataToCurrentInputBuffer(segment);
|
||||
mInputBuffer->RemoveElementsAt(0, length);
|
||||
AppendDataToCurrentInputBuffer(mInputBuffer->To(length));
|
||||
mInputBuffer->RemoveFront(length);
|
||||
}
|
||||
|
||||
RefPtr<CodedFrameProcessingPromise> p = mProcessingPromise.Ensure(__func__);
|
||||
|
@ -1543,13 +1543,7 @@ void TrackBuffersManager::CompleteCodedFrameProcessing() {
|
|||
HasAudio() ? mAudioTracks.mDemuxer->GetEvictionOffset(
|
||||
mAudioTracks.mLastParsedEndTime)
|
||||
: INT64_MAX);
|
||||
ErrorResult rv;
|
||||
mCurrentInputBuffer->EvictBefore(safeToEvict, rv);
|
||||
if (rv.Failed()) {
|
||||
rv.SuppressException();
|
||||
RejectProcessing(NS_ERROR_OUT_OF_MEMORY, __func__);
|
||||
return;
|
||||
}
|
||||
mCurrentInputBuffer->EvictBefore(safeToEvict);
|
||||
|
||||
mInputDemuxer->NotifyDataRemoved();
|
||||
RecreateParser(true);
|
||||
|
@ -2252,7 +2246,7 @@ void TrackBuffersManager::RecreateParser(bool aReuseInitData) {
|
|||
DDLINKCHILD("parser", mParser.get());
|
||||
if (aReuseInitData && mInitData) {
|
||||
int64_t start, end;
|
||||
mParser->ParseStartAndEndTimestamps(mInitData, start, end);
|
||||
mParser->ParseStartAndEndTimestamps(MediaSpan(mInitData), start, end);
|
||||
mProcessedInput = mInitData->Length();
|
||||
} else {
|
||||
mProcessedInput = 0;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "MediaDataDemuxer.h"
|
||||
#include "MediaResult.h"
|
||||
#include "MediaSourceDecoder.h"
|
||||
#include "MediaSpan.h"
|
||||
#include "SourceBufferTask.h"
|
||||
#include "TimeUnits.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
@ -200,7 +201,7 @@ class TrackBuffersManager final
|
|||
|
||||
// The input buffer as per
|
||||
// http://w3c.github.io/media-source/index.html#sourcebuffer-input-buffer
|
||||
RefPtr<MediaByteBuffer> mInputBuffer;
|
||||
Maybe<MediaSpan> mInputBuffer;
|
||||
// Buffer full flag as per
|
||||
// https://w3c.github.io/media-source/#sourcebuffer-buffer-full-flag. Accessed
|
||||
// on both the main thread and the task queue.
|
||||
|
@ -221,7 +222,9 @@ class TrackBuffersManager final
|
|||
nsAutoPtr<ContainerParser> mParser;
|
||||
|
||||
// Demuxer objects and methods.
|
||||
void AppendDataToCurrentInputBuffer(const MediaSpan& aData);
|
||||
void AppendDataToCurrentInputBuffer(MediaByteBuffer* aData);
|
||||
|
||||
RefPtr<MediaByteBuffer> mInitData;
|
||||
// Temporary input buffer to handle partial media segment header.
|
||||
// We store the current input buffer content into it should we need to
|
||||
|
|
|
@ -45,46 +45,46 @@ TEST(ContainerParser, ADTSHeader)
|
|||
|
||||
// Test a valid header.
|
||||
RefPtr<MediaByteBuffer> header = make_adts_header();
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)));
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(MediaSpan(header))));
|
||||
|
||||
// Test variations.
|
||||
uint8_t save = header->ElementAt(1);
|
||||
for (uint8_t i = 1; i < 3; ++i) {
|
||||
// Set non-zero layer.
|
||||
header->ReplaceElementAt(1, (header->ElementAt(1) & 0xf9) | (i << 1));
|
||||
EXPECT_FALSE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)))
|
||||
EXPECT_FALSE(NS_SUCCEEDED(parser->IsInitSegmentPresent(MediaSpan(header))))
|
||||
<< "Accepted non-zero layer in header.";
|
||||
}
|
||||
header->ReplaceElementAt(1, save);
|
||||
save = header->ElementAt(2);
|
||||
header->ReplaceElementAt(2, (header->ElementAt(2) & 0x3b) | (15 << 2));
|
||||
EXPECT_FALSE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)))
|
||||
EXPECT_FALSE(NS_SUCCEEDED(parser->IsInitSegmentPresent(MediaSpan(header))))
|
||||
<< "Accepted explicit frequency in header.";
|
||||
header->ReplaceElementAt(2, save);
|
||||
|
||||
// Test a short header.
|
||||
header->SetLength(6);
|
||||
EXPECT_FALSE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)))
|
||||
EXPECT_FALSE(NS_SUCCEEDED(parser->IsInitSegmentPresent(MediaSpan(header))))
|
||||
<< "Accepted too-short header.";
|
||||
EXPECT_FALSE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(header)))
|
||||
EXPECT_FALSE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(MediaSpan(header))))
|
||||
<< "Found media segment when there was just a partial header.";
|
||||
|
||||
// Test a header with short data.
|
||||
header = make_adts_header();
|
||||
header->AppendElements(1);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)))
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(MediaSpan(header))))
|
||||
<< "Rejected a valid header.";
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(header)))
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(MediaSpan(header))))
|
||||
<< "Rejected a one-byte media segment.";
|
||||
|
||||
// Test parse results.
|
||||
header = make_adts_header();
|
||||
EXPECT_FALSE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(header)))
|
||||
EXPECT_FALSE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(MediaSpan(header))))
|
||||
<< "Found media segment when there was just a header.";
|
||||
int64_t start = 0;
|
||||
int64_t end = 0;
|
||||
EXPECT_TRUE(
|
||||
NS_FAILED(parser->ParseStartAndEndTimestamps(header, start, end)));
|
||||
EXPECT_TRUE(NS_FAILED(
|
||||
parser->ParseStartAndEndTimestamps(MediaSpan(header), start, end)));
|
||||
|
||||
EXPECT_TRUE(parser->HasInitData());
|
||||
EXPECT_TRUE(parser->HasCompleteInitData());
|
||||
|
@ -111,22 +111,22 @@ TEST(ContainerParser, ADTSBlankMedia)
|
|||
|
||||
// Test the header only.
|
||||
RefPtr<MediaByteBuffer> header = make_adts_header();
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)));
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(MediaSpan(header))));
|
||||
|
||||
// Test with the correct length of (invalid) frame data.
|
||||
size_t header_length = header->Length();
|
||||
size_t data_length = 24;
|
||||
size_t frame_length = header_length + data_length;
|
||||
header->AppendElements(data_length);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)))
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(MediaSpan(header))))
|
||||
<< "Rejected a valid header.";
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(header)))
|
||||
EXPECT_TRUE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(MediaSpan(header))))
|
||||
<< "Rejected a full (but zeroed) media segment.";
|
||||
int64_t start = 0;
|
||||
int64_t end = 0;
|
||||
// We don't report timestamps from ADTS.
|
||||
EXPECT_TRUE(
|
||||
NS_FAILED(parser->ParseStartAndEndTimestamps(header, start, end)));
|
||||
EXPECT_TRUE(NS_FAILED(
|
||||
parser->ParseStartAndEndTimestamps(MediaSpan(header), start, end)));
|
||||
EXPECT_EQ(start, 0);
|
||||
EXPECT_EQ(end, 0);
|
||||
|
||||
|
|
|
@ -140,6 +140,7 @@ EXPORTS += [
|
|||
'MediaResult.h',
|
||||
'MediaSegment.h',
|
||||
'MediaShutdownManager.h',
|
||||
'MediaSpan.h',
|
||||
'MediaStatistics.h',
|
||||
'MediaStreamGraph.h',
|
||||
'MediaStreamListener.h',
|
||||
|
|
Загрузка…
Ссылка в новой задаче