Commit ccf4603b authored by Chih-Yu Huang's avatar Chih-Yu Huang Committed by Commit Bot

media/gpu: V4L2SVD: Support VP9 show_existing_frame feature.

The VP9 show_existing_frame feature allows a decoded frame be
outputted multiple times. This CL supports this feature at V4L2SVD
implementation. Before sending VideoFrame to the client, we set the
correct timestamp corresponding to the bitstream ID. This makes it
possible for the same video frame memory to be output several times
with different timestamps.

BUG=941330
TEST=Play video with show_existing_frame at File app.

Change-Id: I2f78f59ff40e49c0c724cf43a54bcb233097cbcb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1631230
Commit-Queue: Chih-Yu Huang <akahuang@chromium.org>
Auto-Submit: Chih-Yu Huang <akahuang@chromium.org>
Reviewed-by: default avatarAlexandre Courbot <acourbot@chromium.org>
Reviewed-by: default avatarHirokazu Honda <hiroh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#676375}
parent b60c8b5a
......@@ -82,9 +82,14 @@ struct V4L2SliceVideoDecoder::OutputRequest {
const OutputRequestType type;
// The surface to be outputted.
scoped_refptr<V4L2DecodeSurface> surface;
static OutputRequest Surface(scoped_refptr<V4L2DecodeSurface> s) {
return OutputRequest(std::move(s));
// The timestamp of the output frame. Because a surface might be outputted
// multiple times with different timestamp, we need to store timestamp out of
// surface.
base::TimeDelta timestamp;
static OutputRequest Surface(scoped_refptr<V4L2DecodeSurface> s,
base::TimeDelta t) {
return OutputRequest(std::move(s), t);
}
static OutputRequest FlushFence() { return OutputRequest(kFlushFence); }
......@@ -101,8 +106,8 @@ struct V4L2SliceVideoDecoder::OutputRequest {
OutputRequest(OutputRequest&&) = default;
private:
explicit OutputRequest(scoped_refptr<V4L2DecodeSurface> s)
: type(kSurface), surface(std::move(s)) {}
OutputRequest(scoped_refptr<V4L2DecodeSurface> s, base::TimeDelta t)
: type(kSurface), surface(std::move(s)), timestamp(t) {}
explicit OutputRequest(OutputRequestType t) : type(t) {}
DISALLOW_COPY_AND_ASSIGN(OutputRequest);
......@@ -547,6 +552,10 @@ void V4L2SliceVideoDecoder::EnqueueDecodeTask(DecodeRequest request) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(state_ == State::kDecoding || state_ == State::kFlushing);
if (!request.buffer->end_of_stream()) {
bitstream_id_to_timestamp_.emplace(request.bitstream_id,
request.buffer->timestamp());
}
decode_request_queue_.push(std::move(request));
// If we are already decoding, then we don't need to pump again.
if (!current_decode_request_)
......@@ -671,7 +680,7 @@ void V4L2SliceVideoDecoder::PumpOutputSurfaces() {
scoped_refptr<V4L2DecodeSurface> surface = std::move(request.surface);
DCHECK(surface->video_frame());
RunOutputCB(surface->video_frame());
RunOutputCB(surface->video_frame(), request.timestamp);
break;
}
}
......@@ -859,10 +868,16 @@ void V4L2SliceVideoDecoder::SurfaceReady(
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(3);
auto it = bitstream_id_to_timestamp_.find(bitstream_id);
DCHECK(it != bitstream_id_to_timestamp_.end());
base::TimeDelta timestamp = it->second;
bitstream_id_to_timestamp_.erase(it);
// TODO(akahuang): Update visible_rect at the output frame.
dec_surface->SetVisibleRect(visible_rect);
output_request_queue_.push(OutputRequest::Surface(std::move(dec_surface)));
output_request_queue_.push(
OutputRequest::Surface(std::move(dec_surface), timestamp));
PumpOutputSurfaces();
}
......@@ -1041,10 +1056,23 @@ void V4L2SliceVideoDecoder::RunDecodeCB(DecodeCB cb, DecodeStatus status) {
base::BindOnce(std::move(cb), status));
}
void V4L2SliceVideoDecoder::RunOutputCB(scoped_refptr<VideoFrame> frame) {
void V4L2SliceVideoDecoder::RunOutputCB(scoped_refptr<VideoFrame> frame,
base::TimeDelta timestamp) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(4) << "timestamp: " << timestamp;
// The attribute of the frame is needed to update. Wrap the frame again.
if (frame->timestamp() != timestamp) {
scoped_refptr<VideoFrame> wrapped_frame = VideoFrame::WrapVideoFrame(
*frame, frame->format(), frame->visible_rect(), frame->natural_size());
wrapped_frame->set_timestamp(timestamp);
wrapped_frame->AddDestructionObserver(base::BindOnce(
base::DoNothing::Once<scoped_refptr<VideoFrame>>(), std::move(frame)));
frame = std::move(wrapped_frame);
}
frame->metadata()->SetBoolean(VideoFrameMetadata::POWER_EFFICIENT, true);
scoped_refptr<VideoFrame> converted_frame =
frame_converter_->ConvertFrame(std::move(frame));
if (!converted_frame) {
......@@ -1052,6 +1080,7 @@ void V4L2SliceVideoDecoder::RunOutputCB(scoped_refptr<VideoFrame> frame) {
SetState(State::kError);
return;
}
// Although the document of VideoDecoder says "should run |output_cb| as soon
// as possible (without thread trampolining)", MojoVideoDecoderService still
// assumes the callback is called at original thread.
......
......@@ -188,7 +188,7 @@ class MEDIA_GPU_EXPORT V4L2SliceVideoDecoder : public VideoDecoder,
// Get the next bitsream ID.
int32_t GetNextBitstreamId();
// Convert the frame and call the output callback.
void RunOutputCB(scoped_refptr<VideoFrame> frame);
void RunOutputCB(scoped_refptr<VideoFrame> frame, base::TimeDelta timestamp);
// Call the decode callback and count the number of pending callbacks.
void RunDecodeCB(DecodeCB cb, DecodeStatus status);
// Change the state and check the state transition is valid.
......@@ -230,6 +230,9 @@ class MEDIA_GPU_EXPORT V4L2SliceVideoDecoder : public VideoDecoder,
scoped_refptr<V4L2Queue> input_queue_;
scoped_refptr<V4L2Queue> output_queue_;
// The mapping between bitstream id and the timestamp.
std::map<int32_t, base::TimeDelta> bitstream_id_to_timestamp_;
// Queue of pending decode request.
base::queue<DecodeRequest> decode_request_queue_;
// Surfaces enqueued to V4L2 device. Since we are stateless, they are
......
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