blob: a773abdf4967d800f5bc6b45770aa1da6e06465e [file] [log] [blame]
// Copyright 2018 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/v4l2/v4l2_decode_surface.h"
#include <linux/media.h>
#include <linux/videodev2.h>
#include <poll.h>
#include <sys/ioctl.h>
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/stringprintf.h"
#include "media/gpu/macros.h"
namespace media {
V4L2DecodeSurface::V4L2DecodeSurface(V4L2WritableBufferRef input_buffer,
V4L2WritableBufferRef output_buffer,
scoped_refptr<VideoFrame> frame)
: input_buffer_(std::move(input_buffer)),
output_buffer_(std::move(output_buffer)),
video_frame_(std::move(frame)),
input_record_(input_buffer_.BufferId()),
output_record_(output_buffer_.BufferId()),
decoded_(false) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
V4L2DecodeSurface::~V4L2DecodeSurface() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOGF(5) << "Releasing output record id=" << output_record_;
if (release_cb_)
std::move(release_cb_).Run();
}
void V4L2DecodeSurface::SetDecoded() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!decoded_);
decoded_ = true;
// We can now drop references to all reference surfaces for this surface
// as we are done with decoding.
reference_surfaces_.clear();
// And finally execute and drop the decode done callback, if set.
if (done_cb_)
std::move(done_cb_).Run();
}
void V4L2DecodeSurface::SetVisibleRect(const gfx::Rect& visible_rect) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
visible_rect_ = visible_rect;
}
void V4L2DecodeSurface::SetReferenceSurfaces(
std::vector<scoped_refptr<V4L2DecodeSurface>> ref_surfaces) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(reference_surfaces_.empty());
#if DCHECK_IS_ON()
for (const auto& ref : reference_surfaces_)
DCHECK_NE(ref->output_record(), output_record_);
#endif
reference_surfaces_ = std::move(ref_surfaces);
}
void V4L2DecodeSurface::SetDecodeDoneCallback(base::OnceClosure done_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!done_cb_);
done_cb_ = std::move(done_cb);
}
void V4L2DecodeSurface::SetReleaseCallback(base::OnceClosure release_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!release_cb_);
release_cb_ = std::move(release_cb);
}
std::string V4L2DecodeSurface::ToString() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::string out;
base::StringAppendF(&out, "Buffer %d -> %d. ", input_record_, output_record_);
base::StringAppendF(&out, "Reference surfaces:");
for (const auto& ref : reference_surfaces_) {
DCHECK_NE(ref->output_record(), output_record_);
base::StringAppendF(&out, " %d", ref->output_record());
}
return out;
}
void V4L2ConfigStoreDecodeSurface::PrepareSetCtrls(
struct v4l2_ext_controls* ctrls) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_NE(ctrls, nullptr);
DCHECK_GT(config_store_, 0u);
ctrls->config_store = config_store_;
}
uint64_t V4L2ConfigStoreDecodeSurface::GetReferenceID() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Control store uses the output buffer ID as reference.
return output_record();
}
bool V4L2ConfigStoreDecodeSurface::Submit() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GT(config_store_, 0u);
input_buffer().SetConfigStore(config_store_);
if (!std::move(input_buffer()).QueueMMap()) {
return false;
}
switch (output_buffer().Memory()) {
case V4L2_MEMORY_MMAP:
return std::move(output_buffer()).QueueMMap();
case V4L2_MEMORY_DMABUF:
return std::move(output_buffer()).QueueDMABuf(video_frame());
default:
NOTREACHED() << "We should only use MMAP or DMABUF.";
}
return false;
}
void V4L2RequestDecodeSurface::PrepareSetCtrls(
struct v4l2_ext_controls* ctrls) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_NE(ctrls, nullptr);
request_ref_.ApplyCtrls(ctrls);
}
uint64_t V4L2RequestDecodeSurface::GetReferenceID() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Convert the input buffer ID to what the internal representation of
// the timestamp we submitted will be (tv_usec * 1000).
return output_record() * 1000;
}
bool V4L2RequestDecodeSurface::Submit() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Use the output buffer index as the timestamp.
// Since the client is supposed to keep the output buffer out of the V4L2
// queue for as long as it is used as a reference frame, this ensures that
// all the requests we submit have unique IDs at any point in time.
struct timeval timestamp = {
.tv_sec = 0,
.tv_usec = output_record()
};
input_buffer().SetTimeStamp(timestamp);
if (!std::move(input_buffer()).QueueMMap(&request_ref_)) {
return false;
}
bool result = false;
switch (output_buffer().Memory()) {
case V4L2_MEMORY_MMAP:
result = std::move(output_buffer()).QueueMMap();
break;
case V4L2_MEMORY_DMABUF:
result = std::move(output_buffer()).QueueDMABuf(video_frame());
break;
default:
NOTREACHED() << "We should only use MMAP or DMABUF.";
}
if (!result)
return result;
return std::move(request_ref_).Submit().has_value();
}
} // namespace media