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