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/. */
9 #include "GrallocImages.h"
11 #include "VideoUtils.h"
12 #include "ImageContainer.h"
16 using namespace mozilla::gfx
;
17 using layers::ImageContainer
;
18 using layers::PlanarYCbCrImage
;
19 using layers::PlanarYCbCrData
;
21 // Verify these values are sane. Once we've checked the frame sizes, we then
22 // can do less integer overflow checking.
23 static_assert(MAX_VIDEO_WIDTH
< PlanarYCbCrImage::MAX_DIMENSION
,
24 "MAX_VIDEO_WIDTH is too large");
25 static_assert(MAX_VIDEO_HEIGHT
< PlanarYCbCrImage::MAX_DIMENSION
,
26 "MAX_VIDEO_HEIGHT is too large");
27 static_assert(PlanarYCbCrImage::MAX_DIMENSION
< UINT32_MAX
/ PlanarYCbCrImage::MAX_DIMENSION
,
28 "MAX_DIMENSION*MAX_DIMENSION doesn't fit in 32 bits");
31 AudioData::EnsureAudioBuffer()
35 mAudioBuffer
= SharedBuffer::Create(mFrames
*mChannels
*sizeof(AudioDataValue
));
37 AudioDataValue
* data
= static_cast<AudioDataValue
*>(mAudioBuffer
->Data());
38 for (uint32_t i
= 0; i
< mFrames
; ++i
) {
39 for (uint32_t j
= 0; j
< mChannels
; ++j
) {
40 data
[j
*mFrames
+ i
] = mAudioData
[i
*mChannels
+ j
];
46 ValidatePlane(const VideoData::YCbCrBuffer::Plane
& aPlane
)
48 return aPlane
.mWidth
<= PlanarYCbCrImage::MAX_DIMENSION
&&
49 aPlane
.mHeight
<= PlanarYCbCrImage::MAX_DIMENSION
&&
50 aPlane
.mWidth
* aPlane
.mHeight
< MAX_VIDEO_WIDTH
* MAX_VIDEO_HEIGHT
&&
55 IsYV12Format(const VideoData::YCbCrBuffer::Plane
& aYPlane
,
56 const VideoData::YCbCrBuffer::Plane
& aCbPlane
,
57 const VideoData::YCbCrBuffer::Plane
& aCrPlane
)
60 aYPlane
.mWidth
% 2 == 0 &&
61 aYPlane
.mHeight
% 2 == 0 &&
62 aYPlane
.mWidth
/ 2 == aCbPlane
.mWidth
&&
63 aYPlane
.mHeight
/ 2 == aCbPlane
.mHeight
&&
64 aCbPlane
.mWidth
== aCrPlane
.mWidth
&&
65 aCbPlane
.mHeight
== aCrPlane
.mHeight
;
68 VideoData::VideoData(int64_t aOffset
, int64_t aTime
, int64_t aDuration
, int64_t aTimecode
)
69 : MediaData(VIDEO_FRAME
, aOffset
, aTime
, aDuration
),
74 MOZ_COUNT_CTOR(VideoData
);
75 NS_ASSERTION(mDuration
>= 0, "Frame must have non-negative duration.");
78 VideoData::VideoData(int64_t aOffset
,
84 : MediaData(VIDEO_FRAME
, aOffset
, aTime
, aDuration
),
90 MOZ_COUNT_CTOR(VideoData
);
91 NS_ASSERTION(mDuration
>= 0, "Frame must have non-negative duration.");
94 VideoData::~VideoData()
96 MOZ_COUNT_DTOR(VideoData
);
100 VideoData
* VideoData::ShallowCopyUpdateDuration(VideoData
* aOther
,
103 VideoData
* v
= new VideoData(aOther
->mOffset
,
109 v
->mImage
= aOther
->mImage
;
113 VideoData
* VideoData::Create(VideoInfo
& aInfo
,
114 ImageContainer
* aContainer
,
119 const YCbCrBuffer
& aBuffer
,
124 if (!aImage
&& !aContainer
) {
125 // Create a dummy VideoData with no image. This gives us something to
126 // send to media streams if necessary.
127 nsAutoPtr
<VideoData
> v(new VideoData(aOffset
,
136 // The following situation should never happen unless there is a bug
138 if (aBuffer
.mPlanes
[1].mWidth
!= aBuffer
.mPlanes
[2].mWidth
||
139 aBuffer
.mPlanes
[1].mHeight
!= aBuffer
.mPlanes
[2].mHeight
) {
140 NS_ERROR("C planes with different sizes");
144 // The following situations could be triggered by invalid input
145 if (aPicture
.width
<= 0 || aPicture
.height
<= 0) {
146 NS_WARNING("Empty picture rect");
149 if (!ValidatePlane(aBuffer
.mPlanes
[0]) || !ValidatePlane(aBuffer
.mPlanes
[1]) ||
150 !ValidatePlane(aBuffer
.mPlanes
[2])) {
151 NS_WARNING("Invalid plane size");
155 // Ensure the picture size specified in the headers can be extracted out of
156 // the frame we've been supplied without indexing out of bounds.
157 CheckedUint32 xLimit
= aPicture
.x
+ CheckedUint32(aPicture
.width
);
158 CheckedUint32 yLimit
= aPicture
.y
+ CheckedUint32(aPicture
.height
);
159 if (!xLimit
.isValid() || xLimit
.value() > aBuffer
.mPlanes
[0].mStride
||
160 !yLimit
.isValid() || yLimit
.value() > aBuffer
.mPlanes
[0].mHeight
)
162 // The specified picture dimensions can't be contained inside the video
163 // frame, we'll stomp memory if we try to copy it. Fail.
164 NS_WARNING("Overflowing picture rect");
168 nsAutoPtr
<VideoData
> v(new VideoData(aOffset
,
174 const YCbCrBuffer::Plane
&Y
= aBuffer
.mPlanes
[0];
175 const YCbCrBuffer::Plane
&Cb
= aBuffer
.mPlanes
[1];
176 const YCbCrBuffer::Plane
&Cr
= aBuffer
.mPlanes
[2];
179 // Currently our decoder only knows how to output to PLANAR_YCBCR
181 ImageFormat format
[2] = {PLANAR_YCBCR
, GRALLOC_PLANAR_YCBCR
};
182 if (IsYV12Format(Y
, Cb
, Cr
)) {
183 v
->mImage
= aContainer
->CreateImage(format
, 2);
185 v
->mImage
= aContainer
->CreateImage(format
, 1);
194 NS_ASSERTION(v
->mImage
->GetFormat() == PLANAR_YCBCR
||
195 v
->mImage
->GetFormat() == GRALLOC_PLANAR_YCBCR
,
197 PlanarYCbCrImage
* videoImage
= static_cast<PlanarYCbCrImage
*>(v
->mImage
.get());
199 PlanarYCbCrData data
;
200 data
.mYChannel
= Y
.mData
+ Y
.mOffset
;
201 data
.mYSize
= gfxIntSize(Y
.mWidth
, Y
.mHeight
);
202 data
.mYStride
= Y
.mStride
;
203 data
.mYSkip
= Y
.mSkip
;
204 data
.mCbChannel
= Cb
.mData
+ Cb
.mOffset
;
205 data
.mCrChannel
= Cr
.mData
+ Cr
.mOffset
;
206 data
.mCbCrSize
= gfxIntSize(Cb
.mWidth
, Cb
.mHeight
);
207 data
.mCbCrStride
= Cb
.mStride
;
208 data
.mCbSkip
= Cb
.mSkip
;
209 data
.mCrSkip
= Cr
.mSkip
;
210 data
.mPicX
= aPicture
.x
;
211 data
.mPicY
= aPicture
.y
;
212 data
.mPicSize
= gfxIntSize(aPicture
.width
, aPicture
.height
);
213 data
.mStereoMode
= aInfo
.mStereoMode
;
215 videoImage
->SetDelayedConversion(true);
217 videoImage
->SetData(data
);
219 videoImage
->SetDataNoCopy(data
);
225 VideoData
* VideoData::Create(VideoInfo
& aInfo
,
226 ImageContainer
* aContainer
,
230 const YCbCrBuffer
& aBuffer
,
235 return Create(aInfo
, aContainer
, nullptr, aOffset
, aTime
, aDuration
, aBuffer
,
236 aKeyframe
, aTimecode
, aPicture
);
239 VideoData
* VideoData::Create(VideoInfo
& aInfo
,
244 const YCbCrBuffer
& aBuffer
,
249 return Create(aInfo
, nullptr, aImage
, aOffset
, aTime
, aDuration
, aBuffer
,
250 aKeyframe
, aTimecode
, aPicture
);
253 VideoData
* VideoData::CreateFromImage(VideoInfo
& aInfo
,
254 ImageContainer
* aContainer
,
258 const nsRefPtr
<Image
>& aImage
,
263 nsAutoPtr
<VideoData
> v(new VideoData(aOffset
,
273 #ifdef MOZ_OMX_DECODER
274 VideoData
* VideoData::Create(VideoInfo
& aInfo
,
275 ImageContainer
* aContainer
,
279 mozilla::layers::GraphicBufferLocked
* aBuffer
,
285 // Create a dummy VideoData with no image. This gives us something to
286 // send to media streams if necessary.
287 nsAutoPtr
<VideoData
> v(new VideoData(aOffset
,
296 // The following situations could be triggered by invalid input
297 if (aPicture
.width
<= 0 || aPicture
.height
<= 0) {
298 NS_WARNING("Empty picture rect");
302 // Ensure the picture size specified in the headers can be extracted out of
303 // the frame we've been supplied without indexing out of bounds.
304 CheckedUint32 xLimit
= aPicture
.x
+ CheckedUint32(aPicture
.width
);
305 CheckedUint32 yLimit
= aPicture
.y
+ CheckedUint32(aPicture
.height
);
306 if (!xLimit
.isValid() || !yLimit
.isValid())
308 // The specified picture dimensions can't be contained inside the video
309 // frame, we'll stomp memory if we try to copy it. Fail.
310 NS_WARNING("Overflowing picture rect");
314 nsAutoPtr
<VideoData
> v(new VideoData(aOffset
,
321 ImageFormat format
= GRALLOC_PLANAR_YCBCR
;
322 v
->mImage
= aContainer
->CreateImage(&format
, 1);
326 NS_ASSERTION(v
->mImage
->GetFormat() == GRALLOC_PLANAR_YCBCR
,
328 typedef mozilla::layers::GrallocImage GrallocImage
;
329 GrallocImage
* videoImage
= static_cast<GrallocImage
*>(v
->mImage
.get());
330 GrallocImage::GrallocData data
;
332 data
.mPicSize
= gfxIntSize(aPicture
.width
, aPicture
.height
);
333 data
.mGraphicBuffer
= aBuffer
;
335 videoImage
->SetData(data
);
339 #endif // MOZ_OMX_DECODER
341 } // namespace mozilla