2 * Copyright 2013, Mozilla Foundation and contributors
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
21 #include "BigEndian.h"
22 #include "ClearKeyUtils.h"
23 #include "ClearKeyDecryptionManager.h"
24 #include "VideoDecoder.h"
25 #include "content_decryption_module.h"
26 #include "mozilla/CheckedInt.h"
30 VideoDecoder::VideoDecoder(cdm::Host_10
* aHost
)
31 : mHost(aHost
), mHasShutdown(false) {
32 CK_LOGD("VideoDecoder created");
34 // We drop the ref in DecodingComplete().
37 mDecoder
= new WMFH264Decoder();
39 uint32_t cores
= std::max(1u, std::thread::hardware_concurrency());
41 HRESULT hr
= mDecoder
->Init(cores
);
43 CK_LOGE("Failed to initialize mDecoder!");
47 VideoDecoder::~VideoDecoder() { CK_LOGD("VideoDecoder destroyed"); }
49 cdm::Status
VideoDecoder::InitDecode(const cdm::VideoDecoderConfig_2
& aConfig
) {
50 CK_LOGD("VideoDecoder::InitDecode");
53 CK_LOGD("VideoDecoder::InitDecode failed to init WMFH264Decoder");
55 return cdm::Status::kDecodeError
;
58 return cdm::Status::kSuccess
;
61 cdm::Status
VideoDecoder::Decode(const cdm::InputBuffer_2
& aInputBuffer
,
62 cdm::VideoFrame
* aVideoFrame
) {
63 CK_LOGD("VideoDecoder::Decode");
64 // If the input buffer we have been passed has a null buffer, it means we
66 if (!aInputBuffer
.data
) {
67 // This will drain the decoder until there are no frames left to drain,
68 // whereupon it will return 'NeedsMoreData'.
69 CK_LOGD("VideoDecoder::Decode Input buffer null: Draining");
70 return Drain(aVideoFrame
);
73 DecodeData
* data
= new DecodeData();
74 Assign(data
->mBuffer
, aInputBuffer
.data
, aInputBuffer
.data_size
);
75 data
->mTimestamp
= aInputBuffer
.timestamp
;
76 data
->mCrypto
= CryptoMetaData(&aInputBuffer
);
78 AutoPtr
<DecodeData
> d(data
);
81 if (!data
|| !mDecoder
) {
82 CK_LOGE("Decode job not set up correctly!");
83 return cdm::Status::kDecodeError
;
86 std::vector
<uint8_t>& buffer
= data
->mBuffer
;
88 if (data
->mCrypto
.IsValid()) {
90 ClearKeyDecryptionManager::Get()->Decrypt(buffer
, data
->mCrypto
);
92 if (STATUS_FAILED(rv
)) {
93 CK_LOGARRAY("Failed to decrypt video using key ", aInputBuffer
.key_id
,
94 aInputBuffer
.key_id_size
);
99 hr
= mDecoder
->Input(buffer
.data(), buffer
.size(), data
->mTimestamp
);
101 CK_LOGD("VideoDecoder::Decode() Input ret hr=0x%x", hr
);
104 assert(hr
!= MF_E_TRANSFORM_NEED_MORE_INPUT
);
106 CK_LOGE("VideoDecoder::Decode() decode failed ret=0x%x%s", hr
,
107 ((hr
== MF_E_NOTACCEPTING
) ? " (MF_E_NOTACCEPTING)" : ""));
108 CK_LOGD("Decode failed. The decoder is not accepting input");
109 return cdm::Status::kDecodeError
;
112 return OutputFrame(aVideoFrame
);
115 cdm::Status
VideoDecoder::OutputFrame(cdm::VideoFrame
* aVideoFrame
) {
116 CK_LOGD("VideoDecoder::OutputFrame");
120 // Read all the output from the decoder. Ideally, this would be a while loop
121 // where we read the output and check the result as the condition. However,
122 // this produces a memory leak connected to assigning a new CComPtr to the
123 // address of the old one, which avoids the CComPtr cleaning up.
125 CComPtr
<IMFSample
> output
;
126 hr
= mDecoder
->Output(&output
);
132 CK_LOGD("VideoDecoder::OutputFrame Decoder output ret=0x%x", hr
);
134 mOutputQueue
.push(output
);
135 CK_LOGD("VideoDecoder::OutputFrame: Queue size: %u", mOutputQueue
.size());
138 // If we don't have any inputs, we need more data.
139 if (mOutputQueue
.empty()) {
140 CK_LOGD("Decode failed. Not enought data; Requesting more input");
141 return cdm::Status::kNeedMoreData
;
144 // We will get a MF_E_TRANSFORM_NEED_MORE_INPUT every time, as we always
145 // consume everything in the buffer.
146 if (hr
!= MF_E_TRANSFORM_NEED_MORE_INPUT
&& FAILED(hr
)) {
147 CK_LOGD("Decode failed output ret=0x%x", hr
);
148 return cdm::Status::kDecodeError
;
151 CComPtr
<IMFSample
> result
= mOutputQueue
.front();
154 // The Chromium CDM API doesn't have support for negative strides, though
155 // they are theoretically possible in real world data.
156 if (mDecoder
->GetStride() <= 0) {
157 CK_LOGD("VideoDecoder::OutputFrame Failed! (negative stride)");
158 return cdm::Status::kDecodeError
;
161 const IntRect
& picture
= mDecoder
->GetPictureRegion();
162 hr
= SampleToVideoFrame(result
, picture
.width
, picture
.height
,
163 mDecoder
->GetStride(), mDecoder
->GetFrameHeight(),
166 CK_LOGD("VideoDecoder::OutputFrame Failed!");
167 return cdm::Status::kDecodeError
;
170 CK_LOGD("VideoDecoder::OutputFrame Succeeded.");
171 return cdm::Status::kSuccess
;
175 VideoDecoder::SampleToVideoFrame(IMFSample
* aSample
, int32_t aPictureWidth
,
176 int32_t aPictureHeight
, int32_t aStride
,
177 int32_t aFrameHeight
,
178 cdm::VideoFrame
* aVideoFrame
) {
179 CK_LOGD("[%p] VideoDecoder::SampleToVideoFrame()", this);
181 ENSURE(aSample
!= nullptr, E_POINTER
);
182 ENSURE(aVideoFrame
!= nullptr, E_POINTER
);
185 CComPtr
<IMFMediaBuffer
> mediaBuffer
;
187 aVideoFrame
->SetFormat(cdm::kI420
);
189 // Must convert to contiguous mediaBuffer to use IMD2DBuffer interface.
190 hr
= aSample
->ConvertToContiguousBuffer(&mediaBuffer
);
191 ENSURE(SUCCEEDED(hr
), hr
);
193 // Try and use the IMF2DBuffer interface if available, otherwise fallback
194 // to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient,
195 // but only some systems (Windows 8?) support it.
196 BYTE
* data
= nullptr;
198 CComPtr
<IMF2DBuffer
> twoDBuffer
;
199 hr
= mediaBuffer
->QueryInterface(static_cast<IMF2DBuffer
**>(&twoDBuffer
));
201 hr
= twoDBuffer
->Lock2D(&data
, &stride
);
202 ENSURE(SUCCEEDED(hr
), hr
);
204 hr
= mediaBuffer
->Lock(&data
, nullptr, nullptr);
205 ENSURE(SUCCEEDED(hr
), hr
);
209 // WMF stores the U and V planes 16-row-aligned, so we need to add padding
210 // to the row heights to ensure the source offsets of the Y'CbCr planes are
211 // referenced properly.
212 // YV12, planar format: [YYYY....][UUUU....][VVVV....]
213 // i.e., Y, then U, then V.
214 uint32_t padding
= 0;
215 if (aFrameHeight
% 16 != 0) {
216 padding
= 16 - (aFrameHeight
% 16);
218 uint32_t srcYSize
= stride
* (aFrameHeight
+ padding
);
219 uint32_t srcUVSize
= stride
* (aFrameHeight
+ padding
) / 4;
220 uint32_t halfStride
= (stride
+ 1) / 2;
222 aVideoFrame
->SetStride(cdm::VideoPlane::kYPlane
, stride
);
223 aVideoFrame
->SetStride(cdm::VideoPlane::kUPlane
, halfStride
);
224 aVideoFrame
->SetStride(cdm::VideoPlane::kVPlane
, halfStride
);
226 aVideoFrame
->SetSize(cdm::Size
{aPictureWidth
, aPictureHeight
});
228 // Note: We allocate the minimal sized buffer required to send the
229 // frame back over to the parent process. This is so that we request the
230 // same sized frame as the buffer allocator expects.
231 using mozilla::CheckedUint32
;
232 CheckedUint32 bufferSize
= CheckedUint32(stride
) * aPictureHeight
+
233 ((CheckedUint32(stride
) * aPictureHeight
) / 4) * 2;
235 // If the buffer is bigger than the max for a 32 bit, fail to avoid buffer
237 if (!bufferSize
.isValid()) {
238 CK_LOGD("VideoDecoder::SampleToFrame Buffersize bigger than UINT32_MAX");
242 // Get the buffer from the host.
243 cdm::Buffer
* buffer
= mHost
->Allocate(bufferSize
.value());
244 aVideoFrame
->SetFrameBuffer(buffer
);
246 // Make sure the buffer is non-null (allocate guarantees it will be of
249 CK_LOGD("VideoDecoder::SampleToFrame Out of memory");
250 return E_OUTOFMEMORY
;
253 uint8_t* outBuffer
= buffer
->Data();
255 aVideoFrame
->SetPlaneOffset(cdm::VideoPlane::kYPlane
, 0);
257 // Offset of U plane is the size of the Y plane, excluding the padding that
259 uint32_t dstUOffset
= stride
* aPictureHeight
;
260 aVideoFrame
->SetPlaneOffset(cdm::VideoPlane::kUPlane
, dstUOffset
);
262 // Offset of the V plane is the size of the Y plane + the size of the U plane,
263 // excluding any padding WMF adds.
264 uint32_t dstVOffset
= stride
* aPictureHeight
+ (stride
* aPictureHeight
) / 4;
265 aVideoFrame
->SetPlaneOffset(cdm::VideoPlane::kVPlane
, dstVOffset
);
267 // Copy the pixel data, excluding WMF's padding.
268 memcpy(outBuffer
, data
, stride
* aPictureHeight
);
269 memcpy(outBuffer
+ dstUOffset
, data
+ srcYSize
,
270 (stride
* aPictureHeight
) / 4);
271 memcpy(outBuffer
+ dstVOffset
, data
+ srcYSize
+ srcUVSize
,
272 (stride
* aPictureHeight
) / 4);
275 twoDBuffer
->Unlock2D();
277 mediaBuffer
->Unlock();
281 hr
= aSample
->GetSampleTime(&hns
);
282 ENSURE(SUCCEEDED(hr
), hr
);
284 aVideoFrame
->SetTimestamp(HNsToUsecs(hns
));
289 void VideoDecoder::Reset() {
290 CK_LOGD("VideoDecoder::Reset");
296 // Remove all the frames from the output queue.
297 while (!mOutputQueue
.empty()) {
302 cdm::Status
VideoDecoder::Drain(cdm::VideoFrame
* aVideoFrame
) {
303 CK_LOGD("VideoDecoder::Drain()");
306 CK_LOGD("Drain failed! Decoder was not initialized");
307 return cdm::Status::kDecodeError
;
312 // Return any pending output.
313 return OutputFrame(aVideoFrame
);
316 void VideoDecoder::DecodingComplete() {
317 CK_LOGD("VideoDecoder::DecodingComplete()");
321 // Release the reference we added in the constructor. There may be
322 // WrapRefCounted tasks that also hold references to us, and keep
323 // us alive a little longer.