blob: aa4b37e836053e9bb2cb9d060a9dc35389bb9b5b [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/webcodecs/plane.h"
#include <string.h>
#include "third_party/blink/renderer/platform/bindings/exception_code.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "ui/gfx/gpu_memory_buffer.h"
namespace blink {
namespace {
void CopyPlane(const uint8_t* plane,
size_t copy_size,
size_t trailing_zeros_size,
uint8_t* dest) {
// Copy plane bytes.
memcpy(dest, plane, copy_size);
// Zero trailing padding bytes.
memset(dest + copy_size, 0, trailing_zeros_size);
}
} // namespace
Plane::Plane(scoped_refptr<VideoFrameHandle> handle, size_t plane)
: handle_(std::move(handle)), plane_(plane) {
#if DCHECK_IS_ON()
// Validate the plane index, but only if the handle is valid.
auto local_frame = handle_->frame();
if (local_frame) {
DCHECK(local_frame->IsMappable() || local_frame->HasGpuMemoryBuffer());
DCHECK_LT(plane, local_frame->layout().num_planes());
}
#endif // DCHECK_IS_ON()
}
uint32_t Plane::stride() const {
auto local_frame = handle_->frame();
if (!local_frame)
return 0;
// TODO(sandersd): Consider returning row_bytes() instead. This would imply
// removing padding bytes in copyInto().
return local_frame->stride(plane_);
}
uint32_t Plane::rows() const {
auto local_frame = handle_->frame();
if (!local_frame)
return 0;
return local_frame->rows(plane_);
}
uint32_t Plane::length() const {
auto local_frame = handle_->frame();
if (!local_frame)
return 0;
// Note: this could be slightly larger than the actual data size. readInto()
// will pad with zeros.
return local_frame->rows(plane_) * local_frame->stride(plane_);
}
void Plane::readInto(MaybeShared<DOMArrayBufferView> dst,
ExceptionState& exception_state) {
auto local_frame = handle_->frame();
if (!local_frame) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Cannot read from closed VideoFrame.");
return;
}
// Note: these methods all return int.
size_t rows = local_frame->rows(plane_);
size_t row_bytes = local_frame->row_bytes(plane_);
size_t stride = local_frame->stride(plane_);
DCHECK_GT(rows, 0u); // should fail VideoFrame::IsValidConfig()
DCHECK_GT(row_bytes, 0u); // should fail VideoFrame::IsValidConfig()
DCHECK_GE(stride, row_bytes);
size_t total_size = rows * stride;
size_t trailing_zeros_size = stride - row_bytes;
size_t copy_size = total_size - trailing_zeros_size;
// Note: byteLength is zero if the buffer is detached.
uint8_t* base = static_cast<uint8_t*>(dst->BaseAddressMaybeShared());
if (!base) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Destination buffer is not valid.");
return;
}
if (total_size > dst->byteLength()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"Destination buffer is not large enough.");
return;
}
if (local_frame->IsMappable()) {
CopyPlane(local_frame->data(plane_), copy_size, trailing_zeros_size, base);
return;
}
DCHECK(local_frame->HasGpuMemoryBuffer());
auto* gmb = local_frame->GetGpuMemoryBuffer();
if (!gmb->Map()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Could not map video frame into memory.");
return;
}
CopyPlane(static_cast<const uint8_t*>(gmb->memory(plane_)), copy_size,
trailing_zeros_size, base);
gmb->Unmap();
}
} // namespace blink