Bug 1648309 - P2. Add RemoteArrayOfByteBuffer and ArrayOfRemoteMediaRawData objects. r=padenot,mjf

Those two objects can be used to pack multiple array of objects into a minimal amount of shmem.

An ArrayOfRemoteByteBuffer will take at most a single shmem and perform a single memory allocation..
Similarly, an ArrayOfMediaRawData will pack multiple MediaRawData in at most 3 shmems (one for each array of bytes a MediaRawData contains).

They are designed to work in combination with a ShmemPool which will own each of the allocated Shmem and so can be re-used over and over.

Differential Revision: https://phabricator.services.mozilla.com/D91537
This commit is contained in:
Jean-Yves Avenard 2020-09-29 04:39:14 +00:00
Родитель da79b617d7
Коммит 2c63e2e5b0
5 изменённых файлов: 440 добавлений и 7 удалений

Просмотреть файл

@ -452,6 +452,18 @@ MediaRawData::MediaRawData(const uint8_t* aData, size_t aSize,
mBuffer(aData, aSize),
mAlphaBuffer(aAlphaData, aAlphaSize) {}
MediaRawData::MediaRawData(AlignedByteBuffer&& aData)
: MediaData(Type::RAW_DATA),
mCrypto(mCryptoInternal),
mBuffer(std::move(aData)) {}
MediaRawData::MediaRawData(AlignedByteBuffer&& aData,
AlignedByteBuffer&& aAlphaData)
: MediaData(Type::RAW_DATA),
mCrypto(mCryptoInternal),
mBuffer(std::move(aData)),
mAlphaBuffer(std::move(aAlphaData)) {}
already_AddRefed<MediaRawData> MediaRawData::Clone() const {
RefPtr<MediaRawData> s = new MediaRawData;
s->mTimecode = mTimecode;

Просмотреть файл

@ -610,6 +610,8 @@ class MediaRawData final : public MediaData {
MediaRawData(const uint8_t* aData, size_t aSize);
MediaRawData(const uint8_t* aData, size_t aSize, const uint8_t* aAlphaData,
size_t aAlphaSize);
explicit MediaRawData(AlignedByteBuffer&& aData);
MediaRawData(AlignedByteBuffer&& aData, AlignedByteBuffer&& aAlphaData);
// Pointer to data or null if not-yet allocated
const uint8_t* Data() const { return mBuffer.Data(); }
@ -658,6 +660,7 @@ class MediaRawData final : public MediaData {
private:
friend class MediaRawDataWriter;
friend class ArrayOfRemoteMediaRawData;
AlignedByteBuffer mBuffer;
AlignedByteBuffer mAlphaBuffer;
CryptoSample mCryptoInternal;

Просмотреть файл

@ -0,0 +1,239 @@
/* -*- 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/. */
#include "RemoteMediaData.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/dom/MediaIPCUtils.h"
#include "mozilla/ipc/Shmem.h"
namespace mozilla {
bool RemoteArrayOfByteBuffer::AllocateShmem(
size_t aSize, std::function<ShmemBuffer(size_t)>& aAllocator) {
ShmemBuffer buffer = aAllocator(aSize);
if (!buffer.Valid()) {
return false;
}
mBuffers.emplace(std::move(buffer.Get()));
return true;
}
uint8_t* RemoteArrayOfByteBuffer::BuffersStartAddress() const {
MOZ_ASSERT(mBuffers);
return mBuffers->get<uint8_t>();
}
bool RemoteArrayOfByteBuffer::Check(size_t aOffset, size_t aSizeInBytes) const {
return mBuffers && detail::IsAddValid(aOffset, aSizeInBytes) &&
aOffset + aSizeInBytes <= mBuffers->Size<uint8_t>();
}
void RemoteArrayOfByteBuffer::Write(size_t aOffset, const void* aSourceAddr,
size_t aSizeInBytes) {
if (!aSizeInBytes) {
return;
}
MOZ_DIAGNOSTIC_ASSERT(Check(aOffset, aSizeInBytes),
"Allocated Shmem is too small");
memcpy(BuffersStartAddress() + aOffset, aSourceAddr, aSizeInBytes);
}
RemoteArrayOfByteBuffer::RemoteArrayOfByteBuffer() = default;
RemoteArrayOfByteBuffer::RemoteArrayOfByteBuffer(
const nsTArray<RefPtr<MediaByteBuffer>>& aArray,
std::function<ShmemBuffer(size_t)>& aAllocator) {
// Determine the total size we will need for this object.
size_t totalSize = 0;
for (const auto& buffer : aArray) {
if (buffer) {
totalSize += buffer->Length();
}
}
if (totalSize) {
if (!AllocateShmem(totalSize, aAllocator)) {
return;
}
}
size_t offset = 0;
for (const auto& buffer : aArray) {
size_t sizeBuffer = buffer ? buffer->Length() : 0;
if (totalSize && sizeBuffer) {
Write(offset, buffer->Elements(), sizeBuffer);
}
mOffsets.AppendElement(OffsetEntry{offset, sizeBuffer});
offset += sizeBuffer;
}
mIsValid = true;
}
RemoteArrayOfByteBuffer& RemoteArrayOfByteBuffer::operator=(
RemoteArrayOfByteBuffer&& aOther) noexcept {
mIsValid = aOther.mIsValid;
mBuffers = std::move(aOther.mBuffers);
mOffsets = std::move(aOther.mOffsets);
aOther.mIsValid = false;
return *this;
}
RemoteArrayOfByteBuffer::~RemoteArrayOfByteBuffer() = default;
already_AddRefed<MediaByteBuffer> RemoteArrayOfByteBuffer::MediaByteBufferAt(
size_t aIndex) const {
MOZ_ASSERT(aIndex < Count());
const OffsetEntry& entry = mOffsets[aIndex];
if (!mBuffers || !Get<1>(entry)) {
// It's an empty one.
return nullptr;
}
size_t entrySize = Get<1>(entry);
if (!Check(Get<0>(entry), entrySize)) {
// This Shmem is corrupted and can't contain the data we are about to
// retrieve. We return an empty array instead of asserting to allow for
// recovery.
return nullptr;
}
RefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(entrySize);
buffer->SetLength(entrySize);
memcpy(buffer->Elements(), mBuffers->get<uint8_t>() + Get<0>(entry),
entrySize);
return buffer.forget();
}
/*static */ void ipc::IPDLParamTraits<RemoteArrayOfByteBuffer>::Write(
IPC::Message* aMsg, ipc::IProtocol* aActor,
const RemoteArrayOfByteBuffer& aVar) {
WriteIPDLParam(aMsg, aActor, aVar.mIsValid);
// We need the following gymnastic as the Shmem transfered over IPC will be
// revoked. We must create a temporary one instead so that it can be recycled
// later back into the original ShmemPool.
if (aVar.mBuffers) {
WriteIPDLParam(aMsg, aActor, Some(ipc::Shmem(*aVar.mBuffers)));
} else {
WriteIPDLParam(aMsg, aActor, Maybe<ipc::Shmem>());
}
WriteIPDLParam(aMsg, aActor, aVar.mOffsets);
}
/* static */ bool ipc::IPDLParamTraits<RemoteArrayOfByteBuffer>::Read(
const IPC::Message* aMsg, PickleIterator* aIter,
mozilla::ipc::IProtocol* aActor, RemoteArrayOfByteBuffer* aVar) {
return ReadIPDLParam(aMsg, aIter, aActor, &aVar->mIsValid) &&
ReadIPDLParam(aMsg, aIter, aActor, &aVar->mBuffers) &&
ReadIPDLParam(aMsg, aIter, aActor, &aVar->mOffsets);
}
bool ArrayOfRemoteMediaRawData::Fill(
const nsTArray<RefPtr<MediaRawData>>& aData,
std::function<ShmemBuffer(size_t)>&& aAllocator) {
nsTArray<AlignedByteBuffer> dataBuffers(aData.Length());
nsTArray<AlignedByteBuffer> alphaBuffers(aData.Length());
nsTArray<RefPtr<MediaByteBuffer>> extraDataBuffers(aData.Length());
for (auto&& entry : aData) {
dataBuffers.AppendElement(std::move(entry->mBuffer));
alphaBuffers.AppendElement(std::move(entry->mAlphaBuffer));
extraDataBuffers.AppendElement(std::move(entry->mExtraData));
mSamples.AppendElement(RemoteMediaRawData{
MediaDataIPDL(entry->mOffset, entry->mTime, entry->mTimecode,
entry->mDuration, entry->mKeyframe),
entry->mEOS, entry->mDiscardPadding,
entry->mOriginalPresentationWindow});
}
mBuffers = RemoteArrayOfByteBuffer(dataBuffers, aAllocator);
if (!mBuffers.IsValid()) {
return false;
}
mAlphaBuffers = RemoteArrayOfByteBuffer(alphaBuffers, aAllocator);
if (!mAlphaBuffers.IsValid()) {
return false;
}
mExtraDatas = RemoteArrayOfByteBuffer(extraDataBuffers, aAllocator);
return mExtraDatas.IsValid();
}
already_AddRefed<MediaRawData> ArrayOfRemoteMediaRawData::ElementAt(
size_t aIndex) const {
if (!IsValid()) {
return nullptr;
}
MOZ_ASSERT(aIndex < Count());
MOZ_DIAGNOSTIC_ASSERT(mBuffers.Count() == Count() &&
mAlphaBuffers.Count() == Count() &&
mExtraDatas.Count() == Count(),
"Something ain't right here");
const auto& sample = mSamples[aIndex];
AlignedByteBuffer data = mBuffers.AlignedBufferAt<uint8_t>(aIndex);
if (mBuffers.SizeAt(aIndex) && !data) {
// OOM
return nullptr;
}
AlignedByteBuffer alphaData = mAlphaBuffers.AlignedBufferAt<uint8_t>(aIndex);
if (mAlphaBuffers.SizeAt(aIndex) && !alphaData) {
// OOM
return nullptr;
}
RefPtr<MediaRawData> rawData;
if (mAlphaBuffers.SizeAt(aIndex)) {
rawData = new MediaRawData(std::move(data), std::move(alphaData));
} else {
rawData = new MediaRawData(std::move(data));
}
rawData->mOffset = sample.mBase.offset();
rawData->mTime = sample.mBase.time();
rawData->mTimecode = sample.mBase.timecode();
rawData->mDuration = sample.mBase.duration();
rawData->mKeyframe = sample.mBase.keyframe();
rawData->mEOS = sample.mEOS;
rawData->mDiscardPadding = sample.mDiscardPadding;
rawData->mExtraData = mExtraDatas.MediaByteBufferAt(aIndex);
return rawData.forget();
}
/*static */ void ipc::IPDLParamTraits<ArrayOfRemoteMediaRawData*>::Write(
IPC::Message* aMsg, ipc::IProtocol* aActor,
ArrayOfRemoteMediaRawData* aVar) {
WriteIPDLParam(aMsg, aActor, std::move(aVar->mSamples));
WriteIPDLParam(aMsg, aActor, std::move(aVar->mBuffers));
WriteIPDLParam(aMsg, aActor, std::move(aVar->mAlphaBuffers));
WriteIPDLParam(aMsg, aActor, std::move(aVar->mExtraDatas));
}
/* static */ bool ipc::IPDLParamTraits<ArrayOfRemoteMediaRawData*>::Read(
const IPC::Message* aMsg, PickleIterator* aIter,
mozilla::ipc::IProtocol* aActor, RefPtr<ArrayOfRemoteMediaRawData>* aVar) {
auto array = MakeRefPtr<ArrayOfRemoteMediaRawData>();
if (!ReadIPDLParam(aMsg, aIter, aActor, &array->mSamples) ||
!ReadIPDLParam(aMsg, aIter, aActor, &array->mBuffers) ||
!ReadIPDLParam(aMsg, aIter, aActor, &array->mAlphaBuffers) ||
!ReadIPDLParam(aMsg, aIter, aActor, &array->mExtraDatas)) {
return false;
}
*aVar = std::move(array);
return true;
}
/* static */ void
ipc::IPDLParamTraits<ArrayOfRemoteMediaRawData::RemoteMediaRawData>::Write(
IPC::Message* aMsg, ipc::IProtocol* aActor, const paramType& aVar) {
WriteIPDLParam(aMsg, aActor, aVar.mBase);
WriteIPDLParam(aMsg, aActor, aVar.mEOS);
WriteIPDLParam(aMsg, aActor, aVar.mDiscardPadding);
WriteIPDLParam(aMsg, aActor, aVar.mOriginalPresentationWindow);
}
/* static */ bool
ipc::IPDLParamTraits<ArrayOfRemoteMediaRawData::RemoteMediaRawData>::Read(
const IPC::Message* aMsg, PickleIterator* aIter, ipc::IProtocol* aActor,
paramType* aVar) {
MediaDataIPDL mBase;
return ReadIPDLParam(aMsg, aIter, aActor, &aVar->mBase) &&
ReadIPDLParam(aMsg, aIter, aActor, &aVar->mEOS) &&
ReadIPDLParam(aMsg, aIter, aActor, &aVar->mDiscardPadding) &&
ReadIPDLParam(aMsg, aIter, aActor, &aVar->mOriginalPresentationWindow);
};
} // namespace mozilla

Просмотреть файл

@ -7,15 +7,26 @@
#ifndef mozilla_dom_media_ipc_RemoteMediaData_h
#define mozilla_dom_media_ipc_RemoteMediaData_h
#include <functional>
#include "MediaData.h"
#include "PlatformDecoderModule.h"
#include "ipc/IPCMessageUtils.h"
#include "mozilla/GfxMessageUtils.h"
#include "mozilla/PMediaDecoderParams.h"
#include "mozilla/RemoteImageHolder.h"
#include "mozilla/ShmemPool.h"
#include "mozilla/gfx/Rect.h"
namespace mozilla {
class ShmemPool;
namespace ipc {
class IProtocol;
class Shmem;
} // namespace ipc
//-----------------------------------------------------------------------------
// Declaration of the IPDL type |struct RemoteVideoData|
//
@ -23,9 +34,7 @@ namespace mozilla {
// (see bug 1664362)
class RemoteVideoData final {
private:
typedef mozilla::MediaDataIPDL MediaDataIPDL;
typedef mozilla::gfx::IntSize IntSize;
typedef mozilla::RemoteImageHolder RemoteImageHolder;
public:
RemoteVideoData() = default;
@ -74,7 +83,7 @@ class ArrayOfRemoteVideoData final {
ArrayOfRemoteVideoData(const ArrayOfRemoteVideoData& aOther) {
MOZ_CRASH("Should never be used but declared by generated IPDL binding");
}
ArrayOfRemoteVideoData& operator=(ArrayOfRemoteVideoData&& aOther) {
ArrayOfRemoteVideoData& operator=(ArrayOfRemoteVideoData&& aOther) noexcept {
if (this != &aOther) {
mArray = std::move(aOther.mArray);
}
@ -100,6 +109,143 @@ class ArrayOfRemoteVideoData final {
nsTArray<RemoteVideoData> mArray;
};
/* The class will pack either an array of AlignedBuffer or MediaByteBuffer
* into a single Shmem objects. */
class RemoteArrayOfByteBuffer {
public:
RemoteArrayOfByteBuffer();
template <typename Type>
RemoteArrayOfByteBuffer(const nsTArray<AlignedBuffer<Type>>& aArray,
std::function<ShmemBuffer(size_t)>& aAllocator) {
// Determine the total size we will need for this object.
size_t totalSize = 0;
for (auto& buffer : aArray) {
totalSize += buffer.Size();
}
if (totalSize) {
if (!AllocateShmem(totalSize, aAllocator)) {
return;
}
}
size_t offset = 0;
for (auto& buffer : aArray) {
if (totalSize && buffer && buffer.Size()) {
Write(offset, buffer.Data(), buffer.Size());
}
mOffsets.AppendElement(OffsetEntry{offset, buffer.Size()});
offset += buffer.Size();
}
mIsValid = true;
}
RemoteArrayOfByteBuffer(const nsTArray<RefPtr<MediaByteBuffer>>& aArray,
std::function<ShmemBuffer(size_t)>& aAllocator);
RemoteArrayOfByteBuffer& operator=(RemoteArrayOfByteBuffer&& aOther) noexcept;
// Return the packed aIndexth buffer as an AlignedByteBuffer.
// The operation is fallible should an out of memory be encountered. The
// result should be tested accordingly.
template <typename Type>
AlignedBuffer<Type> AlignedBufferAt(size_t aIndex) const {
MOZ_ASSERT(aIndex < Count());
const OffsetEntry& entry = mOffsets[aIndex];
size_t entrySize = Get<1>(entry);
if (!mBuffers || !entrySize) {
// It's an empty one.
return AlignedBuffer<Type>();
}
if (!Check(Get<0>(entry), entrySize)) {
// This Shmem is corrupted and can't contain the data we are about to
// retrieve. We return an empty array instead of asserting to allow for
// recovery.
return AlignedBuffer<Type>();
}
if (0 != entrySize % sizeof(Type)) {
// There's an error, that entry can't represent this data.
return AlignedBuffer<Type>();
}
return AlignedBuffer<Type>(
reinterpret_cast<Type*>(BuffersStartAddress() + Get<0>(entry)),
entrySize / sizeof(Type));
}
// Return the packed aIndexth buffer as aMediaByteBuffer.
// Will return nullptr if the packed buffer was originally empty.
already_AddRefed<MediaByteBuffer> MediaByteBufferAt(size_t aIndex) const;
// Return the size of the aIndexth buffer.
size_t SizeAt(size_t aIndex) const { return Get<1>(mOffsets[aIndex]); }
// Return false if an out of memory error was encountered during construction.
bool IsValid() const { return mIsValid; };
// Return the number of buffers packed into this entity.
size_t Count() const { return mOffsets.Length(); }
virtual ~RemoteArrayOfByteBuffer();
private:
friend struct ipc::IPDLParamTraits<RemoteArrayOfByteBuffer>;
// Allocate shmem, false if an error occurred.
bool AllocateShmem(size_t aSize,
std::function<ShmemBuffer(size_t)>& aAllocator);
// The starting address of the Shmem
uint8_t* BuffersStartAddress() const;
// Check that the allocated Shmem can contain such range.
bool Check(size_t aOffset, size_t aSizeInBytes) const;
void Write(size_t aOffset, const void* aSourceAddr, size_t aSizeInBytes);
// Set to false is the buffer isn't initialized yet or a memory error occurred
// during construction.
bool mIsValid = false;
// The packed data. The Maybe will be empty if all buffers packed were
// orignally empty.
Maybe<ipc::Shmem> mBuffers;
// The offset to the start of the individual buffer and its size (all in
// bytes)
typedef Tuple<size_t, size_t> OffsetEntry;
nsTArray<OffsetEntry> mOffsets;
};
/* The class will pack an array of MediaRawData using at most three Shmem
* objects. Under the most common scenaria, only two Shmems will be used as
* there are few videos with an alpha channel in the wild.
* We unfortunately can't populate the array at construction nor present an
* interface similar to an actual nsTArray or the ArrayOfRemoteVideoData above
* as currently IPC serialization is always non-fallible. So we must create the
* object first, fill it to determine if we ran out of memory and then send the
* object over IPC.
*/
class ArrayOfRemoteMediaRawData {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ArrayOfRemoteMediaRawData)
public:
// Fill the content, return false if an OOM occurred.
bool Fill(const nsTArray<RefPtr<MediaRawData>>& aData,
std::function<ShmemBuffer(size_t)>&& aAllocator);
// Return the aIndexth MediaRawData or nullptr if a memory error occurred.
already_AddRefed<MediaRawData> ElementAt(size_t aIndex) const;
// Return the number of MediaRawData stored in this container.
size_t Count() const { return mSamples.Length(); }
bool IsEmpty() const { return Count() == 0; }
bool IsValid() const {
return mBuffers.IsValid() && mAlphaBuffers.IsValid() &&
mExtraDatas.IsValid();
}
struct RemoteMediaRawData {
MediaDataIPDL mBase;
bool mEOS;
uint32_t mDiscardPadding;
Maybe<media::TimeInterval> mOriginalPresentationWindow;
};
private:
friend struct ipc::IPDLParamTraits<ArrayOfRemoteMediaRawData*>;
virtual ~ArrayOfRemoteMediaRawData() = default;
nsTArray<RemoteMediaRawData> mSamples;
RemoteArrayOfByteBuffer mBuffers;
RemoteArrayOfByteBuffer mAlphaBuffers;
RemoteArrayOfByteBuffer mExtraDatas;
};
namespace ipc {
template <>
@ -126,15 +272,15 @@ struct IPDLParamTraits<RemoteVideoData> {
};
template <>
struct IPDLParamTraits<mozilla::ArrayOfRemoteVideoData*> {
typedef mozilla::ArrayOfRemoteVideoData paramType;
struct IPDLParamTraits<ArrayOfRemoteVideoData*> {
typedef ArrayOfRemoteVideoData paramType;
static void Write(IPC::Message* aMsg, mozilla::ipc::IProtocol* aActor,
paramType* aVar) {
WriteIPDLParam(aMsg, aActor, std::move(aVar->mArray));
}
static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
mozilla::ipc::IProtocol* aActor, RefPtr<paramType>* aVar) {
ipc::IProtocol* aActor, RefPtr<paramType>* aVar) {
nsTArray<RemoteVideoData> array;
if (!ReadIPDLParam(aMsg, aIter, aActor, &array)) {
return false;
@ -145,8 +291,40 @@ struct IPDLParamTraits<mozilla::ArrayOfRemoteVideoData*> {
}
};
template <>
struct IPDLParamTraits<RemoteArrayOfByteBuffer> {
typedef RemoteArrayOfByteBuffer paramType;
// We do not want to move the RemoteArrayOfByteBuffer as we want to recycle
// the shmem it contains for another time.
static void Write(IPC::Message* aMsg, ipc::IProtocol* aActor,
const paramType& aVar);
static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
ipc::IProtocol* aActor, paramType* aVar);
};
template <>
struct IPDLParamTraits<ArrayOfRemoteMediaRawData::RemoteMediaRawData> {
typedef ArrayOfRemoteMediaRawData::RemoteMediaRawData paramType;
static void Write(IPC::Message* aMsg, ipc::IProtocol* aActor,
const paramType& aVar);
static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
ipc::IProtocol* aActor, paramType* aVar);
};
template <>
struct IPDLParamTraits<ArrayOfRemoteMediaRawData*> {
typedef ArrayOfRemoteMediaRawData paramType;
static void Write(IPC::Message* aMsg, ipc::IProtocol* aActor,
paramType* aVar);
static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
ipc::IProtocol* aActor, RefPtr<paramType>* aVar);
};
} // namespace ipc
} // namespace mozilla
#endif // mozilla_dom_media_ipc_RemoteMediaData_h
#endif // mozilla_dom_media_ipc_RemoteMediaData_h

Просмотреть файл

@ -49,6 +49,7 @@ SOURCES += [
'RemoteDecoderModule.cpp',
'RemoteDecoderParent.cpp',
'RemoteImageHolder.cpp',
'RemoteMediaData.cpp',
'RemoteMediaDataDecoder.cpp',
'RemoteVideoDecoder.cpp',
]