diff options
Diffstat (limited to 'dom/media/ipc/RemoteMediaData.cpp')
-rw-r--r-- | dom/media/ipc/RemoteMediaData.cpp | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/dom/media/ipc/RemoteMediaData.cpp b/dom/media/ipc/RemoteMediaData.cpp new file mode 100644 index 0000000000..bd323b7333 --- /dev/null +++ b/dom/media/ipc/RemoteMediaData.cpp @@ -0,0 +1,374 @@ +/* -*- 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<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 && mBuffers->IsReadable() && + 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 || !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<MediaByteBuffer> buffer = new MediaByteBuffer(entrySize); + buffer->SetLength(entrySize); + memcpy(buffer->Elements(), mBuffers->get<uint8_t>() + std::get<0>(entry), + entrySize); + return buffer.forget(); +} + +/*static */ void ipc::IPDLParamTraits<RemoteArrayOfByteBuffer>::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<ipc::Shmem>()); + } + WriteIPDLParam(aWriter, aActor, aVar.mOffsets); +} + +/* static */ bool ipc::IPDLParamTraits<RemoteArrayOfByteBuffer>::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<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()); + 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<PlaybackStage> 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<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]; + PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::CopyDemuxedData, + sample.mHeight); + 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); + 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<ArrayOfRemoteMediaRawData*>::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<ArrayOfRemoteMediaRawData*>::Read( + IPC::MessageReader* aReader, mozilla::ipc::IProtocol* aActor, + RefPtr<ArrayOfRemoteMediaRawData>* aVar) { + auto array = MakeRefPtr<ArrayOfRemoteMediaRawData>(); + 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<ArrayOfRemoteMediaRawData::RemoteMediaRawData>::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<ArrayOfRemoteMediaRawData::RemoteMediaRawData>::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<RefPtr<AudioData>>& aData, + std::function<ShmemBuffer(size_t)>&& aAllocator) { + nsTArray<AlignedAudioBuffer> 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<AudioData> 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<AudioDataValue>(aIndex); + if (mBuffers.SizeAt(aIndex) && !data) { + // OOM + return nullptr; + } + auto audioData = MakeRefPtr<AudioData>( + 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<ArrayOfRemoteAudioData*>::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<ArrayOfRemoteAudioData*>::Read( + IPC::MessageReader* aReader, mozilla::ipc::IProtocol* aActor, + RefPtr<ArrayOfRemoteAudioData>* aVar) { + auto array = MakeRefPtr<ArrayOfRemoteAudioData>(); + if (!ReadIPDLParam(aReader, aActor, &array->mSamples) || + !ReadIPDLParam(aReader, aActor, &array->mBuffers)) { + return false; + } + *aVar = std::move(array); + return true; +} + +/* static */ void +ipc::IPDLParamTraits<ArrayOfRemoteAudioData::RemoteAudioData>::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<ArrayOfRemoteAudioData::RemoteAudioData>::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 |