#include "media/gpu/chromeos/oop_video_decoder.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/posix/eintr_wrapper.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "build/build_config.h"
#include "chromeos/components/cdm_factory_daemon/cdm_context_for_oopvd_impl.h"
#include "media/base/format_utils.h"
#include "media/base/media_switches.h"
#include "media/base/video_util.h"
#include "media/gpu/buffer_validation.h"
#include "media/gpu/chromeos/native_pixmap_frame_resource.h"
#include "media/gpu/chromeos/platform_video_frame_utils.h"
#include "media/gpu/chromeos/video_frame_resource.h"
#include "media/gpu/macros.h"
#include "media/mojo/common/mojo_decoder_buffer_converter.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#if BUILDFLAG(USE_VAAPI)
#include "media/gpu/vaapi/vaapi_wrapper.h"
#endif
namespace media {
namespace {
constexpr size_t kTimestampCacheSize = 128;
scoped_refptr<FrameResource> CreateDecodedFrameResource(
const VideoFrameLayout& layout,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
std::vector<base::ScopedFD> dmabuf_fds,
base::TimeDelta timestamp,
const VideoFrameMetadata& metadata,
const gfx::ColorSpace& color_space,
const std::optional<gfx::HDRMetadata>& hdr_metadata) {
scoped_refptr<media::NativePixmapFrameResource> native_pixmap_frame =
NativePixmapFrameResource::Create(layout, visible_rect, natural_size,
std::move(dmabuf_fds), timestamp);
if (!native_pixmap_frame) {
VLOGF(2) << "Could not create a NativePixmap-backed FrameResource";
return nullptr;
}
native_pixmap_frame->set_metadata(metadata);
native_pixmap_frame->set_color_space(color_space);
native_pixmap_frame->set_hdr_metadata(hdr_metadata);
return native_pixmap_frame;
}
class OOPVideoDecoderSupportedConfigsManager {
public:
static OOPVideoDecoderSupportedConfigsManager& Instance() {
static base::NoDestructor<OOPVideoDecoderSupportedConfigsManager> instance;
return *instance;
}
std::optional<SupportedVideoDecoderConfigs> Get() {
base::AutoLock lock(lock_);
return configs_;
}
VideoDecoderType GetDecoderType() {
base::AutoLock lock(lock_);
CHECK(decoder_type_.has_value());
return *decoder_type_;
}
uint32_t GetInterfaceVersion() {
base::AutoLock lock(lock_);
CHECK(interface_version_.has_value());
return *interface_version_;
}
void NotifySupportKnown(
mojo::PendingRemote<mojom::VideoDecoder> oop_video_decoder,
base::OnceCallback<void(mojo::PendingRemote<mojom::VideoDecoder>)> cb) {
base::ReleasableAutoLock lock(&lock_);
if ((configs_ && interface_version_) || disconnected_) {
lock.Release();
std::move(cb).Run(std::move(oop_video_decoder));
return;
} else if (!waiting_callbacks_.empty()) {
waiting_callbacks_.emplace(
std::move(oop_video_decoder), std::move(cb),
base::SequencedTaskRunner::GetCurrentDefault());
return;
}
CHECK(!configs_.has_value() && !interface_version_.has_value());
oop_video_decoder_.Bind(std::move(oop_video_decoder));
oop_video_decoder_.set_disconnect_handler(base::BindOnce(
&OOPVideoDecoderSupportedConfigsManager::OnDecoderDisconnected,
base::Unretained(this)));
oop_video_decoder_.QueryVersion(base::BindOnce(
&OOPVideoDecoderSupportedConfigsManager::OnGetInterfaceVersion,
base::Unretained(this)));
oop_video_decoder_->GetSupportedConfigs(base::BindOnce(
&OOPVideoDecoderSupportedConfigsManager::OnGetSupportedConfigs,
base::Unretained(this)));
waiting_callbacks_.emplace(mojo::PendingRemote<mojom::VideoDecoder>(),
std::move(cb),
base::SequencedTaskRunner::GetCurrentDefault());
}
void ResetForTesting() {
base::AutoLock lock(lock_);
oop_video_decoder_.reset();
disconnected_ = false;
configs_.reset();
decoder_type_.reset();
interface_version_.reset();
config_retry_count_ = 0u;
while (!waiting_callbacks_.empty()) {
waiting_callbacks_.pop();
}
}
private:
friend class base::NoDestructor<OOPVideoDecoderSupportedConfigsManager>;
OOPVideoDecoderSupportedConfigsManager() = default;
~OOPVideoDecoderSupportedConfigsManager() = default;
void OnDecoderDisconnected() {
base::AutoLock lock(lock_);
configs_.emplace();
decoder_type_ = std::nullopt;
interface_version_ = std::nullopt;
disconnected_ = true;
MaybeNotifyWaitingCallbacks();
}
void OnGetInterfaceVersion(uint32_t interface_version) {
base::AutoLock lock(lock_);
DCHECK(!interface_version_);
CHECK(!disconnected_);
interface_version_ = interface_version;
MaybeNotifyWaitingCallbacks();
}
void GetSupportedConfigs() {
base::AutoLock lock(lock_);
if (!disconnected_) {
oop_video_decoder_->GetSupportedConfigs(base::BindOnce(
&OOPVideoDecoderSupportedConfigsManager::OnGetSupportedConfigs,
base::Unretained(this)));
}
}
void OnGetSupportedConfigs(const SupportedVideoDecoderConfigs& configs,
VideoDecoderType decoder_type) {
base::AutoLock lock(lock_);
DCHECK(!configs_);
DCHECK(!decoder_type_);
CHECK(!disconnected_);
constexpr uint32_t kMaxConfigRetries = 20;
if (decoder_type == VideoDecoderType::kVda ||
decoder_type == VideoDecoderType::kVaapi ||
decoder_type == VideoDecoderType::kV4L2) {
if (configs.empty() && config_retry_count_ < kMaxConfigRetries) {
VLOGF(1) << "OOPVD failed getting configs, retry after delay";
config_retry_count_++;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
&OOPVideoDecoderSupportedConfigsManager::GetSupportedConfigs,
base::Unretained(this)),
base::Milliseconds(250));
return;
}
configs_ = configs;
decoder_type_ = decoder_type;
} else {
configs_.emplace();
}
MaybeNotifyWaitingCallbacks();
}
void MaybeNotifyWaitingCallbacks() EXCLUSIVE_LOCKS_REQUIRED(lock_) {
if (!disconnected_ &&
(!configs_.has_value() || !interface_version_.has_value())) {
return;
}
CHECK(!disconnected_ || (configs_.has_value() && configs_->empty()));
while (!waiting_callbacks_.empty()) {
WaitingCallbackContext waiting_callback =
std::move(waiting_callbacks_.front());
waiting_callbacks_.pop();
mojo::PendingRemote<mojom::VideoDecoder> oop_video_decoder =
waiting_callback.oop_video_decoder
? std::move(waiting_callback.oop_video_decoder)
: oop_video_decoder_.Unbind();
if (waiting_callback.cb_task_runner->RunsTasksInCurrentSequence()) {
base::AutoUnlock unlock(lock_);
std::move(waiting_callback.cb).Run(std::move(oop_video_decoder));
} else {
waiting_callback.cb_task_runner->PostTask(
FROM_HERE, base::BindOnce(std::move(waiting_callback.cb),
std::move(oop_video_decoder)));
}
}
}
base::Lock lock_;
mojo::Remote<mojom::VideoDecoder> oop_video_decoder_;
bool disconnected_ GUARDED_BY(lock_) = false;
std::optional<SupportedVideoDecoderConfigs> configs_ GUARDED_BY(lock_);
std::optional<VideoDecoderType> decoder_type_ GUARDED_BY(lock_);
std::optional<uint32_t> interface_version_ GUARDED_BY(lock_);
uint32_t config_retry_count_ GUARDED_BY(lock_) = 0;
struct WaitingCallbackContext {
WaitingCallbackContext(
mojo::PendingRemote<mojom::VideoDecoder> oop_video_decoder,
base::OnceCallback<void(mojo::PendingRemote<mojom::VideoDecoder>)> cb,
scoped_refptr<base::SequencedTaskRunner> cb_task_runner)
: oop_video_decoder(std::move(oop_video_decoder)),
cb(std::move(cb)),
cb_task_runner(std::move(cb_task_runner)) {}
mojo::PendingRemote<mojom::VideoDecoder> oop_video_decoder;
base::OnceCallback<void(mojo::PendingRemote<mojom::VideoDecoder>)> cb;
scoped_refptr<base::SequencedTaskRunner> cb_task_runner;
};
base::queue<WaitingCallbackContext> waiting_callbacks_ GUARDED_BY(lock_);
};
}
std::unique_ptr<VideoDecoderMixin> OOPVideoDecoder::Create(
mojo::PendingRemote<mojom::VideoDecoder> pending_remote_decoder,
std::unique_ptr<media::MediaLog> media_log,
scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
base::WeakPtr<VideoDecoderMixin::Client> client) {
return base::WrapUnique<VideoDecoderMixin>(new OOPVideoDecoder(
std::move(media_log), std::move(decoder_task_runner), std::move(client),
std::move(pending_remote_decoder)));
}
void OOPVideoDecoder::NotifySupportKnown(
mojo::PendingRemote<mojom::VideoDecoder> oop_video_decoder,
base::OnceCallback<void(mojo::PendingRemote<mojom::VideoDecoder>)> cb) {
OOPVideoDecoderSupportedConfigsManager::Instance().NotifySupportKnown(
std::move(oop_video_decoder), std::move(cb));
}
std::optional<SupportedVideoDecoderConfigs>
OOPVideoDecoder::GetSupportedConfigs() {
return OOPVideoDecoderSupportedConfigsManager::Instance().Get();
}
void OOPVideoDecoder::ResetGlobalStateForTesting() {
OOPVideoDecoderSupportedConfigsManager::Instance()
.ResetForTesting();
}
OOPVideoDecoder::OOPVideoDecoder(
std::unique_ptr<media::MediaLog> media_log,
scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
base::WeakPtr<VideoDecoderMixin::Client> client,
mojo::PendingRemote<mojom::VideoDecoder> pending_remote_decoder)
: VideoDecoderMixin(std::move(media_log),
std::move(decoder_task_runner),
std::move(client)),
fake_timestamp_to_real_timestamp_cache_(kTimestampCacheSize),
remote_decoder_(std::move(pending_remote_decoder)),
weak_this_factory_(this) {
VLOGF(2);
DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
remote_decoder_.set_disconnect_handler(
base::BindOnce(&OOPVideoDecoder::Stop, base::Unretained(this)));
mojo::ScopedDataPipeConsumerHandle remote_consumer_handle;
mojo_decoder_buffer_writer_ = MojoDecoderBufferWriter::Create(
GetDefaultDecoderBufferConverterCapacity(DemuxerStream::VIDEO),
&remote_consumer_handle);
CHECK(mojo_decoder_buffer_writer_);
DCHECK(!video_frame_handle_releaser_remote_.is_bound());
mojo::PendingReceiver<mojom::VideoFrameHandleReleaser>
video_frame_handle_releaser_receiver =
video_frame_handle_releaser_remote_.BindNewPipeAndPassReceiver();
video_frame_handle_releaser_remote_.set_disconnect_handler(
base::BindOnce(&OOPVideoDecoder::Stop, base::Unretained(this)));
DCHECK(!media_log_receiver_.is_bound());
CHECK(!has_error_);
remote_decoder_->Construct(client_receiver_.BindNewEndpointAndPassRemote(),
media_log_receiver_.BindNewPipeAndPassRemote(),
std::move(video_frame_handle_releaser_receiver),
std::move(remote_consumer_handle),
media::mojom::CommandBufferIdPtr(),
gfx::ColorSpace());
}
OOPVideoDecoder::~OOPVideoDecoder() {
VLOGF(2);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& pending_decode : pending_decodes_) {
decoder_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(pending_decode.second),
DecoderStatus::Codes::kAborted));
}
}
void OOPVideoDecoder::Initialize(const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
InitCB init_cb,
const PipelineOutputCB& output_cb,
const WaitingCB& waiting_cb) {
DVLOGF(2) << config.AsHumanReadableString();
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!init_cb_);
CHECK(!HasPendingDecodeCallbacks());
CHECK(!reset_cb_);
fake_timestamp_to_real_timestamp_cache_.Clear();
if (has_error_) {
std::move(init_cb).Run(DecoderStatus::Codes::kFailed);
return;
}
mojo::PendingRemote<mojom::CdmContextForOOPVD>
pending_remote_cdm_context_for_oopvd;
if (config.is_encrypted()) {
#if BUILDFLAG(IS_CHROMEOS)
DCHECK(!cdm_context_for_oopvd_ ||
cdm_context == cdm_context_for_oopvd_->cdm_context());
if (!cdm_context_for_oopvd_) {
if (!cdm_context || !cdm_context->GetChromeOsCdmContext()) {
std::move(init_cb).Run(
DecoderStatus::Codes::kUnsupportedEncryptionMode);
return;
}
cdm_context_for_oopvd_ =
std::make_unique<chromeos::CdmContextForOOPVDImpl>(cdm_context);
cdm_context_for_oopvd_receiver_ =
std::make_unique<mojo::Receiver<mojom::CdmContextForOOPVD>>(
cdm_context_for_oopvd_.get(),
pending_remote_cdm_context_for_oopvd
.InitWithNewPipeAndPassReceiver());
cdm_context_for_oopvd_receiver_->set_disconnect_handler(
base::BindOnce(&OOPVideoDecoder::Stop, base::Unretained(this)));
}
#else
std::move(init_cb).Run(DecoderStatus::Codes::kUnsupportedEncryptionMode);
return;
#endif
}
initialized_for_protected_content_ = config.is_encrypted();
needs_transcryption_ = false;
init_cb_ = std::move(init_cb);
output_cb_ = output_cb;
waiting_cb_ = waiting_cb;
remote_decoder_->Initialize(config, low_delay,
pending_remote_cdm_context_for_oopvd
? mojom::Cdm::NewCdmContext(std::move(
pending_remote_cdm_context_for_oopvd))
: nullptr,
base::BindOnce(&OOPVideoDecoder::OnInitializeDone,
weak_this_factory_.GetWeakPtr()));
}
void OOPVideoDecoder::OnInitializeDone(const DecoderStatus& status,
bool needs_bitstream_conversion,
int32_t max_decode_requests,
VideoDecoderType decoder_type,
bool needs_transcryption) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!has_error_);
if (max_decode_requests <= 0) {
Stop();
return;
}
const VideoDecoderType expected_decoder_type =
OOPVideoDecoderSupportedConfigsManager::Instance().GetDecoderType();
if (!status.is_ok() || decoder_type != expected_decoder_type ||
(remote_decoder_type_ != VideoDecoderType::kUnknown &&
remote_decoder_type_ != decoder_type)) {
Stop();
return;
}
needs_bitstream_conversion_ = needs_bitstream_conversion;
max_decode_requests_ = max_decode_requests;
remote_decoder_type_ = decoder_type;
needs_transcryption_ =
initialized_for_protected_content_ && needs_transcryption;
std::move(init_cb_).Run(status);
}
void OOPVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
DecodeCB decode_cb) {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!init_cb_);
CHECK(!reset_cb_);
CHECK(!is_flushing_);
if (has_error_ || remote_decoder_type_ == VideoDecoderType::kUnknown) {
DeferDecodeCallback(std::move(decode_cb),
DecoderStatus::Codes::kNotInitialized);
return;
}
if (decode_counter_ == std::numeric_limits<uint64_t>::max()) {
DeferDecodeCallback(std::move(decode_cb), DecoderStatus::Codes::kFailed);
return;
}
CHECK(buffer);
base::ScopedClosureRunner buffer_timestamp_restorer;
if (!buffer->end_of_stream()) {
const base::TimeDelta next_fake_timestamp =
current_fake_timestamp_ + base::Microseconds(1u);
if (next_fake_timestamp == current_fake_timestamp_) {
DeferDecodeCallback(std::move(decode_cb), DecoderStatus::Codes::kFailed);
return;
}
current_fake_timestamp_ = next_fake_timestamp;
DCHECK(
fake_timestamp_to_real_timestamp_cache_.Peek(current_fake_timestamp_) ==
fake_timestamp_to_real_timestamp_cache_.end());
fake_timestamp_to_real_timestamp_cache_.Put(current_fake_timestamp_,
buffer->timestamp());
buffer_timestamp_restorer.ReplaceClosure(base::BindOnce(
[](scoped_refptr<DecoderBuffer> decoder_buffer,
base::TimeDelta original_timestamp) {
decoder_buffer->set_timestamp(original_timestamp);
},
buffer, buffer->timestamp()));
buffer->set_timestamp(current_fake_timestamp_);
}
const uint64_t decode_id = decode_counter_++;
pending_decodes_.insert({decode_id, std::move(decode_cb)});
mojom::DecoderBufferPtr mojo_buffer =
mojo_decoder_buffer_writer_->WriteDecoderBuffer(buffer);
if (!mojo_buffer) {
Stop();
return;
}
is_flushing_ = buffer->end_of_stream();
remote_decoder_->Decode(
std::move(mojo_buffer),
base::BindOnce(&OOPVideoDecoder::OnDecodeDone,
weak_this_factory_.GetWeakPtr(), decode_id, is_flushing_));
}
void OOPVideoDecoder::OnDecodeDone(uint64_t decode_id,
bool is_flush_cb,
const DecoderStatus& status) {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!has_error_);
CHECK(!pending_decodes_.empty());
if (pending_decodes_.cbegin()->first != decode_id) {
VLOGF(2) << "Unexpected decode callback for request " << decode_id;
Stop();
return;
}
if (is_flush_cb) {
CHECK(is_flushing_);
CHECK_EQ(num_deferred_decode_cbs_, 0u);
if (pending_decodes_.size() != 1) {
VLOGF(2) << "Received a flush callback while having pending decodes";
Stop();
return;
}
fake_timestamp_to_real_timestamp_cache_.Clear();
is_flushing_ = false;
}
auto it = pending_decodes_.begin();
DecodeCB decode_cb = std::move(it->second);
pending_decodes_.erase(it);
std::move(decode_cb).Run(status);
}
void OOPVideoDecoder::DeferDecodeCallback(DecodeCB decode_cb,
const DecoderStatus& status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK_LT(num_deferred_decode_cbs_, std::numeric_limits<uint64_t>::max());
num_deferred_decode_cbs_++;
decoder_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&OOPVideoDecoder::CallDeferredDecodeCallback,
weak_this_factory_.GetWeakPtr(),
std::move(decode_cb), status));
}
void OOPVideoDecoder::CallDeferredDecodeCallback(DecodeCB decode_cb,
const DecoderStatus& status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(decode_cb).Run(status);
num_deferred_decode_cbs_--;
}
bool OOPVideoDecoder::HasPendingDecodeCallbacks() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return !pending_decodes_.empty() || num_deferred_decode_cbs_ > 0;
}
void OOPVideoDecoder::Reset(base::OnceClosure reset_cb) {
DVLOGF(2);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!init_cb_);
CHECK(!reset_cb_);
reset_cb_ = std::move(reset_cb);
if (has_error_ || remote_decoder_type_ == VideoDecoderType::kUnknown) {
decoder_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&OOPVideoDecoder::CallResetCallback,
weak_this_factory_.GetWeakPtr()));
return;
}
remote_decoder_->Reset(base::BindOnce(&OOPVideoDecoder::OnResetDone,
weak_this_factory_.GetWeakPtr()));
}
void OOPVideoDecoder::OnResetDone() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!has_error_);
CHECK(reset_cb_);
CHECK_EQ(num_deferred_decode_cbs_, 0u);
if (!pending_decodes_.empty()) {
VLOGF(2) << "Received a reset callback while having pending decodes";
Stop();
return;
}
fake_timestamp_to_real_timestamp_cache_.Clear();
CallResetCallback();
}
void OOPVideoDecoder::CallResetCallback() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(reset_cb_);
std::move(reset_cb_).Run();
}
void OOPVideoDecoder::Stop() {
VLOGF(2);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (has_error_)
return;
has_error_ = true;
weak_this_factory_.InvalidateWeakPtrs();
base::WeakPtr<OOPVideoDecoder> weak_this = weak_this_factory_.GetWeakPtr();
client_receiver_.reset();
media_log_receiver_.reset();
remote_decoder_.reset();
mojo_decoder_buffer_writer_.reset();
video_frame_handle_releaser_remote_.reset();
fake_timestamp_to_real_timestamp_cache_.Clear();
#if BUILDFLAG(IS_CHROMEOS)
cdm_context_for_oopvd_receiver_.reset();
cdm_context_for_oopvd_.reset();
#endif
if (init_cb_)
std::move(init_cb_).Run(DecoderStatus::Codes::kFailed);
if (!weak_this)
return;
for (auto& pending_decode : pending_decodes_) {
DeferDecodeCallback(std::move(pending_decode.second),
DecoderStatus::Codes::kFailed);
}
pending_decodes_.clear();
is_flushing_ = false;
if (reset_cb_) {
decoder_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&OOPVideoDecoder::CallResetCallback,
weak_this_factory_.GetWeakPtr()));
}
}
void OOPVideoDecoder::ReleaseVideoFrame(
const base::UnguessableToken& release_token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!has_error_);
CHECK(video_frame_handle_releaser_remote_.is_bound());
video_frame_handle_releaser_remote_->ReleaseVideoFrame(
release_token, {});
}
void OOPVideoDecoder::ApplyResolutionChange() {
NOTREACHED();
}
bool OOPVideoDecoder::NeedsBitstreamConversion() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!has_error_);
CHECK_NE(remote_decoder_type_, VideoDecoderType::kUnknown);
return needs_bitstream_conversion_;
}
bool OOPVideoDecoder::CanReadWithoutStalling() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!init_cb_);
CHECK(!has_error_);
return can_read_without_stalling_;
}
int OOPVideoDecoder::GetMaxDecodeRequests() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!has_error_);
CHECK_NE(remote_decoder_type_, VideoDecoderType::kUnknown);
return base::strict_cast<int>(max_decode_requests_);
}
VideoDecoderType OOPVideoDecoder::GetDecoderType() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!init_cb_);
CHECK(!reset_cb_);
return VideoDecoderType::kOutOfProcess;
}
bool OOPVideoDecoder::IsPlatformDecoder() const {
NOTREACHED();
}
bool OOPVideoDecoder::NeedsTranscryption() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return needs_transcryption_;
}
void OOPVideoDecoder::OnVideoFrameDecoded(
const scoped_refptr<VideoFrame>& frame,
bool can_read_without_stalling,
const std::optional<base::UnguessableToken>& release_token) {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!has_error_);
if (init_cb_) {
VLOGF(2) << "Received a decoded frame while waiting for initialization";
Stop();
return;
}
if (frame->metadata().end_of_stream) {
VLOGF(2) << "Unexpectedly received an EOS frame";
Stop();
return;
}
if (!frame->metadata().allow_overlay) {
VLOGF(2) << "Unexpectedly received a frame with allow_overlay = false";
Stop();
return;
}
if (!frame->metadata().read_lock_fences_enabled) {
VLOGF(2) << "Unexpectedly received a frame with read_lock_fences_enabled ="
" false";
Stop();
return;
}
if (!frame->metadata().power_efficient) {
VLOGF(2) << "Unexpectedly received a frame with power_efficient = false";
Stop();
return;
}
if (frame->metadata().hw_protected && !frame->metadata().protected_video) {
VLOGF(2) << "Unexpectedly received a frame with hw_protected = true but "
"protected_video = false";
Stop();
return;
}
VideoFrameMetadata metadata_to_propagate;
metadata_to_propagate.allow_overlay = true;
metadata_to_propagate.end_of_stream = false;
metadata_to_propagate.read_lock_fences_enabled = true;
metadata_to_propagate.protected_video = frame->metadata().protected_video;
metadata_to_propagate.hw_protected = frame->metadata().hw_protected;
metadata_to_propagate.needs_detiling = frame->metadata().needs_detiling;
metadata_to_propagate.power_efficient = true;
if (!release_token.has_value()) {
VLOGF(2) << "Did not receive a valid release token";
Stop();
return;
}
static const bool extract_shared_image =
base::FeatureList::IsEnabled(kUseSharedImageInOOPVDProcess);
std::vector<base::ScopedFD> duped_fds;
if (extract_shared_image) {
if (frame->storage_type() != VideoFrame::STORAGE_OPAQUE) {
VLOGF(2) << "Received a frame with an unexpected storage type";
Stop();
return;
}
if (!frame->HasSharedImage()) {
VLOGF(2) << "Received a frame without shared image";
Stop();
return;
}
const size_t num_fds = frame->NumDmabufFds();
if (num_fds > 0) {
VLOGF(2) << "Received a frame with DMA buffer FD's";
Stop();
return;
}
} else {
if (frame->storage_type() != VideoFrame::STORAGE_DMABUFS) {
VLOGF(2) << "Received a frame with an unexpected storage type";
Stop();
return;
}
CHECK(gfx::Rect(frame->coded_size()).Contains(frame->visible_rect()));
const size_t num_fds = frame->NumDmabufFds();
if (0 == num_fds) {
VLOGF(2) << "Received a frame with zero DMA buffer FD's";
Stop();
return;
}
duped_fds.reserve(num_fds);
for (size_t i = 0; i < num_fds; ++i) {
if (frame->GetDmabufFd(i) < 0) {
VLOGF(2) << "Received at least one invalid FD";
Stop();
return;
}
duped_fds.emplace_back(HANDLE_EINTR(dup(frame->GetDmabufFd(i))));
if (!duped_fds.back().is_valid()) {
VLOGF(2) << "Failed to dup() an FD";
Stop();
return;
}
}
}
CHECK(gfx::Rect(frame->coded_size()).Contains(frame->visible_rect()));
const base::TimeDelta fake_timestamp = frame->timestamp();
auto it = fake_timestamp_to_real_timestamp_cache_.Get(fake_timestamp);
if (it == fake_timestamp_to_real_timestamp_cache_.end()) {
VLOGF(2) << "Received an unexpected decoded frame";
Stop();
return;
}
const base::TimeDelta real_timestamp = it->second;
if (!frame->metadata().tracking_token.has_value() ||
frame->metadata().tracking_token->is_empty()) {
VLOGF(2) << "Received a frame with a missing or invalid tracking token";
Stop();
return;
}
if (!initialized_for_protected_content_ &&
(metadata_to_propagate.protected_video ||
metadata_to_propagate.hw_protected)) {
VLOGF(2) << "Received a frame with unexpected metadata from a decoder that "
"was not configured for protected content";
Stop();
return;
}
if (initialized_for_protected_content_ &&
(!metadata_to_propagate.protected_video ||
!metadata_to_propagate.hw_protected)) {
VLOGF(2) << "Received a frame with unexpected metadata from a decoder that "
"was configured for protected content";
Stop();
return;
}
scoped_refptr<FrameResource> wrapped_frame;
if (extract_shared_image) {
frame->set_timestamp(real_timestamp);
frame->AddDestructionObserver(base::BindPostTaskToCurrentDefault(
base::BindOnce(&OOPVideoDecoder::ReleaseVideoFrame,
weak_this_factory_.GetWeakPtr(), *release_token)));
wrapped_frame = VideoFrameResource::Create(std::move(frame));
} else {
const VideoPixelFormat format = frame->format();
const gfx::Size coded_size = frame->coded_size();
const gfx::Rect visible_rect = frame->visible_rect();
const gfx::Size natural_size = frame->natural_size();
const gfx::ColorSpace color_space = frame->ColorSpace();
const std::optional<gfx::HDRMetadata> hdr_metadata = frame->hdr_metadata();
const VideoFrameMetadata metadata = metadata_to_propagate;
const base::UnguessableToken received_tracking_token =
*frame->metadata().tracking_token;
if (!received_token_to_decoded_frame_map_.empty()) {
const VideoPixelFormat current_format =
received_token_to_decoded_frame_map_.cbegin()->second->format();
const gfx::Size& current_coded_size =
received_token_to_decoded_frame_map_.cbegin()->second->coded_size();
const gfx::Size& current_visible_rect_size_from_origin =
GetRectSizeFromOrigin(received_token_to_decoded_frame_map_.cbegin()
->second->visible_rect());
const bool currently_uses_protected =
received_token_to_decoded_frame_map_.cbegin()
->second->metadata()
.hw_protected;
if (format != current_format || coded_size != current_coded_size ||
GetRectSizeFromOrigin(visible_rect) !=
current_visible_rect_size_from_origin ||
metadata.hw_protected != currently_uses_protected) {
received_token_to_decoded_frame_map_.clear();
generated_token_to_decoded_frame_map_.clear();
}
}
scoped_refptr<FrameResource> frame_to_wrap;
auto decoded_frame_it =
received_token_to_decoded_frame_map_.find(received_tracking_token);
if (decoded_frame_it != received_token_to_decoded_frame_map_.end()) {
frame_to_wrap = decoded_frame_it->second;
CHECK_EQ(frame_to_wrap->format(), format);
CHECK_EQ(frame_to_wrap->coded_size(), coded_size);
CHECK_EQ(GetRectSizeFromOrigin(frame_to_wrap->visible_rect()),
GetRectSizeFromOrigin(visible_rect));
CHECK_EQ(frame_to_wrap->metadata().hw_protected, metadata.hw_protected);
} else {
scoped_refptr<FrameResource> native_pixmap_frame =
CreateDecodedFrameResource(
frame->layout(), visible_rect, natural_size, std::move(duped_fds),
fake_timestamp, metadata, color_space, hdr_metadata);
if (!native_pixmap_frame) {
Stop();
return;
}
received_token_to_decoded_frame_map_[received_tracking_token] =
native_pixmap_frame;
generated_token_to_decoded_frame_map_[native_pixmap_frame
->tracking_token()] =
native_pixmap_frame.get();
frame_to_wrap = std::move(native_pixmap_frame);
}
wrapped_frame =
frame_to_wrap->CreateWrappingFrame(visible_rect, natural_size);
if (!wrapped_frame) {
VLOGF(2) << "Could not wrap the frame";
Stop();
return;
}
wrapped_frame->set_timestamp(real_timestamp);
wrapped_frame->set_color_space(color_space);
wrapped_frame->set_hdr_metadata(hdr_metadata);
wrapped_frame->set_metadata(metadata);
wrapped_frame->AddDestructionObserver(base::BindPostTaskToCurrentDefault(
base::BindOnce(&OOPVideoDecoder::ReleaseVideoFrame,
weak_this_factory_.GetWeakPtr(), *release_token)));
}
can_read_without_stalling_ = can_read_without_stalling;
if (output_cb_) {
output_cb_.Run(std::move(wrapped_frame));
}
}
void OOPVideoDecoder::OnWaiting(WaitingReason reason) {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!has_error_);
if (reason == WaitingReason::kNoCdm) {
VLOGF(2) << "Received an unexpected WaitingReason";
Stop();
return;
}
if (waiting_cb_)
waiting_cb_.Run(reason);
}
void OOPVideoDecoder::RequestOverlayInfo() {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void OOPVideoDecoder::AddLogRecord(const MediaLogRecord& event) {
VLOGF(2);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
FrameResource* OOPVideoDecoder::GetOriginalFrame(
const base::UnguessableToken& tracking_token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!tracking_token.is_empty());
auto it = generated_token_to_decoded_frame_map_.find(tracking_token);
return (it == generated_token_to_decoded_frame_map_.end()) ? nullptr
: it->second;
}
}