content/media/MediaData.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/MediaData.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,422 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +#include "MediaData.h"
    1.10 +#include "MediaInfo.h"
    1.11 +#ifdef MOZ_OMX_DECODER
    1.12 +#include "GrallocImages.h"
    1.13 +#include "mozilla/layers/TextureClient.h"
    1.14 +#endif
    1.15 +#include "VideoUtils.h"
    1.16 +#include "ImageContainer.h"
    1.17 +
    1.18 +#ifdef MOZ_WIDGET_GONK
    1.19 +#include <cutils/properties.h>
    1.20 +#endif
    1.21 +
    1.22 +namespace mozilla {
    1.23 +
    1.24 +using namespace mozilla::gfx;
    1.25 +using layers::ImageContainer;
    1.26 +using layers::PlanarYCbCrImage;
    1.27 +using layers::PlanarYCbCrData;
    1.28 +
    1.29 +void
    1.30 +AudioData::EnsureAudioBuffer()
    1.31 +{
    1.32 +  if (mAudioBuffer)
    1.33 +    return;
    1.34 +  mAudioBuffer = SharedBuffer::Create(mFrames*mChannels*sizeof(AudioDataValue));
    1.35 +
    1.36 +  AudioDataValue* data = static_cast<AudioDataValue*>(mAudioBuffer->Data());
    1.37 +  for (uint32_t i = 0; i < mFrames; ++i) {
    1.38 +    for (uint32_t j = 0; j < mChannels; ++j) {
    1.39 +      data[j*mFrames + i] = mAudioData[i*mChannels + j];
    1.40 +    }
    1.41 +  }
    1.42 +}
    1.43 +
    1.44 +static bool
    1.45 +ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
    1.46 +{
    1.47 +  return aPlane.mWidth <= PlanarYCbCrImage::MAX_DIMENSION &&
    1.48 +         aPlane.mHeight <= PlanarYCbCrImage::MAX_DIMENSION &&
    1.49 +         aPlane.mWidth * aPlane.mHeight < MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
    1.50 +         aPlane.mStride > 0;
    1.51 +}
    1.52 +
    1.53 +#ifdef MOZ_WIDGET_GONK
    1.54 +static bool
    1.55 +IsYV12Format(const VideoData::YCbCrBuffer::Plane& aYPlane,
    1.56 +             const VideoData::YCbCrBuffer::Plane& aCbPlane,
    1.57 +             const VideoData::YCbCrBuffer::Plane& aCrPlane)
    1.58 +{
    1.59 +  return
    1.60 +    aYPlane.mWidth % 2 == 0 &&
    1.61 +    aYPlane.mHeight % 2 == 0 &&
    1.62 +    aYPlane.mWidth / 2 == aCbPlane.mWidth &&
    1.63 +    aYPlane.mHeight / 2 == aCbPlane.mHeight &&
    1.64 +    aCbPlane.mWidth == aCrPlane.mWidth &&
    1.65 +    aCbPlane.mHeight == aCrPlane.mHeight;
    1.66 +}
    1.67 +
    1.68 +static bool
    1.69 +IsInEmulator()
    1.70 +{
    1.71 +  char propQemu[PROPERTY_VALUE_MAX];
    1.72 +  property_get("ro.kernel.qemu", propQemu, "");
    1.73 +  return !strncmp(propQemu, "1", 1);
    1.74 +}
    1.75 +
    1.76 +#endif
    1.77 +
    1.78 +VideoData::VideoData(int64_t aOffset, int64_t aTime, int64_t aDuration, int64_t aTimecode)
    1.79 +  : MediaData(VIDEO_FRAME, aOffset, aTime, aDuration),
    1.80 +    mTimecode(aTimecode),
    1.81 +    mDuplicate(true),
    1.82 +    mKeyframe(false)
    1.83 +{
    1.84 +  MOZ_COUNT_CTOR(VideoData);
    1.85 +  NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
    1.86 +}
    1.87 +
    1.88 +VideoData::VideoData(int64_t aOffset,
    1.89 +                     int64_t aTime,
    1.90 +                     int64_t aDuration,
    1.91 +                     bool aKeyframe,
    1.92 +                     int64_t aTimecode,
    1.93 +                     IntSize aDisplay)
    1.94 +  : MediaData(VIDEO_FRAME, aOffset, aTime, aDuration),
    1.95 +    mDisplay(aDisplay),
    1.96 +    mTimecode(aTimecode),
    1.97 +    mDuplicate(false),
    1.98 +    mKeyframe(aKeyframe)
    1.99 +{
   1.100 +  MOZ_COUNT_CTOR(VideoData);
   1.101 +  NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
   1.102 +}
   1.103 +
   1.104 +VideoData::~VideoData()
   1.105 +{
   1.106 +  MOZ_COUNT_DTOR(VideoData);
   1.107 +}
   1.108 +
   1.109 +size_t
   1.110 +VideoData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   1.111 +{
   1.112 +  size_t size = aMallocSizeOf(this);
   1.113 +
   1.114 +  // Currently only PLANAR_YCBCR has a well defined function for determining
   1.115 +  // it's size, so reporting is limited to that type.
   1.116 +  if (mImage && mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
   1.117 +    const mozilla::layers::PlanarYCbCrImage* img =
   1.118 +        static_cast<const mozilla::layers::PlanarYCbCrImage*>(mImage.get());
   1.119 +    size += img->SizeOfIncludingThis(aMallocSizeOf);
   1.120 +  }
   1.121 +
   1.122 +  return size;
   1.123 +}
   1.124 +
   1.125 +/* static */
   1.126 +VideoData* VideoData::ShallowCopyUpdateDuration(VideoData* aOther,
   1.127 +                                                int64_t aDuration)
   1.128 +{
   1.129 +  VideoData* v = new VideoData(aOther->mOffset,
   1.130 +                               aOther->mTime,
   1.131 +                               aDuration,
   1.132 +                               aOther->mKeyframe,
   1.133 +                               aOther->mTimecode,
   1.134 +                               aOther->mDisplay);
   1.135 +  v->mImage = aOther->mImage;
   1.136 +  return v;
   1.137 +}
   1.138 +
   1.139 +/* static */
   1.140 +VideoData* VideoData::ShallowCopyUpdateTimestamp(VideoData* aOther,
   1.141 +                                                 int64_t aTimestamp)
   1.142 +{
   1.143 +  NS_ENSURE_TRUE(aOther, nullptr);
   1.144 +  VideoData* v = new VideoData(aOther->mOffset,
   1.145 +                               aTimestamp,
   1.146 +                               aOther->GetEndTime() - aTimestamp,
   1.147 +                               aOther->mKeyframe,
   1.148 +                               aOther->mTimecode,
   1.149 +                               aOther->mDisplay);
   1.150 +  v->mImage = aOther->mImage;
   1.151 +  return v;
   1.152 +}
   1.153 +
   1.154 +/* static */
   1.155 +void VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage,
   1.156 +                                    VideoInfo& aInfo,
   1.157 +                                    const YCbCrBuffer &aBuffer,
   1.158 +                                    const IntRect& aPicture,
   1.159 +                                    bool aCopyData)
   1.160 +{
   1.161 +  if (!aVideoImage) {
   1.162 +    return;
   1.163 +  }
   1.164 +  const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
   1.165 +  const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
   1.166 +  const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
   1.167 +
   1.168 +  PlanarYCbCrData data;
   1.169 +  data.mYChannel = Y.mData + Y.mOffset;
   1.170 +  data.mYSize = IntSize(Y.mWidth, Y.mHeight);
   1.171 +  data.mYStride = Y.mStride;
   1.172 +  data.mYSkip = Y.mSkip;
   1.173 +  data.mCbChannel = Cb.mData + Cb.mOffset;
   1.174 +  data.mCrChannel = Cr.mData + Cr.mOffset;
   1.175 +  data.mCbCrSize = IntSize(Cb.mWidth, Cb.mHeight);
   1.176 +  data.mCbCrStride = Cb.mStride;
   1.177 +  data.mCbSkip = Cb.mSkip;
   1.178 +  data.mCrSkip = Cr.mSkip;
   1.179 +  data.mPicX = aPicture.x;
   1.180 +  data.mPicY = aPicture.y;
   1.181 +  data.mPicSize = aPicture.Size();
   1.182 +  data.mStereoMode = aInfo.mStereoMode;
   1.183 +
   1.184 +  aVideoImage->SetDelayedConversion(true);
   1.185 +  if (aCopyData) {
   1.186 +    aVideoImage->SetData(data);
   1.187 +  } else {
   1.188 +    aVideoImage->SetDataNoCopy(data);
   1.189 +  }
   1.190 +}
   1.191 +
   1.192 +/* static */
   1.193 +VideoData* VideoData::Create(VideoInfo& aInfo,
   1.194 +                             ImageContainer* aContainer,
   1.195 +                             Image* aImage,
   1.196 +                             int64_t aOffset,
   1.197 +                             int64_t aTime,
   1.198 +                             int64_t aDuration,
   1.199 +                             const YCbCrBuffer& aBuffer,
   1.200 +                             bool aKeyframe,
   1.201 +                             int64_t aTimecode,
   1.202 +                             const IntRect& aPicture)
   1.203 +{
   1.204 +  if (!aImage && !aContainer) {
   1.205 +    // Create a dummy VideoData with no image. This gives us something to
   1.206 +    // send to media streams if necessary.
   1.207 +    nsAutoPtr<VideoData> v(new VideoData(aOffset,
   1.208 +                                         aTime,
   1.209 +                                         aDuration,
   1.210 +                                         aKeyframe,
   1.211 +                                         aTimecode,
   1.212 +                                         aInfo.mDisplay.ToIntSize()));
   1.213 +    return v.forget();
   1.214 +  }
   1.215 +
   1.216 +  // The following situation should never happen unless there is a bug
   1.217 +  // in the decoder
   1.218 +  if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth ||
   1.219 +      aBuffer.mPlanes[1].mHeight != aBuffer.mPlanes[2].mHeight) {
   1.220 +    NS_ERROR("C planes with different sizes");
   1.221 +    return nullptr;
   1.222 +  }
   1.223 +
   1.224 +  // The following situations could be triggered by invalid input
   1.225 +  if (aPicture.width <= 0 || aPicture.height <= 0) {
   1.226 +    NS_WARNING("Empty picture rect");
   1.227 +    return nullptr;
   1.228 +  }
   1.229 +  if (!ValidatePlane(aBuffer.mPlanes[0]) || !ValidatePlane(aBuffer.mPlanes[1]) ||
   1.230 +      !ValidatePlane(aBuffer.mPlanes[2])) {
   1.231 +    NS_WARNING("Invalid plane size");
   1.232 +    return nullptr;
   1.233 +  }
   1.234 +
   1.235 +  // Ensure the picture size specified in the headers can be extracted out of
   1.236 +  // the frame we've been supplied without indexing out of bounds.
   1.237 +  CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
   1.238 +  CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
   1.239 +  if (!xLimit.isValid() || xLimit.value() > aBuffer.mPlanes[0].mStride ||
   1.240 +      !yLimit.isValid() || yLimit.value() > aBuffer.mPlanes[0].mHeight)
   1.241 +  {
   1.242 +    // The specified picture dimensions can't be contained inside the video
   1.243 +    // frame, we'll stomp memory if we try to copy it. Fail.
   1.244 +    NS_WARNING("Overflowing picture rect");
   1.245 +    return nullptr;
   1.246 +  }
   1.247 +
   1.248 +  nsAutoPtr<VideoData> v(new VideoData(aOffset,
   1.249 +                                       aTime,
   1.250 +                                       aDuration,
   1.251 +                                       aKeyframe,
   1.252 +                                       aTimecode,
   1.253 +                                       aInfo.mDisplay.ToIntSize()));
   1.254 +#ifdef MOZ_WIDGET_GONK
   1.255 +  const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
   1.256 +  const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
   1.257 +  const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
   1.258 +#endif
   1.259 +
   1.260 +  if (!aImage) {
   1.261 +    // Currently our decoder only knows how to output to ImageFormat::PLANAR_YCBCR
   1.262 +    // format.
   1.263 +#ifdef MOZ_WIDGET_GONK
   1.264 +    if (IsYV12Format(Y, Cb, Cr) && !IsInEmulator()) {
   1.265 +      v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
   1.266 +    }
   1.267 +#endif
   1.268 +    if (!v->mImage) {
   1.269 +      v->mImage = aContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
   1.270 +    }
   1.271 +  } else {
   1.272 +    v->mImage = aImage;
   1.273 +  }
   1.274 +
   1.275 +  if (!v->mImage) {
   1.276 +    return nullptr;
   1.277 +  }
   1.278 +  NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::PLANAR_YCBCR ||
   1.279 +               v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR,
   1.280 +               "Wrong format?");
   1.281 +  PlanarYCbCrImage* videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
   1.282 +
   1.283 +  if (!aImage) {
   1.284 +    VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
   1.285 +                                   true /* aCopyData */);
   1.286 +  } else {
   1.287 +    VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
   1.288 +                                   false /* aCopyData */);
   1.289 +  }
   1.290 +
   1.291 +#ifdef MOZ_WIDGET_GONK
   1.292 +  if (!videoImage->IsValid() && !aImage && IsYV12Format(Y, Cb, Cr)) {
   1.293 +    // Failed to allocate gralloc. Try fallback.
   1.294 +    v->mImage = aContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
   1.295 +    if (!v->mImage) {
   1.296 +      return nullptr;
   1.297 +    }
   1.298 +    videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
   1.299 +    VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
   1.300 +                                   true /* aCopyData */);
   1.301 +  }
   1.302 +#endif
   1.303 +  return v.forget();
   1.304 +}
   1.305 +
   1.306 +/* static */
   1.307 +VideoData* VideoData::Create(VideoInfo& aInfo,
   1.308 +                             ImageContainer* aContainer,
   1.309 +                             int64_t aOffset,
   1.310 +                             int64_t aTime,
   1.311 +                             int64_t aDuration,
   1.312 +                             const YCbCrBuffer& aBuffer,
   1.313 +                             bool aKeyframe,
   1.314 +                             int64_t aTimecode,
   1.315 +                             const IntRect& aPicture)
   1.316 +{
   1.317 +  return Create(aInfo, aContainer, nullptr, aOffset, aTime, aDuration, aBuffer,
   1.318 +                aKeyframe, aTimecode, aPicture);
   1.319 +}
   1.320 +
   1.321 +/* static */
   1.322 +VideoData* VideoData::Create(VideoInfo& aInfo,
   1.323 +                             Image* aImage,
   1.324 +                             int64_t aOffset,
   1.325 +                             int64_t aTime,
   1.326 +                             int64_t aDuration,
   1.327 +                             const YCbCrBuffer& aBuffer,
   1.328 +                             bool aKeyframe,
   1.329 +                             int64_t aTimecode,
   1.330 +                             const IntRect& aPicture)
   1.331 +{
   1.332 +  return Create(aInfo, nullptr, aImage, aOffset, aTime, aDuration, aBuffer,
   1.333 +                aKeyframe, aTimecode, aPicture);
   1.334 +}
   1.335 +
   1.336 +/* static */
   1.337 +VideoData* VideoData::CreateFromImage(VideoInfo& aInfo,
   1.338 +                                      ImageContainer* aContainer,
   1.339 +                                      int64_t aOffset,
   1.340 +                                      int64_t aTime,
   1.341 +                                      int64_t aDuration,
   1.342 +                                      const nsRefPtr<Image>& aImage,
   1.343 +                                      bool aKeyframe,
   1.344 +                                      int64_t aTimecode,
   1.345 +                                      const IntRect& aPicture)
   1.346 +{
   1.347 +  nsAutoPtr<VideoData> v(new VideoData(aOffset,
   1.348 +                                       aTime,
   1.349 +                                       aDuration,
   1.350 +                                       aKeyframe,
   1.351 +                                       aTimecode,
   1.352 +                                       aInfo.mDisplay.ToIntSize()));
   1.353 +  v->mImage = aImage;
   1.354 +  return v.forget();
   1.355 +}
   1.356 +
   1.357 +#ifdef MOZ_OMX_DECODER
   1.358 +/* static */
   1.359 +VideoData* VideoData::Create(VideoInfo& aInfo,
   1.360 +                             ImageContainer* aContainer,
   1.361 +                             int64_t aOffset,
   1.362 +                             int64_t aTime,
   1.363 +                             int64_t aDuration,
   1.364 +                             mozilla::layers::TextureClient* aBuffer,
   1.365 +                             bool aKeyframe,
   1.366 +                             int64_t aTimecode,
   1.367 +                             const IntRect& aPicture)
   1.368 +{
   1.369 +  if (!aContainer) {
   1.370 +    // Create a dummy VideoData with no image. This gives us something to
   1.371 +    // send to media streams if necessary.
   1.372 +    nsAutoPtr<VideoData> v(new VideoData(aOffset,
   1.373 +                                         aTime,
   1.374 +                                         aDuration,
   1.375 +                                         aKeyframe,
   1.376 +                                         aTimecode,
   1.377 +                                         aInfo.mDisplay.ToIntSize()));
   1.378 +    return v.forget();
   1.379 +  }
   1.380 +
   1.381 +  // The following situations could be triggered by invalid input
   1.382 +  if (aPicture.width <= 0 || aPicture.height <= 0) {
   1.383 +    NS_WARNING("Empty picture rect");
   1.384 +    return nullptr;
   1.385 +  }
   1.386 +
   1.387 +  // Ensure the picture size specified in the headers can be extracted out of
   1.388 +  // the frame we've been supplied without indexing out of bounds.
   1.389 +  CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
   1.390 +  CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
   1.391 +  if (!xLimit.isValid() || !yLimit.isValid())
   1.392 +  {
   1.393 +    // The specified picture dimensions can't be contained inside the video
   1.394 +    // frame, we'll stomp memory if we try to copy it. Fail.
   1.395 +    NS_WARNING("Overflowing picture rect");
   1.396 +    return nullptr;
   1.397 +  }
   1.398 +
   1.399 +  nsAutoPtr<VideoData> v(new VideoData(aOffset,
   1.400 +                                       aTime,
   1.401 +                                       aDuration,
   1.402 +                                       aKeyframe,
   1.403 +                                       aTimecode,
   1.404 +                                       aInfo.mDisplay.ToIntSize()));
   1.405 +
   1.406 +  v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
   1.407 +  if (!v->mImage) {
   1.408 +    return nullptr;
   1.409 +  }
   1.410 +  NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR,
   1.411 +               "Wrong format?");
   1.412 +  typedef mozilla::layers::GrallocImage GrallocImage;
   1.413 +  GrallocImage* videoImage = static_cast<GrallocImage*>(v->mImage.get());
   1.414 +  GrallocImage::GrallocData data;
   1.415 +
   1.416 +  data.mPicSize = aPicture.Size();
   1.417 +  data.mGraphicBuffer = aBuffer;
   1.418 +
   1.419 +  videoImage->SetData(data);
   1.420 +
   1.421 +  return v.forget();
   1.422 +}
   1.423 +#endif  // MOZ_OMX_DECODER
   1.424 +
   1.425 +} // namespace mozilla

mercurial