| Index: media/gpu/v4l2_video_decode_accelerator.cc
|
| diff --git a/media/gpu/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2_video_decode_accelerator.cc
|
| index b4ac8a19f993babcca1d4833d9a59ce3e83dc9e4..b2fbecdc549f7a46fa64f95b7eb4b930cfca608d 100644
|
| --- a/media/gpu/v4l2_video_decode_accelerator.cc
|
| +++ b/media/gpu/v4l2_video_decode_accelerator.cc
|
| @@ -164,6 +164,8 @@ V4L2VideoDecodeAccelerator::V4L2VideoDecodeAccelerator(
|
| decoder_decode_buffer_tasks_scheduled_(0),
|
| decoder_frames_at_client_(0),
|
| decoder_flushing_(false),
|
| + decoder_cmd_supported_(false),
|
| + flush_awaiting_last_output_buffer_(false),
|
| reset_pending_(false),
|
| decoder_partial_frame_pending_(false),
|
| input_streamon_(false),
|
| @@ -282,6 +284,8 @@ bool V4L2VideoDecodeAccelerator::Initialize(const Config& config,
|
| if (!CreateInputBuffers())
|
| return false;
|
|
|
| + decoder_cmd_supported_ = IsDecoderCmdSupported();
|
| +
|
| if (!decoder_thread_.Start()) {
|
| LOGF(ERROR) << "decoder thread failed to start";
|
| return false;
|
| @@ -1222,7 +1226,29 @@ void V4L2VideoDecodeAccelerator::Enqueue() {
|
| // Drain the pipe of completed decode buffers.
|
| const int old_inputs_queued = input_buffer_queued_count_;
|
| while (!input_ready_queue_.empty()) {
|
| - if (!EnqueueInputRecord())
|
| + const int buffer = input_ready_queue_.front();
|
| + InputRecord& input_record = input_buffer_map_[buffer];
|
| + if (input_record.input_id == kFlushBufferId && decoder_cmd_supported_) {
|
| + // Send the flush command after all input buffers are dequeued. This makes
|
| + // sure all previous resolution changes have been handled because the
|
| + // driver must hold the input buffer that triggers resolution change. The
|
| + // driver cannot decode data in it without new output buffers. If we send
|
| + // the flush now and a queued input buffer triggers resolution change
|
| + // later, the driver will send an output buffer that has
|
| + // V4L2_BUF_FLAG_LAST. But some queued input buffer have not been decoded
|
| + // yet. Also, V4L2VDA calls STREAMOFF and STREAMON after resolution
|
| + // change. They implicitly send a V4L2_DEC_CMD_STOP and V4L2_DEC_CMD_START
|
| + // to the decoder.
|
| + if (input_buffer_queued_count_ == 0) {
|
| + if (!SendDecoderCmdStop())
|
| + return;
|
| + input_ready_queue_.pop();
|
| + free_input_buffers_.push_back(buffer);
|
| + input_record.input_id = -1;
|
| + } else {
|
| + break;
|
| + }
|
| + } else if (!EnqueueInputRecord())
|
| return;
|
| }
|
| if (old_inputs_queued == 0 && input_buffer_queued_count_ != 0) {
|
| @@ -1358,6 +1384,9 @@ bool V4L2VideoDecodeAccelerator::DequeueOutputBuffer() {
|
| if (errno == EAGAIN) {
|
| // EAGAIN if we're just out of buffers to dequeue.
|
| return false;
|
| + } else if (errno == EPIPE) {
|
| + DVLOGF(3) << "Got EPIPE. Last output buffer was already dequeued.";
|
| + return false;
|
| }
|
| PLOGF(ERROR) << "ioctl() failed: VIDIOC_DQBUF";
|
| NOTIFY_ERROR(PLATFORM_FAILURE);
|
| @@ -1394,6 +1423,17 @@ bool V4L2VideoDecodeAccelerator::DequeueOutputBuffer() {
|
| output_record.cleared = true;
|
| }
|
| }
|
| + if (dqbuf.flags & V4L2_BUF_FLAG_LAST) {
|
| + DVLOGF(3) << "Got last output buffer. Waiting last buffer="
|
| + << flush_awaiting_last_output_buffer_;
|
| + if (flush_awaiting_last_output_buffer_) {
|
| + flush_awaiting_last_output_buffer_ = false;
|
| + struct v4l2_decoder_cmd cmd;
|
| + memset(&cmd, 0, sizeof(cmd));
|
| + cmd.cmd = V4L2_DEC_CMD_START;
|
| + IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_DECODER_CMD, &cmd);
|
| + }
|
| + }
|
| return true;
|
| }
|
|
|
| @@ -1568,15 +1608,27 @@ void V4L2VideoDecodeAccelerator::NotifyFlushDoneIfNeeded() {
|
| // * All image processor buffers are returned.
|
| if (!decoder_input_queue_.empty()) {
|
| if (decoder_input_queue_.front()->input_id !=
|
| - decoder_delay_bitstream_buffer_id_)
|
| + decoder_delay_bitstream_buffer_id_) {
|
| + DVLOGF(3) << "Some input bitstream buffers are not queued.";
|
| return;
|
| + }
|
| }
|
| - if (decoder_current_input_buffer_ != -1)
|
| + if (decoder_current_input_buffer_ != -1) {
|
| + DVLOGF(3) << "Current input buffer != -1";
|
| return;
|
| - if ((input_ready_queue_.size() + input_buffer_queued_count_) != 0)
|
| + }
|
| + if ((input_ready_queue_.size() + input_buffer_queued_count_) != 0) {
|
| + DVLOGF(3) << "Some input buffers are not dequeued.";
|
| return;
|
| - if (image_processor_bitstream_buffer_ids_.size() != 0)
|
| + }
|
| + if (image_processor_bitstream_buffer_ids_.size() != 0) {
|
| + DVLOGF(3) << "Waiting for image processor to complete.";
|
| return;
|
| + }
|
| + if (flush_awaiting_last_output_buffer_) {
|
| + DVLOGF(3) << "Waiting for last output buffer.";
|
| + return;
|
| + }
|
|
|
| // TODO(posciak): crbug.com/270039. Exynos requires a streamoff-streamon
|
| // sequence after flush to continue, even if we are not resetting. This would
|
| @@ -1603,6 +1655,35 @@ void V4L2VideoDecodeAccelerator::NotifyFlushDoneIfNeeded() {
|
| ScheduleDecodeBufferTaskIfNeeded();
|
| }
|
|
|
| +bool V4L2VideoDecodeAccelerator::IsDecoderCmdSupported() {
|
| + // CMD_STOP should always succeed. If the decoder is started, the command can
|
| + // flush it. If the decoder is stopped, the command does nothing. We use this
|
| + // to know if a driver supports V4L2_DEC_CMD_STOP to flush.
|
| + struct v4l2_decoder_cmd cmd;
|
| + memset(&cmd, 0, sizeof(cmd));
|
| + cmd.cmd = V4L2_DEC_CMD_STOP;
|
| + if (device_->Ioctl(VIDIOC_TRY_DECODER_CMD, &cmd) != 0) {
|
| + DVLOGF(3) "V4L2_DEC_CMD_STOP is not supported.";
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool V4L2VideoDecodeAccelerator::SendDecoderCmdStop() {
|
| + DVLOGF(2);
|
| + DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
|
| + DCHECK(!flush_awaiting_last_output_buffer_);
|
| +
|
| + struct v4l2_decoder_cmd cmd;
|
| + memset(&cmd, 0, sizeof(cmd));
|
| + cmd.cmd = V4L2_DEC_CMD_STOP;
|
| + IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_DECODER_CMD, &cmd);
|
| + flush_awaiting_last_output_buffer_ = true;
|
| +
|
| + return true;
|
| +}
|
| +
|
| void V4L2VideoDecodeAccelerator::ResetTask() {
|
| DVLOGF(3);
|
| DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
|
| @@ -1785,6 +1866,9 @@ bool V4L2VideoDecodeAccelerator::StopOutputStream() {
|
| IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
|
| output_streamon_ = false;
|
|
|
| + // Output stream is stopped. No need to wait for the buffer anymore.
|
| + flush_awaiting_last_output_buffer_ = false;
|
| +
|
| for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
|
| // After streamoff, the device drops ownership of all buffers, even if we
|
| // don't dequeue them explicitly. Some of them may still be owned by the
|
|
|