Commit 82da0800 authored by Hirokazu Honda's avatar Hirokazu Honda Committed by Commit Bot

media/gpu/v4l2VEA: Execute S_FMT with frame size given by client

V4L2VideoEncodeAccelerator requests its client to allocate video frame buffer
with a frame size. If the client allocates video frame buffer using GBM API, the
frame size of the created video frame can be different from the requested one.
In that case, we need to reconfigure v4l2 drivers with the new frame size by
S_FMT. The reconfiguration must happen only once during encoding and when the
first frame is fed.

Bug: 979115
Test: CtsMediaTestCases on kevin
Change-Id: Ie245001fad631f5d3ff66c0d7280037bea21a8b9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1722835
Commit-Queue: Hirokazu Honda <hiroh@chromium.org>
Reviewed-by: default avatarAlexandre Courbot <acourbot@chromium.org>
Cr-Commit-Position: refs/heads/master@{#683114}
parent d28483fc
......@@ -175,7 +175,8 @@ void GpuArcVideoEncodeAccelerator::EncodeSharedMemory(
}
auto layout = media::VideoFrameLayout::CreateWithPlanes(
format, coded_size_, std::move(layout_planes));
format, gfx::Size(layout_planes[0].stride, coded_size_.height()),
std::move(layout_planes));
if (!layout) {
DLOG(ERROR) << "Failed to create VideoFrameLayout.";
client_->NotifyError(Error::kInvalidArgumentError);
......
......@@ -613,6 +613,13 @@ void V4L2VideoEncodeAccelerator::EncodeTask(scoped_refptr<VideoFrame> frame,
return;
}
if (frame &&
!ReconfigureFormatIfNeeded(frame->format(), frame->coded_size())) {
NOTIFY_ERROR(kInvalidArgumentError);
encoder_state_ = kError;
return;
}
if (image_processor_) {
image_processor_input_queue_.emplace(std::move(frame), force_keyframe);
InputImageProcessorTask();
......@@ -622,6 +629,70 @@ void V4L2VideoEncodeAccelerator::EncodeTask(scoped_refptr<VideoFrame> frame,
}
}
bool V4L2VideoEncodeAccelerator::ReconfigureFormatIfNeeded(
VideoPixelFormat format,
const gfx::Size& new_frame_size) {
// We should apply the frame size change to ImageProcessor if there is.
if (image_processor_) {
// Stride is the same. There is no need of executing S_FMT again.
if (image_processor_->input_layout().coded_size() == new_frame_size) {
return true;
}
VLOGF(2) << "Call S_FMT with a new size=" << new_frame_size.ToString()
<< ", the previous size ="
<< device_input_layout_->coded_size().ToString();
if (input_streamon_ || output_streamon_) {
VLOGF(1) << "Input frame size is changed during encoding";
NOTIFY_ERROR(kInvalidArgumentError);
return false;
}
// TODO(hiroh): Decide the appropriate planar in some way.
auto input_layout = VideoFrameLayout::CreateMultiPlanar(
format, new_frame_size,
std::vector<VideoFrameLayout::Plane>(VideoFrame::NumPlanes(format)));
if (!input_layout) {
VLOGF(1) << "Invalid image processor input layout";
return false;
}
if (!CreateImageProcessor(*input_layout, *device_input_layout_,
visible_size_)) {
NOTIFY_ERROR(kPlatformFailureError);
return false;
}
if (image_processor_->input_layout().coded_size().width() !=
new_frame_size.width()) {
NOTIFY_ERROR(kPlatformFailureError);
return false;
}
return true;
}
if (new_frame_size != device_input_layout_->coded_size()) {
VLOGF(2) << "Call S_FMT with a new size=" << new_frame_size.ToString()
<< ", the previous size ="
<< device_input_layout_->coded_size().ToString();
if (input_streamon_ || output_streamon_) {
VLOGF(1) << "Input frame size is changed during encoding";
NOTIFY_ERROR(kInvalidArgumentError);
return false;
}
if (!NegotiateInputFormat(device_input_layout_->format(), new_frame_size)) {
NOTIFY_ERROR(kPlatformFailureError);
return false;
}
if (device_input_layout_->coded_size().width() != new_frame_size.width()) {
NOTIFY_ERROR(kPlatformFailureError);
return false;
}
}
return true;
}
void V4L2VideoEncodeAccelerator::InputImageProcessorTask() {
if (free_image_processor_output_buffer_indices_.empty())
return;
......@@ -808,6 +879,12 @@ void V4L2VideoEncodeAccelerator::Enqueue() {
}
}
if (!input_streamon_) {
// We don't have to enqueue any buffers in the output queue until we enqueue
// buffers in the input queue. This enables to call S_FMT in Encode() on
// the first frame.
return;
}
// Enqueue all the outputs we can.
const int old_outputs_queued = output_buffer_queued_count_;
while (!free_output_buffers_.empty() && !encoder_output_queue_.empty()) {
......@@ -1240,7 +1317,8 @@ bool V4L2VideoEncodeAccelerator::SetOutputFormat(
}
bool V4L2VideoEncodeAccelerator::NegotiateInputFormat(
VideoPixelFormat input_format) {
VideoPixelFormat input_format,
const gfx::Size& size) {
VLOGF(2);
DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
DCHECK(!input_streamon_);
......@@ -1257,30 +1335,29 @@ bool V4L2VideoEncodeAccelerator::NegotiateInputFormat(
}
for (const auto pix_fmt : pix_fmt_candidates) {
auto trying_format = V4L2Device::V4L2PixFmtToVideoPixelFormat(pix_fmt);
DCHECK_NE(trying_format, PIXEL_FORMAT_UNKNOWN);
size_t planes_count = VideoFrame::NumPlanes(trying_format);
size_t planes_count = V4L2Device::GetNumPlanesOfV4L2PixFmt(pix_fmt);
DCHECK_GT(planes_count, 0u);
DCHECK_LE(planes_count, static_cast<size_t>(VIDEO_MAX_PLANES));
VLOGF(2) << "Trying S_FMT with " << FourccToString(pix_fmt) << " ("
<< trying_format << ").";
DVLOGF(3) << "Trying S_FMT with " << FourccToString(pix_fmt);
struct v4l2_format format{};
format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
format.fmt.pix_mp.width = visible_size_.width();
format.fmt.pix_mp.height = visible_size_.height();
format.fmt.pix_mp.width = size.width();
format.fmt.pix_mp.height = size.height();
format.fmt.pix_mp.pixelformat = pix_fmt;
format.fmt.pix_mp.num_planes = planes_count;
if (device_->Ioctl(VIDIOC_S_FMT, &format) == 0 &&
format.fmt.pix_mp.pixelformat == pix_fmt) {
VLOGF(2) << "Success: S_FMT with " << FourccToString(pix_fmt);
DVLOGF(3) << "Success: S_FMT with " << FourccToString(pix_fmt);
device_input_layout_ = V4L2Device::V4L2FormatToVideoFrameLayout(format);
if (!device_input_layout_) {
VLOGF(1) << "Invalid device_input_layout_";
return false;
}
VLOG(2) << "Negotiated device_input_layout_: " << *device_input_layout_;
DVLOG(3) << "Negotiated device_input_layout_: " << *device_input_layout_;
if (!gfx::Rect(device_input_layout_->coded_size())
.Contains(gfx::Rect(visible_size_))) {
VLOGF(1) << "Input size " << visible_size_.ToString()
.Contains(gfx::Rect(size))) {
VLOGF(1) << "Input size " << size.ToString()
<< " exceeds encoder capability. Size encoder can handle: "
<< device_input_layout_->coded_size().ToString();
return false;
......@@ -1305,7 +1382,7 @@ bool V4L2VideoEncodeAccelerator::SetFormats(VideoPixelFormat input_format,
if (!SetOutputFormat(output_profile))
return false;
if (!NegotiateInputFormat(input_format))
if (!NegotiateInputFormat(input_format, visible_size_))
return false;
struct v4l2_rect visible_rect;
......
......@@ -210,10 +210,16 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator
bool SetFormats(VideoPixelFormat input_format,
VideoCodecProfile output_profile);
// Reconfigure format of input buffers and image processor if frame size
// given by client is different from one set in input buffers.
bool ReconfigureFormatIfNeeded(VideoPixelFormat format,
const gfx::Size& new_frame_size);
// Try to set up the device to the input format we were Initialized() with,
// or if the device doesn't support it, use one it can support, so that we
// can later instantiate an ImageProcessor to convert to it.
bool NegotiateInputFormat(VideoPixelFormat input_format);
bool NegotiateInputFormat(VideoPixelFormat input_format,
const gfx::Size& frame_size);
// Set up the device to the output format requested in Initialize().
bool SetOutputFormat(VideoCodecProfile output_profile);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment