RemoteMediaData.cpp (14674B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "RemoteMediaData.h" 8 9 #include "PerformanceRecorder.h" 10 #include "mozilla/CheckedInt.h" 11 #include "mozilla/dom/MediaIPCUtils.h" 12 #include "mozilla/ipc/Shmem.h" 13 14 namespace mozilla { 15 16 bool RemoteArrayOfByteBuffer::AllocateShmem( 17 size_t aSize, std::function<ShmemBuffer(size_t)>& aAllocator) { 18 ShmemBuffer buffer = aAllocator(aSize); 19 if (!buffer.Valid()) { 20 return false; 21 } 22 mBuffers.emplace(std::move(buffer.Get())); 23 return true; 24 } 25 26 uint8_t* RemoteArrayOfByteBuffer::BuffersStartAddress() const { 27 MOZ_ASSERT(mBuffers); 28 return mBuffers->get<uint8_t>(); 29 } 30 31 bool RemoteArrayOfByteBuffer::Check(size_t aOffset, size_t aSizeInBytes) const { 32 if (!mBuffers || !mBuffers->IsReadable()) { 33 return false; 34 } 35 auto size = CheckedInt<size_t>(aOffset) + aSizeInBytes; 36 return size.isValid() && size.value() <= mBuffers->Size<uint8_t>(); 37 } 38 39 void RemoteArrayOfByteBuffer::Write(size_t aOffset, const void* aSourceAddr, 40 size_t aSizeInBytes) { 41 if (!aSizeInBytes) { 42 return; 43 } 44 MOZ_DIAGNOSTIC_ASSERT(Check(aOffset, aSizeInBytes), 45 "Allocated Shmem is too small"); 46 memcpy(BuffersStartAddress() + aOffset, aSourceAddr, aSizeInBytes); 47 } 48 49 RemoteArrayOfByteBuffer::RemoteArrayOfByteBuffer() = default; 50 51 RemoteArrayOfByteBuffer::RemoteArrayOfByteBuffer( 52 const nsTArray<RefPtr<MediaByteBuffer>>& aArray, 53 std::function<ShmemBuffer(size_t)>& aAllocator) { 54 // Determine the total size we will need for this object. 55 size_t totalSize = 0; 56 for (const auto& buffer : aArray) { 57 if (buffer) { 58 totalSize += buffer->Length(); 59 } 60 } 61 if (totalSize) { 62 if (!AllocateShmem(totalSize, aAllocator)) { 63 return; 64 } 65 } 66 size_t offset = 0; 67 for (const auto& buffer : aArray) { 68 size_t sizeBuffer = buffer ? buffer->Length() : 0; 69 if (totalSize && sizeBuffer) { 70 Write(offset, buffer->Elements(), sizeBuffer); 71 } 72 mOffsets.AppendElement(OffsetEntry{offset, sizeBuffer}); 73 offset += sizeBuffer; 74 } 75 mIsValid = true; 76 } 77 78 RemoteArrayOfByteBuffer& RemoteArrayOfByteBuffer::operator=( 79 RemoteArrayOfByteBuffer&& aOther) noexcept { 80 mIsValid = aOther.mIsValid; 81 mBuffers = std::move(aOther.mBuffers); 82 mOffsets = std::move(aOther.mOffsets); 83 aOther.mIsValid = false; 84 return *this; 85 } 86 87 RemoteArrayOfByteBuffer::~RemoteArrayOfByteBuffer() = default; 88 89 already_AddRefed<MediaByteBuffer> RemoteArrayOfByteBuffer::MediaByteBufferAt( 90 size_t aIndex) const { 91 MOZ_ASSERT(aIndex < Count()); 92 const OffsetEntry& entry = mOffsets[aIndex]; 93 if (!mBuffers || !std::get<1>(entry)) { 94 // It's an empty one. 95 return nullptr; 96 } 97 size_t entrySize = std::get<1>(entry); 98 if (!Check(std::get<0>(entry), entrySize)) { 99 // This Shmem is corrupted and can't contain the data we are about to 100 // retrieve. We return an empty array instead of asserting to allow for 101 // recovery. 102 return nullptr; 103 } 104 RefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(entrySize); 105 buffer->SetLength(entrySize); 106 memcpy(buffer->Elements(), mBuffers->get<uint8_t>() + std::get<0>(entry), 107 entrySize); 108 return buffer.forget(); 109 } 110 111 } // namespace mozilla 112 113 /*static */ void IPC::ParamTraits<mozilla::RemoteArrayOfByteBuffer>::Write( 114 IPC::MessageWriter* aWriter, const mozilla::RemoteArrayOfByteBuffer& aVar) { 115 WriteParam(aWriter, aVar.mIsValid); 116 // We need the following gymnastic as the Shmem transfered over IPC will be 117 // revoked. We must create a temporary one instead so that it can be recycled 118 // later back into the original ShmemPool. 119 if (aVar.mBuffers) { 120 WriteParam(aWriter, mozilla::Some(mozilla::ipc::Shmem(*aVar.mBuffers))); 121 } else { 122 WriteParam(aWriter, mozilla::Maybe<mozilla::ipc::Shmem>()); 123 } 124 WriteParam(aWriter, aVar.mOffsets); 125 } 126 127 /* static */ bool IPC::ParamTraits<mozilla::RemoteArrayOfByteBuffer>::Read( 128 IPC::MessageReader* aReader, mozilla::RemoteArrayOfByteBuffer* aVar) { 129 return ReadParam(aReader, &aVar->mIsValid) && 130 ReadParam(aReader, &aVar->mBuffers) && 131 ReadParam(aReader, &aVar->mOffsets); 132 } 133 134 namespace mozilla { 135 136 bool ArrayOfRemoteMediaRawData::Fill( 137 const nsTArray<RefPtr<MediaRawData>>& aData, 138 std::function<ShmemBuffer(size_t)>&& aAllocator) { 139 nsTArray<AlignedByteBuffer> dataBuffers(aData.Length()); 140 nsTArray<AlignedByteBuffer> alphaBuffers(aData.Length()); 141 nsTArray<RefPtr<MediaByteBuffer>> extraDataBuffers(aData.Length()); 142 int32_t height = 0; 143 for (auto&& entry : aData) { 144 dataBuffers.AppendElement(std::move(entry->mBuffer)); 145 alphaBuffers.AppendElement(std::move(entry->mAlphaBuffer)); 146 extraDataBuffers.AppendElement(std::move(entry->mExtraData)); 147 if (auto&& info = entry->mTrackInfo; info && info->GetAsVideoInfo()) { 148 height = info->GetAsVideoInfo()->mImage.height; 149 } 150 mSamples.AppendElement(RemoteMediaRawData{ 151 MediaDataIPDL(entry->mOffset, entry->mTime, entry->mTimecode, 152 entry->mDuration, entry->mKeyframe), 153 entry->mEOS, height, entry->mTemporalLayerId, 154 entry->mOriginalPresentationWindow, 155 entry->mCrypto.IsEncrypted() && entry->mShouldCopyCryptoToRemoteRawData 156 ? Some(CryptoInfo{ 157 entry->mCrypto.mCryptoScheme, 158 entry->mCrypto.mIV, 159 entry->mCrypto.mConstantIV, 160 entry->mCrypto.mKeyId, 161 entry->mCrypto.mPlainSizes, 162 entry->mCrypto.mEncryptedSizes, 163 entry->mCrypto.mCryptByteBlock, 164 entry->mCrypto.mSkipByteBlock, 165 }) 166 : Nothing()}); 167 } 168 PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::CopyDemuxedData, 169 height); 170 mBuffers = RemoteArrayOfByteBuffer(dataBuffers, aAllocator); 171 if (!mBuffers.IsValid()) { 172 return false; 173 } 174 mAlphaBuffers = RemoteArrayOfByteBuffer(alphaBuffers, aAllocator); 175 if (!mAlphaBuffers.IsValid()) { 176 return false; 177 } 178 mExtraDatas = RemoteArrayOfByteBuffer(extraDataBuffers, aAllocator); 179 if (!mExtraDatas.IsValid()) { 180 return false; 181 } 182 perfRecorder.Record(); 183 return true; 184 } 185 186 already_AddRefed<MediaRawData> ArrayOfRemoteMediaRawData::ElementAt( 187 size_t aIndex) const { 188 if (!IsValid()) { 189 return nullptr; 190 } 191 MOZ_ASSERT(aIndex < Count()); 192 MOZ_DIAGNOSTIC_ASSERT(mBuffers.Count() == Count() && 193 mAlphaBuffers.Count() == Count() && 194 mExtraDatas.Count() == Count(), 195 "Something ain't right here"); 196 const auto& sample = mSamples[aIndex]; 197 PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::CopyDemuxedData, 198 sample.mHeight); 199 AlignedByteBuffer data = mBuffers.AlignedBufferAt<uint8_t>(aIndex); 200 if (mBuffers.SizeAt(aIndex) && !data) { 201 // OOM 202 return nullptr; 203 } 204 AlignedByteBuffer alphaData = mAlphaBuffers.AlignedBufferAt<uint8_t>(aIndex); 205 if (mAlphaBuffers.SizeAt(aIndex) && !alphaData) { 206 // OOM 207 return nullptr; 208 } 209 RefPtr<MediaRawData> rawData; 210 if (mAlphaBuffers.SizeAt(aIndex)) { 211 rawData = new MediaRawData(std::move(data), std::move(alphaData)); 212 } else { 213 rawData = new MediaRawData(std::move(data)); 214 } 215 rawData->mOffset = sample.mBase.offset(); 216 rawData->mTime = sample.mBase.time(); 217 rawData->mTimecode = sample.mBase.timecode(); 218 rawData->mDuration = sample.mBase.duration(); 219 rawData->mKeyframe = sample.mBase.keyframe(); 220 rawData->mEOS = sample.mEOS; 221 rawData->mTemporalLayerId = sample.mTemporalLayerId; 222 rawData->mExtraData = mExtraDatas.MediaByteBufferAt(aIndex); 223 if (sample.mCryptoConfig) { 224 CryptoSample& cypto = rawData->GetWritableCrypto(); 225 cypto.mCryptoScheme = sample.mCryptoConfig->mEncryptionScheme(); 226 cypto.mIV = std::move(sample.mCryptoConfig->mIV()); 227 cypto.mConstantIV = std::move(sample.mCryptoConfig->mConstantIV()); 228 cypto.mIVSize = cypto.mIV.Length(); 229 cypto.mKeyId = std::move(sample.mCryptoConfig->mKeyId()); 230 cypto.mPlainSizes = std::move(sample.mCryptoConfig->mClearBytes()); 231 cypto.mEncryptedSizes = std::move(sample.mCryptoConfig->mCipherBytes()); 232 cypto.mCryptByteBlock = sample.mCryptoConfig->mCryptByteBlock(); 233 cypto.mSkipByteBlock = sample.mCryptoConfig->mSkipByteBlock(); 234 } 235 perfRecorder.Record(); 236 return rawData.forget(); 237 } 238 239 } // namespace mozilla 240 241 /*static */ void IPC::ParamTraits<mozilla::ArrayOfRemoteMediaRawData*>::Write( 242 MessageWriter* aWriter, mozilla::ArrayOfRemoteMediaRawData* aVar) { 243 WriteParam(aWriter, std::move(aVar->mSamples)); 244 WriteParam(aWriter, std::move(aVar->mBuffers)); 245 WriteParam(aWriter, std::move(aVar->mAlphaBuffers)); 246 WriteParam(aWriter, std::move(aVar->mExtraDatas)); 247 } 248 249 /* static */ bool IPC::ParamTraits<mozilla::ArrayOfRemoteMediaRawData*>::Read( 250 MessageReader* aReader, RefPtr<mozilla::ArrayOfRemoteMediaRawData>* aVar) { 251 auto array = mozilla::MakeRefPtr<mozilla::ArrayOfRemoteMediaRawData>(); 252 if (!ReadParam(aReader, &array->mSamples) || 253 !ReadParam(aReader, &array->mBuffers) || 254 !ReadParam(aReader, &array->mAlphaBuffers) || 255 !ReadParam(aReader, &array->mExtraDatas)) { 256 return false; 257 } 258 *aVar = std::move(array); 259 return true; 260 } 261 262 /* static */ void 263 IPC::ParamTraits<mozilla::ArrayOfRemoteMediaRawData::RemoteMediaRawData>::Write( 264 MessageWriter* aWriter, const paramType& aVar) { 265 WriteParam(aWriter, aVar.mBase); 266 WriteParam(aWriter, aVar.mEOS); 267 WriteParam(aWriter, aVar.mHeight); 268 WriteParam(aWriter, aVar.mTemporalLayerId); 269 WriteParam(aWriter, aVar.mOriginalPresentationWindow); 270 WriteParam(aWriter, aVar.mCryptoConfig); 271 } 272 273 /* static */ bool 274 IPC::ParamTraits<mozilla::ArrayOfRemoteMediaRawData::RemoteMediaRawData>::Read( 275 MessageReader* aReader, paramType* aVar) { 276 mozilla::MediaDataIPDL mBase; 277 return ReadParam(aReader, &aVar->mBase) && ReadParam(aReader, &aVar->mEOS) && 278 ReadParam(aReader, &aVar->mHeight) && 279 ReadParam(aReader, &aVar->mTemporalLayerId) && 280 ReadParam(aReader, &aVar->mOriginalPresentationWindow) && 281 ReadParam(aReader, &aVar->mCryptoConfig); 282 }; 283 284 namespace mozilla { 285 286 bool ArrayOfRemoteAudioData::Fill( 287 const AudioData* aData, std::function<ShmemBuffer(size_t)>&& aAllocator) { 288 mSamples.AppendElement(RemoteAudioData{ 289 MediaDataIPDL(aData->mOffset, aData->mTime, aData->mTimecode, 290 aData->mDuration, aData->mKeyframe), 291 aData->mChannels, aData->mRate, uint32_t(aData->mChannelMap), 292 aData->mOriginalTime, aData->mTrimWindow, aData->mFrames, 293 aData->mDataOffset}); 294 mBuffers = RemoteArrayOfByteBuffer(aData->mAudioData, aAllocator); 295 if (!mBuffers.IsValid()) { 296 return false; 297 } 298 return true; 299 } 300 301 bool ArrayOfRemoteAudioData::Fill( 302 const nsTArray<RefPtr<AudioData>>& aData, 303 std::function<ShmemBuffer(size_t)>&& aAllocator) { 304 nsTArray<AlignedAudioBuffer> dataBuffers(aData.Length()); 305 for (auto&& entry : aData) { 306 dataBuffers.AppendElement(std::move(entry->mAudioData)); 307 mSamples.AppendElement(RemoteAudioData{ 308 MediaDataIPDL(entry->mOffset, entry->mTime, entry->mTimecode, 309 entry->mDuration, entry->mKeyframe), 310 entry->mChannels, entry->mRate, uint32_t(entry->mChannelMap), 311 entry->mOriginalTime, entry->mTrimWindow, entry->mFrames, 312 entry->mDataOffset}); 313 } 314 mBuffers = RemoteArrayOfByteBuffer(dataBuffers, aAllocator); 315 if (!mBuffers.IsValid()) { 316 return false; 317 } 318 return true; 319 } 320 321 already_AddRefed<AudioData> ArrayOfRemoteAudioData::ElementAt( 322 size_t aIndex) const { 323 if (!IsValid()) { 324 return nullptr; 325 } 326 MOZ_ASSERT(aIndex < Count()); 327 MOZ_DIAGNOSTIC_ASSERT(mBuffers.Count() == Count(), 328 "Something ain't right here"); 329 const auto& sample = mSamples[aIndex]; 330 AlignedAudioBuffer data = mBuffers.AlignedBufferAt<AudioDataValue>(aIndex); 331 if (mBuffers.SizeAt(aIndex) && !data) { 332 // OOM 333 return nullptr; 334 } 335 auto audioData = MakeRefPtr<AudioData>( 336 sample.mBase.offset(), sample.mBase.time(), std::move(data), 337 sample.mChannels, sample.mRate, sample.mChannelMap); 338 // An AudioData's duration is set at construction time based on the size of 339 // the provided buffer. However, if a trim window is set, this value will be 340 // incorrect. We have to re-set it to what it actually was. 341 audioData->mDuration = sample.mBase.duration(); 342 audioData->mOriginalTime = sample.mOriginalTime; 343 audioData->mTrimWindow = sample.mTrimWindow; 344 audioData->mFrames = sample.mFrames; 345 audioData->mDataOffset = sample.mDataOffset; 346 return audioData.forget(); 347 } 348 349 } // namespace mozilla 350 351 /*static */ void IPC::ParamTraits<mozilla::ArrayOfRemoteAudioData*>::Write( 352 IPC::MessageWriter* aWriter, mozilla::ArrayOfRemoteAudioData* aVar) { 353 WriteParam(aWriter, std::move(aVar->mSamples)); 354 WriteParam(aWriter, std::move(aVar->mBuffers)); 355 } 356 357 /* static */ bool IPC::ParamTraits<mozilla::ArrayOfRemoteAudioData*>::Read( 358 IPC::MessageReader* aReader, 359 RefPtr<mozilla::ArrayOfRemoteAudioData>* aVar) { 360 auto array = mozilla::MakeRefPtr<mozilla::ArrayOfRemoteAudioData>(); 361 if (!ReadParam(aReader, &array->mSamples) || 362 !ReadParam(aReader, &array->mBuffers)) { 363 return false; 364 } 365 *aVar = std::move(array); 366 return true; 367 } 368 369 /* static */ void 370 IPC::ParamTraits<mozilla::ArrayOfRemoteAudioData::RemoteAudioData>::Write( 371 IPC::MessageWriter* aWriter, const paramType& aVar) { 372 WriteParam(aWriter, aVar.mBase); 373 WriteParam(aWriter, aVar.mChannels); 374 WriteParam(aWriter, aVar.mRate); 375 WriteParam(aWriter, aVar.mChannelMap); 376 WriteParam(aWriter, aVar.mOriginalTime); 377 WriteParam(aWriter, aVar.mTrimWindow); 378 WriteParam(aWriter, aVar.mFrames); 379 WriteParam(aWriter, aVar.mDataOffset); 380 } 381 382 /* static */ bool 383 IPC::ParamTraits<mozilla::ArrayOfRemoteAudioData::RemoteAudioData>::Read( 384 IPC::MessageReader* aReader, paramType* aVar) { 385 mozilla::MediaDataIPDL mBase; 386 return ReadParam(aReader, &aVar->mBase) && 387 ReadParam(aReader, &aVar->mChannels) && 388 ReadParam(aReader, &aVar->mRate) && 389 ReadParam(aReader, &aVar->mChannelMap) && 390 ReadParam(aReader, &aVar->mOriginalTime) && 391 ReadParam(aReader, &aVar->mTrimWindow) && 392 ReadParam(aReader, &aVar->mFrames) && 393 ReadParam(aReader, &aVar->mDataOffset); 394 };