// 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