summaryrefslogtreecommitdiffstats
path: root/dom/media/ipc/RemoteMediaData.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/media/ipc/RemoteMediaData.cpp354
1 files changed, 354 insertions, 0 deletions
diff --git a/dom/media/ipc/RemoteMediaData.cpp b/dom/media/ipc/RemoteMediaData.cpp
new file mode 100644
index 0000000000..e7ab4aeefe
--- /dev/null
+++ b/dom/media/ipc/RemoteMediaData.cpp
@@ -0,0 +1,354 @@
+/* -*- 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 || !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::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});
+ }
+ 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);
+ 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);
+}
+
+/* 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);
+};
+
+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