blob: f777cadc961e449eec44bb0cd022cc5410fc3e29 [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 "media/gpu/chromeos/vd_video_decode_accelerator.h"
#include <memory>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "gpu/ipc/common/gpu_memory_buffer_support.h"
#include "media/base/format_utils.h"
#include "media/base/media_util.h"
#include "media/base/video_color_space.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
#include "media/base/video_frame_metadata.h"
#include "media/base/video_transformation.h"
#include "media/base/video_types.h"
#include "media/base/waiting.h"
#include "media/gpu/buffer_validation.h"
#include "media/gpu/chromeos/gpu_buffer_layout.h"
#include "media/gpu/macros.h"
#include "media/media_buildflags.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gl/gl_bindings.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
// gn check does not account for BUILDFLAG(), so including these headers will
// make gn check fail for builds other than ash-chrome. See gn help nogncheck
// for more information.
#include "chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.h" // nogncheck
#include "media/gpu/chromeos/secure_buffer.pb.h" // nogncheck
#include "third_party/cros_system_api/constants/cdm_oemcrypto.h" // nogncheck
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
namespace media {
namespace {
// VideoDecoder copies the timestamp from DecodeBuffer to its corresponding
// VideoFrame. However, VideoDecodeAccelerator uses bitstream ID to find the
// corresponding output picture. Therefore, we store bitstream ID at the
// timestamp field. These two functions are used for converting between
// bitstream ID and fake timestamp.
base::TimeDelta BitstreamIdToFakeTimestamp(int32_t bitstream_id) {
return base::Milliseconds(bitstream_id);
}
int32_t FakeTimestampToBitstreamId(base::TimeDelta timestamp) {
return static_cast<int32_t>(timestamp.InMilliseconds());
}
std::vector<ColorPlaneLayout> ExtractColorPlaneLayout(
const gfx::GpuMemoryBufferHandle& gmb_handle) {
std::vector<ColorPlaneLayout> planes;
for (const auto& plane : gmb_handle.native_pixmap_handle.planes)
planes.emplace_back(plane.stride, plane.offset, plane.size);
return planes;
}
// TODO(akahuang): Move this function to a utility file.
template <class T>
std::string VectorToString(const std::vector<T>& vec) {
std::ostringstream result;
std::string delim;
result << "[";
for (auto& v : vec) {
result << delim << v;
if (delim.size() == 0)
delim = ", ";
}
result << "]";
return result.str();
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
scoped_refptr<DecoderBuffer> DecryptBitstreamBuffer(
BitstreamBuffer bitstream_buffer) {
// Check to see if we have our secure buffer tag and then extract the
// decrypt parameters.
auto mem_region = base::UnsafeSharedMemoryRegion::Deserialize(
bitstream_buffer.DuplicateRegion());
if (!mem_region.IsValid()) {
DVLOG(2) << "Invalid shared memory region";
return nullptr;
}
const size_t available_size =
mem_region.GetSize() -
base::checked_cast<size_t>(bitstream_buffer.offset());
auto mapping = mem_region.Map();
if (!mapping.IsValid()) {
DVLOG(2) << "Failed mapping shared memory";
return nullptr;
}
// Checks if this buffer contains the details needed for HW protected video
// decoding.
// The header is 1KB in size (cdm_oemcrypto::kSecureBufferHeaderSize).
// It consists of 3 components.
// 1. Marker tag - cdm_oemcrypto::kSecureBufferTag
// 2. unsigned 32-bit size of #3
// 3. Serialized ArcSecureBufferForChrome proto
uint8_t* data = mapping.GetMemoryAs<uint8_t>();
if (!data) {
DVLOG(2) << "Failed accessing shared memory";
return nullptr;
}
// Apply the offset here so we don't need to worry about page alignment in the
// mapping.
data += bitstream_buffer.offset();
if (available_size <= cdm_oemcrypto::kSecureBufferHeaderSize ||
memcmp(data, cdm_oemcrypto::kSecureBufferTag,
cdm_oemcrypto::kSecureBufferTagLen)) {
return nullptr;
}
VLOG(2) << "Detected secure buffer format in VDVDA";
// Read the protobuf size.
uint32_t proto_size = 0;
memcpy(&proto_size, data + cdm_oemcrypto::kSecureBufferTagLen,
sizeof(uint32_t));
if (proto_size > cdm_oemcrypto::kSecureBufferHeaderSize -
cdm_oemcrypto::kSecureBufferProtoOffset) {
DVLOG(2) << "Proto size goes beyond header size";
return nullptr;
}
// Read the serialized proto.
std::string serialized_proto(
data + cdm_oemcrypto::kSecureBufferProtoOffset,
data + cdm_oemcrypto::kSecureBufferProtoOffset + proto_size);
chromeos::cdm::ArcSecureBufferForChrome buffer_proto;
if (!buffer_proto.ParseFromString(serialized_proto)) {
DVLOG(2) << "Failed deserializing secure buffer proto";
return nullptr;
}
// Now extract the DecryptConfig info from the protobuf.
std::vector<media::SubsampleEntry> subsamples;
size_t buffer_size = 0;
for (const auto& subsample : buffer_proto.subsample()) {
buffer_size += subsample.clear_bytes() + subsample.cypher_bytes();
subsamples.emplace_back(subsample.clear_bytes(), subsample.cypher_bytes());
}
absl::optional<EncryptionPattern> pattern = absl::nullopt;
if (buffer_proto.has_pattern()) {
pattern.emplace(buffer_proto.pattern().cypher_bytes(),
buffer_proto.pattern().clear_bytes());
}
// Now create the DecryptConfig and set it in the decoder buffer.
scoped_refptr<DecoderBuffer> buffer = bitstream_buffer.ToDecoderBuffer(
cdm_oemcrypto::kSecureBufferHeaderSize, buffer_size);
if (!buffer) {
DVLOG(2) << "Secure buffer data goes beyond shared memory size";
return nullptr;
}
if (buffer_proto.encryption_scheme() !=
chromeos::cdm::ArcSecureBufferForChrome::NONE) {
buffer->set_decrypt_config(std::make_unique<DecryptConfig>(
buffer_proto.encryption_scheme() ==
chromeos::cdm::ArcSecureBufferForChrome::CBCS
? EncryptionScheme::kCbcs
: EncryptionScheme::kCenc,
buffer_proto.key_id(), buffer_proto.iv(), std::move(subsamples),
std::move(pattern)));
}
return buffer;
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
} // namespace
// static
std::unique_ptr<VideoDecodeAccelerator> VdVideoDecodeAccelerator::Create(
CreateVideoDecoderCb create_vd_cb,
Client* client,
const Config& config,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
std::unique_ptr<VideoDecodeAccelerator> vda(new VdVideoDecodeAccelerator(
std::move(create_vd_cb), std::move(task_runner)));
if (!vda->Initialize(config, client))
return nullptr;
return vda;
}
VdVideoDecodeAccelerator::VdVideoDecodeAccelerator(
CreateVideoDecoderCb create_vd_cb,
scoped_refptr<base::SequencedTaskRunner> client_task_runner)
: create_vd_cb_(std::move(create_vd_cb)),
client_task_runner_(std::move(client_task_runner)) {
VLOGF(2);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
weak_this_ = weak_this_factory_.GetWeakPtr();
}
void VdVideoDecodeAccelerator::Destroy() {
VLOGF(2);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
weak_this_factory_.InvalidateWeakPtrs();
// Because VdaVideoFramePool is blocked for this callback, we must call the
// callback before destroying.
if (notify_layout_changed_cb_)
std::move(notify_layout_changed_cb_)
.Run(CroStatus::Codes::kFailedToGetFrameLayout);
client_ = nullptr;
vd_.reset();
delete this;
}
VdVideoDecodeAccelerator::~VdVideoDecodeAccelerator() {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
}
bool VdVideoDecodeAccelerator::Initialize(const Config& config,
Client* client) {
VLOGF(2) << "config: " << config.AsHumanReadableString();
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
#if !BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
if (config.is_encrypted()) {
VLOGF(1) << "Encrypted streams are not supported";
return false;
}
#endif // !BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
if (config.output_mode != Config::OutputMode::IMPORT) {
VLOGF(1) << "Only IMPORT OutputMode is supported.";
return false;
}
if (!config.is_deferred_initialization_allowed) {
VLOGF(1) << "Only is_deferred_initialization_allowed is supported.";
return false;
}
// In case we are re-initializing for encrypted content.
if (!vd_) {
std::unique_ptr<VdaVideoFramePool> frame_pool =
std::make_unique<VdaVideoFramePool>(weak_this_, client_task_runner_);
vd_ = create_vd_cb_.Run(client_task_runner_, std::move(frame_pool),
std::make_unique<VideoFrameConverter>(),
std::make_unique<NullMediaLog>());
if (!vd_)
return false;
client_ = client;
}
media::CdmContext* cdm_context = nullptr;
#if BUILDFLAG(IS_CHROMEOS_ASH)
is_encrypted_ = config.is_encrypted();
if (is_encrypted_)
cdm_context = chromeos::ChromeOsCdmFactory::GetArcCdmContext();
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
VideoDecoderConfig vd_config(
VideoCodecProfileToVideoCodec(config.profile), config.profile,
VideoDecoderConfig::AlphaMode::kIsOpaque, config.container_color_space,
VideoTransformation(), config.initial_expected_coded_size,
gfx::Rect(config.initial_expected_coded_size),
config.initial_expected_coded_size, std::vector<uint8_t>(),
config.encryption_scheme);
auto init_cb =
base::BindOnce(&VdVideoDecodeAccelerator::OnInitializeDone, weak_this_);
auto output_cb =
base::BindRepeating(&VdVideoDecodeAccelerator::OnFrameReady, weak_this_);
vd_->Initialize(std::move(vd_config), false /* low_delay */, cdm_context,
std::move(init_cb), std::move(output_cb), WaitingCB());
return true;
}
void VdVideoDecodeAccelerator::OnInitializeDone(DecoderStatus status) {
DVLOGF(3) << "success: " << status.is_ok();
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DCHECK(client_);
client_->NotifyInitializationComplete(status);
}
void VdVideoDecodeAccelerator::Decode(BitstreamBuffer bitstream_buffer) {
const int32_t bitstream_id = bitstream_buffer.id();
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (is_encrypted_) {
scoped_refptr<DecoderBuffer> buffer =
DecryptBitstreamBuffer(std::move(bitstream_buffer));
// This happens in the error case.
if (!buffer) {
OnError(FROM_HERE, PLATFORM_FAILURE);
return;
}
Decode(std::move(buffer), bitstream_id);
return;
}
#endif // BUILFLAG(IS_CHROMEOS_ASH)
Decode(bitstream_buffer.ToDecoderBuffer(), bitstream_id);
}
void VdVideoDecodeAccelerator::Decode(scoped_refptr<DecoderBuffer> buffer,
int32_t bitstream_id) {
DVLOGF(4) << "bitstream_id:" << bitstream_id;
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DCHECK(vd_);
// Set timestamp field as bitstream buffer id, because we can only use
// timestamp field to find the corresponding output frames. Also, VDA doesn't
// care about timestamp.
buffer->set_timestamp(BitstreamIdToFakeTimestamp(bitstream_id));
vd_->Decode(std::move(buffer),
base::BindOnce(&VdVideoDecodeAccelerator::OnDecodeDone,
weak_this_, bitstream_id));
}
void VdVideoDecodeAccelerator::OnDecodeDone(int32_t bitstream_buffer_id,
DecoderStatus status) {
DVLOGF(4) << "status: " << status.group() << ":"
<< static_cast<int>(status.code());
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DCHECK(client_);
if (!status.is_ok() && status.code() != DecoderStatus::Codes::kAborted) {
OnError(FROM_HERE, PLATFORM_FAILURE);
return;
}
client_->NotifyEndOfBitstreamBuffer(bitstream_buffer_id);
}
void VdVideoDecodeAccelerator::OnFrameReady(scoped_refptr<VideoFrame> frame) {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DCHECK(frame);
DCHECK(client_);
absl::optional<Picture> picture = GetPicture(*frame);
if (!picture) {
VLOGF(1) << "Failed to get picture.";
OnError(FROM_HERE, PLATFORM_FAILURE);
return;
}
// Record that the picture is sent to the client.
auto it = picture_at_client_.find(picture->picture_buffer_id());
if (it == picture_at_client_.end()) {
// We haven't sent the buffer to the client. Set |num_sent| = 1;
picture_at_client_.emplace(picture->picture_buffer_id(),
std::make_pair(std::move(frame), 1));
} else {
// We already sent the buffer to the client (only happen when using VP9
// show_existing_frame feature). Increase |num_sent|;
++(it->second.second);
}
client_->PictureReady(*picture);
}
void VdVideoDecodeAccelerator::Flush() {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DCHECK(vd_);
vd_->Decode(
DecoderBuffer::CreateEOSBuffer(),
base::BindOnce(&VdVideoDecodeAccelerator::OnFlushDone, weak_this_));
}
void VdVideoDecodeAccelerator::OnFlushDone(DecoderStatus status) {
DVLOGF(3) << "status: " << static_cast<int>(status.code());
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DCHECK(client_);
switch (status.code()) {
case DecoderStatus::Codes::kOk:
client_->NotifyFlushDone();
break;
case DecoderStatus::Codes::kAborted:
// Do nothing.
break;
default:
OnError(FROM_HERE, PLATFORM_FAILURE);
break;
}
}
void VdVideoDecodeAccelerator::Reset() {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DCHECK(vd_);
if (is_resetting_) {
VLOGF(1) << "The previous Reset() has not finished yet, aborted.";
return;
}
is_resetting_ = true;
if (notify_layout_changed_cb_) {
std::move(notify_layout_changed_cb_).Run(CroStatus::Codes::kResetRequired);
import_frame_cb_.Reset();
}
vd_->Reset(
base::BindOnce(&VdVideoDecodeAccelerator::OnResetDone, weak_this_));
}
void VdVideoDecodeAccelerator::OnResetDone() {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DCHECK(client_);
DCHECK(is_resetting_);
is_resetting_ = false;
client_->NotifyResetDone();
}
void VdVideoDecodeAccelerator::RequestFrames(
const Fourcc& fourcc,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
size_t max_num_frames,
NotifyLayoutChangedCb notify_layout_changed_cb,
ImportFrameCb import_frame_cb) {
VLOGF(2);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DCHECK(client_);
DCHECK(!notify_layout_changed_cb_);
// Stop tracking currently-allocated pictures, otherwise the count will be
// corrupted as we import new frames with the same IDs as the old ones.
// The client should still have its own reference to the frame data, which
// will keep it valid for as long as it needs it.
picture_at_client_.clear();
notify_layout_changed_cb_ = std::move(notify_layout_changed_cb);
import_frame_cb_ = std::move(import_frame_cb);
// We need to check if Reset() was received before RequestFrames() so that we
// can unblock the frame pool in that case.
if (is_resetting_) {
std::move(notify_layout_changed_cb_).Run(CroStatus::Codes::kResetRequired);
import_frame_cb_.Reset();
return;
}
// After calling ProvidePictureBuffersWithVisibleRect(), the client might
// still send buffers with old coded size. We temporarily store at
// |pending_coded_size_|.
pending_coded_size_ = coded_size;
client_->ProvidePictureBuffersWithVisibleRect(
max_num_frames, fourcc.ToVideoPixelFormat(), 1 /* textures_per_buffer */,
coded_size, visible_rect, GL_TEXTURE_EXTERNAL_OES);
}
void VdVideoDecodeAccelerator::AssignPictureBuffers(
const std::vector<PictureBuffer>& buffers) {
VLOGF(2);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
// After AssignPictureBuffers() is called, the buffers sent from
// ImportBufferForPicture() should be with new coded size. Now we can update
// |coded_size_|.
coded_size_ = pending_coded_size_;
}
void VdVideoDecodeAccelerator::ImportBufferForPicture(
int32_t picture_buffer_id,
VideoPixelFormat pixel_format,
gfx::GpuMemoryBufferHandle gmb_handle) {
DVLOGF(4) << "picture_buffer_id: " << picture_buffer_id;
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
if (!import_frame_cb_)
return;
// The first imported picture after requesting buffers.
// |notify_layout_changed_cb_| must be called in this clause because it blocks
// VdaVideoFramePool.
if (notify_layout_changed_cb_) {
auto fourcc = Fourcc::FromVideoPixelFormat(pixel_format);
if (!fourcc) {
VLOGF(1) << "Failed to convert to Fourcc.";
import_frame_cb_.Reset();
std::move(notify_layout_changed_cb_)
.Run(CroStatus::Codes::kFailedToChangeResolution);
return;
}
CHECK(media::VerifyGpuMemoryBufferHandle(pixel_format, coded_size_,
gmb_handle));
const uint64_t modifier = gmb_handle.type == gfx::NATIVE_PIXMAP
? gmb_handle.native_pixmap_handle.modifier
: gfx::NativePixmapHandle::kNoModifier;
std::vector<ColorPlaneLayout> planes = ExtractColorPlaneLayout(gmb_handle);
layout_ = VideoFrameLayout::CreateWithPlanes(
pixel_format, coded_size_, planes,
VideoFrameLayout::kBufferAddressAlignment, modifier);
if (!layout_) {
VLOGF(1) << "Failed to create VideoFrameLayout. format: "
<< VideoPixelFormatToString(pixel_format)
<< ", coded_size: " << coded_size_.ToString()
<< ", planes: " << VectorToString(planes)
<< ", modifier: " << std::hex << modifier;
import_frame_cb_.Reset();
std::move(notify_layout_changed_cb_)
.Run(CroStatus::Codes::kFailedToChangeResolution);
return;
}
auto gb_layout =
GpuBufferLayout::Create(*fourcc, coded_size_, planes, modifier);
if (!gb_layout) {
VLOGF(1) << "Failed to create GpuBufferLayout. fourcc: "
<< fourcc->ToString()
<< ", coded_size: " << coded_size_.ToString()
<< ", planes: " << VectorToString(planes)
<< ", modifier: " << std::hex << modifier;
layout_ = absl::nullopt;
import_frame_cb_.Reset();
std::move(notify_layout_changed_cb_)
.Run(CroStatus::Codes::kFailedToChangeResolution);
return;
}
std::move(notify_layout_changed_cb_).Run(*gb_layout);
}
if (!layout_)
return;
CHECK(media::VerifyGpuMemoryBufferHandle(pixel_format, layout_->coded_size(),
gmb_handle));
auto buffer_format = VideoPixelFormatToGfxBufferFormat(pixel_format);
CHECK(buffer_format);
// Usage is SCANOUT_VDA_WRITE because we are just wrapping the dmabuf in a
// GpuMemoryBuffer. This buffer is just for decoding purposes, so having
// the dmabufs mmapped is not necessary.
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
gpu::GpuMemoryBufferSupport().CreateGpuMemoryBufferImplFromHandle(
std::move(gmb_handle), layout_->coded_size(), *buffer_format,
gfx::BufferUsage::SCANOUT_VDA_WRITE, base::NullCallback());
if (!gpu_memory_buffer) {
VLOGF(1) << "Failed to create GpuMemoryBuffer. format: "
<< gfx::BufferFormatToString(*buffer_format)
<< ", coded_size: " << layout_->coded_size().ToString();
return;
}
const gpu::MailboxHolder mailbox_holder[VideoFrame::kMaxPlanes] = {};
// VideoFrame::WrapVideoFrame() will check whether the updated visible_rect
// is sub rect of the original visible_rect. Therefore we set visible_rect
// as large as coded_size to guarantee this condition.
scoped_refptr<VideoFrame> origin_frame =
VideoFrame::WrapExternalGpuMemoryBuffer(
gfx::Rect(layout_->coded_size()), layout_->coded_size(),
std::move(gpu_memory_buffer), mailbox_holder, base::NullCallback(),
base::TimeDelta());
auto res = frame_id_to_picture_id_.emplace(
origin_frame->GetGpuMemoryBuffer()->GetId(), picture_buffer_id);
// The frame ID should not be inside the map before insertion.
DCHECK(res.second);
// |wrapped_frame| is used to keep |origin_frame| alive until everyone
// released |wrapped_frame|. Then GpuMemoryBufferId will be available at
// OnFrameReleased().
scoped_refptr<VideoFrame> wrapped_frame = VideoFrame::WrapVideoFrame(
origin_frame, origin_frame->format(), origin_frame->visible_rect(),
origin_frame->natural_size());
wrapped_frame->AddDestructionObserver(
base::BindOnce(&VdVideoDecodeAccelerator::OnFrameReleasedThunk,
weak_this_, client_task_runner_, std::move(origin_frame)));
// This should not happen - picture_at_client_ should either be initially
// empty, or be cleared as RequestFrames() is called. However for extra safety
// let's make sure the slot for the picture buffer ID is free, otherwise we
// might lose track of the reference count and keep frames out of the pool
// forever.
if (picture_at_client_.erase(picture_buffer_id) > 0) {
VLOGF(1) << "Picture " << picture_buffer_id
<< " still referenced, dropping it.";
}
import_frame_cb_.Run(std::move(wrapped_frame));
}
absl::optional<Picture> VdVideoDecodeAccelerator::GetPicture(
const VideoFrame& frame) {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
auto it = frame_id_to_picture_id_.find(frame.GetGpuMemoryBuffer()->GetId());
if (it == frame_id_to_picture_id_.end()) {
VLOGF(1) << "Failed to find the picture buffer id.";
return absl::nullopt;
}
int32_t picture_buffer_id = it->second;
int32_t bitstream_id = FakeTimestampToBitstreamId(frame.timestamp());
return absl::make_optional(Picture(picture_buffer_id, bitstream_id,
frame.visible_rect(), frame.ColorSpace(),
frame.metadata().allow_overlay));
}
// static
void VdVideoDecodeAccelerator::OnFrameReleasedThunk(
absl::optional<base::WeakPtr<VdVideoDecodeAccelerator>> weak_this,
scoped_refptr<base::SequencedTaskRunner> task_runner,
scoped_refptr<VideoFrame> origin_frame) {
DVLOGF(4);
DCHECK(weak_this);
task_runner->PostTask(
FROM_HERE, base::BindOnce(&VdVideoDecodeAccelerator::OnFrameReleased,
*weak_this, std::move(origin_frame)));
}
void VdVideoDecodeAccelerator::OnFrameReleased(
scoped_refptr<VideoFrame> origin_frame) {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
auto it =
frame_id_to_picture_id_.find(origin_frame->GetGpuMemoryBuffer()->GetId());
DCHECK(it != frame_id_to_picture_id_.end());
int32_t picture_buffer_id = it->second;
frame_id_to_picture_id_.erase(it);
client_->DismissPictureBuffer(picture_buffer_id);
}
void VdVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_buffer_id) {
DVLOGF(4) << "picture_buffer_id: " << picture_buffer_id;
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
auto it = picture_at_client_.find(picture_buffer_id);
if (it == picture_at_client_.end()) {
DVLOGF(3) << picture_buffer_id << " has already been dismissed, ignore.";
return;
}
size_t& num_sent = it->second.second;
DCHECK_NE(num_sent, 0u);
--num_sent;
// The count of calling VDA::ReusePictureBuffer() is the same as calling
// Client::PictureReady(). Now we could really reuse the buffer.
if (num_sent == 0)
picture_at_client_.erase(it);
}
void VdVideoDecodeAccelerator::OnError(base::Location location, Error error) {
LOG(ERROR) << "Failed at " << location.ToString()
<< ", error code: " << static_cast<int>(error);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
client_->NotifyError(error);
}
} // namespace media