Line data Source code
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 :
7 : #include "MediaData.h"
8 :
9 : #include "ImageContainer.h"
10 : #include "MediaInfo.h"
11 : #include "VideoUtils.h"
12 : #include "YCbCrUtils.h"
13 : #include "mozilla/layers/ImageBridgeChild.h"
14 : #include "mozilla/layers/KnowsCompositor.h"
15 : #include "mozilla/layers/SharedRGBImage.h"
16 :
17 : #ifdef MOZ_WIDGET_GONK
18 : #include <cutils/properties.h>
19 : #endif
20 : #include <stdint.h>
21 :
22 : #ifdef XP_WIN
23 : #include "mozilla/layers/D3D11YCbCrImage.h"
24 : #endif
25 :
26 : namespace mozilla {
27 :
28 : using namespace mozilla::gfx;
29 : using layers::ImageContainer;
30 : using layers::PlanarYCbCrImage;
31 : using layers::PlanarYCbCrData;
32 : using media::TimeUnit;
33 :
34 : const char* AudioData::sTypeName = "audio";
35 : const char* VideoData::sTypeName = "video";
36 :
37 : void
38 0 : AudioData::EnsureAudioBuffer()
39 : {
40 0 : if (mAudioBuffer)
41 0 : return;
42 0 : mAudioBuffer = SharedBuffer::Create(mFrames*mChannels*sizeof(AudioDataValue));
43 :
44 0 : AudioDataValue* data = static_cast<AudioDataValue*>(mAudioBuffer->Data());
45 0 : for (uint32_t i = 0; i < mFrames; ++i) {
46 0 : for (uint32_t j = 0; j < mChannels; ++j) {
47 0 : data[j*mFrames + i] = mAudioData[i*mChannels + j];
48 : }
49 : }
50 : }
51 :
52 : size_t
53 0 : AudioData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
54 : {
55 : size_t size =
56 0 : aMallocSizeOf(this) + mAudioData.SizeOfExcludingThis(aMallocSizeOf);
57 0 : if (mAudioBuffer) {
58 0 : size += mAudioBuffer->SizeOfIncludingThis(aMallocSizeOf);
59 : }
60 0 : return size;
61 : }
62 :
63 : bool
64 0 : AudioData::IsAudible() const
65 : {
66 0 : if (!mAudioData) {
67 0 : return false;
68 : }
69 :
70 0 : for (uint32_t frame = 0; frame < mFrames; ++frame) {
71 0 : for (uint32_t channel = 0; channel < mChannels; ++channel) {
72 0 : if (mAudioData[frame * mChannels + channel] != 0) {
73 0 : return true;
74 : }
75 : }
76 : }
77 0 : return false;
78 : }
79 :
80 : /* static */
81 : already_AddRefed<AudioData>
82 0 : AudioData::TransferAndUpdateTimestampAndDuration(AudioData* aOther,
83 : const TimeUnit& aTimestamp,
84 : const TimeUnit& aDuration)
85 : {
86 0 : NS_ENSURE_TRUE(aOther, nullptr);
87 : RefPtr<AudioData> v = new AudioData(aOther->mOffset,
88 : aTimestamp,
89 : aDuration,
90 0 : aOther->mFrames,
91 0 : Move(aOther->mAudioData),
92 0 : aOther->mChannels,
93 0 : aOther->mRate);
94 0 : return v.forget();
95 : }
96 :
97 : static bool
98 0 : ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
99 : {
100 0 : return aPlane.mWidth <= PlanarYCbCrImage::MAX_DIMENSION
101 0 : && aPlane.mHeight <= PlanarYCbCrImage::MAX_DIMENSION
102 0 : && aPlane.mWidth * aPlane.mHeight < MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT
103 0 : && aPlane.mStride > 0;
104 : }
105 :
106 0 : static bool ValidateBufferAndPicture(const VideoData::YCbCrBuffer& aBuffer,
107 : const IntRect& aPicture)
108 : {
109 : // The following situation should never happen unless there is a bug
110 : // in the decoder
111 0 : if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth
112 0 : || aBuffer.mPlanes[1].mHeight != aBuffer.mPlanes[2].mHeight) {
113 0 : NS_ERROR("C planes with different sizes");
114 0 : return false;
115 : }
116 :
117 : // The following situations could be triggered by invalid input
118 0 : if (aPicture.width <= 0 || aPicture.height <= 0) {
119 : // In debug mode, makes the error more noticeable
120 0 : MOZ_ASSERT(false, "Empty picture rect");
121 : return false;
122 : }
123 0 : if (!ValidatePlane(aBuffer.mPlanes[0])
124 0 : || !ValidatePlane(aBuffer.mPlanes[1])
125 0 : || !ValidatePlane(aBuffer.mPlanes[2])) {
126 0 : NS_WARNING("Invalid plane size");
127 0 : return false;
128 : }
129 :
130 : // Ensure the picture size specified in the headers can be extracted out of
131 : // the frame we've been supplied without indexing out of bounds.
132 0 : CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
133 0 : CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
134 0 : if (!xLimit.isValid()
135 0 : || xLimit.value() > aBuffer.mPlanes[0].mStride
136 0 : || !yLimit.isValid()
137 0 : || yLimit.value() > aBuffer.mPlanes[0].mHeight)
138 : {
139 : // The specified picture dimensions can't be contained inside the video
140 : // frame, we'll stomp memory if we try to copy it. Fail.
141 0 : NS_WARNING("Overflowing picture rect");
142 0 : return false;
143 : }
144 0 : return true;
145 : }
146 :
147 : #ifdef MOZ_WIDGET_GONK
148 : static bool
149 : IsYV12Format(const VideoData::YCbCrBuffer::Plane& aYPlane,
150 : const VideoData::YCbCrBuffer::Plane& aCbPlane,
151 : const VideoData::YCbCrBuffer::Plane& aCrPlane)
152 : {
153 : return
154 : aYPlane.mWidth % 2 == 0
155 : && aYPlane.mHeight % 2 == 0
156 : && aYPlane.mWidth / 2 == aCbPlane.mWidth
157 : && aYPlane.mHeight / 2 == aCbPlane.mHeight
158 : && aCbPlane.mWidth == aCrPlane.mWidth
159 : && aCbPlane.mHeight == aCrPlane.mHeight;
160 : }
161 :
162 : static bool
163 : IsInEmulator()
164 : {
165 : char propQemu[PROPERTY_VALUE_MAX];
166 : property_get("ro.kernel.qemu", propQemu, "");
167 : return !strncmp(propQemu, "1", 1);
168 : }
169 :
170 : #endif
171 :
172 0 : VideoData::VideoData(int64_t aOffset,
173 : const TimeUnit& aTime,
174 : const TimeUnit& aDuration,
175 : bool aKeyframe,
176 : const TimeUnit& aTimecode,
177 : IntSize aDisplay,
178 0 : layers::ImageContainer::FrameID aFrameID)
179 : : MediaData(VIDEO_DATA, aOffset, aTime, aDuration, 1)
180 : , mDisplay(aDisplay)
181 : , mFrameID(aFrameID)
182 0 : , mSentToCompositor(false)
183 : {
184 0 : MOZ_ASSERT(!mDuration.IsNegative(), "Frame must have non-negative duration.");
185 0 : mKeyframe = aKeyframe;
186 0 : mTimecode = aTimecode;
187 0 : }
188 :
189 0 : VideoData::~VideoData()
190 : {
191 0 : }
192 :
193 : void
194 0 : VideoData::SetListener(UniquePtr<Listener> aListener)
195 : {
196 0 : MOZ_ASSERT(!mSentToCompositor,
197 : "Listener should be registered before sending data");
198 :
199 0 : mListener = Move(aListener);
200 0 : }
201 :
202 : void
203 0 : VideoData::MarkSentToCompositor()
204 : {
205 0 : if (mSentToCompositor) {
206 0 : return;
207 : }
208 :
209 0 : mSentToCompositor = true;
210 0 : if (mListener != nullptr) {
211 0 : mListener->OnSentToCompositor();
212 0 : mListener = nullptr;
213 : }
214 : }
215 :
216 : size_t
217 0 : VideoData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
218 : {
219 0 : size_t size = aMallocSizeOf(this);
220 :
221 : // Currently only PLANAR_YCBCR has a well defined function for determining
222 : // it's size, so reporting is limited to that type.
223 0 : if (mImage && mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
224 : const mozilla::layers::PlanarYCbCrImage* img =
225 0 : static_cast<const mozilla::layers::PlanarYCbCrImage*>(mImage.get());
226 0 : size += img->SizeOfIncludingThis(aMallocSizeOf);
227 : }
228 :
229 0 : return size;
230 : }
231 :
232 : void
233 0 : VideoData::UpdateDuration(const TimeUnit& aDuration)
234 : {
235 0 : MOZ_ASSERT(!aDuration.IsNegative());
236 0 : mDuration = aDuration;
237 0 : }
238 :
239 : void
240 0 : VideoData::UpdateTimestamp(const TimeUnit& aTimestamp)
241 : {
242 0 : MOZ_ASSERT(!aTimestamp.IsNegative());
243 :
244 0 : auto updatedDuration = GetEndTime() - aTimestamp;
245 0 : MOZ_ASSERT(!updatedDuration.IsNegative());
246 :
247 0 : mTime = aTimestamp;
248 0 : mDuration = updatedDuration;
249 0 : }
250 :
251 : PlanarYCbCrData
252 0 : ConstructPlanarYCbCrData(const VideoInfo& aInfo,
253 : const VideoData::YCbCrBuffer& aBuffer,
254 : const IntRect& aPicture)
255 : {
256 0 : const VideoData::YCbCrBuffer::Plane& Y = aBuffer.mPlanes[0];
257 0 : const VideoData::YCbCrBuffer::Plane& Cb = aBuffer.mPlanes[1];
258 0 : const VideoData::YCbCrBuffer::Plane& Cr = aBuffer.mPlanes[2];
259 :
260 0 : PlanarYCbCrData data;
261 0 : data.mYChannel = Y.mData + Y.mOffset;
262 0 : data.mYSize = IntSize(Y.mWidth, Y.mHeight);
263 0 : data.mYStride = Y.mStride;
264 0 : data.mYSkip = Y.mSkip;
265 0 : data.mCbChannel = Cb.mData + Cb.mOffset;
266 0 : data.mCrChannel = Cr.mData + Cr.mOffset;
267 0 : data.mCbCrSize = IntSize(Cb.mWidth, Cb.mHeight);
268 0 : data.mCbCrStride = Cb.mStride;
269 0 : data.mCbSkip = Cb.mSkip;
270 0 : data.mCrSkip = Cr.mSkip;
271 0 : data.mPicX = aPicture.x;
272 0 : data.mPicY = aPicture.y;
273 0 : data.mPicSize = aPicture.Size();
274 0 : data.mStereoMode = aInfo.mStereoMode;
275 0 : data.mYUVColorSpace = aBuffer.mYUVColorSpace;
276 0 : return data;
277 : }
278 :
279 : /* static */ bool
280 0 : VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage,
281 : const VideoInfo& aInfo,
282 : const YCbCrBuffer &aBuffer,
283 : const IntRect& aPicture,
284 : bool aCopyData)
285 : {
286 0 : if (!aVideoImage) {
287 0 : return false;
288 : }
289 :
290 0 : PlanarYCbCrData data = ConstructPlanarYCbCrData(aInfo, aBuffer, aPicture);
291 :
292 0 : aVideoImage->SetDelayedConversion(true);
293 0 : if (aCopyData) {
294 0 : return aVideoImage->CopyData(data);
295 : } else {
296 0 : return aVideoImage->AdoptData(data);
297 : }
298 : }
299 :
300 : /* static */
301 : already_AddRefed<VideoData>
302 0 : VideoData::CreateAndCopyData(const VideoInfo& aInfo,
303 : ImageContainer* aContainer,
304 : int64_t aOffset,
305 : const TimeUnit& aTime,
306 : const TimeUnit& aDuration,
307 : const YCbCrBuffer& aBuffer,
308 : bool aKeyframe,
309 : const TimeUnit& aTimecode,
310 : const IntRect& aPicture,
311 : layers::KnowsCompositor* aAllocator)
312 : {
313 0 : if (!aContainer) {
314 : // Create a dummy VideoData with no image. This gives us something to
315 : // send to media streams if necessary.
316 : RefPtr<VideoData> v(new VideoData(aOffset,
317 : aTime,
318 : aDuration,
319 : aKeyframe,
320 : aTimecode,
321 0 : aInfo.mDisplay,
322 0 : 0));
323 0 : return v.forget();
324 : }
325 :
326 0 : if (!ValidateBufferAndPicture(aBuffer, aPicture)) {
327 0 : return nullptr;
328 : }
329 :
330 : RefPtr<VideoData> v(new VideoData(aOffset,
331 : aTime,
332 : aDuration,
333 : aKeyframe,
334 : aTimecode,
335 0 : aInfo.mDisplay,
336 0 : 0));
337 : #ifdef MOZ_WIDGET_GONK
338 : const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
339 : const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
340 : const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
341 : #endif
342 :
343 : // Currently our decoder only knows how to output to ImageFormat::PLANAR_YCBCR
344 : // format.
345 : #ifdef MOZ_WIDGET_GONK
346 : if (IsYV12Format(Y, Cb, Cr) && !IsInEmulator()) {
347 : v->mImage = new layers::GrallocImage();
348 : }
349 : #elif XP_WIN
350 : if (aAllocator && aAllocator->GetCompositorBackendType()
351 : == layers::LayersBackend::LAYERS_D3D11) {
352 : RefPtr<layers::D3D11YCbCrImage> d3d11Image = new layers::D3D11YCbCrImage();
353 : PlanarYCbCrData data = ConstructPlanarYCbCrData(aInfo, aBuffer, aPicture);
354 : if (d3d11Image->SetData(layers::ImageBridgeChild::GetSingleton()
355 : ? layers::ImageBridgeChild::GetSingleton().get()
356 : : aAllocator,
357 : aContainer, data)) {
358 : v->mImage = d3d11Image;
359 : return v.forget();
360 : }
361 : }
362 : #endif
363 0 : if (!v->mImage) {
364 0 : v->mImage = aContainer->CreatePlanarYCbCrImage();
365 : }
366 :
367 0 : if (!v->mImage) {
368 0 : return nullptr;
369 : }
370 0 : NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::PLANAR_YCBCR,
371 : "Wrong format?");
372 0 : PlanarYCbCrImage* videoImage = v->mImage->AsPlanarYCbCrImage();
373 0 : MOZ_ASSERT(videoImage);
374 :
375 0 : if (!VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
376 : true /* aCopyData */)) {
377 0 : return nullptr;
378 : }
379 :
380 : #ifdef MOZ_WIDGET_GONK
381 : if (!videoImage->IsValid() && IsYV12Format(Y, Cb, Cr)) {
382 : // Failed to allocate gralloc. Try fallback.
383 : v->mImage = aContainer->CreatePlanarYCbCrImage();
384 : if (!v->mImage) {
385 : return nullptr;
386 : }
387 : videoImage = v->mImage->AsPlanarYCbCrImage();
388 : if (!VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
389 : true /* aCopyData */)) {
390 : return nullptr;
391 : }
392 : }
393 : #endif
394 0 : return v.forget();
395 : }
396 :
397 :
398 : /* static */
399 : already_AddRefed<VideoData>
400 0 : VideoData::CreateAndCopyData(const VideoInfo& aInfo,
401 : ImageContainer* aContainer,
402 : int64_t aOffset,
403 : const TimeUnit& aTime,
404 : const TimeUnit& aDuration,
405 : const YCbCrBuffer& aBuffer,
406 : const YCbCrBuffer::Plane &aAlphaPlane,
407 : bool aKeyframe,
408 : const TimeUnit& aTimecode,
409 : const IntRect& aPicture)
410 : {
411 0 : if (!aContainer) {
412 : // Create a dummy VideoData with no image. This gives us something to
413 : // send to media streams if necessary.
414 : RefPtr<VideoData> v(new VideoData(aOffset,
415 : aTime,
416 : aDuration,
417 : aKeyframe,
418 : aTimecode,
419 0 : aInfo.mDisplay,
420 0 : 0));
421 0 : return v.forget();
422 : }
423 :
424 0 : if (!ValidateBufferAndPicture(aBuffer, aPicture)) {
425 0 : return nullptr;
426 : }
427 :
428 : RefPtr<VideoData> v(new VideoData(aOffset,
429 : aTime,
430 : aDuration,
431 : aKeyframe,
432 : aTimecode,
433 0 : aInfo.mDisplay,
434 0 : 0));
435 :
436 : // Convert from YUVA to BGRA format on the software side.
437 : RefPtr<layers::SharedRGBImage> videoImage =
438 0 : aContainer->CreateSharedRGBImage();
439 0 : v->mImage = videoImage;
440 :
441 0 : if (!v->mImage) {
442 0 : return nullptr;
443 : }
444 0 : if (!videoImage->Allocate(IntSize(aBuffer.mPlanes[0].mWidth,
445 0 : aBuffer.mPlanes[0].mHeight),
446 : SurfaceFormat::B8G8R8A8)) {
447 0 : return nullptr;
448 : }
449 0 : uint8_t* argb_buffer = videoImage->GetBuffer();
450 0 : IntSize size = videoImage->GetSize();
451 :
452 : // The naming convention for libyuv and associated utils is word-order.
453 : // The naming convention in the gfx stack is byte-order.
454 0 : ConvertYCbCrAToARGB(aBuffer.mPlanes[0].mData,
455 0 : aBuffer.mPlanes[1].mData,
456 0 : aBuffer.mPlanes[2].mData,
457 0 : aAlphaPlane.mData,
458 0 : aBuffer.mPlanes[0].mStride, aBuffer.mPlanes[1].mStride,
459 0 : argb_buffer, size.width * 4,
460 0 : size.width, size.height);
461 :
462 0 : return v.forget();
463 : }
464 :
465 : /* static */
466 : already_AddRefed<VideoData>
467 0 : VideoData::CreateFromImage(const IntSize& aDisplay,
468 : int64_t aOffset,
469 : const TimeUnit& aTime,
470 : const TimeUnit& aDuration,
471 : const RefPtr<Image>& aImage,
472 : bool aKeyframe,
473 : const TimeUnit& aTimecode)
474 : {
475 : RefPtr<VideoData> v(new VideoData(aOffset,
476 : aTime,
477 : aDuration,
478 : aKeyframe,
479 : aTimecode,
480 : aDisplay,
481 0 : 0));
482 0 : v->mImage = aImage;
483 0 : return v.forget();
484 : }
485 :
486 0 : MediaRawData::MediaRawData()
487 : : MediaData(RAW_DATA, 0)
488 0 : , mCrypto(mCryptoInternal)
489 : {
490 0 : }
491 :
492 0 : MediaRawData::MediaRawData(const uint8_t* aData, size_t aSize)
493 : : MediaData(RAW_DATA, 0)
494 : , mCrypto(mCryptoInternal)
495 0 : , mBuffer(aData, aSize)
496 : {
497 0 : }
498 :
499 0 : MediaRawData::MediaRawData(const uint8_t* aData, size_t aSize,
500 0 : const uint8_t* aAlphaData, size_t aAlphaSize)
501 : : MediaData(RAW_DATA, 0)
502 : , mCrypto(mCryptoInternal)
503 : , mBuffer(aData, aSize)
504 0 : , mAlphaBuffer(aAlphaData, aAlphaSize)
505 : {
506 0 : }
507 :
508 : already_AddRefed<MediaRawData>
509 0 : MediaRawData::Clone() const
510 : {
511 0 : RefPtr<MediaRawData> s = new MediaRawData;
512 0 : s->mTimecode = mTimecode;
513 0 : s->mTime = mTime;
514 0 : s->mDuration = mDuration;
515 0 : s->mOffset = mOffset;
516 0 : s->mKeyframe = mKeyframe;
517 0 : s->mExtraData = mExtraData;
518 0 : s->mCryptoInternal = mCryptoInternal;
519 0 : s->mTrackInfo = mTrackInfo;
520 0 : s->mEOS = mEOS;
521 0 : if (!s->mBuffer.Append(mBuffer.Data(), mBuffer.Length())) {
522 0 : return nullptr;
523 : }
524 0 : if (!s->mAlphaBuffer.Append(mAlphaBuffer.Data(), mAlphaBuffer.Length())) {
525 0 : return nullptr;
526 : }
527 0 : return s.forget();
528 : }
529 :
530 0 : MediaRawData::~MediaRawData()
531 : {
532 0 : }
533 :
534 : size_t
535 0 : MediaRawData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
536 : {
537 0 : size_t size = aMallocSizeOf(this);
538 0 : size += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
539 0 : return size;
540 : }
541 :
542 : MediaRawDataWriter*
543 0 : MediaRawData::CreateWriter()
544 : {
545 0 : return new MediaRawDataWriter(this);
546 : }
547 :
548 0 : MediaRawDataWriter::MediaRawDataWriter(MediaRawData* aMediaRawData)
549 : : mCrypto(aMediaRawData->mCryptoInternal)
550 0 : , mTarget(aMediaRawData)
551 : {
552 0 : }
553 :
554 : bool
555 0 : MediaRawDataWriter::SetSize(size_t aSize)
556 : {
557 0 : return mTarget->mBuffer.SetLength(aSize);
558 : }
559 :
560 : bool
561 0 : MediaRawDataWriter::Prepend(const uint8_t* aData, size_t aSize)
562 : {
563 0 : return mTarget->mBuffer.Prepend(aData, aSize);
564 : }
565 :
566 : bool
567 0 : MediaRawDataWriter::Replace(const uint8_t* aData, size_t aSize)
568 : {
569 0 : return mTarget->mBuffer.Replace(aData, aSize);
570 : }
571 :
572 : void
573 0 : MediaRawDataWriter::Clear()
574 : {
575 0 : mTarget->mBuffer.Clear();
576 0 : }
577 :
578 : uint8_t*
579 0 : MediaRawDataWriter::Data()
580 : {
581 0 : return mTarget->mBuffer.Data();
582 : }
583 :
584 : size_t
585 0 : MediaRawDataWriter::Size()
586 : {
587 0 : return mTarget->Size();
588 : }
589 :
590 : } // namespace mozilla
|