1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143

media / gpu / windows / d3d_video_decoder_wrapper.cc [blame]

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "media/gpu/windows/d3d_video_decoder_wrapper.h"

#include <dxva.h>

#include "base/strings/string_number_conversions.h"
#include "media/base/media_log.h"

namespace media {

D3DVideoDecoderWrapper::D3DVideoDecoderWrapper(MediaLog* media_log)
    : media_log_(media_log) {}

D3DVideoDecoderWrapper::~D3DVideoDecoderWrapper() = default;

ScopedRandomAccessD3DInputBuffer
D3DVideoDecoderWrapper::GetPictureParametersBuffer(uint32_t desired_size) {
  DCHECK(!HasPendingBuffer(BufferType::kPictureParameters));
  return ScopedRandomAccessD3DInputBuffer(
      GetBuffer(BufferType::kPictureParameters, desired_size));
}

ScopedRandomAccessD3DInputBuffer
D3DVideoDecoderWrapper::GetInverseQuantizationMatrixBuffer(
    uint32_t desired_size) {
  DCHECK(!HasPendingBuffer(BufferType::kInverseQuantizationMatrix));
  return ScopedRandomAccessD3DInputBuffer(
      GetBuffer(BufferType::kInverseQuantizationMatrix, desired_size));
}

ScopedRandomAccessD3DInputBuffer D3DVideoDecoderWrapper::GetSliceControlBuffer(
    uint32_t desired_size) {
  DCHECK(!HasPendingBuffer(BufferType::kSliceControl));
  return ScopedRandomAccessD3DInputBuffer(
      GetBuffer(BufferType::kSliceControl, desired_size));
}

ScopedSequenceD3DInputBuffer& D3DVideoDecoderWrapper::GetBitstreamBuffer(
    uint32_t desired_size) {
  // Reuse the bitstream buffer before the slices are submitted.
  if (!bitstream_buffer_) {
    bitstream_buffer_.emplace(GetBuffer(BufferType::kBitstream, desired_size));
  }
  return bitstream_buffer_.value();
}

template <typename DXVASliceData>
bool D3DVideoDecoderWrapper::AppendBitstreamAndSliceDataWithStartCode(
    base::span<const uint8_t> bitstream,
    base::span<const uint8_t> start_code) {
  // Ideally all slices in a frame are put in the same bitstream buffer.
  // However the bitstream buffer may not fit all the data, so split on the
  // necessary boundaries.
  const size_t total_size = start_code.size() + bitstream.size();
  if (GetBitstreamBuffer(total_size).empty()) {
    return false;
  }

  // GetBitstreamBuffer() will allocate `bitstream_buffer_` which is easier to
  // use directly below due to the complexities of partial submissions.
  DCHECK(bitstream_buffer_);
  DCHECK(!bitstream_buffer_->empty());

  size_t data_offset = 0;
  while (data_offset < bitstream.size()) {
    uint32_t bytes_submitted = 0;
    uint32_t buffer_offset = bitstream_buffer_->BytesWritten();

    const bool contains_start = data_offset == 0;
    if (contains_start && start_code.size() > 0) {
      if (bitstream_buffer_->BytesAvailable() < start_code.size()) {
        if (!SubmitAndGetBitstreamBuffer(total_size)) {
          return false;
        }
        buffer_offset = bytes_submitted = 0u;
      }
      bytes_submitted += bitstream_buffer_->Write(start_code);
    }

    if (bitstream_buffer_->BytesAvailable() == 0) {
      if (!SubmitAndGetBitstreamBuffer(total_size - data_offset)) {
        return false;
      }
      buffer_offset = bytes_submitted = 0u;
    }

    const auto data_bytes_submitted =
        bitstream_buffer_->Write(bitstream.subspan(data_offset));
    bytes_submitted += data_bytes_submitted;
    data_offset += data_bytes_submitted;
    const bool contains_end = data_offset == bitstream.size();
    DXVASliceData slice_info{
        .BSNALunitDataLocation = buffer_offset,
        .SliceBytesInBuffer = bytes_submitted,
        .wBadSliceChopping = static_cast<USHORT>((contains_start ? 0 : 2) |
                                                 (contains_end ? 0 : 1))};

    const uint8_t* byte_ptr = reinterpret_cast<const uint8_t*>(&slice_info);
    slice_info_bytes_.insert(slice_info_bytes_.end(), byte_ptr,
                             byte_ptr + sizeof(slice_info));
  }

  return true;
}

bool D3DVideoDecoderWrapper::SubmitAndGetBitstreamBuffer(size_t needed_size) {
  DCHECK(bitstream_buffer_);
  DCHECK(!bitstream_buffer_->empty());

  if (bitstream_buffer_->BytesAvailable() < needed_size && !SubmitSlice()) {
    return false;
  }

  if (GetBitstreamBuffer(needed_size).empty()) {
    return false;
  }

  CHECK_EQ(bitstream_buffer_->BytesWritten(), 0u);
  CHECK_GT(bitstream_buffer_->BytesAvailable(), 0u);
  return true;
}

template bool D3DVideoDecoderWrapper::AppendBitstreamAndSliceDataWithStartCode<
    DXVA_Slice_H264_Short>(base::span<const uint8_t> bitstream,
                           base::span<const uint8_t> start_code);

template bool D3DVideoDecoderWrapper::AppendBitstreamAndSliceDataWithStartCode<
    DXVA_Slice_HEVC_Short>(base::span<const uint8_t> bitstream,
                           base::span<const uint8_t> start_code);

template bool D3DVideoDecoderWrapper::AppendBitstreamAndSliceDataWithStartCode<
    DXVA_Slice_VPx_Short>(base::span<const uint8_t> bitstream,
                          base::span<const uint8_t> start_code);

}  // namespace media