summaryrefslogtreecommitdiffstats
path: root/dom/media/MediaData.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/MediaData.h')
-rw-r--r--dom/media/MediaData.h762
1 files changed, 762 insertions, 0 deletions
diff --git a/dom/media/MediaData.h b/dom/media/MediaData.h
new file mode 100644
index 0000000000..ee1e204815
--- /dev/null
+++ b/dom/media/MediaData.h
@@ -0,0 +1,762 @@
+/* -*- 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/. */
+#if !defined(MediaData_h)
+# define MediaData_h
+
+# include "AudioConfig.h"
+# include "AudioSampleFormat.h"
+# include "ImageTypes.h"
+# include "MediaResult.h"
+# include "SharedBuffer.h"
+# include "TimeUnits.h"
+# include "mozilla/CheckedInt.h"
+# include "mozilla/Maybe.h"
+# include "mozilla/PodOperations.h"
+# include "mozilla/RefPtr.h"
+# include "mozilla/Result.h"
+# include "mozilla/Span.h"
+# include "mozilla/UniquePtr.h"
+# include "mozilla/UniquePtrExtensions.h"
+# include "mozilla/gfx/Rect.h"
+# include "nsString.h"
+# include "nsTArray.h"
+
+namespace mozilla {
+
+namespace layers {
+class Image;
+class ImageContainer;
+class KnowsCompositor;
+} // namespace layers
+
+class MediaByteBuffer;
+class TrackInfoSharedPtr;
+
+// AlignedBuffer:
+// Memory allocations are fallibles. Methods return a boolean indicating if
+// memory allocations were successful. Return values should always be checked.
+// AlignedBuffer::mData will be nullptr if no memory has been allocated or if
+// an error occurred during construction.
+// Existing data is only ever modified if new memory allocation has succeeded
+// and preserved if not.
+//
+// The memory referenced by mData will always be Alignment bytes aligned and the
+// underlying buffer will always have a size such that Alignment bytes blocks
+// can be used to read the content, regardless of the mSize value. Buffer is
+// zeroed on creation, elements are not individually constructed.
+// An Alignment value of 0 means that the data isn't aligned.
+//
+// Type must be trivially copyable.
+//
+// AlignedBuffer can typically be used in place of UniquePtr<Type[]> however
+// care must be taken as all memory allocations are fallible.
+// Example:
+// auto buffer = MakeUniqueFallible<float[]>(samples)
+// becomes: AlignedFloatBuffer buffer(samples)
+//
+// auto buffer = MakeUnique<float[]>(samples)
+// becomes:
+// AlignedFloatBuffer buffer(samples);
+// if (!buffer) { return NS_ERROR_OUT_OF_MEMORY; }
+class InflatableShortBuffer;
+template <typename Type, int Alignment = 32>
+class AlignedBuffer {
+ public:
+ friend InflatableShortBuffer;
+ AlignedBuffer()
+ : mData(nullptr), mLength(0), mBuffer(nullptr), mCapacity(0) {}
+
+ explicit AlignedBuffer(size_t aLength)
+ : mData(nullptr), mLength(0), mBuffer(nullptr), mCapacity(0) {
+ if (EnsureCapacity(aLength)) {
+ mLength = aLength;
+ }
+ }
+
+ AlignedBuffer(const Type* aData, size_t aLength) : AlignedBuffer(aLength) {
+ if (!mData) {
+ return;
+ }
+ PodCopy(mData, aData, aLength);
+ }
+
+ AlignedBuffer(const AlignedBuffer& aOther)
+ : AlignedBuffer(aOther.Data(), aOther.Length()) {}
+
+ AlignedBuffer(AlignedBuffer&& aOther) noexcept
+ : mData(aOther.mData),
+ mLength(aOther.mLength),
+ mBuffer(std::move(aOther.mBuffer)),
+ mCapacity(aOther.mCapacity) {
+ aOther.mData = nullptr;
+ aOther.mLength = 0;
+ aOther.mCapacity = 0;
+ }
+
+ AlignedBuffer& operator=(AlignedBuffer&& aOther) noexcept {
+ this->~AlignedBuffer();
+ new (this) AlignedBuffer(std::move(aOther));
+ return *this;
+ }
+
+ Type* Data() const { return mData; }
+ size_t Length() const { return mLength; }
+ size_t Size() const { return mLength * sizeof(Type); }
+ Type& operator[](size_t aIndex) {
+ MOZ_ASSERT(aIndex < mLength);
+ return mData[aIndex];
+ }
+ const Type& operator[](size_t aIndex) const {
+ MOZ_ASSERT(aIndex < mLength);
+ return mData[aIndex];
+ }
+ // Set length of buffer, allocating memory as required.
+ // If memory is allocated, additional buffer area is filled with 0.
+ bool SetLength(size_t aLength) {
+ if (aLength > mLength && !EnsureCapacity(aLength)) {
+ return false;
+ }
+ mLength = aLength;
+ return true;
+ }
+ // Add aData at the beginning of buffer.
+ bool Prepend(const Type* aData, size_t aLength) {
+ if (!EnsureCapacity(aLength + mLength)) {
+ return false;
+ }
+
+ // Shift the data to the right by aLength to leave room for the new data.
+ PodMove(mData + aLength, mData, mLength);
+ PodCopy(mData, aData, aLength);
+
+ mLength += aLength;
+ return true;
+ }
+ // Add aData at the end of buffer.
+ bool Append(const Type* aData, size_t aLength) {
+ if (!EnsureCapacity(aLength + mLength)) {
+ return false;
+ }
+
+ PodCopy(mData + mLength, aData, aLength);
+
+ mLength += aLength;
+ return true;
+ }
+ // Replace current content with aData.
+ bool Replace(const Type* aData, size_t aLength) {
+ // If aLength is smaller than our current length, we leave the buffer as is,
+ // only adjusting the reported length.
+ if (!EnsureCapacity(aLength)) {
+ return false;
+ }
+
+ PodCopy(mData, aData, aLength);
+ mLength = aLength;
+ return true;
+ }
+ // Clear the memory buffer. Will set target mData and mLength to 0.
+ void Clear() {
+ mLength = 0;
+ mData = nullptr;
+ }
+
+ // Methods for reporting memory.
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+ size_t size = aMallocSizeOf(this);
+ size += aMallocSizeOf(mBuffer.get());
+ return size;
+ }
+ // AlignedBuffer is typically allocated on the stack. As such, you likely
+ // want to use SizeOfExcludingThis
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(mBuffer.get());
+ }
+ size_t ComputedSizeOfExcludingThis() const { return mCapacity; }
+
+ // For backward compatibility with UniquePtr<Type[]>
+ Type* get() const { return mData; }
+ explicit operator bool() const { return mData != nullptr; }
+
+ // Size in bytes of extra space allocated for padding.
+ static size_t AlignmentPaddingSize() { return AlignmentOffset() * 2; }
+
+ void PopFront(size_t aCount) {
+ MOZ_DIAGNOSTIC_ASSERT(mLength >= aCount, "Popping too many elements.");
+ PodMove(mData, mData + aCount, mLength - aCount);
+ mLength -= aCount;
+ }
+
+ void PopBack(size_t aCount) {
+ MOZ_DIAGNOSTIC_ASSERT(mLength >= aCount, "Popping too many elements.");
+ mLength -= aCount;
+ }
+
+ private:
+ static size_t AlignmentOffset() { return Alignment ? Alignment - 1 : 0; }
+
+ // Ensure that the backend buffer can hold aLength data. Will update mData.
+ // Will enforce that the start of allocated data is always Alignment bytes
+ // aligned and that it has sufficient end padding to allow for Alignment bytes
+ // block read as required by some data decoders.
+ // Returns false if memory couldn't be allocated.
+ bool EnsureCapacity(size_t aLength) {
+ if (!aLength) {
+ // No need to allocate a buffer yet.
+ return true;
+ }
+ const CheckedInt<size_t> sizeNeeded =
+ CheckedInt<size_t>(aLength) * sizeof(Type) + AlignmentPaddingSize();
+
+ if (!sizeNeeded.isValid() || sizeNeeded.value() >= INT32_MAX) {
+ // overflow or over an acceptable size.
+ return false;
+ }
+ if (mData && mCapacity >= sizeNeeded.value()) {
+ return true;
+ }
+ auto newBuffer = MakeUniqueFallible<uint8_t[]>(sizeNeeded.value());
+ if (!newBuffer) {
+ return false;
+ }
+
+ // Find alignment address.
+ const uintptr_t alignmask = AlignmentOffset();
+ Type* newData = reinterpret_cast<Type*>(
+ (reinterpret_cast<uintptr_t>(newBuffer.get()) + alignmask) &
+ ~alignmask);
+ MOZ_ASSERT(uintptr_t(newData) % (AlignmentOffset() + 1) == 0);
+
+ MOZ_ASSERT(!mLength || mData);
+
+ PodZero(newData + mLength, aLength - mLength);
+ if (mLength) {
+ PodCopy(newData, mData, mLength);
+ }
+
+ mBuffer = std::move(newBuffer);
+ mCapacity = sizeNeeded.value();
+ mData = newData;
+
+ return true;
+ }
+ Type* mData;
+ size_t mLength{}; // number of elements
+ UniquePtr<uint8_t[]> mBuffer;
+ size_t mCapacity{}; // in bytes
+};
+
+using AlignedByteBuffer = AlignedBuffer<uint8_t>;
+using AlignedFloatBuffer = AlignedBuffer<float>;
+using AlignedShortBuffer = AlignedBuffer<int16_t>;
+using AlignedAudioBuffer = AlignedBuffer<AudioDataValue>;
+
+// A buffer in which int16_t audio can be written to, and then converted to
+// float32 audio without reallocating.
+// This class is useful when an API hands out int16_t audio but the samples
+// need to be immediately converted to f32.
+class InflatableShortBuffer {
+ public:
+ explicit InflatableShortBuffer(size_t aElementCount)
+ : mBuffer(aElementCount * 2) {}
+ AlignedFloatBuffer Inflate() {
+ // Convert the data from int16_t to f32 in place, in the same buffer.
+ // The reason this works is because the buffer has in fact twice the
+ // capacity, and the loop goes backward.
+ float* output = reinterpret_cast<float*>(mBuffer.mData);
+ for (size_t i = Length(); i--;) {
+ output[i] = AudioSampleToFloat(mBuffer.mData[i]);
+ }
+ AlignedFloatBuffer rv;
+ rv.mBuffer = std::move(mBuffer.mBuffer);
+ rv.mCapacity = mBuffer.mCapacity;
+ rv.mLength = Length();
+ rv.mData = output;
+ return rv;
+ }
+ size_t Length() const { return mBuffer.mLength / 2; }
+ int16_t* get() const { return mBuffer.get(); }
+ explicit operator bool() const { return mBuffer.mData != nullptr; }
+
+ protected:
+ AlignedShortBuffer mBuffer;
+};
+
+// Container that holds media samples.
+class MediaData {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaData)
+
+ enum class Type : uint8_t { AUDIO_DATA = 0, VIDEO_DATA, RAW_DATA, NULL_DATA };
+ static const char* TypeToStr(Type aType) {
+ switch (aType) {
+ case Type::AUDIO_DATA:
+ return "AUDIO_DATA";
+ case Type::VIDEO_DATA:
+ return "VIDEO_DATA";
+ case Type::RAW_DATA:
+ return "RAW_DATA";
+ case Type::NULL_DATA:
+ return "NULL_DATA";
+ default:
+ MOZ_CRASH("bad value");
+ }
+ }
+
+ MediaData(Type aType, int64_t aOffset, const media::TimeUnit& aTimestamp,
+ const media::TimeUnit& aDuration)
+ : mType(aType),
+ mOffset(aOffset),
+ mTime(aTimestamp),
+ mTimecode(aTimestamp),
+ mDuration(aDuration),
+ mKeyframe(false) {}
+
+ // Type of contained data.
+ const Type mType;
+
+ // Approximate byte offset where this data was demuxed from its media.
+ int64_t mOffset;
+
+ // Start time of sample.
+ media::TimeUnit mTime;
+
+ // Codec specific internal time code. For Ogg based codecs this is the
+ // granulepos.
+ media::TimeUnit mTimecode;
+
+ // Duration of sample, in microseconds.
+ media::TimeUnit mDuration;
+
+ bool mKeyframe;
+
+ media::TimeUnit GetEndTime() const { return mTime + mDuration; }
+
+ media::TimeUnit GetEndTimecode() const { return mTimecode + mDuration; }
+
+ bool HasValidTime() const {
+ return mTime.IsValid() && mTimecode.IsValid() && mDuration.IsValid() &&
+ GetEndTime().IsValid() && GetEndTimecode().IsValid();
+ }
+
+ template <typename ReturnType>
+ const ReturnType* As() const {
+ MOZ_ASSERT(this->mType == ReturnType::sType);
+ return static_cast<const ReturnType*>(this);
+ }
+
+ template <typename ReturnType>
+ ReturnType* As() {
+ MOZ_ASSERT(this->mType == ReturnType::sType);
+ return static_cast<ReturnType*>(this);
+ }
+
+ protected:
+ explicit MediaData(Type aType) : mType(aType), mOffset(0), mKeyframe(false) {}
+
+ virtual ~MediaData() = default;
+};
+
+// NullData is for decoder generating a sample which doesn't need to be
+// rendered.
+class NullData : public MediaData {
+ public:
+ NullData(int64_t aOffset, const media::TimeUnit& aTime,
+ const media::TimeUnit& aDuration)
+ : MediaData(Type::NULL_DATA, aOffset, aTime, aDuration) {}
+
+ static const Type sType = Type::NULL_DATA;
+};
+
+// Holds chunk a decoded audio frames.
+class AudioData : public MediaData {
+ public:
+ AudioData(int64_t aOffset, const media::TimeUnit& aTime,
+ AlignedAudioBuffer&& aData, uint32_t aChannels, uint32_t aRate,
+ uint32_t aChannelMap = AudioConfig::ChannelLayout::UNKNOWN_MAP);
+
+ static const Type sType = Type::AUDIO_DATA;
+ static const char* sTypeName;
+
+ // Access the buffer as a Span.
+ Span<AudioDataValue> Data() const;
+
+ // Amount of frames for contained data.
+ uint32_t Frames() const { return mFrames; }
+
+ // Trim the audio buffer such that its apparent content fits within the aTrim
+ // interval. The actual data isn't removed from the buffer and a followup call
+ // to SetTrimWindow could restore the content. mDuration, mTime and mFrames
+ // will be adjusted accordingly.
+ // Warning: rounding may occurs, in which case the new start time of the audio
+ // sample may still be lesser than aTrim.mStart.
+ bool SetTrimWindow(const media::TimeInterval& aTrim);
+
+ // Get the internal audio buffer to be moved. After this call the original
+ // AudioData will be emptied and can't be used again.
+ AlignedAudioBuffer MoveableData();
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
+ // If mAudioBuffer is null, creates it from mAudioData.
+ void EnsureAudioBuffer();
+
+ // Return true if the adjusted time is valid. Caller should handle error when
+ // the result is invalid.
+ bool AdjustForStartTime(const media::TimeUnit& aStartTime);
+
+ // This method is used to adjust the original start time, which would change
+ // `mTime` and `mOriginalTime` together, and should only be used for data
+ // which hasn't been trimmed before.
+ void SetOriginalStartTime(const media::TimeUnit& aStartTime);
+
+ const uint32_t mChannels;
+ // The AudioConfig::ChannelLayout map. Channels are ordered as per SMPTE
+ // definition. A value of UNKNOWN_MAP indicates unknown layout.
+ // ChannelMap is an unsigned bitmap compatible with Windows' WAVE and FFmpeg
+ // channel map.
+ const AudioConfig::ChannelLayout::ChannelMap mChannelMap;
+ const uint32_t mRate;
+
+ // At least one of mAudioBuffer/mAudioData must be non-null.
+ // mChannels channels, each with mFrames frames
+ RefPtr<SharedBuffer> mAudioBuffer;
+
+ protected:
+ ~AudioData() = default;
+
+ private:
+ friend class ArrayOfRemoteAudioData;
+ AudioDataValue* GetAdjustedData() const;
+ media::TimeUnit mOriginalTime;
+ // mFrames frames, each with mChannels values
+ AlignedAudioBuffer mAudioData;
+ Maybe<media::TimeInterval> mTrimWindow;
+ // Amount of frames for contained data.
+ uint32_t mFrames;
+ size_t mDataOffset = 0;
+};
+
+namespace layers {
+class TextureClient;
+class PlanarYCbCrImage;
+} // namespace layers
+
+class VideoInfo;
+
+// Holds a decoded video frame, in YCbCr format. These are queued in the reader.
+class VideoData : public MediaData {
+ public:
+ using IntRect = gfx::IntRect;
+ using IntSize = gfx::IntSize;
+ using ColorDepth = gfx::ColorDepth;
+ using ColorRange = gfx::ColorRange;
+ using YUVColorSpace = gfx::YUVColorSpace;
+ using ColorSpace2 = gfx::ColorSpace2;
+ using ChromaSubsampling = gfx::ChromaSubsampling;
+ using ImageContainer = layers::ImageContainer;
+ using Image = layers::Image;
+ using PlanarYCbCrImage = layers::PlanarYCbCrImage;
+
+ static const Type sType = Type::VIDEO_DATA;
+ static const char* sTypeName;
+
+ // YCbCr data obtained from decoding the video. The index's are:
+ // 0 = Y
+ // 1 = Cb
+ // 2 = Cr
+ struct YCbCrBuffer {
+ struct Plane {
+ uint8_t* mData;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mStride;
+ uint32_t mSkip;
+ };
+
+ Plane mPlanes[3]{};
+ YUVColorSpace mYUVColorSpace = YUVColorSpace::Identity;
+ ColorSpace2 mColorPrimaries = ColorSpace2::UNKNOWN;
+ ColorDepth mColorDepth = ColorDepth::COLOR_8;
+ ColorRange mColorRange = ColorRange::LIMITED;
+ ChromaSubsampling mChromaSubsampling = ChromaSubsampling::FULL;
+ };
+
+ // Constructs a VideoData object. If aImage is nullptr, creates a new Image
+ // holding a copy of the YCbCr data passed in aBuffer. If aImage is not
+ // nullptr, it's stored as the underlying video image and aBuffer is assumed
+ // to point to memory within aImage so no copy is made. aTimecode is a codec
+ // specific number representing the timestamp of the frame of video data.
+ // Returns nsnull if an error occurs. This may indicate that memory couldn't
+ // be allocated to create the VideoData object, or it may indicate some
+ // problem with the input data (e.g. negative stride).
+
+ static bool UseUseNV12ForSoftwareDecodedVideoIfPossible(
+ layers::KnowsCompositor* aAllocator);
+
+ // Creates a new VideoData containing a deep copy of aBuffer. May use
+ // aContainer to allocate an Image to hold the copied data.
+ static Result<already_AddRefed<VideoData>, MediaResult> CreateAndCopyData(
+ const VideoInfo& aInfo, ImageContainer* aContainer, int64_t aOffset,
+ const media::TimeUnit& aTime, const media::TimeUnit& aDuration,
+ const YCbCrBuffer& aBuffer, bool aKeyframe,
+ const media::TimeUnit& aTimecode, const IntRect& aPicture,
+ layers::KnowsCompositor* aAllocator);
+
+ static already_AddRefed<VideoData> CreateAndCopyData(
+ const VideoInfo& aInfo, ImageContainer* aContainer, int64_t aOffset,
+ const media::TimeUnit& aTime, const media::TimeUnit& aDuration,
+ const YCbCrBuffer& aBuffer, const YCbCrBuffer::Plane& aAlphaPlane,
+ bool aKeyframe, const media::TimeUnit& aTimecode,
+ const IntRect& aPicture);
+
+ static already_AddRefed<VideoData> CreateFromImage(
+ const IntSize& aDisplay, int64_t aOffset, const media::TimeUnit& aTime,
+ const media::TimeUnit& aDuration, const RefPtr<Image>& aImage,
+ bool aKeyframe, const media::TimeUnit& aTimecode);
+
+ // Initialize PlanarYCbCrImage. Only When aCopyData is true,
+ // video data is copied to PlanarYCbCrImage.
+ static MediaResult SetVideoDataToImage(PlanarYCbCrImage* aVideoImage,
+ const VideoInfo& aInfo,
+ const YCbCrBuffer& aBuffer,
+ const IntRect& aPicture,
+ bool aCopyData);
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
+ // Dimensions at which to display the video frame. The picture region
+ // will be scaled to this size. This is should be the picture region's
+ // dimensions scaled with respect to its aspect ratio.
+ const IntSize mDisplay;
+
+ // This frame's image.
+ RefPtr<Image> mImage;
+
+ ColorDepth GetColorDepth() const;
+
+ uint32_t mFrameID;
+
+ VideoData(int64_t aOffset, const media::TimeUnit& aTime,
+ const media::TimeUnit& aDuration, bool aKeyframe,
+ const media::TimeUnit& aTimecode, IntSize aDisplay,
+ uint32_t aFrameID);
+
+ nsCString ToString() const;
+
+ void MarkSentToCompositor() { mSentToCompositor = true; }
+ bool IsSentToCompositor() { return mSentToCompositor; }
+
+ void UpdateDuration(const media::TimeUnit& aDuration);
+ void UpdateTimestamp(const media::TimeUnit& aTimestamp);
+
+ // Return true if the adjusted time is valid. Caller should handle error when
+ // the result is invalid.
+ bool AdjustForStartTime(const media::TimeUnit& aStartTime);
+
+ void SetNextKeyFrameTime(const media::TimeUnit& aTime) {
+ mNextKeyFrameTime = aTime;
+ }
+
+ const media::TimeUnit& NextKeyFrameTime() const { return mNextKeyFrameTime; }
+
+ protected:
+ ~VideoData();
+
+ bool mSentToCompositor;
+ media::TimeUnit mNextKeyFrameTime;
+};
+
+enum class CryptoScheme : uint8_t {
+ None,
+ Cenc,
+ Cbcs,
+};
+
+const char* CryptoSchemeToString(const CryptoScheme& aScheme);
+
+class CryptoTrack {
+ public:
+ CryptoTrack()
+ : mCryptoScheme(CryptoScheme::None),
+ mIVSize(0),
+ mCryptByteBlock(0),
+ mSkipByteBlock(0) {}
+ CryptoScheme mCryptoScheme;
+ int32_t mIVSize;
+ CopyableTArray<uint8_t> mKeyId;
+ uint8_t mCryptByteBlock;
+ uint8_t mSkipByteBlock;
+ CopyableTArray<uint8_t> mConstantIV;
+
+ bool IsEncrypted() const { return mCryptoScheme != CryptoScheme::None; }
+};
+
+class CryptoSample : public CryptoTrack {
+ public:
+ // The num clear bytes in each subsample. The nth element in the array is the
+ // number of clear bytes at the start of the nth subsample.
+ // Clear sizes are stored as uint16_t in containers per ISO/IEC
+ // 23001-7, but we store them as uint32_t for 2 reasons
+ // - The Widevine CDM accepts clear sizes as uint32_t.
+ // - When converting samples to Annex B we modify the clear sizes and
+ // clear sizes near UINT16_MAX can overflow if stored in a uint16_t.
+ CopyableTArray<uint32_t> mPlainSizes;
+ // The num encrypted bytes in each subsample. The nth element in the array is
+ // the number of encrypted bytes at the start of the nth subsample.
+ CopyableTArray<uint32_t> mEncryptedSizes;
+ CopyableTArray<uint8_t> mIV;
+ CopyableTArray<CopyableTArray<uint8_t>> mInitDatas;
+ nsString mInitDataType;
+};
+
+// MediaRawData is a MediaData container used to store demuxed, still compressed
+// samples.
+// Use MediaRawData::CreateWriter() to obtain a MediaRawDataWriter object that
+// provides methods to modify and manipulate the data.
+// Memory allocations are fallible. Methods return a boolean indicating if
+// memory allocations were successful. Return values should always be checked.
+// MediaRawData::mData will be nullptr if no memory has been allocated or if
+// an error occurred during construction.
+// Existing data is only ever modified if new memory allocation has succeeded
+// and preserved if not.
+//
+// The memory referenced by mData will always be 32 bytes aligned and the
+// underlying buffer will always have a size such that 32 bytes blocks can be
+// used to read the content, regardless of the mSize value. Buffer is zeroed
+// on creation.
+//
+// Typical usage: create new MediaRawData; create the associated
+// MediaRawDataWriter, call SetSize() to allocate memory, write to mData,
+// up to mSize bytes.
+
+class MediaRawData;
+
+class MediaRawDataWriter {
+ public:
+ // Pointer to data or null if not-yet allocated
+ uint8_t* Data();
+ // Writeable size of buffer.
+ size_t Size();
+ // Writeable reference to MediaRawData::mCryptoInternal
+ CryptoSample& mCrypto;
+
+ // Data manipulation methods. mData and mSize may be updated accordingly.
+
+ // Set size of buffer, allocating memory as required.
+ // If memory is allocated, additional buffer area is filled with 0.
+ [[nodiscard]] bool SetSize(size_t aSize);
+ // Add aData at the beginning of buffer.
+ [[nodiscard]] bool Prepend(const uint8_t* aData, size_t aSize);
+ [[nodiscard]] bool Append(const uint8_t* aData, size_t aSize);
+ // Replace current content with aData.
+ [[nodiscard]] bool Replace(const uint8_t* aData, size_t aSize);
+ // Clear the memory buffer. Will set target mData and mSize to 0.
+ void Clear();
+ // Remove aSize bytes from the front of the sample.
+ void PopFront(size_t aSize);
+
+ private:
+ friend class MediaRawData;
+ explicit MediaRawDataWriter(MediaRawData* aMediaRawData);
+ [[nodiscard]] bool EnsureSize(size_t aSize);
+ MediaRawData* mTarget;
+};
+
+class MediaRawData final : public MediaData {
+ public:
+ MediaRawData();
+ MediaRawData(const uint8_t* aData, size_t aSize);
+ MediaRawData(const uint8_t* aData, size_t aSize, const uint8_t* aAlphaData,
+ size_t aAlphaSize);
+ explicit MediaRawData(AlignedByteBuffer&& aData);
+ MediaRawData(AlignedByteBuffer&& aData, AlignedByteBuffer&& aAlphaData);
+
+ // Pointer to data or null if not-yet allocated
+ const uint8_t* Data() const { return mBuffer.Data(); }
+ // Pointer to alpha data or null if not-yet allocated
+ const uint8_t* AlphaData() const { return mAlphaBuffer.Data(); }
+ // Size of buffer.
+ size_t Size() const { return mBuffer.Length(); }
+ size_t AlphaSize() const { return mAlphaBuffer.Length(); }
+ size_t ComputedSizeOfIncludingThis() const {
+ return sizeof(*this) + mBuffer.ComputedSizeOfExcludingThis() +
+ mAlphaBuffer.ComputedSizeOfExcludingThis();
+ }
+ // Access the buffer as a Span.
+ operator Span<const uint8_t>() { return Span{Data(), Size()}; }
+
+ const CryptoSample& mCrypto;
+ RefPtr<MediaByteBuffer> mExtraData;
+
+ // Used by the Vorbis decoder and Ogg demuxer.
+ // Indicates that this is the last packet of the stream.
+ bool mEOS = false;
+
+ RefPtr<TrackInfoSharedPtr> mTrackInfo;
+
+ // Used to indicate the id of the temporal scalability layer.
+ Maybe<uint8_t> mTemporalLayerId;
+
+ // May contain the original start time and duration of the frames.
+ // mOriginalPresentationWindow.mStart would always be less or equal to mTime
+ // and mOriginalPresentationWindow.mEnd equal or greater to mTime + mDuration.
+ // This is used when the sample should get cropped so that its content will
+ // actually start on mTime and go for mDuration. If this interval is set, then
+ // the decoder should crop the content accordingly.
+ Maybe<media::TimeInterval> mOriginalPresentationWindow;
+
+ // If it's true, the `mCrypto` should be copied into the remote data as well.
+ // Currently this is only used for the media engine DRM playback.
+ bool mShouldCopyCryptoToRemoteRawData = false;
+
+ // It's only used when the remote decoder reconstructs the media raw data.
+ CryptoSample& GetWritableCrypto() { return mCryptoInternal; }
+
+ // Return a deep copy or nullptr if out of memory.
+ already_AddRefed<MediaRawData> Clone() const;
+ // Create a MediaRawDataWriter for this MediaRawData. The writer is not
+ // thread-safe.
+ UniquePtr<MediaRawDataWriter> CreateWriter();
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
+ protected:
+ ~MediaRawData();
+
+ private:
+ friend class MediaRawDataWriter;
+ friend class ArrayOfRemoteMediaRawData;
+ AlignedByteBuffer mBuffer;
+ AlignedByteBuffer mAlphaBuffer;
+ CryptoSample mCryptoInternal;
+ MediaRawData(const MediaRawData&); // Not implemented
+};
+
+// MediaByteBuffer is a ref counted infallible TArray.
+class MediaByteBuffer : public nsTArray<uint8_t> {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaByteBuffer);
+ MediaByteBuffer() = default;
+ explicit MediaByteBuffer(size_t aCapacity) : nsTArray<uint8_t>(aCapacity) {}
+
+ private:
+ ~MediaByteBuffer() = default;
+};
+
+// MediaAlignedByteBuffer is a ref counted AlignedByteBuffer whose memory
+// allocations are fallible.
+class MediaAlignedByteBuffer final : public AlignedByteBuffer {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaAlignedByteBuffer);
+ MediaAlignedByteBuffer() = default;
+ MediaAlignedByteBuffer(const uint8_t* aData, size_t aLength)
+ : AlignedByteBuffer(aData, aLength) {}
+
+ private:
+ ~MediaAlignedByteBuffer() = default;
+};
+
+} // namespace mozilla
+
+#endif // MediaData_h