v4l2: Secure buffer allocation and attaching to DecoderBuffer
Adds allocation of secure buffers for each buffer in the V4L2 OUTPUT queue when in secure playback mode. This is an async call, so it happens during init of the queue and V4L2Decoder blocks invoking the init callback until all allocations have completed or failure occurs. The VideoDecoderPipeline will call into V4L2VideoDecoder to attach secure buffers to DecoderBuffers and also to release them when the DecoderBuffer is done using it. In a follow up CL, we will be submitting the corresponding secure buffers into the actual V4L2Queue. BUG=b:248610914 TEST=Secure playback works w/ full prototype on tomato Change-Id: Ia558f5d2ef4cb4c4769f445b96a240df36c66485 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4844421 Reviewed-by: Oksana Zhuravlova <oksamyt@chromium.org> Reviewed-by: Nathan Hebert <nhebert@chromium.org> Commit-Queue: Jeffrey Kardatzke <jkardatzke@google.com> Cr-Commit-Position: refs/heads/main@{#1193858}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
2980679ac9
commit
08ba128a15
@ -1,3 +1,4 @@
|
||||
include_rules = [
|
||||
"+chromeos/components/cdm_factory_daemon",
|
||||
"+mojo/public/cpp/platform",
|
||||
]
|
||||
|
@ -72,7 +72,8 @@ class V4L2QueueFactory {
|
||||
base::OnceClosure destroy_cb) {
|
||||
return new V4L2Queue(base::BindRepeating(&V4L2Device::Ioctl, dev),
|
||||
base::BindRepeating(&V4L2Device::SchedulePoll, dev),
|
||||
base::BindRepeating(&V4L2Device::Mmap, dev), type,
|
||||
base::BindRepeating(&V4L2Device::Mmap, dev),
|
||||
dev->get_secure_allocate_cb(), type,
|
||||
std::move(destroy_cb));
|
||||
}
|
||||
};
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "media/gpu/media_gpu_export.h"
|
||||
#include "media/gpu/v4l2/v4l2_device_poller.h"
|
||||
#include "media/gpu/v4l2/v4l2_queue.h"
|
||||
#include "media/gpu/v4l2/v4l2_utils.h"
|
||||
#include "media/video/video_decode_accelerator.h"
|
||||
#include "media/video/video_encode_accelerator.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
@ -229,6 +230,14 @@ class MEDIA_GPU_EXPORT V4L2Device
|
||||
// Set periodic keyframe placement (group of pictures length)
|
||||
bool SetGOPLength(uint32_t gop_length);
|
||||
|
||||
void set_secure_allocate_cb(
|
||||
AllocateSecureBufferAsCallback secure_allocate_cb) {
|
||||
secure_allocate_cb_ = secure_allocate_cb;
|
||||
}
|
||||
AllocateSecureBufferAsCallback get_secure_allocate_cb() {
|
||||
return secure_allocate_cb_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class base::RefCountedThreadSafe<V4L2Device>;
|
||||
// Vector of video device node paths and corresponding pixelformats supported
|
||||
@ -289,6 +298,9 @@ class MEDIA_GPU_EXPORT V4L2Device
|
||||
// Associates a v4l2_buf_type to its queue.
|
||||
base::flat_map<enum v4l2_buf_type, V4L2Queue*> queues_;
|
||||
|
||||
// Callback to use for allocating secure buffers.
|
||||
AllocateSecureBufferAsCallback secure_allocate_cb_;
|
||||
|
||||
SEQUENCE_CHECKER(client_sequence_checker_);
|
||||
};
|
||||
|
||||
|
@ -159,12 +159,14 @@ V4L2ExtCtrl::V4L2ExtCtrl(uint32_t id, int32_t val) : V4L2ExtCtrl(id) {
|
||||
// Also provides helper functions.
|
||||
class V4L2Buffer {
|
||||
public:
|
||||
static std::unique_ptr<V4L2Buffer> Create(const IoctlAsCallback& ioctl_cb,
|
||||
const MmapAsCallback& mmap_cb,
|
||||
enum v4l2_buf_type type,
|
||||
enum v4l2_memory memory,
|
||||
const struct v4l2_format& format,
|
||||
size_t buffer_id);
|
||||
static std::unique_ptr<V4L2Buffer> Create(
|
||||
const IoctlAsCallback& ioctl_cb,
|
||||
const MmapAsCallback& mmap_cb,
|
||||
const AllocateSecureBufferAsCallback& allocate_secure_cb,
|
||||
enum v4l2_buf_type type,
|
||||
enum v4l2_memory memory,
|
||||
const struct v4l2_format& format,
|
||||
size_t buffer_id);
|
||||
|
||||
V4L2Buffer(const V4L2Buffer&) = delete;
|
||||
V4L2Buffer& operator=(const V4L2Buffer&) = delete;
|
||||
@ -175,6 +177,10 @@ class V4L2Buffer {
|
||||
size_t GetMemoryUsage() const;
|
||||
const struct v4l2_buffer& v4l2_buffer() const { return v4l2_buffer_; }
|
||||
scoped_refptr<VideoFrame> GetVideoFrame();
|
||||
// Returns true upon successfully claiming the handle, false otherwise.
|
||||
bool ClaimSecureHandle();
|
||||
void ReleaseSecureHandle() { handle_claimed_ = false; }
|
||||
uint64_t GetSecureHandle() { return secure_handle_; }
|
||||
|
||||
private:
|
||||
V4L2Buffer(const IoctlAsCallback& ioctl_cb,
|
||||
@ -185,6 +191,7 @@ class V4L2Buffer {
|
||||
size_t buffer_id);
|
||||
bool Query();
|
||||
scoped_refptr<VideoFrame> CreateVideoFrame();
|
||||
void SecureBufferAllocated(base::ScopedFD secure_fd, uint64_t secure_handle);
|
||||
|
||||
const IoctlAsCallback ioctl_cb_;
|
||||
const MmapAsCallback mmap_cb_;
|
||||
@ -199,20 +206,38 @@ class V4L2Buffer {
|
||||
|
||||
struct v4l2_format format_;
|
||||
scoped_refptr<VideoFrame> video_frame_;
|
||||
base::ScopedFD secure_buffer_;
|
||||
uint64_t secure_handle_ = 0;
|
||||
bool handle_claimed_ = false;
|
||||
base::WeakPtrFactory<V4L2Buffer> weak_factory_{this};
|
||||
};
|
||||
|
||||
std::unique_ptr<V4L2Buffer> V4L2Buffer::Create(const IoctlAsCallback& ioctl_cb,
|
||||
const MmapAsCallback& mmap_cb,
|
||||
enum v4l2_buf_type type,
|
||||
enum v4l2_memory memory,
|
||||
const struct v4l2_format& format,
|
||||
size_t buffer_id) {
|
||||
std::unique_ptr<V4L2Buffer> V4L2Buffer::Create(
|
||||
const IoctlAsCallback& ioctl_cb,
|
||||
const MmapAsCallback& mmap_cb,
|
||||
const AllocateSecureBufferAsCallback& allocate_secure_cb,
|
||||
enum v4l2_buf_type type,
|
||||
enum v4l2_memory memory,
|
||||
const struct v4l2_format& format,
|
||||
size_t buffer_id) {
|
||||
// Not using std::make_unique because constructor is private.
|
||||
std::unique_ptr<V4L2Buffer> buffer(new V4L2Buffer(std::move(ioctl_cb),
|
||||
std::move(mmap_cb), type,
|
||||
memory, format, buffer_id));
|
||||
if (!buffer->Query()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return buffer->Query() ? std::move(buffer) : nullptr;
|
||||
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && allocate_secure_cb) {
|
||||
CHECK_EQ(memory, V4L2_MEMORY_DMABUF);
|
||||
// Invoke the callback for secure buffer allocation. We only use dmabufs for
|
||||
// the OUTPUT queue when doing secure playback.
|
||||
allocate_secure_cb.Run(buffer->v4l2_buffer_.m.planes[0].length,
|
||||
base::BindOnce(&V4L2Buffer::SecureBufferAllocated,
|
||||
buffer->weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
V4L2Buffer::V4L2Buffer(const IoctlAsCallback& ioctl_cb,
|
||||
@ -248,6 +273,24 @@ V4L2Buffer::~V4L2Buffer() {
|
||||
}
|
||||
}
|
||||
|
||||
bool V4L2Buffer::ClaimSecureHandle() {
|
||||
if (handle_claimed_) {
|
||||
return false;
|
||||
}
|
||||
handle_claimed_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void V4L2Buffer::SecureBufferAllocated(base::ScopedFD secure_fd,
|
||||
uint64_t secure_handle) {
|
||||
CHECK(secure_fd.is_valid());
|
||||
CHECK(secure_handle);
|
||||
secure_buffer_ = std::move(secure_fd);
|
||||
secure_handle_ = secure_handle;
|
||||
// Set the FD in the plane data.
|
||||
v4l2_buffer_.m.planes[0].m.fd = secure_buffer_.get();
|
||||
}
|
||||
|
||||
bool V4L2Buffer::Query() {
|
||||
int ret = ioctl_cb_.Run(VIDIOC_QUERYBUF, &v4l2_buffer_);
|
||||
if (ret) {
|
||||
@ -998,12 +1041,14 @@ size_t V4L2ReadableBuffer::BufferId() const {
|
||||
V4L2Queue::V4L2Queue(const IoctlAsCallback& ioctl_cb,
|
||||
const base::RepeatingClosure& schedule_poll_cb,
|
||||
const MmapAsCallback& mmap_cb,
|
||||
const AllocateSecureBufferAsCallback& allocate_secure_cb,
|
||||
enum v4l2_buf_type type,
|
||||
base::OnceClosure destroy_cb)
|
||||
: type_(type),
|
||||
ioctl_cb_(ioctl_cb),
|
||||
schedule_poll_cb_(schedule_poll_cb),
|
||||
mmap_cb_(mmap_cb),
|
||||
allocate_secure_cb_(allocate_secure_cb),
|
||||
destroy_cb_(std::move(destroy_cb)),
|
||||
weak_this_factory_(this) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
@ -1147,8 +1192,8 @@ size_t V4L2Queue::AllocateBuffers(size_t count,
|
||||
|
||||
// Now query all buffer information.
|
||||
for (size_t i = 0; i < reqbufs.count; i++) {
|
||||
auto buffer =
|
||||
V4L2Buffer::Create(ioctl_cb_, mmap_cb_, type_, memory_, *format, i);
|
||||
auto buffer = V4L2Buffer::Create(ioctl_cb_, mmap_cb_, allocate_secure_cb_,
|
||||
type_, memory_, *format, i);
|
||||
|
||||
if (!buffer) {
|
||||
if (!DeallocateBuffers()) {
|
||||
@ -1236,6 +1281,58 @@ class V4L2BufferRefFactory {
|
||||
}
|
||||
};
|
||||
|
||||
CroStatus::Or<uint64_t> V4L2Queue::GetFreeSecureHandle() {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
// No buffers allocated at the moment?
|
||||
if (!free_buffers_) {
|
||||
return CroStatus::Codes::kSecureBufferPoolEmpty;
|
||||
}
|
||||
|
||||
uint64_t sec_handle = 0;
|
||||
// Go through the free list and look up each buffer by its ID to see if it has
|
||||
// been claimed yet. If it's not claimed, claim and return that handle.
|
||||
std::vector<size_t> ids_to_return_to_pool;
|
||||
while (!sec_handle) {
|
||||
auto free_id = free_buffers_->GetFreeBuffer();
|
||||
if (!free_id.has_value()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Go through our buffer list to see if this one is claimed or not.
|
||||
ids_to_return_to_pool.emplace_back(free_id.value());
|
||||
for (auto& buf : buffers_) {
|
||||
if (buf->v4l2_buffer().index == free_id.value()) {
|
||||
if (buf->ClaimSecureHandle()) {
|
||||
sec_handle = buf->GetSecureHandle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto buf_id : ids_to_return_to_pool) {
|
||||
free_buffers_->ReturnBuffer(buf_id);
|
||||
}
|
||||
|
||||
if (!sec_handle) {
|
||||
return CroStatus::Codes::kSecureBufferPoolEmpty;
|
||||
}
|
||||
|
||||
return sec_handle;
|
||||
}
|
||||
|
||||
void V4L2Queue::ReleaseSecureHandle(uint64_t secure_handle) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
// Go through the list of buffers and find the matching one with the secure
|
||||
// handle and release that one.
|
||||
for (auto& buf : buffers_) {
|
||||
if (buf->GetSecureHandle() == secure_handle) {
|
||||
buf->ReleaseSecureHandle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
absl::optional<V4L2WritableBufferRef> V4L2Queue::GetFreeBuffer() {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "media/base/video_codecs.h"
|
||||
#include "media/base/video_decoder_config.h"
|
||||
#include "media/base/video_frame.h"
|
||||
#include "media/gpu/chromeos/chromeos_status.h"
|
||||
#include "media/gpu/chromeos/fourcc.h"
|
||||
#include "media/gpu/media_gpu_export.h"
|
||||
#include "media/gpu/v4l2/v4l2_utils.h"
|
||||
@ -461,6 +462,13 @@ class MEDIA_GPU_EXPORT V4L2Queue
|
||||
// Returns |memory_|, memory type of last buffers allocated by this V4L2Queue.
|
||||
[[nodiscard]] v4l2_memory GetMemoryType() const;
|
||||
|
||||
// This returns the secure handle for a free buffer and then tags that buffer
|
||||
// as having its handle claimed. It expects another call later to
|
||||
// ReleaseSecureHandle to return control of the secure handle back to the
|
||||
// queue.
|
||||
[[nodiscard]] CroStatus::Or<uint64_t> GetFreeSecureHandle();
|
||||
void ReleaseSecureHandle(uint64_t secure_handle);
|
||||
|
||||
// Return a reference to a free buffer for the caller to prepare and submit,
|
||||
// or nullopt if no buffer is currently free.
|
||||
//
|
||||
@ -578,6 +586,8 @@ class MEDIA_GPU_EXPORT V4L2Queue
|
||||
const base::RepeatingClosure schedule_poll_cb_
|
||||
GUARDED_BY_CONTEXT(sequence_checker_);
|
||||
const MmapAsCallback mmap_cb_ GUARDED_BY_CONTEXT(sequence_checker_);
|
||||
const AllocateSecureBufferAsCallback allocate_secure_cb_
|
||||
GUARDED_BY_CONTEXT(sequence_checker_);
|
||||
|
||||
// Callback to call in this queue's destructor.
|
||||
base::OnceClosure destroy_cb_;
|
||||
@ -585,6 +595,7 @@ class MEDIA_GPU_EXPORT V4L2Queue
|
||||
V4L2Queue(const IoctlAsCallback& ioctl_cb,
|
||||
const base::RepeatingClosure& schedule_poll_cb,
|
||||
const MmapAsCallback& mmap_cb,
|
||||
const AllocateSecureBufferAsCallback& allocate_secure_cb,
|
||||
enum v4l2_buf_type type,
|
||||
base::OnceClosure destroy_cb);
|
||||
friend class V4L2QueueFactory;
|
||||
|
@ -359,12 +359,12 @@ void V4L2StatefulVideoDecoder::Initialize(const VideoDecoderConfig& config,
|
||||
// [1]
|
||||
// https://www.kernel.org/doc/html/v5.15/userspace-api/media/v4l/dev-decoder.html#initialization
|
||||
|
||||
OUTPUT_queue_ = base::WrapRefCounted(
|
||||
new V4L2Queue(base::BindRepeating(&HandledIoctl, device_fd_.get()),
|
||||
/*schedule_poll_cb=*/base::DoNothing(),
|
||||
/*mmap_cb=*/base::BindRepeating(&Mmap, device_fd_.get()),
|
||||
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
|
||||
/*destroy_cb=*/base::DoNothing()));
|
||||
OUTPUT_queue_ = base::WrapRefCounted(new V4L2Queue(
|
||||
base::BindRepeating(&HandledIoctl, device_fd_.get()),
|
||||
/*schedule_poll_cb=*/base::DoNothing(),
|
||||
/*mmap_cb=*/base::BindRepeating(&Mmap, device_fd_.get()),
|
||||
AllocateSecureBufferAsCallback(), V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
|
||||
/*destroy_cb=*/base::DoNothing()));
|
||||
|
||||
const auto profile_as_v4l2_fourcc =
|
||||
VideoCodecProfileToV4L2PixFmt(config.profile(), /*slice_based=*/false);
|
||||
@ -636,12 +636,12 @@ bool V4L2StatefulVideoDecoder::InitializeCAPTUREQueue() {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
DCHECK(IsInitialized()) << "V4L2StatefulVideoDecoder must be Initialize()d";
|
||||
|
||||
CAPTURE_queue_ = base::WrapRefCounted(
|
||||
new V4L2Queue(base::BindRepeating(&HandledIoctl, device_fd_.get()),
|
||||
/*schedule_poll_cb=*/base::DoNothing(),
|
||||
/*mmap_cb=*/base::BindRepeating(&Mmap, device_fd_.get()),
|
||||
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||
/*destroy_cb=*/base::DoNothing()));
|
||||
CAPTURE_queue_ = base::WrapRefCounted(new V4L2Queue(
|
||||
base::BindRepeating(&HandledIoctl, device_fd_.get()),
|
||||
/*schedule_poll_cb=*/base::DoNothing(),
|
||||
/*mmap_cb=*/base::BindRepeating(&Mmap, device_fd_.get()),
|
||||
AllocateSecureBufferAsCallback(), V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||
/*destroy_cb=*/base::DoNothing()));
|
||||
|
||||
const auto v4l2_format_or_error = CAPTURE_queue_->GetFormat();
|
||||
if (!v4l2_format_or_error.first || v4l2_format_or_error.second != kIoctlOk) {
|
||||
|
@ -10,9 +10,12 @@
|
||||
#include <linux/videodev2.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "base/files/scoped_file.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
#include "media/base/video_codecs.h"
|
||||
#include "mojo/public/cpp/platform/platform_handle.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
#ifndef V4L2_PIX_FMT_QC08C
|
||||
@ -39,6 +42,16 @@ using IoctlAsCallback = base::RepeatingCallback<int(int, void*)>;
|
||||
using MmapAsCallback =
|
||||
base::RepeatingCallback<void*(void*, unsigned int, int, int, unsigned int)>;
|
||||
|
||||
// This is the callback invoked after successfully allocating a secure buffer.
|
||||
// Invocation of this is guaranteed to pass a valid FD w/ the corresponding
|
||||
// secure handle.
|
||||
using SecureBufferAllocatedCB =
|
||||
base::OnceCallback<void(base::ScopedFD fd, uint64_t secure_handle)>;
|
||||
|
||||
using AllocateSecureBufferAsCallback =
|
||||
base::RepeatingCallback<void(uint32_t size,
|
||||
SecureBufferAllocatedCB callback)>;
|
||||
|
||||
// Numerical value of ioctl() OK return value;
|
||||
constexpr int kIoctlOk = 0;
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "media/gpu/v4l2/v4l2_utils.h"
|
||||
#include "media/gpu/v4l2/v4l2_video_decoder_backend_stateful.h"
|
||||
#include "media/gpu/v4l2/v4l2_video_decoder_backend_stateless.h"
|
||||
#include "mojo/public/cpp/bindings/callback_helpers.h"
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
// gn check does not account for BUILDFLAG(), so including this header will
|
||||
@ -39,6 +40,10 @@
|
||||
#include "chromeos/components/cdm_factory_daemon/chromeos_cdm_context.h" // nogncheck
|
||||
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
|
||||
// TODO(jkardatzke): Remove these once they are in linux/videodev2.h.
|
||||
#define V4L2_CID_MPEG_MTK_BASE (0x00990000 | 0x2000)
|
||||
#define V4L2_CID_MPEG_MTK_GET_SECURE_HANDLE (V4L2_CID_MPEG_MTK_BASE + 8)
|
||||
|
||||
namespace media {
|
||||
|
||||
namespace {
|
||||
@ -283,6 +288,13 @@ void V4L2VideoDecoder::Initialize(const VideoDecoderConfig& config,
|
||||
if (backend_)
|
||||
backend_ = nullptr;
|
||||
}
|
||||
if (config.is_encrypted()) {
|
||||
device_->set_secure_allocate_cb(
|
||||
base::BindRepeating(&V4L2VideoDecoder::AllocateSecureBuffer,
|
||||
weak_this_for_callbacks_.GetWeakPtr()));
|
||||
} else {
|
||||
device_->set_secure_allocate_cb(AllocateSecureBufferAsCallback());
|
||||
}
|
||||
|
||||
DCHECK(!input_queue_);
|
||||
DCHECK(!output_queue_);
|
||||
@ -320,6 +332,13 @@ void V4L2VideoDecoder::Initialize(const VideoDecoderConfig& config,
|
||||
|
||||
// Call init_cb.
|
||||
output_cb_ = std::move(output_cb);
|
||||
|
||||
// Call init_cb
|
||||
if (pending_secure_allocate_callbacks_) {
|
||||
// We need to wait for these to complete before we invoke the init callback.
|
||||
pending_init_cb_ = std::move(init_cb);
|
||||
return;
|
||||
}
|
||||
SetState(State::kInitialized);
|
||||
std::move(init_cb).Run(DecoderStatus::Codes::kOk);
|
||||
}
|
||||
@ -427,9 +446,14 @@ V4L2Status V4L2VideoDecoder::InitializeBackend() {
|
||||
}
|
||||
|
||||
const size_t num_OUTPUT_buffers = backend_->GetNumOUTPUTQueueBuffers();
|
||||
VLOGF(1) << "Requesting: " << num_OUTPUT_buffers
|
||||
<< " OUTPUT buffers of type V4L2_MEMORY_MMAP";
|
||||
if (input_queue_->AllocateBuffers(num_OUTPUT_buffers, V4L2_MEMORY_MMAP,
|
||||
// Secure playback uses dmabufs for the OUTPUT queue, otherwise we use mmap
|
||||
// buffers.
|
||||
v4l2_memory input_queue_memory =
|
||||
!!cdm_context_ref_ ? V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP;
|
||||
VLOGF(1) << "Requesting: " << num_OUTPUT_buffers << " OUTPUT buffers of type "
|
||||
<< (input_queue_memory == V4L2_MEMORY_MMAP ? "V4L2_MEMORY_MMAP"
|
||||
: "V4L2_MEMORY_DMABUF");
|
||||
if (input_queue_->AllocateBuffers(num_OUTPUT_buffers, input_queue_memory,
|
||||
incoherent_) == 0) {
|
||||
VLOGF(1) << "Failed to allocate input buffer.";
|
||||
return V4L2Status::Codes::kFailedResourceAllocation;
|
||||
@ -438,6 +462,84 @@ V4L2Status V4L2VideoDecoder::InitializeBackend() {
|
||||
return V4L2Status::Codes::kOk;
|
||||
}
|
||||
|
||||
void V4L2VideoDecoder::AllocateSecureBuffer(uint32_t size,
|
||||
SecureBufferAllocatedCB callback) {
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
pending_secure_allocate_callbacks_++;
|
||||
// Wrap this with a default handler if it gets dropped somehow or otherwise we
|
||||
// could hang waiting to finish init.
|
||||
cdm_context_ref_->GetCdmContext()
|
||||
->GetChromeOsCdmContext()
|
||||
->AllocateSecureBuffer(
|
||||
size,
|
||||
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
|
||||
base::BindPostTaskToCurrentDefault(base::BindOnce(
|
||||
&V4L2VideoDecoder::AllocateSecureBufferCB,
|
||||
weak_this_for_callbacks_.GetWeakPtr(), std::move(callback))),
|
||||
mojo::PlatformHandle()));
|
||||
#else
|
||||
NOTREACHED();
|
||||
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
}
|
||||
|
||||
void V4L2VideoDecoder::AllocateSecureBufferCB(SecureBufferAllocatedCB callback,
|
||||
mojo::PlatformHandle mojo_fd) {
|
||||
if (state_ == State::kError) {
|
||||
// Drop this and return, we've already entered the error state from a prior
|
||||
// failed callback so we have nothing to do.
|
||||
return;
|
||||
}
|
||||
CHECK(!!pending_init_cb_);
|
||||
if (!mojo_fd.is_valid()) {
|
||||
LOG(ERROR) << "Invalid Mojo FD returned for secure buffer allocation";
|
||||
SetState(State::kError);
|
||||
std::move(pending_init_cb_)
|
||||
.Run(DecoderStatus::Codes::kUnsupportedEncryptionMode);
|
||||
return;
|
||||
}
|
||||
base::ScopedFD secure_fd = mojo_fd.TakeFD();
|
||||
if (!secure_fd.is_valid()) {
|
||||
LOG(ERROR) << "Invalid FD returned for secure buffer allocation";
|
||||
SetState(State::kError);
|
||||
std::move(pending_init_cb_)
|
||||
.Run(DecoderStatus::Codes::kUnsupportedEncryptionMode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Also resolve the secure handle in case failure occurs there, then we know
|
||||
// what we pass into the callback is all valid.
|
||||
struct v4l2_ext_control ctrl;
|
||||
struct v4l2_ext_controls ctrls;
|
||||
memset(&ctrls, 0, sizeof(ctrls));
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
ctrl.id = V4L2_CID_MPEG_MTK_GET_SECURE_HANDLE;
|
||||
ctrl.value = secure_fd.get();
|
||||
|
||||
ctrls.count = 1;
|
||||
ctrls.which = V4L2_CTRL_WHICH_CUR_VAL;
|
||||
ctrls.controls = &ctrl;
|
||||
|
||||
if (device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ctrls)) {
|
||||
PLOG(ERROR) << "Failed getting secure buffer identifier for FD "
|
||||
<< secure_fd.get();
|
||||
SetState(State::kError);
|
||||
std::move(pending_init_cb_)
|
||||
.Run(DecoderStatus::Codes::kUnsupportedEncryptionMode);
|
||||
return;
|
||||
}
|
||||
uint64_t secure_handle = static_cast<uint64_t>(ctrl.value);
|
||||
|
||||
// We have the secure buffer and secure handle, pass it into the callback.
|
||||
std::move(callback).Run(std::move(secure_fd), secure_handle);
|
||||
|
||||
CHECK_GT(pending_secure_allocate_callbacks_, 0u);
|
||||
pending_secure_allocate_callbacks_--;
|
||||
if (!pending_secure_allocate_callbacks_) {
|
||||
SetState(State::kInitialized);
|
||||
std::move(pending_init_cb_).Run(DecoderStatus::Codes::kOk);
|
||||
}
|
||||
}
|
||||
|
||||
bool V4L2VideoDecoder::SetupInputFormat(uint32_t fourcc) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
|
||||
|
||||
@ -816,6 +918,33 @@ bool V4L2VideoDecoder::NeedsTranscryption() {
|
||||
return !!cdm_context_ref_;
|
||||
}
|
||||
|
||||
CroStatus V4L2VideoDecoder::AttachSecureBuffer(
|
||||
scoped_refptr<DecoderBuffer>& buffer) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
|
||||
if (!cdm_context_ref_) {
|
||||
return CroStatus::Codes::kOk;
|
||||
}
|
||||
|
||||
if (state_ == State::kError) {
|
||||
return CroStatus::Codes::kUnableToAllocateSecureBuffer;
|
||||
}
|
||||
|
||||
auto secure_handle_or_error = input_queue_->GetFreeSecureHandle();
|
||||
if (!secure_handle_or_error.has_value()) {
|
||||
// This may only mean we are currently out of buffers, it's not necessarily
|
||||
// a fatal error.
|
||||
return std::move(secure_handle_or_error).error().code();
|
||||
}
|
||||
buffer->WritableSideData().secure_handle =
|
||||
std::move(secure_handle_or_error).value();
|
||||
return CroStatus::Codes::kOk;
|
||||
}
|
||||
|
||||
void V4L2VideoDecoder::ReleaseSecureBuffer(uint64_t secure_handle) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
|
||||
input_queue_->ReleaseSecureHandle(secure_handle);
|
||||
}
|
||||
|
||||
CroStatus V4L2VideoDecoder::ContinueChangeResolution(
|
||||
const gfx::Size& pic_size,
|
||||
const gfx::Rect& visible_rect,
|
||||
|
@ -69,6 +69,8 @@ class MEDIA_GPU_EXPORT V4L2VideoDecoder
|
||||
void ApplyResolutionChange() override;
|
||||
size_t GetMaxOutputFramePoolSize() const override;
|
||||
bool NeedsTranscryption() override;
|
||||
CroStatus AttachSecureBuffer(scoped_refptr<DecoderBuffer>& buffer) override;
|
||||
void ReleaseSecureBuffer(uint64_t secure_handle) override;
|
||||
|
||||
// V4L2VideoDecoderBackend::Client implementation
|
||||
void OnBackendError() override;
|
||||
@ -157,6 +159,18 @@ class MEDIA_GPU_EXPORT V4L2VideoDecoder
|
||||
// until InitializeBackend() is called.
|
||||
V4L2Status InitializeBackend();
|
||||
|
||||
// Performs allocation of a secure buffer by invoking the Mojo call on the
|
||||
// CdmContext. This will only invoke the passed in callback on a successful
|
||||
// allocation, otherwise this will cause the decoder init to fail.
|
||||
void AllocateSecureBuffer(uint32_t size, SecureBufferAllocatedCB callback);
|
||||
|
||||
// Callback from invoking the Mojo call to allocate a secure buffer. This
|
||||
// validates the FD and also resolves it to a secure handle before invoking
|
||||
// the callback. If there's anything wrong with the passed in arguments or
|
||||
// resolving the handle, this will cause a failure in decoder initialization.
|
||||
void AllocateSecureBufferCB(SecureBufferAllocatedCB callback,
|
||||
mojo::PlatformHandle secure_buffer);
|
||||
|
||||
// Pages with multiple V4L2VideoDecoder instances might run out of memory
|
||||
// (e.g. b/170870476) or crash (e.g. crbug.com/1109312). To avoid that and
|
||||
// while the investigation goes on, limit the maximum number of simultaneous
|
||||
@ -198,6 +212,8 @@ class MEDIA_GPU_EXPORT V4L2VideoDecoder
|
||||
// We need to use a CdmContextRef to ensure the lifetime of the CdmContext
|
||||
// backing it while we are alive. This also indicates secure playback mode.
|
||||
std::unique_ptr<CdmContextRef> cdm_context_ref_;
|
||||
uint32_t pending_secure_allocate_callbacks_ = 0;
|
||||
InitCB pending_init_cb_;
|
||||
|
||||
SEQUENCE_CHECKER(decoder_sequence_checker_);
|
||||
|
||||
@ -209,6 +225,8 @@ class MEDIA_GPU_EXPORT V4L2VideoDecoder
|
||||
// |decoder_task_runner_|.
|
||||
base::WeakPtr<V4L2VideoDecoder> weak_this_for_polling_;
|
||||
base::WeakPtrFactory<V4L2VideoDecoder> weak_this_for_polling_factory_;
|
||||
|
||||
base::WeakPtrFactory<V4L2VideoDecoder> weak_this_for_callbacks_{this};
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
|
Reference in New Issue
Block a user