tor-browser

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

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 }