/* -*- 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/. */ #ifndef mozilla_dom_media_ipc_RemoteMediaData_h #define mozilla_dom_media_ipc_RemoteMediaData_h #include #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| // // We can't use the generated binding in order to use move semantics properly // (see bug 1664362) class RemoteVideoData final { private: typedef mozilla::gfx::IntSize IntSize; public: RemoteVideoData() = default; RemoteVideoData(const MediaDataIPDL& aBase, const IntSize& aDisplay, RemoteImageHolder&& aImage, int32_t aFrameID) : mBase(aBase), mDisplay(aDisplay), mImage(std::move(aImage)), mFrameID(aFrameID) {} // This is equivalent to the old RemoteVideoDataIPDL object and is similar to // the RemoteAudioDataIPDL object. To ensure style consistency we use the IPDL // naming convention here. MediaDataIPDL& base() { return mBase; } const MediaDataIPDL& base() const { return mBase; } IntSize& display() { return mDisplay; } const IntSize& display() const { return mDisplay; } RemoteImageHolder& image() { return mImage; } const RemoteImageHolder& image() const { return mImage; } int32_t& frameID() { return mFrameID; } const int32_t& frameID() const { return mFrameID; } private: friend struct ipc::IPDLParamTraits; MediaDataIPDL mBase; IntSize mDisplay; RemoteImageHolder mImage; int32_t mFrameID; }; // Until bug 1572054 is resolved, we can't move our objects when using IPDL's // union or array. They are always copied. So we make the class refcounted to // and always pass it by pointed to bypass the problem for now. class ArrayOfRemoteVideoData final { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ArrayOfRemoteVideoData) public: ArrayOfRemoteVideoData() = default; ArrayOfRemoteVideoData(ArrayOfRemoteVideoData&& aOther) : mArray(std::move(aOther.mArray)) {} explicit ArrayOfRemoteVideoData(nsTArray&& aOther) : mArray(std::move(aOther)) {} ArrayOfRemoteVideoData(const ArrayOfRemoteVideoData& aOther) { MOZ_CRASH("Should never be used but declared by generated IPDL binding"); } ArrayOfRemoteVideoData& operator=(ArrayOfRemoteVideoData&& aOther) noexcept { if (this != &aOther) { mArray = std::move(aOther.mArray); } return *this; } ArrayOfRemoteVideoData& operator=(nsTArray&& aOther) { mArray = std::move(aOther); return *this; } void AppendElements(nsTArray&& aOther) { mArray.AppendElements(std::move(aOther)); } void Append(RemoteVideoData&& aVideo) { mArray.AppendElement(std::move(aVideo)); } const nsTArray& Array() const { return mArray; } nsTArray& Array() { return mArray; } private: ~ArrayOfRemoteVideoData() = default; friend struct ipc::IPDLParamTraits; nsTArray mArray; }; /* The class will pack either an array of AlignedBuffer or MediaByteBuffer * into a single Shmem objects. */ class RemoteArrayOfByteBuffer { public: RemoteArrayOfByteBuffer(); template RemoteArrayOfByteBuffer(const nsTArray>& aArray, std::function& 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>& aArray, std::function& 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 AlignedBuffer 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(); } 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(); } if (0 != entrySize % sizeof(Type)) { // There's an error, that entry can't represent this data. return AlignedBuffer(); } return AlignedBuffer( reinterpret_cast(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 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; // Allocate shmem, false if an error occurred. bool AllocateShmem(size_t aSize, std::function& 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 mBuffers; // The offset to the start of the individual buffer and its size (all in // bytes) typedef Tuple OffsetEntry; nsTArray 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>& aData, std::function&& aAllocator); // Return the aIndexth MediaRawData or nullptr if a memory error occurred. already_AddRefed 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 mOriginalPresentationWindow; }; private: friend struct ipc::IPDLParamTraits; virtual ~ArrayOfRemoteMediaRawData() = default; nsTArray mSamples; RemoteArrayOfByteBuffer mBuffers; RemoteArrayOfByteBuffer mAlphaBuffers; RemoteArrayOfByteBuffer mExtraDatas; }; /* The class will pack an array of MediaAudioData using at most a single Shmem * objects. * 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 ArrayOfRemoteAudioData final { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ArrayOfRemoteAudioData) public: // Fill the content, return false if an OOM occurred. bool Fill(const nsTArray>& aData, std::function&& aAllocator); // Return the aIndexth MediaRawData or nullptr if a memory error occurred. already_AddRefed 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(); } struct RemoteAudioData { friend struct ipc::IPDLParamTraits; MediaDataIPDL mBase; uint32_t mChannels; uint32_t mRate; uint32_t mChannelMap; media::TimeUnit mOriginalTime; Maybe mTrimWindow; uint32_t mFrames; size_t mDataOffset; }; private: friend struct ipc::IPDLParamTraits; ~ArrayOfRemoteAudioData() = default; nsTArray mSamples; RemoteArrayOfByteBuffer mBuffers; }; namespace ipc { template <> struct IPDLParamTraits { typedef RemoteVideoData paramType; static void Write(IPC::Message* aMsg, ipc::IProtocol* aActor, paramType&& aVar) { WriteIPDLParam(aMsg, aActor, std::move(aVar.mBase)); WriteIPDLParam(aMsg, aActor, std::move(aVar.mDisplay)); WriteIPDLParam(aMsg, aActor, std::move(aVar.mImage)); aMsg->WriteBytes(&aVar.mFrameID, 4); } static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, mozilla::ipc::IProtocol* aActor, paramType* aVar) { if (!ReadIPDLParam(aMsg, aIter, aActor, &aVar->mBase) || !ReadIPDLParam(aMsg, aIter, aActor, &aVar->mDisplay) || !ReadIPDLParam(aMsg, aIter, aActor, &aVar->mImage) || !aMsg->ReadBytesInto(aIter, &aVar->mFrameID, 4)) { return false; } return true; } }; template <> struct IPDLParamTraits { 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, ipc::IProtocol* aActor, RefPtr* aVar) { nsTArray array; if (!ReadIPDLParam(aMsg, aIter, aActor, &array)) { return false; } auto results = MakeRefPtr(std::move(array)); *aVar = std::move(results); return true; } }; template <> struct IPDLParamTraits { 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 { 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 { 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* aVar); }; template <> struct IPDLParamTraits { typedef ArrayOfRemoteAudioData::RemoteAudioData 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 { typedef ArrayOfRemoteAudioData 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* aVar); }; } // namespace ipc } // namespace mozilla #endif // mozilla_dom_media_ipc_RemoteMediaData_h