VideoDecoder.cpp (10252B)
1 /* 2 * Copyright 2013, Mozilla Foundation and contributors 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17 #include <algorithm> 18 #include <cstdint> 19 20 #include "BigEndian.h" 21 #include "ClearKeyUtils.h" 22 #include "ClearKeyDecryptionManager.h" 23 #include "VideoDecoder.h" 24 #include "content_decryption_module.h" 25 #include "mozilla/CheckedInt.h" 26 27 using namespace wmf; 28 29 VideoDecoder::VideoDecoder(cdm::Host_11* aHost) 30 : mHost(aHost), mHasShutdown(false) { 31 CK_LOGD("VideoDecoder created"); 32 33 // We drop the ref in DecodingComplete(). 34 AddRef(); 35 36 mDecoder = new WMFH264Decoder(); 37 38 uint32_t cores = std::max(1u, std::thread::hardware_concurrency()); 39 40 HRESULT hr = mDecoder->Init(cores); 41 if (FAILED(hr)) { 42 CK_LOGE("Failed to initialize mDecoder!"); 43 } 44 } 45 46 VideoDecoder::~VideoDecoder() { CK_LOGD("VideoDecoder destroyed"); } 47 48 cdm::Status VideoDecoder::InitDecode(const cdm::VideoDecoderConfig_2& aConfig) { 49 CK_LOGD("VideoDecoder::InitDecode"); 50 51 if (!mDecoder) { 52 CK_LOGD("VideoDecoder::InitDecode failed to init WMFH264Decoder"); 53 54 return cdm::Status::kDecodeError; 55 } 56 57 return cdm::Status::kSuccess; 58 } 59 60 cdm::Status VideoDecoder::Decode(const cdm::InputBuffer_2& aInputBuffer, 61 cdm::VideoFrame* aVideoFrame) { 62 CK_LOGD("VideoDecoder::Decode"); 63 // If the input buffer we have been passed has a null buffer, it means we 64 // should drain. 65 if (!aInputBuffer.data) { 66 // This will drain the decoder until there are no frames left to drain, 67 // whereupon it will return 'NeedsMoreData'. 68 CK_LOGD("VideoDecoder::Decode Input buffer null: Draining"); 69 return Drain(aVideoFrame); 70 } 71 72 DecodeData* data = new DecodeData(); 73 Assign(data->mBuffer, aInputBuffer.data, aInputBuffer.data_size); 74 data->mTimestamp = aInputBuffer.timestamp; 75 data->mCrypto = CryptoMetaData(&aInputBuffer); 76 77 AutoPtr<DecodeData> d(data); 78 HRESULT hr; 79 80 if (!data || !mDecoder) { 81 CK_LOGE("Decode job not set up correctly!"); 82 return cdm::Status::kDecodeError; 83 } 84 85 std::vector<uint8_t>& buffer = data->mBuffer; 86 87 if (data->mCrypto.IsValid()) { 88 cdm::Status rv = 89 ClearKeyDecryptionManager::Get()->Decrypt(buffer, data->mCrypto); 90 91 if (STATUS_FAILED(rv)) { 92 CK_LOGARRAY("Failed to decrypt video using key ", aInputBuffer.key_id, 93 aInputBuffer.key_id_size); 94 return rv; 95 } 96 } 97 98 hr = mDecoder->Input(buffer.data(), buffer.size(), data->mTimestamp); 99 100 CK_LOGD("VideoDecoder::Decode() Input ret hr=0x%x", hr); 101 102 if (FAILED(hr)) { 103 assert(hr != MF_E_TRANSFORM_NEED_MORE_INPUT); 104 105 CK_LOGE("VideoDecoder::Decode() decode failed ret=0x%x%s", hr, 106 ((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : "")); 107 CK_LOGD("Decode failed. The decoder is not accepting input"); 108 return cdm::Status::kDecodeError; 109 } 110 111 return OutputFrame(aVideoFrame); 112 } 113 114 cdm::Status VideoDecoder::OutputFrame(cdm::VideoFrame* aVideoFrame) { 115 CK_LOGD("VideoDecoder::OutputFrame"); 116 117 HRESULT hr = S_OK; 118 119 // Read all the output from the decoder. Ideally, this would be a while loop 120 // where we read the output and check the result as the condition. However, 121 // this produces a memory leak connected to assigning a new CComPtr to the 122 // address of the old one, which avoids the CComPtr cleaning up. 123 while (true) { 124 CComPtr<IMFSample> output; 125 hr = mDecoder->Output(&output); 126 127 if (hr != S_OK) { 128 break; 129 } 130 131 CK_LOGD("VideoDecoder::OutputFrame Decoder output ret=0x%x", hr); 132 133 mOutputQueue.push(output); 134 CK_LOGD("VideoDecoder::OutputFrame: Queue size: %u", mOutputQueue.size()); 135 } 136 137 // If we don't have any inputs, we need more data. 138 if (mOutputQueue.empty()) { 139 CK_LOGD("Decode failed. Not enought data; Requesting more input"); 140 return cdm::Status::kNeedMoreData; 141 } 142 143 // We will get a MF_E_TRANSFORM_NEED_MORE_INPUT every time, as we always 144 // consume everything in the buffer. 145 if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT && FAILED(hr)) { 146 CK_LOGD("Decode failed output ret=0x%x", hr); 147 return cdm::Status::kDecodeError; 148 } 149 150 CComPtr<IMFSample> result = mOutputQueue.front(); 151 mOutputQueue.pop(); 152 153 // The Chromium CDM API doesn't have support for negative strides, though 154 // they are theoretically possible in real world data. 155 if (mDecoder->GetStride() <= 0) { 156 CK_LOGD("VideoDecoder::OutputFrame Failed! (negative stride)"); 157 return cdm::Status::kDecodeError; 158 } 159 160 const IntRect& picture = mDecoder->GetPictureRegion(); 161 hr = SampleToVideoFrame(result, picture.width, picture.height, 162 mDecoder->GetStride(), mDecoder->GetFrameHeight(), 163 aVideoFrame); 164 if (FAILED(hr)) { 165 CK_LOGD("VideoDecoder::OutputFrame Failed!"); 166 return cdm::Status::kDecodeError; 167 } 168 169 CK_LOGD("VideoDecoder::OutputFrame Succeeded."); 170 return cdm::Status::kSuccess; 171 } 172 173 HRESULT 174 VideoDecoder::SampleToVideoFrame(IMFSample* aSample, int32_t aPictureWidth, 175 int32_t aPictureHeight, int32_t aStride, 176 int32_t aFrameHeight, 177 cdm::VideoFrame* aVideoFrame) { 178 CK_LOGD("[%p] VideoDecoder::SampleToVideoFrame()", this); 179 180 ENSURE(aSample != nullptr, E_POINTER); 181 ENSURE(aVideoFrame != nullptr, E_POINTER); 182 183 HRESULT hr; 184 CComPtr<IMFMediaBuffer> mediaBuffer; 185 186 aVideoFrame->SetFormat(cdm::kI420); 187 188 // Must convert to contiguous mediaBuffer to use IMD2DBuffer interface. 189 hr = aSample->ConvertToContiguousBuffer(&mediaBuffer); 190 ENSURE(SUCCEEDED(hr), hr); 191 192 // Try and use the IMF2DBuffer interface if available, otherwise fallback 193 // to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient, 194 // but only some systems (Windows 8?) support it. 195 BYTE* data = nullptr; 196 LONG stride = 0; 197 CComPtr<IMF2DBuffer> twoDBuffer; 198 hr = mediaBuffer->QueryInterface(static_cast<IMF2DBuffer**>(&twoDBuffer)); 199 if (SUCCEEDED(hr)) { 200 hr = twoDBuffer->Lock2D(&data, &stride); 201 ENSURE(SUCCEEDED(hr), hr); 202 } else { 203 hr = mediaBuffer->Lock(&data, nullptr, nullptr); 204 ENSURE(SUCCEEDED(hr), hr); 205 stride = aStride; 206 } 207 208 // WMF stores the U and V planes 16-row-aligned, so we need to add padding 209 // to the row heights to ensure the source offsets of the Y'CbCr planes are 210 // referenced properly. 211 // YV12, planar format: [YYYY....][UUUU....][VVVV....] 212 // i.e., Y, then U, then V. 213 uint32_t padding = 0; 214 if (aFrameHeight % 16 != 0) { 215 padding = 16 - (aFrameHeight % 16); 216 } 217 uint32_t srcYSize = stride * (aFrameHeight + padding); 218 uint32_t srcUVSize = stride * (aFrameHeight + padding) / 4; 219 uint32_t halfStride = (stride + 1) / 2; 220 221 aVideoFrame->SetStride(cdm::kYPlane, stride); 222 aVideoFrame->SetStride(cdm::kUPlane, halfStride); 223 aVideoFrame->SetStride(cdm::kVPlane, halfStride); 224 225 aVideoFrame->SetSize(cdm::Size{aPictureWidth, aPictureHeight}); 226 227 // Note: We allocate the minimal sized buffer required to send the 228 // frame back over to the parent process. This is so that we request the 229 // same sized frame as the buffer allocator expects. 230 using mozilla::CheckedUint32; 231 CheckedUint32 bufferSize = CheckedUint32(stride) * aPictureHeight + 232 ((CheckedUint32(stride) * aPictureHeight) / 4) * 2; 233 234 // If the buffer is bigger than the max for a 32 bit, fail to avoid buffer 235 // overflows. 236 if (!bufferSize.isValid()) { 237 CK_LOGD("VideoDecoder::SampleToFrame Buffersize bigger than UINT32_MAX"); 238 return E_FAIL; 239 } 240 241 // Get the buffer from the host. 242 cdm::Buffer* buffer = mHost->Allocate(bufferSize.value()); 243 aVideoFrame->SetFrameBuffer(buffer); 244 245 // Make sure the buffer is non-null (allocate guarantees it will be of 246 // sufficient size). 247 if (!buffer) { 248 CK_LOGD("VideoDecoder::SampleToFrame Out of memory"); 249 return E_OUTOFMEMORY; 250 } 251 252 uint8_t* outBuffer = buffer->Data(); 253 254 aVideoFrame->SetPlaneOffset(cdm::kYPlane, 0); 255 256 // Offset of U plane is the size of the Y plane, excluding the padding that 257 // WMF adds. 258 uint32_t dstUOffset = stride * aPictureHeight; 259 aVideoFrame->SetPlaneOffset(cdm::kUPlane, dstUOffset); 260 261 // Offset of the V plane is the size of the Y plane + the size of the U plane, 262 // excluding any padding WMF adds. 263 uint32_t dstVOffset = stride * aPictureHeight + (stride * aPictureHeight) / 4; 264 aVideoFrame->SetPlaneOffset(cdm::kVPlane, dstVOffset); 265 266 // Copy the pixel data, excluding WMF's padding. 267 memcpy(outBuffer, data, stride * aPictureHeight); 268 memcpy(outBuffer + dstUOffset, data + srcYSize, 269 (stride * aPictureHeight) / 4); 270 memcpy(outBuffer + dstVOffset, data + srcYSize + srcUVSize, 271 (stride * aPictureHeight) / 4); 272 273 if (twoDBuffer) { 274 twoDBuffer->Unlock2D(); 275 } else { 276 mediaBuffer->Unlock(); 277 } 278 279 LONGLONG hns = 0; 280 hr = aSample->GetSampleTime(&hns); 281 ENSURE(SUCCEEDED(hr), hr); 282 283 aVideoFrame->SetTimestamp(HNsToUsecs(hns)); 284 285 return S_OK; 286 } 287 288 void VideoDecoder::Reset() { 289 CK_LOGD("VideoDecoder::Reset"); 290 291 if (mDecoder) { 292 mDecoder->Reset(); 293 } 294 295 // Remove all the frames from the output queue. 296 while (!mOutputQueue.empty()) { 297 mOutputQueue.pop(); 298 } 299 } 300 301 cdm::Status VideoDecoder::Drain(cdm::VideoFrame* aVideoFrame) { 302 CK_LOGD("VideoDecoder::Drain()"); 303 304 if (!mDecoder) { 305 CK_LOGD("Drain failed! Decoder was not initialized"); 306 return cdm::Status::kDecodeError; 307 } 308 309 mDecoder->Drain(); 310 311 // Return any pending output. 312 return OutputFrame(aVideoFrame); 313 } 314 315 void VideoDecoder::DecodingComplete() { 316 CK_LOGD("VideoDecoder::DecodingComplete()"); 317 318 mHasShutdown = true; 319 320 // Release the reference we added in the constructor. There may be 321 // WrapRefCounted tasks that also hold references to us, and keep 322 // us alive a little longer. 323 Release(); 324 }