/* -*- 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 "PerformanceRecorder.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& 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(); } bool RemoteArrayOfByteBuffer::Check(size_t aOffset, size_t aSizeInBytes) const { return mBuffers && mBuffers->IsReadable() && detail::IsAddValid(aOffset, aSizeInBytes) && aOffset + aSizeInBytes <= mBuffers->Size(); } 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>& aArray, std::function& 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 RemoteArrayOfByteBuffer::MediaByteBufferAt( size_t aIndex) const { MOZ_ASSERT(aIndex < Count()); const OffsetEntry& entry = mOffsets[aIndex]; if (!mBuffers || !std::get<1>(entry)) { // It's an empty one. return nullptr; } size_t entrySize = std::get<1>(entry); if (!Check(std::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 buffer = new MediaByteBuffer(entrySize); buffer->SetLength(entrySize); memcpy(buffer->Elements(), mBuffers->get() + std::get<0>(entry), entrySize); return buffer.forget(); } /*static */ void ipc::IPDLParamTraits::Write( IPC::MessageWriter* aWriter, ipc::IProtocol* aActor, const RemoteArrayOfByteBuffer& aVar) { WriteIPDLParam(aWriter, 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(aWriter, aActor, Some(ipc::Shmem(*aVar.mBuffers))); } else { WriteIPDLParam(aWriter, aActor, Maybe()); } WriteIPDLParam(aWriter, aActor, aVar.mOffsets); } /* static */ bool ipc::IPDLParamTraits::Read( IPC::MessageReader* aReader, mozilla::ipc::IProtocol* aActor, RemoteArrayOfByteBuffer* aVar) { return ReadIPDLParam(aReader, aActor, &aVar->mIsValid) && ReadIPDLParam(aReader, aActor, &aVar->mBuffers) && ReadIPDLParam(aReader, aActor, &aVar->mOffsets); } bool ArrayOfRemoteMediaRawData::Fill( const nsTArray>& aData, std::function&& aAllocator) { nsTArray dataBuffers(aData.Length()); nsTArray alphaBuffers(aData.Length()); nsTArray> extraDataBuffers(aData.Length()); int32_t height = 0; for (auto&& entry : aData) { dataBuffers.AppendElement(std::move(entry->mBuffer)); alphaBuffers.AppendElement(std::move(entry->mAlphaBuffer)); extraDataBuffers.AppendElement(std::move(entry->mExtraData)); if (auto&& info = entry->mTrackInfo; info && info->GetAsVideoInfo()) { height = info->GetAsVideoInfo()->mImage.height; } mSamples.AppendElement(RemoteMediaRawData{ MediaDataIPDL(entry->mOffset, entry->mTime, entry->mTimecode, entry->mDuration, entry->mKeyframe), entry->mEOS, height, entry->mDiscardPadding, entry->mOriginalPresentationWindow, entry->mCrypto.IsEncrypted() && entry->mShouldCopyCryptoToRemoteRawData ? Some(CryptoInfo{ entry->mCrypto.mCryptoScheme, entry->mCrypto.mIV, entry->mCrypto.mKeyId, entry->mCrypto.mPlainSizes, entry->mCrypto.mEncryptedSizes, }) : Nothing()}); } PerformanceRecorder perfRecorder(MediaStage::CopyDemuxedData, height); mBuffers = RemoteArrayOfByteBuffer(dataBuffers, aAllocator); if (!mBuffers.IsValid()) { return false; } mAlphaBuffers = RemoteArrayOfByteBuffer(alphaBuffers, aAllocator); if (!mAlphaBuffers.IsValid()) { return false; } mExtraDatas = RemoteArrayOfByteBuffer(extraDataBuffers, aAllocator); if (!mExtraDatas.IsValid()) { return false; } perfRecorder.Record(); return true; } already_AddRefed 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]; PerformanceRecorder perfRecorder(MediaStage::CopyDemuxedData, sample.mHeight); AlignedByteBuffer data = mBuffers.AlignedBufferAt(aIndex); if (mBuffers.SizeAt(aIndex) && !data) { // OOM return nullptr; } AlignedByteBuffer alphaData = mAlphaBuffers.AlignedBufferAt(aIndex); if (mAlphaBuffers.SizeAt(aIndex) && !alphaData) { // OOM return nullptr; } RefPtr 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); if (sample.mCryptoConfig) { CryptoSample& cypto = rawData->GetWritableCrypto(); cypto.mCryptoScheme = sample.mCryptoConfig->mEncryptionScheme(); cypto.mIV = std::move(sample.mCryptoConfig->mIV()); cypto.mIVSize = cypto.mIV.Length(); cypto.mKeyId = std::move(sample.mCryptoConfig->mKeyId()); cypto.mPlainSizes = std::move(sample.mCryptoConfig->mClearBytes()); cypto.mEncryptedSizes = std::move(sample.mCryptoConfig->mCipherBytes()); } perfRecorder.Record(); return rawData.forget(); } /*static */ void ipc::IPDLParamTraits::Write( IPC::MessageWriter* aWriter, ipc::IProtocol* aActor, ArrayOfRemoteMediaRawData* aVar) { WriteIPDLParam(aWriter, aActor, std::move(aVar->mSamples)); WriteIPDLParam(aWriter, aActor, std::move(aVar->mBuffers)); WriteIPDLParam(aWriter, aActor, std::move(aVar->mAlphaBuffers)); WriteIPDLParam(aWriter, aActor, std::move(aVar->mExtraDatas)); } /* static */ bool ipc::IPDLParamTraits::Read( IPC::MessageReader* aReader, mozilla::ipc::IProtocol* aActor, RefPtr* aVar) { auto array = MakeRefPtr(); if (!ReadIPDLParam(aReader, aActor, &array->mSamples) || !ReadIPDLParam(aReader, aActor, &array->mBuffers) || !ReadIPDLParam(aReader, aActor, &array->mAlphaBuffers) || !ReadIPDLParam(aReader, aActor, &array->mExtraDatas)) { return false; } *aVar = std::move(array); return true; } /* static */ void ipc::IPDLParamTraits::Write( IPC::MessageWriter* aWriter, ipc::IProtocol* aActor, const paramType& aVar) { WriteIPDLParam(aWriter, aActor, aVar.mBase); WriteIPDLParam(aWriter, aActor, aVar.mEOS); WriteIPDLParam(aWriter, aActor, aVar.mHeight); WriteIPDLParam(aWriter, aActor, aVar.mDiscardPadding); WriteIPDLParam(aWriter, aActor, aVar.mOriginalPresentationWindow); WriteIPDLParam(aWriter, aActor, aVar.mCryptoConfig); } /* static */ bool ipc::IPDLParamTraits::Read( IPC::MessageReader* aReader, ipc::IProtocol* aActor, paramType* aVar) { MediaDataIPDL mBase; return ReadIPDLParam(aReader, aActor, &aVar->mBase) && ReadIPDLParam(aReader, aActor, &aVar->mEOS) && ReadIPDLParam(aReader, aActor, &aVar->mHeight) && ReadIPDLParam(aReader, aActor, &aVar->mDiscardPadding) && ReadIPDLParam(aReader, aActor, &aVar->mOriginalPresentationWindow) && ReadIPDLParam(aReader, aActor, &aVar->mCryptoConfig); }; bool ArrayOfRemoteAudioData::Fill( const nsTArray>& aData, std::function&& aAllocator) { nsTArray dataBuffers(aData.Length()); for (auto&& entry : aData) { dataBuffers.AppendElement(std::move(entry->mAudioData)); mSamples.AppendElement(RemoteAudioData{ MediaDataIPDL(entry->mOffset, entry->mTime, entry->mTimecode, entry->mDuration, entry->mKeyframe), entry->mChannels, entry->mRate, uint32_t(entry->mChannelMap), entry->mOriginalTime, entry->mTrimWindow, entry->mFrames, entry->mDataOffset}); } mBuffers = RemoteArrayOfByteBuffer(dataBuffers, aAllocator); if (!mBuffers.IsValid()) { return false; } return true; } already_AddRefed ArrayOfRemoteAudioData::ElementAt( size_t aIndex) const { if (!IsValid()) { return nullptr; } MOZ_ASSERT(aIndex < Count()); MOZ_DIAGNOSTIC_ASSERT(mBuffers.Count() == Count(), "Something ain't right here"); const auto& sample = mSamples[aIndex]; AlignedAudioBuffer data = mBuffers.AlignedBufferAt(aIndex); if (mBuffers.SizeAt(aIndex) && !data) { // OOM return nullptr; } auto audioData = MakeRefPtr( sample.mBase.offset(), sample.mBase.time(), std::move(data), sample.mChannels, sample.mRate, sample.mChannelMap); // An AudioData's duration is set at construction time based on the size of // the provided buffer. However, if a trim window is set, this value will be // incorrect. We have to re-set it to what it actually was. audioData->mDuration = sample.mBase.duration(); audioData->mOriginalTime = sample.mOriginalTime; audioData->mTrimWindow = sample.mTrimWindow; audioData->mFrames = sample.mFrames; audioData->mDataOffset = sample.mDataOffset; return audioData.forget(); } /*static */ void ipc::IPDLParamTraits::Write( IPC::MessageWriter* aWriter, ipc::IProtocol* aActor, ArrayOfRemoteAudioData* aVar) { WriteIPDLParam(aWriter, aActor, std::move(aVar->mSamples)); WriteIPDLParam(aWriter, aActor, std::move(aVar->mBuffers)); } /* static */ bool ipc::IPDLParamTraits::Read( IPC::MessageReader* aReader, mozilla::ipc::IProtocol* aActor, RefPtr* aVar) { auto array = MakeRefPtr(); if (!ReadIPDLParam(aReader, aActor, &array->mSamples) || !ReadIPDLParam(aReader, aActor, &array->mBuffers)) { return false; } *aVar = std::move(array); return true; } /* static */ void ipc::IPDLParamTraits::Write( IPC::MessageWriter* aWriter, ipc::IProtocol* aActor, const paramType& aVar) { WriteIPDLParam(aWriter, aActor, aVar.mBase); WriteIPDLParam(aWriter, aActor, aVar.mChannels); WriteIPDLParam(aWriter, aActor, aVar.mRate); WriteIPDLParam(aWriter, aActor, aVar.mChannelMap); WriteIPDLParam(aWriter, aActor, aVar.mOriginalTime); WriteIPDLParam(aWriter, aActor, aVar.mTrimWindow); WriteIPDLParam(aWriter, aActor, aVar.mFrames); WriteIPDLParam(aWriter, aActor, aVar.mDataOffset); } /* static */ bool ipc::IPDLParamTraits::Read( IPC::MessageReader* aReader, ipc::IProtocol* aActor, paramType* aVar) { MediaDataIPDL mBase; if (!ReadIPDLParam(aReader, aActor, &aVar->mBase) || !ReadIPDLParam(aReader, aActor, &aVar->mChannels) || !ReadIPDLParam(aReader, aActor, &aVar->mRate) || !ReadIPDLParam(aReader, aActor, &aVar->mChannelMap) || !ReadIPDLParam(aReader, aActor, &aVar->mOriginalTime) || !ReadIPDLParam(aReader, aActor, &aVar->mTrimWindow) || !ReadIPDLParam(aReader, aActor, &aVar->mFrames) || !ReadIPDLParam(aReader, aActor, &aVar->mDataOffset)) { return false; } return true; }; } // namespace mozilla