diff options
Diffstat (limited to 'dom/media/MediaData.h')
-rw-r--r-- | dom/media/MediaData.h | 677 |
1 files changed, 677 insertions, 0 deletions
diff --git a/dom/media/MediaData.h b/dom/media/MediaData.h new file mode 100644 index 000000000..a79aac6ed --- /dev/null +++ b/dom/media/MediaData.h @@ -0,0 +1,677 @@ +/* -*- 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 "AudioSampleFormat.h" +#include "ImageTypes.h" +#include "nsSize.h" +#include "mozilla/gfx/Rect.h" +#include "nsRect.h" +#include "nsIMemoryReporter.h" +#include "SharedBuffer.h" +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/UniquePtrExtensions.h" +#include "nsTArray.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/PodOperations.h" + +namespace mozilla { + +namespace layers { +class Image; +class ImageContainer; +} // namespace layers + +class MediaByteBuffer; +class SharedTrackInfo; + +// 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; } + +template <typename Type, int Alignment = 32> +class AlignedBuffer +{ +public: + 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) + : mData(aOther.mData) + , mLength(aOther.mLength) + , mBuffer(Move(aOther.mBuffer)) + , mCapacity(aOther.mCapacity) + { + aOther.mData = nullptr; + aOther.mLength = 0; + aOther.mCapacity = 0; + } + + AlignedBuffer& operator=(AlignedBuffer&& aOther) + { + this->~AlignedBuffer(); + new (this) AlignedBuffer(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 length is increased, new 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; + } + +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 = Move(newBuffer); + mCapacity = sizeNeeded.value(); + mData = newData; + + return true; + } + Type* mData; + size_t mLength; + UniquePtr<uint8_t[]> mBuffer; + size_t mCapacity; +}; + +typedef AlignedBuffer<uint8_t> AlignedByteBuffer; +typedef AlignedBuffer<float> AlignedFloatBuffer; +typedef AlignedBuffer<int16_t> AlignedShortBuffer; +typedef AlignedBuffer<AudioDataValue> AlignedAudioBuffer; + +// Container that holds media samples. +class MediaData { +public: + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaData) + + enum Type { + AUDIO_DATA = 0, + VIDEO_DATA, + RAW_DATA, + NULL_DATA + }; + + MediaData(Type aType, + int64_t aOffset, + int64_t aTimestamp, + int64_t aDuration, + uint32_t aFrames) + : mType(aType) + , mOffset(aOffset) + , mTime(aTimestamp) + , mTimecode(aTimestamp) + , mDuration(aDuration) + , mFrames(aFrames) + , 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, in microseconds. + int64_t mTime; + + // Codec specific internal time code. For Ogg based codecs this is the + // granulepos. + int64_t mTimecode; + + // Duration of sample, in microseconds. + int64_t mDuration; + + // Amount of frames for contained data. + const uint32_t mFrames; + + bool mKeyframe; + + int64_t GetEndTime() const { return mTime + mDuration; } + + bool AdjustForStartTime(int64_t aStartTime) + { + mTime = mTime - aStartTime; + return mTime >= 0; + } + + 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: + MediaData(Type aType, uint32_t aFrames) + : mType(aType) + , mOffset(0) + , mTime(0) + , mTimecode(0) + , mDuration(0) + , mFrames(aFrames) + , mKeyframe(false) + {} + + virtual ~MediaData() {} + +}; + +// NullData is for decoder generating a sample which doesn't need to be +// rendered. +class NullData : public MediaData { +public: + NullData(int64_t aOffset, int64_t aTime, int64_t aDuration) + : MediaData(NULL_DATA, aOffset, aTime, aDuration, 0) + {} + + static const Type sType = NULL_DATA; +}; + +// Holds chunk a decoded audio frames. +class AudioData : public MediaData { +public: + + AudioData(int64_t aOffset, + int64_t aTime, + int64_t aDuration, + uint32_t aFrames, + AlignedAudioBuffer&& aData, + uint32_t aChannels, + uint32_t aRate) + : MediaData(sType, aOffset, aTime, aDuration, aFrames) + , mChannels(aChannels) + , mRate(aRate) + , mAudioData(Move(aData)) {} + + static const Type sType = AUDIO_DATA; + static const char* sTypeName; + + // Creates a new AudioData identical to aOther, but with a different + // specified timestamp and duration. All data from aOther is copied + // into the new AudioData but the audio data which is transferred. + // After such call, the original aOther is unusable. + static already_AddRefed<AudioData> + TransferAndUpdateTimestampAndDuration(AudioData* aOther, + int64_t aTimestamp, + int64_t aDuration); + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; + + // If mAudioBuffer is null, creates it from mAudioData. + void EnsureAudioBuffer(); + + // To check whether mAudioData has audible signal, it's used to distinguish + // the audiable data and silent data. + bool IsAudible() const; + + const uint32_t mChannels; + const uint32_t mRate; + // At least one of mAudioBuffer/mAudioData must be non-null. + // mChannels channels, each with mFrames frames + RefPtr<SharedBuffer> mAudioBuffer; + // mFrames frames, each with mChannels values + AlignedAudioBuffer mAudioData; + +protected: + ~AudioData() {} +}; + +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: + typedef gfx::IntRect IntRect; + typedef gfx::IntSize IntSize; + typedef layers::ImageContainer ImageContainer; + typedef layers::Image Image; + typedef layers::PlanarYCbCrImage PlanarYCbCrImage; + + static const Type sType = 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 mOffset; + uint32_t mSkip; + }; + + Plane mPlanes[3]; + YUVColorSpace mYUVColorSpace = YUVColorSpace::BT601; + }; + + // 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). + + + // Creates a new VideoData containing a deep copy of aBuffer. May use aContainer + // to allocate an Image to hold the copied data. + static already_AddRefed<VideoData> CreateAndCopyData(const VideoInfo& aInfo, + ImageContainer* aContainer, + int64_t aOffset, + int64_t aTime, + int64_t aDuration, + const YCbCrBuffer &aBuffer, + bool aKeyframe, + int64_t aTimecode, + const IntRect& aPicture); + + static already_AddRefed<VideoData> CreateAndCopyIntoTextureClient(const VideoInfo& aInfo, + int64_t aOffset, + int64_t aTime, + int64_t aDuration, + layers::TextureClient* aBuffer, + bool aKeyframe, + int64_t aTimecode, + const IntRect& aPicture); + + static already_AddRefed<VideoData> CreateFromImage(const VideoInfo& aInfo, + int64_t aOffset, + int64_t aTime, + int64_t aDuration, + const RefPtr<Image>& aImage, + bool aKeyframe, + int64_t aTimecode, + const IntRect& aPicture); + + // Creates a new VideoData identical to aOther, but with a different + // specified duration. All data from aOther is copied into the new + // VideoData. The new VideoData's mImage field holds a reference to + // aOther's mImage, i.e. the Image is not copied. This function is useful + // in reader backends that can't determine the duration of a VideoData + // until the next frame is decoded, i.e. it's a way to change the const + // duration field on a VideoData. + static already_AddRefed<VideoData> ShallowCopyUpdateDuration(const VideoData* aOther, + int64_t aDuration); + + // Creates a new VideoData identical to aOther, but with a different + // specified timestamp. All data from aOther is copied into the new + // VideoData, as ShallowCopyUpdateDuration() does. + static already_AddRefed<VideoData> ShallowCopyUpdateTimestamp(const VideoData* aOther, + int64_t aTimestamp); + + // Creates a new VideoData identical to aOther, but with a different + // specified timestamp and duration. All data from aOther is copied + // into the new VideoData, as ShallowCopyUpdateDuration() does. + static already_AddRefed<VideoData> + ShallowCopyUpdateTimestampAndDuration(const VideoData* aOther, int64_t aTimestamp, + int64_t aDuration); + + // Initialize PlanarYCbCrImage. Only When aCopyData is true, + // video data is copied to PlanarYCbCrImage. + static bool 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; + + int32_t mFrameID; + + bool mSentToCompositor; + + VideoData(int64_t aOffset, + int64_t aTime, + int64_t aDuration, + bool aKeyframe, + int64_t aTimecode, + IntSize aDisplay, + uint32_t aFrameID); + +protected: + ~VideoData(); +}; + +class CryptoTrack +{ +public: + CryptoTrack() : mValid(false), mMode(0), mIVSize(0) {} + bool mValid; + int32_t mMode; + int32_t mIVSize; + nsTArray<uint8_t> mKeyId; +}; + +class CryptoSample : public CryptoTrack +{ +public: + nsTArray<uint16_t> mPlainSizes; + nsTArray<uint32_t> mEncryptedSizes; + nsTArray<uint8_t> mIV; + nsTArray<nsCString> mSessionIds; +}; + +// 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 size is increased, new buffer area is filled with 0. + bool SetSize(size_t aSize); + // Add aData at the beginning of buffer. + bool Prepend(const uint8_t* aData, size_t aSize); + // Replace current content with aData. + bool Replace(const uint8_t* aData, size_t aSize); + // Clear the memory buffer. Will set target mData and mSize to 0. + void Clear(); + +private: + friend class MediaRawData; + explicit MediaRawDataWriter(MediaRawData* aMediaRawData); + bool EnsureSize(size_t aSize); + MediaRawData* mTarget; +}; + +class MediaRawData : public MediaData { +public: + MediaRawData(); + MediaRawData(const uint8_t* aData, size_t mSize); + + // Pointer to data or null if not-yet allocated + const uint8_t* Data() const { return mBuffer.Data(); } + // Size of buffer. + size_t Size() const { return mBuffer.Length(); } + size_t ComputedSizeOfIncludingThis() const + { + return sizeof(*this) + mBuffer.ComputedSizeOfExcludingThis(); + } + + 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; + + // Indicate to the audio decoder that mDiscardPadding frames should be + // trimmed. + uint32_t mDiscardPadding = 0; + + RefPtr<SharedTrackInfo> mTrackInfo; + + // Return a deep copy or nullptr if out of memory. + virtual already_AddRefed<MediaRawData> Clone() const; + // Create a MediaRawDataWriter for this MediaRawData. The caller must + // delete the writer once done. The writer is not thread-safe. + virtual MediaRawDataWriter* CreateWriter(); + virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; + +protected: + ~MediaRawData(); + +private: + friend class MediaRawDataWriter; + AlignedByteBuffer mBuffer; + 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() {} +}; + +} // namespace mozilla + +#endif // MediaData_h |