Files
palemoon27/dom/media/MediaData.cpp
T
roytam1 15a91f51ae import changes from `dev' branch of rmottola/Arctic-Fox:
- test for bug 1191648 (f9da67e953)
- Bug 1257333 - Port test_AudioNodeDevtoolsAPI.html to mochitest-plain so that it can be turned on in e10s mode; r=mrbkap (39cee7850b)
- Bug 1079844 - Change various non-js/ files/tests/etc. to refer to detaching of ArrayBuffers rather than neutering. (DOM references to "neutering" of DOM things remain as neutering.) r=bz (3ab68d0a1d)
- Bug 1257337 - Port test_AudioParamDevtoolsAPI.html to mochitest-plain so that it can be turned on in e10s mode; r=mrbkap (7f0bc6b0d3)
- Bug 1265403 - Support chaining AudioParam automation methods. r=smaug (89ac09e1a4)
- Bug 1241096 - Add a better memory reporting system for AudioBuffers. r=erahm (a5058066fa)
- Bug 1255655 - Const-ify kCoefficientRgbY. r=jrmuizel. This allows it to be shared between processes. (7e484b4154)
- Bug 1232904 - Use channel.asyncOpen2 within dom/media/IdpSandbox.jsm (r=mt) (99c75688db)
- Bug 1176941 - Capturing more error info from IdP sandbox, r=jib (fe6e628f16)
- Bug 1176941 - Moving console warning to the current window, r=jib (8ce00202a0)
- Bug 1258299 - Move RTCIdentityProvider to a dictionary, r=jib,khuey (36699071e3)
- Bug 1208371 - Forward declare MediaStreamGraph classes in MediaPipeline.h. r=bwc (2c476da3b1)
- Bug 1248154 - Default to 0x0 frame size before receiving any frames. r=pkerr (00357ddee9)
- Bug 1262278 - Rename PlanarYCbCrImage::SetData and PlanarYCbCrImage::SetDataNoCopy r=nical (d13934623c)
- Bug 1245525: Fix some compiler errors in Moz2D type classes. r=jrmuizel (f3188d1490)
- Bug 1256678 - Fall back to loading GDI fonts from system in DrawTargetRecording - r=bas (a584738020)
- Bug 1247405 - Track peak texture memory usage r=nical (29df6e1566)
- Bug 1251065 - Add prefs for logging the current and peak texture usage r=nical (23c62210cc)
- Bug 1231396 - Add a pref to record and print out the number of pixels painted. r=mstange (6179f56806)
- Bug 1262753: P5. Pass number of frames rather than the number of bytes. r=gerald (f45286d4e9)
- Bug 1242774 - only resolve promise when exist. r=jwwang. (e8de9f5d37)
- Bug 1246108 - Don't restart completed audio streams. r=jwwang (738f1780ef)
- Bug 1262753: P6. Perform downmixing in DecodeAudioDataSink. r=kinetik (513906273a)
- Bug 1262753: P7. Add debugging pref to enable/disable downmixer and resample. r=kinetik (3bdc72fef3)
- Bug 1262753: P8. Remove useless assert. r=gerald (075f86cb75)
- Bug 1241486 - Allow using pixmaps to share WebGL frames with the basic compositor when it makes sense. r=jgilbert (2bdab0b4f4)
- Bug 1245550 - add gfx.use-glx-texture-from-pixmap pref to control whether texture_from_pixmap is used. r=nical (a29c7cf85b)
- Bug 1260944 - Fix ANGLE D3D WARP for WebGL. - r=jrmuizel (8bc04a6c86)
- Bug 1241003 - ignore AudioData with 0 frames to avoid silence. r=kinetik. (d9b498ccfe)
- Bug 1245542 - I suspect AudioData::mAudioData/mFrames are poisoned when sample format doesn't match the metadata. Let's ignore these samples to see if crash volume can be reduced. r=kinetik. (1dcbbbbfc6)
- Bug 1259810 - check that D2D1 device creation succeeds and otherwise fall to software backend. r=bas.schouten (0b1a56e488)
- Bug 1257288 - Move IntersectMaybeRects() to gfx/2d/Rect.h. r=kats (75740a568d)
- Bug 1236043 - Don't inflate empty rects. r=jrmuizel (809dbda3fb)
- Bug 913822 - Shutdown I/O time limit for HTTP cache. r=michal (6f3263184b)
- Bug 848994 - p1. Refactor Decoder Doctor - r=cpearce (8ae3326424)
- Bug 848994 - p2. Detect when WMF is not found - r=cpearce (fb6b98d7bd)
- Bug 848994 - p3. Check MediaKeySystem requests - r=cpearce (bf2ba5ea8d)
- Bug 848994 - p4. Record GMP diagnostics - r=cpearce (21e750ca24)
- Bug 1245789 - Add abstract thread wrapper for the GMP thread. r=gerald (37ee00f86c)
- Bug 1250745 - Share GMP processes when doing unencrypted decoding. r=jwwang (c925f991a8)
- Bug 1245789 - Use frame size not display size in GMPVideoDecoder. r=kinetik (a4d7abdbef)
- Bug 1211812 - Use MozPromise to init mozilla::GMPVideoDecoder. r=jwwang (cc736f1f37)
- Bug 1256065. r=gerald (62cdb35926)
- Bug 1221825: P2. Ensure frame memory allocation succeeded. r=cpearce (b6fb351b6a)
- Bug 1197064 - Don't assume positive audio frame offset in GMPAudioDeceoder. r=jya (6bc23cb4a0)
2024-05-22 15:17:33 +08:00

582 lines
18 KiB
C++

/* -*- 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 "MediaData.h"
#include "MediaInfo.h"
#ifdef MOZ_OMX_DECODER
#include "GrallocImages.h"
#include "mozilla/layers/TextureClient.h"
#endif
#include "VideoUtils.h"
#include "ImageContainer.h"
#ifdef MOZ_WIDGET_GONK
#include <cutils/properties.h>
#endif
#include <stdint.h>
namespace mozilla {
using namespace mozilla::gfx;
using layers::ImageContainer;
using layers::PlanarYCbCrImage;
using layers::PlanarYCbCrData;
const char* AudioData::sTypeName = "audio";
const char* VideoData::sTypeName = "video";
void
AudioData::EnsureAudioBuffer()
{
if (mAudioBuffer)
return;
mAudioBuffer = SharedBuffer::Create(mFrames*mChannels*sizeof(AudioDataValue));
AudioDataValue* data = static_cast<AudioDataValue*>(mAudioBuffer->Data());
for (uint32_t i = 0; i < mFrames; ++i) {
for (uint32_t j = 0; j < mChannels; ++j) {
data[j*mFrames + i] = mAudioData[i*mChannels + j];
}
}
}
size_t
AudioData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t size =
aMallocSizeOf(this) + mAudioData.SizeOfExcludingThis(aMallocSizeOf);
if (mAudioBuffer) {
size += mAudioBuffer->SizeOfIncludingThis(aMallocSizeOf);
}
return size;
}
bool
AudioData::IsAudible() const
{
if (!mAudioData) {
return false;
}
for (uint32_t frame = 0; frame < mFrames; ++frame) {
for (uint32_t channel = 0; channel < mChannels; ++channel) {
if (mAudioData[frame * mChannels + channel] != 0) {
return true;
}
}
}
return false;
}
/* static */
already_AddRefed<AudioData>
AudioData::TransferAndUpdateTimestampAndDuration(AudioData* aOther,
int64_t aTimestamp,
int64_t aDuration)
{
NS_ENSURE_TRUE(aOther, nullptr);
RefPtr<AudioData> v = new AudioData(aOther->mOffset,
aTimestamp,
aDuration,
aOther->mFrames,
Move(aOther->mAudioData),
aOther->mChannels,
aOther->mRate);
v->mDiscontinuity = aOther->mDiscontinuity;
return v.forget();
}
static bool
ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
{
return aPlane.mWidth <= PlanarYCbCrImage::MAX_DIMENSION &&
aPlane.mHeight <= PlanarYCbCrImage::MAX_DIMENSION &&
aPlane.mWidth * aPlane.mHeight < MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
aPlane.mStride > 0 && aPlane.mWidth <= aPlane.mStride;
}
#ifdef MOZ_WIDGET_GONK
static bool
IsYV12Format(const VideoData::YCbCrBuffer::Plane& aYPlane,
const VideoData::YCbCrBuffer::Plane& aCbPlane,
const VideoData::YCbCrBuffer::Plane& aCrPlane)
{
return
aYPlane.mWidth % 2 == 0 &&
aYPlane.mHeight % 2 == 0 &&
aYPlane.mWidth / 2 == aCbPlane.mWidth &&
aYPlane.mHeight / 2 == aCbPlane.mHeight &&
aCbPlane.mWidth == aCrPlane.mWidth &&
aCbPlane.mHeight == aCrPlane.mHeight;
}
static bool
IsInEmulator()
{
char propQemu[PROPERTY_VALUE_MAX];
property_get("ro.kernel.qemu", propQemu, "");
return !strncmp(propQemu, "1", 1);
}
#endif
VideoData::VideoData(int64_t aOffset,
int64_t aTime,
int64_t aDuration,
bool aKeyframe,
int64_t aTimecode,
IntSize aDisplay,
layers::ImageContainer::FrameID aFrameID)
: MediaData(VIDEO_DATA, aOffset, aTime, aDuration, 1)
, mDisplay(aDisplay)
, mFrameID(aFrameID)
, mSentToCompositor(false)
{
NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
mKeyframe = aKeyframe;
mTimecode = aTimecode;
}
VideoData::~VideoData()
{
}
size_t
VideoData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t size = aMallocSizeOf(this);
// Currently only PLANAR_YCBCR has a well defined function for determining
// it's size, so reporting is limited to that type.
if (mImage && mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
const mozilla::layers::PlanarYCbCrImage* img =
static_cast<const mozilla::layers::PlanarYCbCrImage*>(mImage.get());
size += img->SizeOfIncludingThis(aMallocSizeOf);
}
return size;
}
/* static */
already_AddRefed<VideoData>
VideoData::ShallowCopyUpdateDuration(const VideoData* aOther,
int64_t aDuration)
{
RefPtr<VideoData> v = new VideoData(aOther->mOffset,
aOther->mTime,
aDuration,
aOther->mKeyframe,
aOther->mTimecode,
aOther->mDisplay,
aOther->mFrameID);
v->mDiscontinuity = aOther->mDiscontinuity;
v->mImage = aOther->mImage;
return v.forget();
}
/* static */
already_AddRefed<VideoData>
VideoData::ShallowCopyUpdateTimestamp(const VideoData* aOther,
int64_t aTimestamp)
{
NS_ENSURE_TRUE(aOther, nullptr);
RefPtr<VideoData> v = new VideoData(aOther->mOffset,
aTimestamp,
aOther->GetEndTime() - aTimestamp,
aOther->mKeyframe,
aOther->mTimecode,
aOther->mDisplay,
aOther->mFrameID);
v->mDiscontinuity = aOther->mDiscontinuity;
v->mImage = aOther->mImage;
return v.forget();
}
/* static */
already_AddRefed<VideoData>
VideoData::ShallowCopyUpdateTimestampAndDuration(const VideoData* aOther,
int64_t aTimestamp,
int64_t aDuration)
{
NS_ENSURE_TRUE(aOther, nullptr);
RefPtr<VideoData> v = new VideoData(aOther->mOffset,
aTimestamp,
aDuration,
aOther->mKeyframe,
aOther->mTimecode,
aOther->mDisplay,
aOther->mFrameID);
v->mDiscontinuity = aOther->mDiscontinuity;
v->mImage = aOther->mImage;
return v.forget();
}
/* static */
bool VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage,
const VideoInfo& aInfo,
const YCbCrBuffer &aBuffer,
const IntRect& aPicture,
bool aCopyData)
{
if (!aVideoImage) {
return false;
}
const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
PlanarYCbCrData data;
data.mYChannel = Y.mData + Y.mOffset;
data.mYSize = IntSize(Y.mWidth, Y.mHeight);
data.mYStride = Y.mStride;
data.mYSkip = Y.mSkip;
data.mCbChannel = Cb.mData + Cb.mOffset;
data.mCrChannel = Cr.mData + Cr.mOffset;
data.mCbCrSize = IntSize(Cb.mWidth, Cb.mHeight);
data.mCbCrStride = Cb.mStride;
data.mCbSkip = Cb.mSkip;
data.mCrSkip = Cr.mSkip;
data.mPicX = aPicture.x;
data.mPicY = aPicture.y;
data.mPicSize = aPicture.Size();
data.mStereoMode = aInfo.mStereoMode;
aVideoImage->SetDelayedConversion(true);
if (aCopyData) {
return aVideoImage->CopyData(data);
} else {
return aVideoImage->AdoptData(data);
}
}
/* static */
already_AddRefed<VideoData>
VideoData::Create(const VideoInfo& aInfo,
ImageContainer* aContainer,
Image* aImage,
int64_t aOffset,
int64_t aTime,
int64_t aDuration,
const YCbCrBuffer& aBuffer,
bool aKeyframe,
int64_t aTimecode,
const IntRect& aPicture)
{
if (!aImage && !aContainer) {
// Create a dummy VideoData with no image. This gives us something to
// send to media streams if necessary.
RefPtr<VideoData> v(new VideoData(aOffset,
aTime,
aDuration,
aKeyframe,
aTimecode,
aInfo.mDisplay,
0));
return v.forget();
}
// The following situation should never happen unless there is a bug
// in the decoder
if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth ||
aBuffer.mPlanes[1].mHeight != aBuffer.mPlanes[2].mHeight) {
NS_ERROR("C planes with different sizes");
return nullptr;
}
// The following situations could be triggered by invalid input
if (aPicture.width <= 0 || aPicture.height <= 0) {
// In debug mode, makes the error more noticeable
MOZ_ASSERT(false, "Empty picture rect");
return nullptr;
}
if (!ValidatePlane(aBuffer.mPlanes[0]) || !ValidatePlane(aBuffer.mPlanes[1]) ||
!ValidatePlane(aBuffer.mPlanes[2])) {
NS_WARNING("Invalid plane size");
return nullptr;
}
// Ensure the picture size specified in the headers can be extracted out of
// the frame we've been supplied without indexing out of bounds.
CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
if (!xLimit.isValid() || xLimit.value() > aBuffer.mPlanes[0].mStride ||
!yLimit.isValid() || yLimit.value() > aBuffer.mPlanes[0].mHeight)
{
// The specified picture dimensions can't be contained inside the video
// frame, we'll stomp memory if we try to copy it. Fail.
NS_WARNING("Overflowing picture rect");
return nullptr;
}
RefPtr<VideoData> v(new VideoData(aOffset,
aTime,
aDuration,
aKeyframe,
aTimecode,
aInfo.mDisplay,
0));
#ifdef MOZ_WIDGET_GONK
const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
#endif
if (!aImage) {
// Currently our decoder only knows how to output to ImageFormat::PLANAR_YCBCR
// format.
#ifdef MOZ_WIDGET_GONK
if (IsYV12Format(Y, Cb, Cr) && !IsInEmulator()) {
v->mImage = new layers::GrallocImage();
}
#endif
if (!v->mImage) {
v->mImage = aContainer->CreatePlanarYCbCrImage();
}
} else {
v->mImage = aImage;
}
if (!v->mImage) {
return nullptr;
}
NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::PLANAR_YCBCR ||
v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR,
"Wrong format?");
PlanarYCbCrImage* videoImage = v->mImage->AsPlanarYCbCrImage();
MOZ_ASSERT(videoImage);
bool shouldCopyData = (aImage == nullptr);
if (!VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
shouldCopyData)) {
return nullptr;
}
#ifdef MOZ_WIDGET_GONK
if (!videoImage->IsValid() && !aImage && IsYV12Format(Y, Cb, Cr)) {
// Failed to allocate gralloc. Try fallback.
v->mImage = aContainer->CreatePlanarYCbCrImage();
if (!v->mImage) {
return nullptr;
}
videoImage = v->mImage->AsPlanarYCbCrImage();
if(!VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
true /* aCopyData */)) {
return nullptr;
}
}
#endif
return v.forget();
}
/* static */
already_AddRefed<VideoData>
VideoData::Create(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)
{
return Create(aInfo, aContainer, nullptr, aOffset, aTime, aDuration, aBuffer,
aKeyframe, aTimecode, aPicture);
}
/* static */
already_AddRefed<VideoData>
VideoData::Create(const VideoInfo& aInfo,
Image* aImage,
int64_t aOffset,
int64_t aTime,
int64_t aDuration,
const YCbCrBuffer& aBuffer,
bool aKeyframe,
int64_t aTimecode,
const IntRect& aPicture)
{
return Create(aInfo, nullptr, aImage, aOffset, aTime, aDuration, aBuffer,
aKeyframe, aTimecode, aPicture);
}
/* static */
already_AddRefed<VideoData>
VideoData::CreateFromImage(const VideoInfo& aInfo,
ImageContainer* aContainer,
int64_t aOffset,
int64_t aTime,
int64_t aDuration,
const RefPtr<Image>& aImage,
bool aKeyframe,
int64_t aTimecode,
const IntRect& aPicture)
{
RefPtr<VideoData> v(new VideoData(aOffset,
aTime,
aDuration,
aKeyframe,
aTimecode,
aInfo.mDisplay,
0));
v->mImage = aImage;
return v.forget();
}
#ifdef MOZ_OMX_DECODER
/* static */
already_AddRefed<VideoData>
VideoData::Create(const VideoInfo& aInfo,
ImageContainer* aContainer,
int64_t aOffset,
int64_t aTime,
int64_t aDuration,
mozilla::layers::TextureClient* aBuffer,
bool aKeyframe,
int64_t aTimecode,
const IntRect& aPicture)
{
if (!aContainer) {
// Create a dummy VideoData with no image. This gives us something to
// send to media streams if necessary.
RefPtr<VideoData> v(new VideoData(aOffset,
aTime,
aDuration,
aKeyframe,
aTimecode,
aInfo.mDisplay,
0));
return v.forget();
}
// The following situations could be triggered by invalid input
if (aPicture.width <= 0 || aPicture.height <= 0) {
NS_WARNING("Empty picture rect");
return nullptr;
}
// Ensure the picture size specified in the headers can be extracted out of
// the frame we've been supplied without indexing out of bounds.
CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
if (!xLimit.isValid() || !yLimit.isValid())
{
// The specified picture dimensions can't be contained inside the video
// frame, we'll stomp memory if we try to copy it. Fail.
NS_WARNING("Overflowing picture rect");
return nullptr;
}
RefPtr<VideoData> v(new VideoData(aOffset,
aTime,
aDuration,
aKeyframe,
aTimecode,
aInfo.mDisplay,
0));
RefPtr<layers::GrallocImage> image = new layers::GrallocImage();
image->AdoptData(aBuffer, aPicture.Size());
v->mImage = image;
return v.forget();
}
#endif // MOZ_OMX_DECODER
MediaRawData::MediaRawData()
: MediaData(RAW_DATA, 0)
, mCrypto(mCryptoInternal)
{
}
MediaRawData::MediaRawData(const uint8_t* aData, size_t aSize)
: MediaData(RAW_DATA, 0)
, mCrypto(mCryptoInternal)
, mBuffer(aData, aSize)
{
}
already_AddRefed<MediaRawData>
MediaRawData::Clone() const
{
RefPtr<MediaRawData> s = new MediaRawData;
s->mTimecode = mTimecode;
s->mTime = mTime;
s->mDuration = mDuration;
s->mOffset = mOffset;
s->mKeyframe = mKeyframe;
s->mExtraData = mExtraData;
s->mCryptoInternal = mCryptoInternal;
s->mTrackInfo = mTrackInfo;
if (!s->mBuffer.Append(mBuffer.Data(), mBuffer.Length())) {
return nullptr;
}
return s.forget();
}
MediaRawData::~MediaRawData()
{
}
size_t
MediaRawData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t size = aMallocSizeOf(this);
size += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
return size;
}
MediaRawDataWriter*
MediaRawData::CreateWriter()
{
return new MediaRawDataWriter(this);
}
MediaRawDataWriter::MediaRawDataWriter(MediaRawData* aMediaRawData)
: mCrypto(aMediaRawData->mCryptoInternal)
, mTarget(aMediaRawData)
{
}
bool
MediaRawDataWriter::SetSize(size_t aSize)
{
return mTarget->mBuffer.SetLength(aSize);
}
bool
MediaRawDataWriter::Prepend(const uint8_t* aData, size_t aSize)
{
return mTarget->mBuffer.Prepend(aData, aSize);
}
bool
MediaRawDataWriter::Replace(const uint8_t* aData, size_t aSize)
{
return mTarget->mBuffer.Replace(aData, aSize);
}
void
MediaRawDataWriter::Clear()
{
mTarget->mBuffer.Clear();
}
uint8_t*
MediaRawDataWriter::Data()
{
return mTarget->mBuffer.Data();
}
size_t
MediaRawDataWriter::Size()
{
return mTarget->Size();
}
} // namespace mozilla