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