Bug 1807268 - Fix verifyOpenAllInNewTabsOptionTest UI test r=ohorvath
[gecko.git] / media / gmp-clearkey / 0.1 / VideoDecoder.cpp
blob6417defdafdb22ccee7bbf6094cb4597d01dc3e3
1 /*
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.
17 #include <algorithm>
18 #include <cstdint>
19 #include <limits>
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"
28 using namespace wmf;
30 VideoDecoder::VideoDecoder(cdm::Host_10* aHost)
31 : mHost(aHost), mHasShutdown(false) {
32 CK_LOGD("VideoDecoder created");
34 // We drop the ref in DecodingComplete().
35 AddRef();
37 mDecoder = new WMFH264Decoder();
39 uint32_t cores = std::max(1u, std::thread::hardware_concurrency());
41 HRESULT hr = mDecoder->Init(cores);
42 if (FAILED(hr)) {
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");
52 if (!mDecoder) {
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
65 // should drain.
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);
79 HRESULT hr;
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()) {
89 cdm::Status rv =
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);
95 return rv;
99 hr = mDecoder->Input(buffer.data(), buffer.size(), data->mTimestamp);
101 CK_LOGD("VideoDecoder::Decode() Input ret hr=0x%x", hr);
103 if (FAILED(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");
118 HRESULT hr = S_OK;
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.
124 while (true) {
125 CComPtr<IMFSample> output;
126 hr = mDecoder->Output(&output);
128 if (hr != S_OK) {
129 break;
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();
152 mOutputQueue.pop();
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(),
164 aVideoFrame);
165 if (FAILED(hr)) {
166 CK_LOGD("VideoDecoder::OutputFrame Failed!");
167 return cdm::Status::kDecodeError;
170 CK_LOGD("VideoDecoder::OutputFrame Succeeded.");
171 return cdm::Status::kSuccess;
174 HRESULT
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);
184 HRESULT hr;
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;
197 LONG stride = 0;
198 CComPtr<IMF2DBuffer> twoDBuffer;
199 hr = mediaBuffer->QueryInterface(static_cast<IMF2DBuffer**>(&twoDBuffer));
200 if (SUCCEEDED(hr)) {
201 hr = twoDBuffer->Lock2D(&data, &stride);
202 ENSURE(SUCCEEDED(hr), hr);
203 } else {
204 hr = mediaBuffer->Lock(&data, nullptr, nullptr);
205 ENSURE(SUCCEEDED(hr), hr);
206 stride = aStride;
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
236 // overflows.
237 if (!bufferSize.isValid()) {
238 CK_LOGD("VideoDecoder::SampleToFrame Buffersize bigger than UINT32_MAX");
239 return E_FAIL;
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
247 // sufficient size).
248 if (!buffer) {
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
258 // WMF adds.
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);
274 if (twoDBuffer) {
275 twoDBuffer->Unlock2D();
276 } else {
277 mediaBuffer->Unlock();
280 LONGLONG hns = 0;
281 hr = aSample->GetSampleTime(&hns);
282 ENSURE(SUCCEEDED(hr), hr);
284 aVideoFrame->SetTimestamp(HNsToUsecs(hns));
286 return S_OK;
289 void VideoDecoder::Reset() {
290 CK_LOGD("VideoDecoder::Reset");
292 if (mDecoder) {
293 mDecoder->Reset();
296 // Remove all the frames from the output queue.
297 while (!mOutputQueue.empty()) {
298 mOutputQueue.pop();
302 cdm::Status VideoDecoder::Drain(cdm::VideoFrame* aVideoFrame) {
303 CK_LOGD("VideoDecoder::Drain()");
305 if (!mDecoder) {
306 CK_LOGD("Drain failed! Decoder was not initialized");
307 return cdm::Status::kDecodeError;
310 mDecoder->Drain();
312 // Return any pending output.
313 return OutputFrame(aVideoFrame);
316 void VideoDecoder::DecodingComplete() {
317 CK_LOGD("VideoDecoder::DecodingComplete()");
319 mHasShutdown = true;
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.
324 Release();