blob: f873ab350f077e404f1d263b6e6d62021a0b80e3 [file]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/mojo/services/stable_video_decoder_service.h"
#include "media/mojo/common/media_type_converters.h"
namespace media {
namespace {
stable::mojom::VideoFramePtr MediaVideoFrameToMojoVideoFrame(
scoped_refptr<VideoFrame> media_frame) {
CHECK(!media_frame->metadata().end_of_stream);
CHECK_EQ(media_frame->storage_type(),
media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER);
CHECK(media_frame->HasGpuMemoryBuffer());
stable::mojom::VideoFramePtr mojo_frame = stable::mojom::VideoFrame::New();
CHECK(mojo_frame);
static_assert(
std::is_same<decltype(media_frame->format()),
decltype(stable::mojom::VideoFrame::format)>::value,
"Unexpected type for media::VideoFrame::format(). If you "
"need to change this assertion, please contact "
"chromeos-gfx-video@google.com.");
mojo_frame->format = media_frame->format();
static_assert(
std::is_same<decltype(media_frame->coded_size()),
std::add_lvalue_reference<std::add_const<
decltype(stable::mojom::VideoFrame::coded_size)>::type>::
type>::value,
"Unexpected type for media::VideoFrame::coded_size(). If you "
"need to change this assertion, please contact "
"chromeos-gfx-video@google.com.");
mojo_frame->coded_size = media_frame->coded_size();
static_assert(
std::is_same<
decltype(media_frame->visible_rect()),
std::add_lvalue_reference<std::add_const<
decltype(stable::mojom::VideoFrame::visible_rect)>::type>::type>::
value,
"Unexpected type for media::VideoFrame::visible_rect(). If you "
"need to change this assertion, please contact "
"chromeos-gfx-video@google.com.");
mojo_frame->visible_rect = media_frame->visible_rect();
static_assert(
std::is_same<
decltype(media_frame->natural_size()),
std::add_lvalue_reference<std::add_const<
decltype(stable::mojom::VideoFrame::natural_size)>::type>::type>::
value,
"Unexpected type for media::VideoFrame::natural_size(). If you "
"need to change this assertion, please contact "
"chromeos-gfx-video@google.com.");
mojo_frame->natural_size = media_frame->natural_size();
static_assert(
std::is_same<decltype(media_frame->timestamp()),
decltype(stable::mojom::VideoFrame::timestamp)>::value,
"Unexpected type for media::VideoFrame::timestamp(). If you "
"need to change this assertion, please contact "
"chromeos-gfx-video@google.com.");
mojo_frame->timestamp = media_frame->timestamp();
gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle =
media_frame->GetGpuMemoryBuffer()->CloneHandle();
CHECK_EQ(gpu_memory_buffer_handle.type, gfx::NATIVE_PIXMAP);
CHECK(!gpu_memory_buffer_handle.native_pixmap_handle.planes.empty());
mojo_frame->gpu_memory_buffer_handle = std::move(gpu_memory_buffer_handle);
static_assert(
std::is_same<
decltype(media_frame->metadata()),
std::add_lvalue_reference<
decltype(stable::mojom::VideoFrame::metadata)>::type>::value,
"Unexpected type for media::VideoFrame::metadata(). If you "
"need to change this assertion, please contact "
"chromeos-gfx-video@google.com.");
mojo_frame->metadata = media_frame->metadata();
static_assert(
std::is_same<decltype(media_frame->ColorSpace()),
decltype(stable::mojom::VideoFrame::color_space)>::value,
"Unexpected type for media::VideoFrame::ColorSpace(). If you "
"need to change this assertion, please contact "
"chromeos-gfx-video@google.com.");
mojo_frame->color_space = media_frame->ColorSpace();
static_assert(
std::is_same<
decltype(media_frame->hdr_metadata()),
std::add_lvalue_reference<std::add_const<
decltype(stable::mojom::VideoFrame::hdr_metadata)>::type>::type>::
value,
"Unexpected type for media::VideoFrame::hdr_metadata(). If you "
"need to change this assertion, please contact "
"chromeos-gfx-video@google.com.");
mojo_frame->hdr_metadata = media_frame->hdr_metadata();
return mojo_frame;
}
} // namespace
StableVideoDecoderService::StableVideoDecoderService(
std::unique_ptr<mojom::VideoDecoder> dst_video_decoder,
MojoCdmServiceContext* cdm_service_context)
: video_decoder_client_receiver_(this),
media_log_receiver_(this),
stable_video_frame_handle_releaser_receiver_(this),
dst_video_decoder_(std::move(dst_video_decoder)),
dst_video_decoder_receiver_(dst_video_decoder_.get())
#if BUILDFLAG(IS_CHROMEOS_ASH)
,
cdm_service_context_(cdm_service_context)
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
{
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!!dst_video_decoder_);
dst_video_decoder_remote_.Bind(
dst_video_decoder_receiver_.BindNewPipeAndPassRemote());
}
StableVideoDecoderService::~StableVideoDecoderService() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (cdm_id_)
cdm_service_context_->UnregisterRemoteCdmContext(cdm_id_.value());
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
void StableVideoDecoderService::GetSupportedConfigs(
GetSupportedConfigsCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
dst_video_decoder_remote_->GetSupportedConfigs(std::move(callback));
}
void StableVideoDecoderService::Construct(
mojo::PendingAssociatedRemote<stable::mojom::VideoDecoderClient>
stable_video_decoder_client_remote,
mojo::PendingRemote<stable::mojom::MediaLog> stable_media_log_remote,
mojo::PendingReceiver<stable::mojom::VideoFrameHandleReleaser>
stable_video_frame_handle_releaser_receiver,
mojo::ScopedDataPipeConsumerHandle decoder_buffer_pipe,
const gfx::ColorSpace& target_color_space) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (video_decoder_client_receiver_.is_bound()) {
mojo::ReportBadMessage("Construct() already called");
return;
}
DCHECK(!video_decoder_client_receiver_.is_bound());
DCHECK(!stable_video_decoder_client_remote_.is_bound());
stable_video_decoder_client_remote_.Bind(
std::move(stable_video_decoder_client_remote));
DCHECK(!media_log_receiver_.is_bound());
DCHECK(!stable_media_log_remote_.is_bound());
stable_media_log_remote_.Bind(std::move(stable_media_log_remote));
DCHECK(!video_frame_handle_releaser_remote_.is_bound());
DCHECK(!stable_video_frame_handle_releaser_receiver_.is_bound());
stable_video_frame_handle_releaser_receiver_.Bind(
std::move(stable_video_frame_handle_releaser_receiver));
dst_video_decoder_remote_->Construct(
video_decoder_client_receiver_.BindNewEndpointAndPassRemote(),
media_log_receiver_.BindNewPipeAndPassRemote(),
video_frame_handle_releaser_remote_.BindNewPipeAndPassReceiver(),
std::move(decoder_buffer_pipe), mojom::CommandBufferIdPtr(),
target_color_space);
}
void StableVideoDecoderService::Initialize(
const VideoDecoderConfig& config,
bool low_delay,
mojo::PendingRemote<stable::mojom::StableCdmContext> cdm_context,
InitializeCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!video_decoder_client_receiver_.is_bound()) {
DVLOG(2) << __func__ << " Construct() must be called first";
std::move(callback).Run(DecoderStatus::Codes::kFailedToCreateDecoder,
/*needs_bitstream_conversion=*/false,
/*max_decode_requests=*/1,
VideoDecoderType::kUnknown);
return;
}
// The |config| should have been validated at deserialization time.
DCHECK(config.IsValidConfig());
if (config.is_encrypted()) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (!cdm_id_) {
if (!cdm_context) {
std::move(callback).Run(DecoderStatus::Codes::kMissingCDM,
/*needs_bitstream_conversion=*/false,
/*max_decode_requests=*/1,
VideoDecoderType::kUnknown);
return;
}
remote_cdm_context_ = base::WrapRefCounted(
new chromeos::RemoteCdmContext(std::move(cdm_context)));
cdm_id_ = cdm_service_context_->RegisterRemoteCdmContext(
remote_cdm_context_.get());
}
#else
std::move(callback).Run(DecoderStatus::Codes::kUnsupportedConfig,
/*needs_bitstream_conversion=*/false,
/*max_decode_requests=*/1,
VideoDecoderType::kUnknown);
return;
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
// Even though this is in-process, we still need to pass a |cdm_id_|
// instead of a media::CdmContext* since this goes through Mojo IPC. This is
// why we need to register with the |cdm_service_context_| above.
dst_video_decoder_remote_->Initialize(config, low_delay, cdm_id_,
std::move(callback));
}
void StableVideoDecoderService::Decode(
const scoped_refptr<DecoderBuffer>& buffer,
DecodeCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!video_decoder_client_receiver_.is_bound()) {
DVLOG(2) << __func__ << " Construct() must be called first";
std::move(callback).Run(DecoderStatus::Codes::kFailedToCreateDecoder);
return;
}
CHECK(buffer);
mojom::DecoderBufferPtr mojo_buffer = mojom::DecoderBuffer::From(*buffer);
CHECK(mojo_buffer);
dst_video_decoder_remote_->Decode(std::move(mojo_buffer),
std::move(callback));
}
void StableVideoDecoderService::Reset(ResetCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!video_decoder_client_receiver_.is_bound()) {
DVLOG(2) << __func__ << " Construct() must be called first";
std::move(callback).Run();
return;
}
dst_video_decoder_remote_->Reset(std::move(callback));
}
void StableVideoDecoderService::ReleaseVideoFrame(
const base::UnguessableToken& release_token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(video_frame_handle_releaser_remote_.is_bound());
// Note: we don't pass a gpu::SyncToken because it's assumed that the client
// (the GPU process) has already waited on the SyncToken that comes from the
// ultimate client (the renderer process) before calling ReleaseVideoFrame()
// on the out-of-process video decoder.
video_frame_handle_releaser_remote_->ReleaseVideoFrame(
release_token, /*release_sync_token=*/absl::nullopt);
}
void StableVideoDecoderService::OnVideoFrameDecoded(
const scoped_refptr<VideoFrame>& frame,
bool can_read_without_stalling,
const absl::optional<base::UnguessableToken>& release_token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(stable_video_decoder_client_remote_.is_bound());
DCHECK(release_token.has_value());
// The mojo traits have been coded assuming these conditions.
CHECK(frame->metadata().allow_overlay);
CHECK(!frame->metadata().end_of_stream);
CHECK(frame->metadata().power_efficient);
stable_video_decoder_client_remote_->OnVideoFrameDecoded(
MediaVideoFrameToMojoVideoFrame(std::move(frame)),
can_read_without_stalling, *release_token);
}
void StableVideoDecoderService::OnWaiting(WaitingReason reason) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(stable_video_decoder_client_remote_.is_bound());
stable_video_decoder_client_remote_->OnWaiting(reason);
}
void StableVideoDecoderService::RequestOverlayInfo(
bool restart_for_transitions) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
NOTREACHED();
}
void StableVideoDecoderService::AddLogRecord(const MediaLogRecord& event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(stable_media_log_remote_.is_bound());
stable_media_log_remote_->AddLogRecord(event);
}
} // namespace media