WMFH264Decoder.cpp (8984B)
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 "WMFH264Decoder.h" 18 19 #include <codecapi.h> 20 21 #include <algorithm> 22 23 namespace wmf { 24 25 WMFH264Decoder::WMFH264Decoder() : mDecoder(nullptr) { 26 memset(&mInputStreamInfo, 0, sizeof(MFT_INPUT_STREAM_INFO)); 27 memset(&mOutputStreamInfo, 0, sizeof(MFT_OUTPUT_STREAM_INFO)); 28 } 29 30 WMFH264Decoder::~WMFH264Decoder() {} 31 32 HRESULT 33 WMFH264Decoder::Init(int32_t aCoreCount) { 34 HRESULT hr; 35 36 hr = CreateMFT(__uuidof(CMSH264DecoderMFT), WMFDecoderDllName(), mDecoder); 37 if (FAILED(hr)) { 38 // Windows 7 Enterprise Server N (which is what Mozilla's mochitests run 39 // on) need a different CLSID to instantiate the H.264 decoder. 40 hr = CreateMFT(CLSID_CMSH264DecMFT, WMFDecoderDllName(), mDecoder); 41 } 42 ENSURE(SUCCEEDED(hr), hr); 43 44 CComPtr<IMFAttributes> attr; 45 hr = mDecoder->GetAttributes(&attr); 46 ENSURE(SUCCEEDED(hr), hr); 47 hr = attr->SetUINT32(CODECAPI_AVDecNumWorkerThreads, 48 GetNumThreads(aCoreCount)); 49 ENSURE(SUCCEEDED(hr), hr); 50 51 hr = SetDecoderInputType(); 52 ENSURE(SUCCEEDED(hr), hr); 53 54 hr = SetDecoderOutputType(); 55 ENSURE(SUCCEEDED(hr), hr); 56 57 hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0); 58 ENSURE(SUCCEEDED(hr), hr); 59 60 hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); 61 ENSURE(SUCCEEDED(hr), hr); 62 63 hr = mDecoder->GetInputStreamInfo(0, &mInputStreamInfo); 64 ENSURE(SUCCEEDED(hr), hr); 65 66 hr = mDecoder->GetOutputStreamInfo(0, &mOutputStreamInfo); 67 ENSURE(SUCCEEDED(hr), hr); 68 69 return S_OK; 70 } 71 72 HRESULT 73 WMFH264Decoder::ConfigureVideoFrameGeometry(IMFMediaType* aMediaType) { 74 ENSURE(aMediaType != nullptr, E_POINTER); 75 HRESULT hr; 76 77 IntRect pictureRegion; 78 hr = wmf::GetPictureRegion(aMediaType, pictureRegion); 79 ENSURE(SUCCEEDED(hr), hr); 80 81 UINT32 width = 0, height = 0; 82 hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height); 83 ENSURE(SUCCEEDED(hr), hr); 84 ENSURE(width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL); 85 ENSURE(height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL); 86 87 UINT32 stride = 0; 88 hr = GetDefaultStride(aMediaType, &stride); 89 ENSURE(SUCCEEDED(hr), hr); 90 ENSURE(stride <= mozilla::MAX_VIDEO_WIDTH, E_FAIL); 91 92 // Success! Save state. 93 mStride = stride; 94 mVideoWidth = width; 95 mVideoHeight = height; 96 mPictureRegion = pictureRegion; 97 98 LOG("WMFH264Decoder frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, " 99 "%d, %d)\n", 100 width, height, mStride, mPictureRegion.x, mPictureRegion.y, 101 mPictureRegion.width, mPictureRegion.height); 102 103 return S_OK; 104 } 105 106 int32_t WMFH264Decoder::GetFrameHeight() const { return mVideoHeight; } 107 108 const IntRect& WMFH264Decoder::GetPictureRegion() const { 109 return mPictureRegion; 110 } 111 112 int32_t WMFH264Decoder::GetStride() const { return mStride; } 113 114 HRESULT 115 WMFH264Decoder::SetDecoderInputType() { 116 HRESULT hr; 117 118 CComPtr<IMFMediaType> type; 119 hr = MFCreateMediaType(&type); 120 ENSURE(SUCCEEDED(hr), hr); 121 122 hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); 123 ENSURE(SUCCEEDED(hr), hr); 124 125 hr = type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264); 126 ENSURE(SUCCEEDED(hr), hr); 127 128 hr = type->SetUINT32(MF_MT_INTERLACE_MODE, 129 MFVideoInterlace_MixedInterlaceOrProgressive); 130 ENSURE(SUCCEEDED(hr), hr); 131 132 hr = mDecoder->SetInputType(0, type, 0); 133 ENSURE(SUCCEEDED(hr), hr); 134 135 return S_OK; 136 } 137 138 HRESULT 139 WMFH264Decoder::SetDecoderOutputType() { 140 HRESULT hr; 141 142 CComPtr<IMFMediaType> type; 143 144 UINT32 typeIndex = 0; 145 while (static_cast<void>(type = nullptr), 146 SUCCEEDED(mDecoder->GetOutputAvailableType(0, typeIndex++, &type))) { 147 GUID subtype; 148 hr = type->GetGUID(MF_MT_SUBTYPE, &subtype); 149 if (FAILED(hr)) { 150 continue; 151 } 152 if (subtype == MFVideoFormat_I420 || subtype == MFVideoFormat_IYUV) { 153 // On Windows 7 Enterprise N the MFT reports it reports IYUV instead 154 // of I420. Other Windows' report I420. The formats are the same, so 155 // support both. 156 hr = mDecoder->SetOutputType(0, type, 0); 157 ENSURE(SUCCEEDED(hr), hr); 158 159 hr = ConfigureVideoFrameGeometry(type); 160 ENSURE(SUCCEEDED(hr), hr); 161 162 return S_OK; 163 } 164 } 165 166 return E_FAIL; 167 } 168 169 HRESULT 170 WMFH264Decoder::SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData) { 171 ENSURE(mDecoder != nullptr, E_POINTER); 172 HRESULT hr = mDecoder->ProcessMessage(aMsg, aData); 173 ENSURE(SUCCEEDED(hr), hr); 174 return S_OK; 175 } 176 177 HRESULT 178 WMFH264Decoder::CreateInputSample(const uint8_t* aData, uint32_t aDataSize, 179 Microseconds aTimestamp, 180 IMFSample** aOutSample) { 181 HRESULT hr; 182 CComPtr<IMFSample> sample; 183 hr = MFCreateSample(&sample); 184 ENSURE(SUCCEEDED(hr), hr); 185 186 CComPtr<IMFMediaBuffer> buffer; 187 int32_t bufferSize = 188 std::max<uint32_t>(uint32_t(mInputStreamInfo.cbSize), aDataSize); 189 UINT32 alignment = 190 (mInputStreamInfo.cbAlignment > 1) ? mInputStreamInfo.cbAlignment - 1 : 0; 191 hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer); 192 ENSURE(SUCCEEDED(hr), hr); 193 194 DWORD maxLength = 0; 195 DWORD currentLength = 0; 196 BYTE* dst = nullptr; 197 hr = buffer->Lock(&dst, &maxLength, ¤tLength); 198 ENSURE(SUCCEEDED(hr), hr); 199 200 // Copy data into sample's buffer. 201 memcpy(dst, aData, aDataSize); 202 203 hr = buffer->Unlock(); 204 ENSURE(SUCCEEDED(hr), hr); 205 206 hr = buffer->SetCurrentLength(aDataSize); 207 ENSURE(SUCCEEDED(hr), hr); 208 209 hr = sample->AddBuffer(buffer); 210 ENSURE(SUCCEEDED(hr), hr); 211 212 hr = sample->SetSampleTime(UsecsToHNs(aTimestamp)); 213 ENSURE(SUCCEEDED(hr), hr); 214 215 *aOutSample = sample.Detach(); 216 217 return S_OK; 218 } 219 220 HRESULT 221 WMFH264Decoder::CreateOutputSample(IMFSample** aOutSample) { 222 HRESULT hr; 223 CComPtr<IMFSample> sample; 224 hr = MFCreateSample(&sample); 225 ENSURE(SUCCEEDED(hr), hr); 226 227 CComPtr<IMFMediaBuffer> buffer; 228 int32_t bufferSize = mOutputStreamInfo.cbSize; 229 UINT32 alignment = (mOutputStreamInfo.cbAlignment > 1) 230 ? mOutputStreamInfo.cbAlignment - 1 231 : 0; 232 hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer); 233 ENSURE(SUCCEEDED(hr), hr); 234 235 hr = sample->AddBuffer(buffer); 236 ENSURE(SUCCEEDED(hr), hr); 237 238 *aOutSample = sample.Detach(); 239 240 return S_OK; 241 } 242 243 HRESULT 244 WMFH264Decoder::GetOutputSample(IMFSample** aOutSample) { 245 HRESULT hr; 246 // We allocate samples for MFT output. 247 MFT_OUTPUT_DATA_BUFFER output = {0}; 248 249 CComPtr<IMFSample> sample; 250 hr = CreateOutputSample(&sample); 251 ENSURE(SUCCEEDED(hr), hr); 252 253 output.pSample = sample; 254 255 DWORD status = 0; 256 hr = mDecoder->ProcessOutput(0, 1, &output, &status); 257 // LOG(L"WMFH264Decoder::GetOutputSample() ProcessOutput returned 0x%x\n", 258 // hr); 259 CComPtr<IMFCollection> events = output.pEvents; // Ensure this is released. 260 261 if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { 262 // Type change. Probably geometric apperature change. 263 hr = SetDecoderOutputType(); 264 ENSURE(SUCCEEDED(hr), hr); 265 266 return GetOutputSample(aOutSample); 267 } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { 268 return MF_E_TRANSFORM_NEED_MORE_INPUT; 269 } 270 // Treat other errors as fatal. 271 ENSURE(SUCCEEDED(hr), hr); 272 273 assert(sample); 274 275 // output.pSample 276 *aOutSample = sample.Detach(); // AddRefs 277 return S_OK; 278 } 279 280 HRESULT 281 WMFH264Decoder::Input(const uint8_t* aData, uint32_t aDataSize, 282 Microseconds aTimestamp) { 283 HRESULT hr; 284 CComPtr<IMFSample> input = nullptr; 285 hr = CreateInputSample(aData, aDataSize, aTimestamp, &input); 286 ENSURE(SUCCEEDED(hr) && input != nullptr, hr); 287 288 hr = mDecoder->ProcessInput(0, input, 0); 289 if (hr == MF_E_NOTACCEPTING) { 290 // MFT *already* has enough data to produce a sample. Retrieve it. 291 LOG("ProcessInput returned MF_E_NOTACCEPTING\n"); 292 return MF_E_NOTACCEPTING; 293 } 294 ENSURE(SUCCEEDED(hr), hr); 295 296 return S_OK; 297 } 298 299 HRESULT 300 WMFH264Decoder::Output(IMFSample** aOutput) { 301 HRESULT hr; 302 CComPtr<IMFSample> outputSample; 303 hr = GetOutputSample(&outputSample); 304 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { 305 return MF_E_TRANSFORM_NEED_MORE_INPUT; 306 } 307 // Treat other errors as fatal. 308 ENSURE(SUCCEEDED(hr) && outputSample, hr); 309 310 *aOutput = outputSample.Detach(); 311 312 return S_OK; 313 } 314 315 HRESULT 316 WMFH264Decoder::Reset() { 317 HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH, 0); 318 ENSURE(SUCCEEDED(hr), hr); 319 320 return S_OK; 321 } 322 323 HRESULT 324 WMFH264Decoder::Drain() { 325 HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0); 326 ENSURE(SUCCEEDED(hr), hr); 327 328 return S_OK; 329 } 330 331 } // namespace wmf