content/media/MediaData.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6 #include "MediaData.h"
michael@0 7 #include "MediaInfo.h"
michael@0 8 #ifdef MOZ_OMX_DECODER
michael@0 9 #include "GrallocImages.h"
michael@0 10 #include "mozilla/layers/TextureClient.h"
michael@0 11 #endif
michael@0 12 #include "VideoUtils.h"
michael@0 13 #include "ImageContainer.h"
michael@0 14
michael@0 15 #ifdef MOZ_WIDGET_GONK
michael@0 16 #include <cutils/properties.h>
michael@0 17 #endif
michael@0 18
michael@0 19 namespace mozilla {
michael@0 20
michael@0 21 using namespace mozilla::gfx;
michael@0 22 using layers::ImageContainer;
michael@0 23 using layers::PlanarYCbCrImage;
michael@0 24 using layers::PlanarYCbCrData;
michael@0 25
michael@0 26 void
michael@0 27 AudioData::EnsureAudioBuffer()
michael@0 28 {
michael@0 29 if (mAudioBuffer)
michael@0 30 return;
michael@0 31 mAudioBuffer = SharedBuffer::Create(mFrames*mChannels*sizeof(AudioDataValue));
michael@0 32
michael@0 33 AudioDataValue* data = static_cast<AudioDataValue*>(mAudioBuffer->Data());
michael@0 34 for (uint32_t i = 0; i < mFrames; ++i) {
michael@0 35 for (uint32_t j = 0; j < mChannels; ++j) {
michael@0 36 data[j*mFrames + i] = mAudioData[i*mChannels + j];
michael@0 37 }
michael@0 38 }
michael@0 39 }
michael@0 40
michael@0 41 static bool
michael@0 42 ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
michael@0 43 {
michael@0 44 return aPlane.mWidth <= PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 45 aPlane.mHeight <= PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 46 aPlane.mWidth * aPlane.mHeight < MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
michael@0 47 aPlane.mStride > 0;
michael@0 48 }
michael@0 49
michael@0 50 #ifdef MOZ_WIDGET_GONK
michael@0 51 static bool
michael@0 52 IsYV12Format(const VideoData::YCbCrBuffer::Plane& aYPlane,
michael@0 53 const VideoData::YCbCrBuffer::Plane& aCbPlane,
michael@0 54 const VideoData::YCbCrBuffer::Plane& aCrPlane)
michael@0 55 {
michael@0 56 return
michael@0 57 aYPlane.mWidth % 2 == 0 &&
michael@0 58 aYPlane.mHeight % 2 == 0 &&
michael@0 59 aYPlane.mWidth / 2 == aCbPlane.mWidth &&
michael@0 60 aYPlane.mHeight / 2 == aCbPlane.mHeight &&
michael@0 61 aCbPlane.mWidth == aCrPlane.mWidth &&
michael@0 62 aCbPlane.mHeight == aCrPlane.mHeight;
michael@0 63 }
michael@0 64
michael@0 65 static bool
michael@0 66 IsInEmulator()
michael@0 67 {
michael@0 68 char propQemu[PROPERTY_VALUE_MAX];
michael@0 69 property_get("ro.kernel.qemu", propQemu, "");
michael@0 70 return !strncmp(propQemu, "1", 1);
michael@0 71 }
michael@0 72
michael@0 73 #endif
michael@0 74
michael@0 75 VideoData::VideoData(int64_t aOffset, int64_t aTime, int64_t aDuration, int64_t aTimecode)
michael@0 76 : MediaData(VIDEO_FRAME, aOffset, aTime, aDuration),
michael@0 77 mTimecode(aTimecode),
michael@0 78 mDuplicate(true),
michael@0 79 mKeyframe(false)
michael@0 80 {
michael@0 81 MOZ_COUNT_CTOR(VideoData);
michael@0 82 NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
michael@0 83 }
michael@0 84
michael@0 85 VideoData::VideoData(int64_t aOffset,
michael@0 86 int64_t aTime,
michael@0 87 int64_t aDuration,
michael@0 88 bool aKeyframe,
michael@0 89 int64_t aTimecode,
michael@0 90 IntSize aDisplay)
michael@0 91 : MediaData(VIDEO_FRAME, aOffset, aTime, aDuration),
michael@0 92 mDisplay(aDisplay),
michael@0 93 mTimecode(aTimecode),
michael@0 94 mDuplicate(false),
michael@0 95 mKeyframe(aKeyframe)
michael@0 96 {
michael@0 97 MOZ_COUNT_CTOR(VideoData);
michael@0 98 NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
michael@0 99 }
michael@0 100
michael@0 101 VideoData::~VideoData()
michael@0 102 {
michael@0 103 MOZ_COUNT_DTOR(VideoData);
michael@0 104 }
michael@0 105
michael@0 106 size_t
michael@0 107 VideoData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 108 {
michael@0 109 size_t size = aMallocSizeOf(this);
michael@0 110
michael@0 111 // Currently only PLANAR_YCBCR has a well defined function for determining
michael@0 112 // it's size, so reporting is limited to that type.
michael@0 113 if (mImage && mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
michael@0 114 const mozilla::layers::PlanarYCbCrImage* img =
michael@0 115 static_cast<const mozilla::layers::PlanarYCbCrImage*>(mImage.get());
michael@0 116 size += img->SizeOfIncludingThis(aMallocSizeOf);
michael@0 117 }
michael@0 118
michael@0 119 return size;
michael@0 120 }
michael@0 121
michael@0 122 /* static */
michael@0 123 VideoData* VideoData::ShallowCopyUpdateDuration(VideoData* aOther,
michael@0 124 int64_t aDuration)
michael@0 125 {
michael@0 126 VideoData* v = new VideoData(aOther->mOffset,
michael@0 127 aOther->mTime,
michael@0 128 aDuration,
michael@0 129 aOther->mKeyframe,
michael@0 130 aOther->mTimecode,
michael@0 131 aOther->mDisplay);
michael@0 132 v->mImage = aOther->mImage;
michael@0 133 return v;
michael@0 134 }
michael@0 135
michael@0 136 /* static */
michael@0 137 VideoData* VideoData::ShallowCopyUpdateTimestamp(VideoData* aOther,
michael@0 138 int64_t aTimestamp)
michael@0 139 {
michael@0 140 NS_ENSURE_TRUE(aOther, nullptr);
michael@0 141 VideoData* v = new VideoData(aOther->mOffset,
michael@0 142 aTimestamp,
michael@0 143 aOther->GetEndTime() - aTimestamp,
michael@0 144 aOther->mKeyframe,
michael@0 145 aOther->mTimecode,
michael@0 146 aOther->mDisplay);
michael@0 147 v->mImage = aOther->mImage;
michael@0 148 return v;
michael@0 149 }
michael@0 150
michael@0 151 /* static */
michael@0 152 void VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage,
michael@0 153 VideoInfo& aInfo,
michael@0 154 const YCbCrBuffer &aBuffer,
michael@0 155 const IntRect& aPicture,
michael@0 156 bool aCopyData)
michael@0 157 {
michael@0 158 if (!aVideoImage) {
michael@0 159 return;
michael@0 160 }
michael@0 161 const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
michael@0 162 const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
michael@0 163 const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
michael@0 164
michael@0 165 PlanarYCbCrData data;
michael@0 166 data.mYChannel = Y.mData + Y.mOffset;
michael@0 167 data.mYSize = IntSize(Y.mWidth, Y.mHeight);
michael@0 168 data.mYStride = Y.mStride;
michael@0 169 data.mYSkip = Y.mSkip;
michael@0 170 data.mCbChannel = Cb.mData + Cb.mOffset;
michael@0 171 data.mCrChannel = Cr.mData + Cr.mOffset;
michael@0 172 data.mCbCrSize = IntSize(Cb.mWidth, Cb.mHeight);
michael@0 173 data.mCbCrStride = Cb.mStride;
michael@0 174 data.mCbSkip = Cb.mSkip;
michael@0 175 data.mCrSkip = Cr.mSkip;
michael@0 176 data.mPicX = aPicture.x;
michael@0 177 data.mPicY = aPicture.y;
michael@0 178 data.mPicSize = aPicture.Size();
michael@0 179 data.mStereoMode = aInfo.mStereoMode;
michael@0 180
michael@0 181 aVideoImage->SetDelayedConversion(true);
michael@0 182 if (aCopyData) {
michael@0 183 aVideoImage->SetData(data);
michael@0 184 } else {
michael@0 185 aVideoImage->SetDataNoCopy(data);
michael@0 186 }
michael@0 187 }
michael@0 188
michael@0 189 /* static */
michael@0 190 VideoData* VideoData::Create(VideoInfo& aInfo,
michael@0 191 ImageContainer* aContainer,
michael@0 192 Image* aImage,
michael@0 193 int64_t aOffset,
michael@0 194 int64_t aTime,
michael@0 195 int64_t aDuration,
michael@0 196 const YCbCrBuffer& aBuffer,
michael@0 197 bool aKeyframe,
michael@0 198 int64_t aTimecode,
michael@0 199 const IntRect& aPicture)
michael@0 200 {
michael@0 201 if (!aImage && !aContainer) {
michael@0 202 // Create a dummy VideoData with no image. This gives us something to
michael@0 203 // send to media streams if necessary.
michael@0 204 nsAutoPtr<VideoData> v(new VideoData(aOffset,
michael@0 205 aTime,
michael@0 206 aDuration,
michael@0 207 aKeyframe,
michael@0 208 aTimecode,
michael@0 209 aInfo.mDisplay.ToIntSize()));
michael@0 210 return v.forget();
michael@0 211 }
michael@0 212
michael@0 213 // The following situation should never happen unless there is a bug
michael@0 214 // in the decoder
michael@0 215 if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth ||
michael@0 216 aBuffer.mPlanes[1].mHeight != aBuffer.mPlanes[2].mHeight) {
michael@0 217 NS_ERROR("C planes with different sizes");
michael@0 218 return nullptr;
michael@0 219 }
michael@0 220
michael@0 221 // The following situations could be triggered by invalid input
michael@0 222 if (aPicture.width <= 0 || aPicture.height <= 0) {
michael@0 223 NS_WARNING("Empty picture rect");
michael@0 224 return nullptr;
michael@0 225 }
michael@0 226 if (!ValidatePlane(aBuffer.mPlanes[0]) || !ValidatePlane(aBuffer.mPlanes[1]) ||
michael@0 227 !ValidatePlane(aBuffer.mPlanes[2])) {
michael@0 228 NS_WARNING("Invalid plane size");
michael@0 229 return nullptr;
michael@0 230 }
michael@0 231
michael@0 232 // Ensure the picture size specified in the headers can be extracted out of
michael@0 233 // the frame we've been supplied without indexing out of bounds.
michael@0 234 CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
michael@0 235 CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
michael@0 236 if (!xLimit.isValid() || xLimit.value() > aBuffer.mPlanes[0].mStride ||
michael@0 237 !yLimit.isValid() || yLimit.value() > aBuffer.mPlanes[0].mHeight)
michael@0 238 {
michael@0 239 // The specified picture dimensions can't be contained inside the video
michael@0 240 // frame, we'll stomp memory if we try to copy it. Fail.
michael@0 241 NS_WARNING("Overflowing picture rect");
michael@0 242 return nullptr;
michael@0 243 }
michael@0 244
michael@0 245 nsAutoPtr<VideoData> v(new VideoData(aOffset,
michael@0 246 aTime,
michael@0 247 aDuration,
michael@0 248 aKeyframe,
michael@0 249 aTimecode,
michael@0 250 aInfo.mDisplay.ToIntSize()));
michael@0 251 #ifdef MOZ_WIDGET_GONK
michael@0 252 const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
michael@0 253 const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
michael@0 254 const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
michael@0 255 #endif
michael@0 256
michael@0 257 if (!aImage) {
michael@0 258 // Currently our decoder only knows how to output to ImageFormat::PLANAR_YCBCR
michael@0 259 // format.
michael@0 260 #ifdef MOZ_WIDGET_GONK
michael@0 261 if (IsYV12Format(Y, Cb, Cr) && !IsInEmulator()) {
michael@0 262 v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
michael@0 263 }
michael@0 264 #endif
michael@0 265 if (!v->mImage) {
michael@0 266 v->mImage = aContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
michael@0 267 }
michael@0 268 } else {
michael@0 269 v->mImage = aImage;
michael@0 270 }
michael@0 271
michael@0 272 if (!v->mImage) {
michael@0 273 return nullptr;
michael@0 274 }
michael@0 275 NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::PLANAR_YCBCR ||
michael@0 276 v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR,
michael@0 277 "Wrong format?");
michael@0 278 PlanarYCbCrImage* videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
michael@0 279
michael@0 280 if (!aImage) {
michael@0 281 VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
michael@0 282 true /* aCopyData */);
michael@0 283 } else {
michael@0 284 VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
michael@0 285 false /* aCopyData */);
michael@0 286 }
michael@0 287
michael@0 288 #ifdef MOZ_WIDGET_GONK
michael@0 289 if (!videoImage->IsValid() && !aImage && IsYV12Format(Y, Cb, Cr)) {
michael@0 290 // Failed to allocate gralloc. Try fallback.
michael@0 291 v->mImage = aContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
michael@0 292 if (!v->mImage) {
michael@0 293 return nullptr;
michael@0 294 }
michael@0 295 videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
michael@0 296 VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
michael@0 297 true /* aCopyData */);
michael@0 298 }
michael@0 299 #endif
michael@0 300 return v.forget();
michael@0 301 }
michael@0 302
michael@0 303 /* static */
michael@0 304 VideoData* VideoData::Create(VideoInfo& aInfo,
michael@0 305 ImageContainer* aContainer,
michael@0 306 int64_t aOffset,
michael@0 307 int64_t aTime,
michael@0 308 int64_t aDuration,
michael@0 309 const YCbCrBuffer& aBuffer,
michael@0 310 bool aKeyframe,
michael@0 311 int64_t aTimecode,
michael@0 312 const IntRect& aPicture)
michael@0 313 {
michael@0 314 return Create(aInfo, aContainer, nullptr, aOffset, aTime, aDuration, aBuffer,
michael@0 315 aKeyframe, aTimecode, aPicture);
michael@0 316 }
michael@0 317
michael@0 318 /* static */
michael@0 319 VideoData* VideoData::Create(VideoInfo& aInfo,
michael@0 320 Image* aImage,
michael@0 321 int64_t aOffset,
michael@0 322 int64_t aTime,
michael@0 323 int64_t aDuration,
michael@0 324 const YCbCrBuffer& aBuffer,
michael@0 325 bool aKeyframe,
michael@0 326 int64_t aTimecode,
michael@0 327 const IntRect& aPicture)
michael@0 328 {
michael@0 329 return Create(aInfo, nullptr, aImage, aOffset, aTime, aDuration, aBuffer,
michael@0 330 aKeyframe, aTimecode, aPicture);
michael@0 331 }
michael@0 332
michael@0 333 /* static */
michael@0 334 VideoData* VideoData::CreateFromImage(VideoInfo& aInfo,
michael@0 335 ImageContainer* aContainer,
michael@0 336 int64_t aOffset,
michael@0 337 int64_t aTime,
michael@0 338 int64_t aDuration,
michael@0 339 const nsRefPtr<Image>& aImage,
michael@0 340 bool aKeyframe,
michael@0 341 int64_t aTimecode,
michael@0 342 const IntRect& aPicture)
michael@0 343 {
michael@0 344 nsAutoPtr<VideoData> v(new VideoData(aOffset,
michael@0 345 aTime,
michael@0 346 aDuration,
michael@0 347 aKeyframe,
michael@0 348 aTimecode,
michael@0 349 aInfo.mDisplay.ToIntSize()));
michael@0 350 v->mImage = aImage;
michael@0 351 return v.forget();
michael@0 352 }
michael@0 353
michael@0 354 #ifdef MOZ_OMX_DECODER
michael@0 355 /* static */
michael@0 356 VideoData* VideoData::Create(VideoInfo& aInfo,
michael@0 357 ImageContainer* aContainer,
michael@0 358 int64_t aOffset,
michael@0 359 int64_t aTime,
michael@0 360 int64_t aDuration,
michael@0 361 mozilla::layers::TextureClient* aBuffer,
michael@0 362 bool aKeyframe,
michael@0 363 int64_t aTimecode,
michael@0 364 const IntRect& aPicture)
michael@0 365 {
michael@0 366 if (!aContainer) {
michael@0 367 // Create a dummy VideoData with no image. This gives us something to
michael@0 368 // send to media streams if necessary.
michael@0 369 nsAutoPtr<VideoData> v(new VideoData(aOffset,
michael@0 370 aTime,
michael@0 371 aDuration,
michael@0 372 aKeyframe,
michael@0 373 aTimecode,
michael@0 374 aInfo.mDisplay.ToIntSize()));
michael@0 375 return v.forget();
michael@0 376 }
michael@0 377
michael@0 378 // The following situations could be triggered by invalid input
michael@0 379 if (aPicture.width <= 0 || aPicture.height <= 0) {
michael@0 380 NS_WARNING("Empty picture rect");
michael@0 381 return nullptr;
michael@0 382 }
michael@0 383
michael@0 384 // Ensure the picture size specified in the headers can be extracted out of
michael@0 385 // the frame we've been supplied without indexing out of bounds.
michael@0 386 CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
michael@0 387 CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
michael@0 388 if (!xLimit.isValid() || !yLimit.isValid())
michael@0 389 {
michael@0 390 // The specified picture dimensions can't be contained inside the video
michael@0 391 // frame, we'll stomp memory if we try to copy it. Fail.
michael@0 392 NS_WARNING("Overflowing picture rect");
michael@0 393 return nullptr;
michael@0 394 }
michael@0 395
michael@0 396 nsAutoPtr<VideoData> v(new VideoData(aOffset,
michael@0 397 aTime,
michael@0 398 aDuration,
michael@0 399 aKeyframe,
michael@0 400 aTimecode,
michael@0 401 aInfo.mDisplay.ToIntSize()));
michael@0 402
michael@0 403 v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
michael@0 404 if (!v->mImage) {
michael@0 405 return nullptr;
michael@0 406 }
michael@0 407 NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR,
michael@0 408 "Wrong format?");
michael@0 409 typedef mozilla::layers::GrallocImage GrallocImage;
michael@0 410 GrallocImage* videoImage = static_cast<GrallocImage*>(v->mImage.get());
michael@0 411 GrallocImage::GrallocData data;
michael@0 412
michael@0 413 data.mPicSize = aPicture.Size();
michael@0 414 data.mGraphicBuffer = aBuffer;
michael@0 415
michael@0 416 videoImage->SetData(data);
michael@0 417
michael@0 418 return v.forget();
michael@0 419 }
michael@0 420 #endif // MOZ_OMX_DECODER
michael@0 421
michael@0 422 } // namespace mozilla

mercurial