Commit ce67bd9d authored by Thomas Guilbert's avatar Thomas Guilbert Committed by Commit Bot

[video-rAF] Add new VideoFrameMetada fields

This CL adds new keys to VideoFrameMetadata's dictionary. These keys
will be used to store new types of information, to be surfaced via the
video.requestAnimationFrame() API.

Bug: 1012063
Change-Id: Ib937fbf762d00afca46f6c1552ea818ecb1b6584
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1904336
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Reviewed-by: default avatarTommi <tommi@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Reviewed-by: default avatarKenneth Russell <kbr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714046}
parent d6d66eab
...@@ -162,9 +162,21 @@ class MEDIA_EXPORT VideoFrameMetadata { ...@@ -162,9 +162,21 @@ class MEDIA_EXPORT VideoFrameMetadata {
TOP_CONTROLS_SHOWN_RATIO, TOP_CONTROLS_SHOWN_RATIO,
// If present, this field represents the local time at which the VideoFrame // If present, this field represents the local time at which the VideoFrame
// was decoded from whichever format it was encoded in. // was decoded from whichever format it was encoded in. Sometimes only
// Use Get/SetTimeTicks() for this key. // DECODE_END_TIME will be present. Use Get/SetTimeTicks() for this key.
DECODE_TIME, DECODE_BEGIN_TIME,
DECODE_END_TIME,
// If present, this field represents the elapsed time from the submission of
// the encoded packet with the same PTS as this frame to the decoder until
// the decoded frame was ready for presentation. Stored as base::TimeDelta.
PROCESSING_TIME,
// The RTP timestamp associated with this video frame. Stored as a double
// since base::DictionaryValue doesn't have a uint32_t type.
//
// https://w3c.github.io/webrtc-pc/#dom-rtcrtpcontributingsource
RTP_TIMESTAMP,
NUM_KEYS NUM_KEYS
}; };
......
...@@ -630,6 +630,8 @@ void DecoderStream<StreamType>::OnDecodeOutputReady( ...@@ -630,6 +630,8 @@ void DecoderStream<StreamType>::OnDecodeOutputReady(
return; return;
} }
traits_->OnOutputReady(output.get());
if (read_cb_) { if (read_cb_) {
// If |ready_outputs_| was non-empty, the read would have already been // If |ready_outputs_| was non-empty, the read would have already been
// satisifed by Read(). // satisifed by Read().
...@@ -987,6 +989,7 @@ void DecoderStream<StreamType>::OnPreparedOutputReady( ...@@ -987,6 +989,7 @@ void DecoderStream<StreamType>::OnPreparedOutputReady(
DCHECK(!unprepared_outputs_.empty()); DCHECK(!unprepared_outputs_.empty());
DCHECK(preparing_output_); DCHECK(preparing_output_);
traits_->OnOutputReady(output.get());
CompletePrepare(output.get()); CompletePrepare(output.get());
unprepared_outputs_.pop_front(); unprepared_outputs_.pop_front();
if (!read_cb_) if (!read_cb_)
......
...@@ -112,6 +112,9 @@ void DecoderStreamTraits<DemuxerStream::AUDIO>::OnConfigChanged( ...@@ -112,6 +112,9 @@ void DecoderStreamTraits<DemuxerStream::AUDIO>::OnConfigChanged(
audio_ts_validator_.reset(new AudioTimestampValidator(config, media_log_)); audio_ts_validator_.reset(new AudioTimestampValidator(config, media_log_));
} }
void DecoderStreamTraits<DemuxerStream::AUDIO>::OnOutputReady(
OutputType* buffer) {}
// Video decoder stream traits implementation. // Video decoder stream traits implementation.
// static // static
...@@ -201,6 +204,7 @@ void DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecode( ...@@ -201,6 +204,7 @@ void DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecode(
frame_metadata_[buffer.timestamp()] = { frame_metadata_[buffer.timestamp()] = {
buffer.discard_padding().first == kInfiniteDuration, // should_drop buffer.discard_padding().first == kInfiniteDuration, // should_drop
buffer.duration(), // duration buffer.duration(), // duration
base::TimeTicks::Now(), // decode_begin_time
}; };
if (!buffer.is_key_frame()) if (!buffer.is_key_frame())
...@@ -220,11 +224,6 @@ void DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecode( ...@@ -220,11 +224,6 @@ void DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecode(
PostDecodeAction DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecodeDone( PostDecodeAction DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecodeDone(
OutputType* buffer) { OutputType* buffer) {
// Add a timestamp here (after decoding completed) to enable buffering delay
// measurements down the line.
buffer->metadata()->SetTimeTicks(media::VideoFrameMetadata::DECODE_TIME,
base::TimeTicks::Now());
auto it = frame_metadata_.find(buffer->timestamp()); auto it = frame_metadata_.find(buffer->timestamp());
// If the frame isn't in |frame_metadata_| it probably was erased below on a // If the frame isn't in |frame_metadata_| it probably was erased below on a
...@@ -234,6 +233,12 @@ PostDecodeAction DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecodeDone( ...@@ -234,6 +233,12 @@ PostDecodeAction DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecodeDone(
if (it == frame_metadata_.end()) if (it == frame_metadata_.end())
return PostDecodeAction::DELIVER; return PostDecodeAction::DELIVER;
// Add a timestamp here to enable buffering delay measurements down the line.
buffer->metadata()->SetTimeTicks(VideoFrameMetadata::DECODE_BEGIN_TIME,
it->second.decode_begin_time);
buffer->metadata()->SetTimeTicks(VideoFrameMetadata::DECODE_END_TIME,
base::TimeTicks::Now());
auto action = it->second.should_drop ? PostDecodeAction::DROP auto action = it->second.should_drop ? PostDecodeAction::DROP
: PostDecodeAction::DELIVER; : PostDecodeAction::DELIVER;
...@@ -252,4 +257,16 @@ PostDecodeAction DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecodeDone( ...@@ -252,4 +257,16 @@ PostDecodeAction DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecodeDone(
return action; return action;
} }
void DecoderStreamTraits<DemuxerStream::VIDEO>::OnOutputReady(
OutputType* buffer) {
base::TimeTicks decode_begin_time;
if (!buffer->metadata()->GetTimeTicks(VideoFrameMetadata::DECODE_BEGIN_TIME,
&decode_begin_time)) {
return;
}
// Tag buffer with elapsed time since creation.
buffer->metadata()->SetTimeDelta(VideoFrameMetadata::PROCESSING_TIME,
base::TimeTicks::Now() - decode_begin_time);
}
} // namespace media } // namespace media
...@@ -62,6 +62,7 @@ class MEDIA_EXPORT DecoderStreamTraits<DemuxerStream::AUDIO> { ...@@ -62,6 +62,7 @@ class MEDIA_EXPORT DecoderStreamTraits<DemuxerStream::AUDIO> {
void OnDecode(const DecoderBuffer& buffer); void OnDecode(const DecoderBuffer& buffer);
PostDecodeAction OnDecodeDone(OutputType* buffer); PostDecodeAction OnDecodeDone(OutputType* buffer);
void OnStreamReset(DemuxerStream* stream); void OnStreamReset(DemuxerStream* stream);
void OnOutputReady(OutputType* output);
private: private:
void OnConfigChanged(const AudioDecoderConfig& config); void OnConfigChanged(const AudioDecoderConfig& config);
...@@ -107,6 +108,7 @@ class MEDIA_EXPORT DecoderStreamTraits<DemuxerStream::VIDEO> { ...@@ -107,6 +108,7 @@ class MEDIA_EXPORT DecoderStreamTraits<DemuxerStream::VIDEO> {
void OnDecode(const DecoderBuffer& buffer); void OnDecode(const DecoderBuffer& buffer);
PostDecodeAction OnDecodeDone(OutputType* buffer); PostDecodeAction OnDecodeDone(OutputType* buffer);
void OnStreamReset(DemuxerStream* stream); void OnStreamReset(DemuxerStream* stream);
void OnOutputReady(OutputType* output);
private: private:
base::TimeDelta last_keyframe_timestamp_; base::TimeDelta last_keyframe_timestamp_;
...@@ -116,6 +118,7 @@ class MEDIA_EXPORT DecoderStreamTraits<DemuxerStream::VIDEO> { ...@@ -116,6 +118,7 @@ class MEDIA_EXPORT DecoderStreamTraits<DemuxerStream::VIDEO> {
struct FrameMetadata { struct FrameMetadata {
bool should_drop = false; bool should_drop = false;
base::TimeDelta duration = kNoTimestamp; base::TimeDelta duration = kNoTimestamp;
base::TimeTicks decode_begin_time;
}; };
base::flat_map<base::TimeDelta, FrameMetadata> frame_metadata_; base::flat_map<base::TimeDelta, FrameMetadata> frame_metadata_;
......
...@@ -43,6 +43,7 @@ namespace media { ...@@ -43,6 +43,7 @@ namespace media {
const int kNumConfigs = 4; const int kNumConfigs = 4;
const int kNumBuffersInOneConfig = 5; const int kNumBuffersInOneConfig = 5;
constexpr base::TimeDelta kPrepareDelay = base::TimeDelta::FromMilliseconds(5);
static std::string GetDecoderName(int i) { static std::string GetDecoderName(int i) {
return std::string("VideoDecoder") + base::NumberToString(i); return std::string("VideoDecoder") + base::NumberToString(i);
...@@ -144,6 +145,15 @@ class VideoDecoderStreamTest ...@@ -144,6 +145,15 @@ class VideoDecoderStreamTest
base::BindOnce(std::move(output_ready_cb), std::move(frame))); base::BindOnce(std::move(output_ready_cb), std::move(frame)));
} }
void PrepareFrameWithDelay(
scoped_refptr<VideoFrame> frame,
VideoDecoderStream::OutputReadyCB output_ready_cb) {
task_environment_.FastForwardBy(kPrepareDelay);
task_environment_.GetMainThreadTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(std::move(output_ready_cb), std::move(frame)));
}
void OnBytesDecoded(int count) { num_decoded_bytes_unreported_ += count; } void OnBytesDecoded(int count) { num_decoded_bytes_unreported_ += count; }
// Callback to create a list of decoders for the DecoderSelector to select // Callback to create a list of decoders for the DecoderSelector to select
...@@ -463,7 +473,8 @@ class VideoDecoderStreamTest ...@@ -463,7 +473,8 @@ class VideoDecoderStreamTest
SatisfyPendingCallback(DECODER_REINIT); SatisfyPendingCallback(DECODER_REINIT);
} }
base::test::SingleThreadTaskEnvironment task_environment_; base::test::SingleThreadTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
StrictMock<MockMediaLog> media_log_; StrictMock<MockMediaLog> media_log_;
std::unique_ptr<VideoDecoderStream> video_decoder_stream_; std::unique_ptr<VideoDecoderStream> video_decoder_stream_;
...@@ -571,6 +582,56 @@ TEST_P(VideoDecoderStreamTest, Read_AfterReset) { ...@@ -571,6 +582,56 @@ TEST_P(VideoDecoderStreamTest, Read_AfterReset) {
Read(); Read();
} }
TEST_P(VideoDecoderStreamTest, Read_ProperMetadata) {
// For testing simplicity, omit parallel decode tests with a delay in frames.
if (GetParam().parallel_decoding > 1 && GetParam().decoding_delay > 0)
return;
if (GetParam().has_prepare) {
// Override the basic PrepareFrame() for a version that moves the MockTime
// by kPrepareDelay. This simulates real work done (e.g. YUV conversion).
video_decoder_stream_->SetPrepareCB(
base::BindRepeating(&VideoDecoderStreamTest::PrepareFrameWithDelay,
base::Unretained(this)));
}
constexpr base::TimeDelta kDecodeDelay =
base::TimeDelta::FromMilliseconds(10);
Initialize();
// Simulate time elapsed by the decoder.
EnterPendingState(DECODER_DECODE);
task_environment_.FastForwardBy(kDecodeDelay);
SatisfyPendingCallback(DECODER_DECODE);
EXPECT_TRUE(frame_read_);
auto* metadata = frame_read_->metadata();
// Verify the decoding metadata is accurate.
base::TimeTicks decode_start;
EXPECT_TRUE(metadata->GetTimeTicks(VideoFrameMetadata::DECODE_BEGIN_TIME,
&decode_start));
base::TimeTicks decode_end;
EXPECT_TRUE(
metadata->GetTimeTicks(VideoFrameMetadata::DECODE_END_TIME, &decode_end));
EXPECT_EQ(decode_end - decode_start, kDecodeDelay);
// Verify the processing metadata is accurate.
const base::TimeDelta expected_processing_time =
GetParam().has_prepare ? (kDecodeDelay + kPrepareDelay) : kDecodeDelay;
base::TimeDelta processing_time;
EXPECT_TRUE(metadata->GetTimeDelta(VideoFrameMetadata::PROCESSING_TIME,
&processing_time));
EXPECT_EQ(processing_time, expected_processing_time);
}
TEST_P(VideoDecoderStreamTest, Read_BlockedDemuxer) { TEST_P(VideoDecoderStreamTest, Read_BlockedDemuxer) {
Initialize(); Initialize();
demuxer_stream_->HoldNextRead(); demuxer_stream_->HoldNextRead();
......
...@@ -100,9 +100,10 @@ void DoNothing(const scoped_refptr<rtc::RefCountInterface>& ref) {} ...@@ -100,9 +100,10 @@ void DoNothing(const scoped_refptr<rtc::RefCountInterface>& ref) {}
void MediaStreamRemoteVideoSource::RemoteVideoSourceDelegate::OnFrame( void MediaStreamRemoteVideoSource::RemoteVideoSourceDelegate::OnFrame(
const webrtc::VideoFrame& incoming_frame) { const webrtc::VideoFrame& incoming_frame) {
const bool render_immediately = incoming_frame.timestamp_us() == 0; const bool render_immediately = incoming_frame.timestamp_us() == 0;
const base::TimeTicks current_time = base::TimeTicks::Now();
const base::TimeDelta incoming_timestamp = const base::TimeDelta incoming_timestamp =
render_immediately render_immediately
? base::TimeTicks::Now() - base::TimeTicks() ? current_time - base::TimeTicks()
: base::TimeDelta::FromMicroseconds(incoming_frame.timestamp_us()); : base::TimeDelta::FromMicroseconds(incoming_frame.timestamp_us());
const base::TimeTicks render_time = const base::TimeTicks render_time =
render_immediately ? base::TimeTicks() + incoming_timestamp render_immediately ? base::TimeTicks() + incoming_timestamp
...@@ -208,8 +209,12 @@ void MediaStreamRemoteVideoSource::RemoteVideoSourceDelegate::OnFrame( ...@@ -208,8 +209,12 @@ void MediaStreamRemoteVideoSource::RemoteVideoSourceDelegate::OnFrame(
video_frame->metadata()->SetTimeTicks( video_frame->metadata()->SetTimeTicks(
media::VideoFrameMetadata::REFERENCE_TIME, render_time); media::VideoFrameMetadata::REFERENCE_TIME, render_time);
} }
video_frame->metadata()->SetTimeTicks(media::VideoFrameMetadata::DECODE_TIME, video_frame->metadata()->SetTimeTicks(
base::TimeTicks::Now()); media::VideoFrameMetadata::DECODE_END_TIME, current_time);
video_frame->metadata()->SetDouble(
media::VideoFrameMetadata::RTP_TIMESTAMP,
static_cast<double>(incoming_frame.timestamp()));
PostCrossThreadTask( PostCrossThreadTask(
*io_task_runner_, FROM_HERE, *io_task_runner_, FROM_HERE,
......
...@@ -525,7 +525,7 @@ viz::CompositorFrame VideoFrameSubmitter::CreateCompositorFrame( ...@@ -525,7 +525,7 @@ viz::CompositorFrame VideoFrameSubmitter::CreateCompositorFrame(
base::TimeTicks value; base::TimeTicks value;
if (video_frame && video_frame->metadata()->GetTimeTicks( if (video_frame && video_frame->metadata()->GetTimeTicks(
media::VideoFrameMetadata::DECODE_TIME, &value)) { media::VideoFrameMetadata::DECODE_END_TIME, &value)) {
TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0("media", "VideoFrameSubmitter", TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0("media", "VideoFrameSubmitter",
*next_frame_token_, value); *next_frame_token_, value);
TRACE_EVENT_ASYNC_STEP_PAST0("media", "VideoFrameSubmitter", TRACE_EVENT_ASYNC_STEP_PAST0("media", "VideoFrameSubmitter",
......
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