Coverage Report

Created: 2018-09-25 14:53

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