tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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, &currentLength);
    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