gecko-dev/dom/media/BufferReader.h
Chris Pearce 42fd2adbaa 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
2019-06-14 02:10:09 +00:00

307 lines
8.1 KiB
C++

/* 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 BUFFER_READER_H_
#define BUFFER_READER_H_
#include "mozilla/EndianUtils.h"
#include "nscore.h"
#include "nsTArray.h"
#include "MediaData.h"
#include "MediaSpan.h"
#include "mozilla/Logging.h"
#include "mozilla/Result.h"
namespace mozilla {
extern mozilla::LazyLogModule gMP4MetadataLog;
class MOZ_RAII BufferReader {
public:
BufferReader() : mPtr(nullptr), mRemaining(0), mLength(0) {}
BufferReader(const uint8_t* aData, size_t aSize)
: mPtr(aData), mRemaining(aSize), mLength(aSize) {}
template <size_t S>
explicit BufferReader(const AutoTArray<uint8_t, S>& aData)
: mPtr(aData.Elements()),
mRemaining(aData.Length()),
mLength(aData.Length()) {}
explicit BufferReader(const nsTArray<uint8_t>& aData)
: mPtr(aData.Elements()),
mRemaining(aData.Length()),
mLength(aData.Length()) {}
explicit BufferReader(const mozilla::MediaByteBuffer* aData)
: 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);
mPtr = aData.Elements();
mRemaining = aData.Length();
mLength = mRemaining;
}
~BufferReader() {}
size_t Offset() const { return mLength - mRemaining; }
size_t Remaining() const { return mRemaining; }
mozilla::Result<uint8_t, nsresult> ReadU8() {
auto ptr = Read(1);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return *ptr;
}
mozilla::Result<uint16_t, nsresult> ReadU16() {
auto ptr = Read(2);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return mozilla::BigEndian::readUint16(ptr);
}
mozilla::Result<int16_t, nsresult> ReadLE16() {
auto ptr = Read(2);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return mozilla::LittleEndian::readInt16(ptr);
}
mozilla::Result<uint32_t, nsresult> ReadU24() {
auto ptr = Read(3);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return ptr[0] << 16 | ptr[1] << 8 | ptr[2];
}
mozilla::Result<int32_t, nsresult> Read24() {
return ReadU24().map([](uint32_t x) { return (int32_t)x; });
}
mozilla::Result<int32_t, nsresult> ReadLE24() {
auto ptr = Read(3);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
int32_t result = int32_t(ptr[2] << 16 | ptr[1] << 8 | ptr[0]);
if (result & 0x00800000u) {
result -= 0x1000000;
}
return result;
}
mozilla::Result<uint32_t, nsresult> ReadU32() {
auto ptr = Read(4);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return mozilla::BigEndian::readUint32(ptr);
}
mozilla::Result<int32_t, nsresult> Read32() {
auto ptr = Read(4);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return mozilla::BigEndian::readInt32(ptr);
}
mozilla::Result<uint64_t, nsresult> ReadU64() {
auto ptr = Read(8);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return mozilla::BigEndian::readUint64(ptr);
}
mozilla::Result<int64_t, nsresult> Read64() {
auto ptr = Read(8);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return mozilla::BigEndian::readInt64(ptr);
}
const uint8_t* Read(size_t aCount) {
if (aCount > mRemaining) {
mPtr += mRemaining;
mRemaining = 0;
return nullptr;
}
mRemaining -= aCount;
const uint8_t* result = mPtr;
mPtr += aCount;
return result;
}
const uint8_t* Rewind(size_t aCount) {
MOZ_ASSERT(aCount <= Offset());
size_t rewind = Offset();
if (aCount < rewind) {
rewind = aCount;
}
mRemaining += rewind;
mPtr -= rewind;
return mPtr;
}
mozilla::Result<uint8_t, nsresult> PeekU8() const {
auto ptr = Peek(1);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return *ptr;
}
mozilla::Result<uint16_t, nsresult> PeekU16() const {
auto ptr = Peek(2);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return mozilla::BigEndian::readUint16(ptr);
}
mozilla::Result<uint32_t, nsresult> PeekU24() const {
auto ptr = Peek(3);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return ptr[0] << 16 | ptr[1] << 8 | ptr[2];
}
mozilla::Result<int32_t, nsresult> Peek24() const {
return PeekU24().map([](uint32_t x) { return (int32_t)x; });
}
mozilla::Result<uint32_t, nsresult> PeekU32() {
auto ptr = Peek(4);
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return mozilla::Err(NS_ERROR_FAILURE);
}
return mozilla::BigEndian::readUint32(ptr);
}
const uint8_t* Peek(size_t aCount) const {
if (aCount > mRemaining) {
return nullptr;
}
return mPtr;
}
const uint8_t* Seek(size_t aOffset) {
if (aOffset >= mLength) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure, offset: %zu", __func__, aOffset));
return nullptr;
}
mPtr = mPtr - Offset() + aOffset;
mRemaining = mLength - aOffset;
return mPtr;
}
const uint8_t* Reset() {
mPtr -= Offset();
mRemaining = mLength;
return mPtr;
}
uint32_t Align() const { return 4 - ((intptr_t)mPtr & 3); }
template <typename T>
bool CanReadType() const {
return mRemaining >= sizeof(T);
}
template <typename T>
T ReadType() {
auto ptr = Read(sizeof(T));
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return 0;
}
return *reinterpret_cast<const T*>(ptr);
}
template <typename T>
MOZ_MUST_USE bool ReadArray(nsTArray<T>& aDest, size_t aLength) {
auto ptr = Read(aLength * sizeof(T));
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return false;
}
aDest.Clear();
aDest.AppendElements(reinterpret_cast<const T*>(ptr), aLength);
return true;
}
template <typename T>
MOZ_MUST_USE bool ReadArray(FallibleTArray<T>& aDest, size_t aLength) {
auto ptr = Read(aLength * sizeof(T));
if (!ptr) {
MOZ_LOG(gMP4MetadataLog, mozilla::LogLevel::Error,
("%s: failure", __func__));
return false;
}
aDest.Clear();
if (!aDest.SetCapacity(aLength, mozilla::fallible)) {
return false;
}
MOZ_ALWAYS_TRUE(aDest.AppendElements(reinterpret_cast<const T*>(ptr),
aLength, mozilla::fallible));
return true;
}
private:
const uint8_t* mPtr;
size_t mRemaining;
size_t mLength;
};
} // namespace mozilla
#endif