123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 |
- // 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 "base/memory/unsafe_shared_memory_region.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 = 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)) {
- // This occurs in Intel implementations when we are in a clear portion.
- return bitstream_buffer.ToDecoderBuffer();
- }
- 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>(),
- /*oop_video_decoder=*/{});
- 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), base::DoNothing());
- 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
|