Commit d9e74151 authored by miu's avatar miu Committed by Commit bot

[Cast] Compute utilization metrics and add performance overlay.

Adds computation of two frame-level utilization metrics to the software
VP8 encoder in Cast: deadline utilization and lossy utilization.  The
first is a measure of how long the encoding of each frame takes compared
to the frame duration.  The second is a measure of the complexity of a
frame, in terms of the quality versus encoded size trade-off.

In a future change, these utilization metrics will be sent as feedback
signals to the producer of the video frames, allowing the producer to
adjust data volumes based on the consumer's capability throughout a
session.  See bug for more details.

Also, this change adds an overlay display, where frame-level performance
metrics are rendered in the lower-right corner of each video frame just
before it is sent.  This provides an "on screen display" of end-to-end
system performance.  This is turned on with a command line argument:
--vmodule=performance_metrics_overlay=3

BUG=156767

Review URL: https://codereview.chromium.org/1148233002

Cr-Commit-Position: refs/heads/master@{#330896}
parent 74a2a34f
...@@ -121,6 +121,10 @@ source_set("sender") { ...@@ -121,6 +121,10 @@ source_set("sender") {
"sender/fake_software_video_encoder.h", "sender/fake_software_video_encoder.h",
"sender/frame_sender.cc", "sender/frame_sender.cc",
"sender/frame_sender.h", "sender/frame_sender.h",
"sender/performance_metrics_overlay.cc",
"sender/performance_metrics_overlay.h",
"sender/sender_encoded_frame.cc",
"sender/sender_encoded_frame.h",
"sender/size_adaptable_video_encoder_base.cc", "sender/size_adaptable_video_encoder_base.cc",
"sender/size_adaptable_video_encoder_base.h", "sender/size_adaptable_video_encoder_base.h",
"sender/software_video_encoder.h", "sender/software_video_encoder.h",
...@@ -172,16 +176,12 @@ source_set("sender") { ...@@ -172,16 +176,12 @@ source_set("sender") {
"sender/h264_vt_encoder.h", "sender/h264_vt_encoder.h",
] ]
libs += [ libs += [ "CoreVideo.framework" ]
"CoreVideo.framework",
]
} }
if (is_mac) { if (is_mac) {
# Required by audio_encoder.cc. # Required by audio_encoder.cc.
libs += [ libs += [ "AudioToolbox.framework" ]
"AudioToolbox.framework",
]
} }
} }
......
...@@ -162,6 +162,10 @@ ...@@ -162,6 +162,10 @@
'sender/fake_software_video_encoder.h', 'sender/fake_software_video_encoder.h',
'sender/frame_sender.cc', 'sender/frame_sender.cc',
'sender/frame_sender.h', 'sender/frame_sender.h',
'sender/performance_metrics_overlay.cc',
'sender/performance_metrics_overlay.h',
'sender/sender_encoded_frame.cc',
'sender/sender_encoded_frame.h',
'sender/size_adaptable_video_encoder_base.cc', 'sender/size_adaptable_video_encoder_base.cc',
'sender/size_adaptable_video_encoder_base.h', 'sender/size_adaptable_video_encoder_base.h',
'sender/software_video_encoder.h', 'sender/software_video_encoder.h',
......
...@@ -71,7 +71,7 @@ struct EncodedFrame { ...@@ -71,7 +71,7 @@ struct EncodedFrame {
}; };
EncodedFrame(); EncodedFrame();
~EncodedFrame(); virtual ~EncodedFrame();
// Convenience accessors to data as an array of uint8 elements. // Convenience accessors to data as an array of uint8 elements.
const uint8* bytes() const { const uint8* bytes() const {
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "media/cast/cast_config.h" #include "media/cast/cast_config.h"
#include "media/cast/receiver/video_decoder.h" #include "media/cast/receiver/video_decoder.h"
#include "media/cast/sender/sender_encoded_frame.h"
#include "media/cast/sender/vp8_encoder.h" #include "media/cast/sender/vp8_encoder.h"
#include "media/cast/test/utility/default_config.h" #include "media/cast/test/utility/default_config.h"
#include "media/cast/test/utility/standalone_cast_environment.h" #include "media/cast/test/utility/standalone_cast_environment.h"
...@@ -84,7 +85,7 @@ class VideoDecoderTest : public ::testing::TestWithParam<Codec> { ...@@ -84,7 +85,7 @@ class VideoDecoderTest : public ::testing::TestWithParam<Codec> {
PopulateVideoFrame(video_frame.get(), 0); PopulateVideoFrame(video_frame.get(), 0);
// Encode |frame| into |encoded_frame->data|. // Encode |frame| into |encoded_frame->data|.
scoped_ptr<EncodedFrame> encoded_frame(new EncodedFrame()); scoped_ptr<SenderEncodedFrame> encoded_frame(new SenderEncodedFrame());
// Test only supports VP8, currently. // Test only supports VP8, currently.
CHECK_EQ(CODEC_VIDEO_VP8, GetParam()); CHECK_EQ(CODEC_VIDEO_VP8, GetParam());
vp8_encoder_.Encode(video_frame, reference_time, encoded_frame.get()); vp8_encoder_.Encode(video_frame, reference_time, encoded_frame.get());
......
...@@ -200,7 +200,7 @@ class ExternalVideoEncoder::VEAClientImpl ...@@ -200,7 +200,7 @@ class ExternalVideoEncoder::VEAClientImpl
} else if (!in_progress_frame_encodes_.empty()) { } else if (!in_progress_frame_encodes_.empty()) {
const InProgressFrameEncode& request = in_progress_frame_encodes_.front(); const InProgressFrameEncode& request = in_progress_frame_encodes_.front();
scoped_ptr<EncodedFrame> encoded_frame(new EncodedFrame()); scoped_ptr<SenderEncodedFrame> encoded_frame(new SenderEncodedFrame());
encoded_frame->dependency = key_frame ? EncodedFrame::KEY : encoded_frame->dependency = key_frame ? EncodedFrame::KEY :
EncodedFrame::DEPENDENT; EncodedFrame::DEPENDENT;
encoded_frame->frame_id = next_frame_id_++; encoded_frame->frame_id = next_frame_id_++;
...@@ -216,6 +216,8 @@ class ExternalVideoEncoder::VEAClientImpl ...@@ -216,6 +216,8 @@ class ExternalVideoEncoder::VEAClientImpl
} }
encoded_frame->data.append( encoded_frame->data.append(
static_cast<const char*>(output_buffer->memory()), payload_size); static_cast<const char*>(output_buffer->memory()), payload_size);
// TODO(miu): Compute and populate the |deadline_utilization| and
// |lossy_utilization| performance metrics in |encoded_frame|.
cast_environment_->PostTask( cast_environment_->PostTask(
CastEnvironment::MAIN, CastEnvironment::MAIN,
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/values.h" #include "base/values.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
#include "media/cast/net/cast_transport_config.h"
#ifndef OFFICIAL_BUILD #ifndef OFFICIAL_BUILD
...@@ -30,7 +29,7 @@ void FakeSoftwareVideoEncoder::Initialize() {} ...@@ -30,7 +29,7 @@ void FakeSoftwareVideoEncoder::Initialize() {}
void FakeSoftwareVideoEncoder::Encode( void FakeSoftwareVideoEncoder::Encode(
const scoped_refptr<media::VideoFrame>& video_frame, const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& reference_time, const base::TimeTicks& reference_time,
EncodedFrame* encoded_frame) { SenderEncodedFrame* encoded_frame) {
DCHECK(encoded_frame); DCHECK(encoded_frame);
if (video_frame->visible_rect().size() != last_frame_size_) { if (video_frame->visible_rect().size() != last_frame_size_) {
...@@ -60,6 +59,14 @@ void FakeSoftwareVideoEncoder::Encode( ...@@ -60,6 +59,14 @@ void FakeSoftwareVideoEncoder::Encode(
base::JSONWriter::Write(values, &encoded_frame->data); base::JSONWriter::Write(values, &encoded_frame->data);
encoded_frame->data.resize( encoded_frame->data.resize(
std::max<size_t>(encoded_frame->data.size(), frame_size_), ' '); std::max<size_t>(encoded_frame->data.size(), frame_size_), ' ');
if (encoded_frame->dependency == EncodedFrame::KEY) {
encoded_frame->deadline_utilization = 1.0;
encoded_frame->lossy_utilization = 6.0;
} else {
encoded_frame->deadline_utilization = 0.8;
encoded_frame->lossy_utilization = 0.8;
}
} }
void FakeSoftwareVideoEncoder::UpdateRates(uint32 new_bitrate) { void FakeSoftwareVideoEncoder::UpdateRates(uint32 new_bitrate) {
......
...@@ -21,7 +21,7 @@ class FakeSoftwareVideoEncoder : public SoftwareVideoEncoder { ...@@ -21,7 +21,7 @@ class FakeSoftwareVideoEncoder : public SoftwareVideoEncoder {
void Initialize() final; void Initialize() final;
void Encode(const scoped_refptr<media::VideoFrame>& video_frame, void Encode(const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& reference_time, const base::TimeTicks& reference_time,
EncodedFrame* encoded_frame) final; SenderEncodedFrame* encoded_frame) final;
void UpdateRates(uint32 new_bitrate) final; void UpdateRates(uint32 new_bitrate) final;
void GenerateKeyFrame() final; void GenerateKeyFrame() final;
void LatestFrameIdToReference(uint32 frame_id) final; void LatestFrameIdToReference(uint32 frame_id) final;
......
...@@ -729,7 +729,7 @@ void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque, ...@@ -729,7 +729,7 @@ void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque,
// frame. VideoToolbox calls the output callback serially, so this is safe. // frame. VideoToolbox calls the output callback serially, so this is safe.
const uint32 frame_id = ++encoder->last_frame_id_; const uint32 frame_id = ++encoder->last_frame_id_;
scoped_ptr<EncodedFrame> encoded_frame(new EncodedFrame()); scoped_ptr<SenderEncodedFrame> encoded_frame(new SenderEncodedFrame());
encoded_frame->frame_id = frame_id; encoded_frame->frame_id = frame_id;
encoded_frame->reference_time = request->reference_time; encoded_frame->reference_time = request->reference_time;
encoded_frame->rtp_timestamp = request->rtp_timestamp; encoded_frame->rtp_timestamp = request->rtp_timestamp;
...@@ -753,6 +753,9 @@ void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque, ...@@ -753,6 +753,9 @@ void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque,
if (has_frame_data) if (has_frame_data)
CopySampleBufferToAnnexBBuffer(sbuf, &encoded_frame->data, keyframe); CopySampleBufferToAnnexBBuffer(sbuf, &encoded_frame->data, keyframe);
// TODO(miu): Compute and populate the |deadline_utilization| and
// |lossy_utilization| performance metrics in |encoded_frame|.
encoder->cast_environment_->PostTask( encoder->cast_environment_->PostTask(
CastEnvironment::MAIN, FROM_HERE, CastEnvironment::MAIN, FROM_HERE,
base::Bind(request->frame_encoded_callback, base::Bind(request->frame_encoded_callback,
......
This diff is collapsed.
// Copyright 2015 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.
#ifndef MEDIA_CAST_SENDER_PERFORMANCE_METRICS_OVERLAY_H_
#define MEDIA_CAST_SENDER_PERFORMANCE_METRICS_OVERLAY_H_
// This module provides a display of frame-level performance metrics, rendered
// in the lower-right corner of a VideoFrame. It looks like this:
//
// +----------------------------------------------------------------+
// | @@@@@@@@@@@@@@@@@@@@@@@ |
// | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
// | @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@ |
// | @@@@@@@@@@@@@ @@@@ |
// | @@@@@@@@@@ @@@@ |
// | @@@@@ @@@ @@@ @@@@ |
// | @@@ @ @@@ @@@@ @@@@ |
// | @@@@ @@@@ @@@@ |
// | @@@@ @@@ @@@ |
// | @@@@ @@ @@@ |
// | @@@@@ @@@ @@@ @@@ |
// | @@@@@ @@@@@ @@@@ @@@@ |
// | @@@@@ @@@@@@@@@@@@@ @@@@ |
// | @@@@@@ @@@@ 1 45% 75% |
// | @@@@@@@@ @@@@@@ 22 16.7 4000 |
// | @@@@@@@@@@@@@@@@ 1280x720 0:15.12 |
// +----------------------------------------------------------------+
//
// Line 1: Reads as, "1 frame ago, the encoder deadline utilization for the
// frame was 45% and the lossy utilization was 75%." Encoder deadline
// utilization is in terms the amount of real-world time it took to encode the
// frame, divided by the maximum amount of time allowed. Lossy utilization is
// the amount of "complexity" in the frame's content versus the target encoded
// byte size, where a value over 100% means the frame's content is too complex
// to encode within the target number of bytes.
//
// Line 2: Reads as, "Capture of this frame took 22 ms. The expected duration
// of this frame is 16.7 ms. The target bitrate for this frame is 4000 kbps."
//
// Line 3: Contains the frame's resolution and media timestamp in
// minutes:seconds.hundredths format.
namespace media {
class VideoFrame;
namespace cast {
// Renders an overlay of frame-level performance metrics in the lower-right
// corner of the |frame|, as described above. The verbose logging level for
// video_frame_overlay.cc determines which lines, if any, are rendered: VLOG
// level 1 renders the bottom line only, level 2 renders the bottom and middle
// lines, and level 3 renders all three lines. So, use the
// --vmodule=performance_metrics_overlay=3 command line argument to turn on
// rendering of the entire overlay.
void MaybeRenderPerformanceMetricsOverlay(int target_bitrate,
int frames_ago,
double deadline_utilization,
double lossy_utilization,
VideoFrame* frame);
} // namespace cast
} // namespace media
#endif // MEDIA_CAST_SENDER_PERFORMANCE_METRICS_OVERLAY_H_
// Copyright 2015 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/cast/sender/sender_encoded_frame.h"
namespace media {
namespace cast {
SenderEncodedFrame::SenderEncodedFrame()
: EncodedFrame(),
deadline_utilization(-1.0),
lossy_utilization(-1.0) {}
SenderEncodedFrame::~SenderEncodedFrame() {}
} // namespace cast
} // namespace media
// Copyright 2015 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.
#ifndef MEDIA_CAST_SENDER_SENDER_ENCODED_FRAME_H_
#define MEDIA_CAST_SENDER_SENDER_ENCODED_FRAME_H_
#include "media/cast/net/cast_transport_config.h"
namespace media {
namespace cast {
// Extends EncodedFrame with additional fields used within the sender-side of
// the library.
struct SenderEncodedFrame : public EncodedFrame {
SenderEncodedFrame();
~SenderEncodedFrame() final;
// The amount of real-world time it took to encode the frame, divided by the
// maximum amount of time allowed. Example: For the software VP8 encoder,
// this would be the elapsed encode time (according to the base::TimeTicks
// clock) divided by the VideoFrame's duration.
//
// Meaningful values are non-negative, with 0.0 [impossibly] representing 0%
// utilization, 1.0 representing 100% utilization, and values greater than 1.0
// indicating the encode time took longer than the media duration of the
// frame. Negative values indicate the field was not computed.
double deadline_utilization;
// The amount of "lossiness" needed to encode the frame within the targeted
// bandwidth. More-complex frame content and/or lower target encode bitrates
// will cause this value to rise.
//
// Meaningful values are non-negative, with 0.0 indicating the frame is very
// simple and/or the target encode bitrate is very large, 1.0 indicating the
// frame contains very complex content and/or the target encode bitrate is
// very small, and values greater than 1.0 indicating the encoder cannot
// encode the frame within the target bitrate (even at its lowest quality
// setting). Negative values indicate the field was not computed.
double lossy_utilization;
};
} // namespace cast
} // namespace media
#endif // MEDIA_CAST_SENDER_SENDER_ENCODED_FRAME_H_
...@@ -156,7 +156,7 @@ void SizeAdaptableVideoEncoderBase::OnEncoderStatusChange( ...@@ -156,7 +156,7 @@ void SizeAdaptableVideoEncoderBase::OnEncoderStatusChange(
void SizeAdaptableVideoEncoderBase::OnEncodedVideoFrame( void SizeAdaptableVideoEncoderBase::OnEncodedVideoFrame(
const FrameEncodedCallback& frame_encoded_callback, const FrameEncodedCallback& frame_encoded_callback,
scoped_ptr<EncodedFrame> encoded_frame) { scoped_ptr<SenderEncodedFrame> encoded_frame) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
--frames_in_encoder_; --frames_in_encoder_;
DCHECK_GE(frames_in_encoder_, 0); DCHECK_GE(frames_in_encoder_, 0);
......
...@@ -85,7 +85,7 @@ class SizeAdaptableVideoEncoderBase : public VideoEncoder { ...@@ -85,7 +85,7 @@ class SizeAdaptableVideoEncoderBase : public VideoEncoder {
// Called by the |encoder_| with the next EncodedFrame. // Called by the |encoder_| with the next EncodedFrame.
void OnEncodedVideoFrame(const FrameEncodedCallback& frame_encoded_callback, void OnEncodedVideoFrame(const FrameEncodedCallback& frame_encoded_callback,
scoped_ptr<EncodedFrame> encoded_frame); scoped_ptr<SenderEncodedFrame> encoded_frame);
const scoped_refptr<CastEnvironment> cast_environment_; const scoped_refptr<CastEnvironment> cast_environment_;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "media/cast/sender/sender_encoded_frame.h"
namespace base { namespace base {
class TimeTicks; class TimeTicks;
...@@ -18,7 +19,6 @@ class VideoFrame; ...@@ -18,7 +19,6 @@ class VideoFrame;
namespace media { namespace media {
namespace cast { namespace cast {
struct EncodedFrame;
class SoftwareVideoEncoder { class SoftwareVideoEncoder {
public: public:
...@@ -31,7 +31,7 @@ class SoftwareVideoEncoder { ...@@ -31,7 +31,7 @@ class SoftwareVideoEncoder {
// Encode a raw image (as a part of a video stream). // Encode a raw image (as a part of a video stream).
virtual void Encode(const scoped_refptr<media::VideoFrame>& video_frame, virtual void Encode(const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& reference_time, const base::TimeTicks& reference_time,
EncodedFrame* encoded_frame) = 0; SenderEncodedFrame* encoded_frame) = 0;
// Update the encoder with a new target bit rate. // Update the encoder with a new target bit rate.
virtual void UpdateRates(uint32 new_bitrate) = 0; virtual void UpdateRates(uint32 new_bitrate) = 0;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
#include "media/cast/cast_config.h" #include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h" #include "media/cast/cast_environment.h"
#include "media/cast/sender/sender_encoded_frame.h"
#include "media/cast/sender/video_frame_factory.h" #include "media/cast/sender/video_frame_factory.h"
namespace media { namespace media {
...@@ -20,7 +21,9 @@ namespace cast { ...@@ -20,7 +21,9 @@ namespace cast {
// All these functions are called from the main cast thread. // All these functions are called from the main cast thread.
class VideoEncoder { class VideoEncoder {
public: public:
typedef base::Callback<void(scoped_ptr<EncodedFrame>)> FrameEncodedCallback; // Callback used to deliver an encoded frame on the Cast MAIN thread.
using FrameEncodedCallback =
base::Callback<void(scoped_ptr<SenderEncodedFrame>)>;
// Creates a VideoEncoder instance from the given |video_config| and based on // Creates a VideoEncoder instance from the given |video_config| and based on
// the current platform's hardware/library support; or null if no // the current platform's hardware/library support; or null if no
......
...@@ -43,7 +43,7 @@ void EncodeVideoFrameOnEncoderThread( ...@@ -43,7 +43,7 @@ void EncodeVideoFrameOnEncoderThread(
dynamic_config.latest_frame_id_to_reference); dynamic_config.latest_frame_id_to_reference);
encoder->UpdateRates(dynamic_config.bit_rate); encoder->UpdateRates(dynamic_config.bit_rate);
scoped_ptr<EncodedFrame> encoded_frame(new EncodedFrame()); scoped_ptr<SenderEncodedFrame> encoded_frame(new SenderEncodedFrame());
encoder->Encode(video_frame, reference_time, encoded_frame.get()); encoder->Encode(video_frame, reference_time, encoded_frame.get());
environment->PostTask( environment->PostTask(
CastEnvironment::MAIN, CastEnvironment::MAIN,
......
...@@ -26,9 +26,6 @@ class VideoEncoderImpl : public VideoEncoder { ...@@ -26,9 +26,6 @@ class VideoEncoderImpl : public VideoEncoder {
int bit_rate; int bit_rate;
}; };
typedef base::Callback<void(scoped_ptr<EncodedFrame>)>
FrameEncodedCallback;
// Returns true if VideoEncoderImpl can be used with the given |video_config|. // Returns true if VideoEncoderImpl can be used with the given |video_config|.
static bool IsSupported(const VideoSenderConfig& video_config); static bool IsSupported(const VideoSenderConfig& video_config);
......
...@@ -219,7 +219,7 @@ class VideoEncoderTest ...@@ -219,7 +219,7 @@ class VideoEncoderTest
uint32 expected_last_referenced_frame_id, uint32 expected_last_referenced_frame_id,
uint32 expected_rtp_timestamp, uint32 expected_rtp_timestamp,
const base::TimeTicks& expected_reference_time, const base::TimeTicks& expected_reference_time,
scoped_ptr<EncodedFrame> encoded_frame) { scoped_ptr<SenderEncodedFrame> encoded_frame) {
EXPECT_TRUE(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); EXPECT_TRUE(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
EXPECT_EQ(expected_frame_id, encoded_frame->frame_id); EXPECT_EQ(expected_frame_id, encoded_frame->frame_id);
...@@ -253,6 +253,10 @@ class VideoEncoderTest ...@@ -253,6 +253,10 @@ class VideoEncoderTest
EXPECT_EQ(expected_last_referenced_frame_id, EXPECT_EQ(expected_last_referenced_frame_id,
encoded_frame->referenced_frame_id); encoded_frame->referenced_frame_id);
EXPECT_FALSE(encoded_frame->data.empty()); EXPECT_FALSE(encoded_frame->data.empty());
ASSERT_TRUE(std::isfinite(encoded_frame->deadline_utilization));
EXPECT_LE(0.0, encoded_frame->deadline_utilization);
ASSERT_TRUE(std::isfinite(encoded_frame->lossy_utilization));
EXPECT_LE(0.0, encoded_frame->lossy_utilization);
} }
++count_frames_delivered_; ++count_frames_delivered_;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "media/cast/cast_defines.h" #include "media/cast/cast_defines.h"
#include "media/cast/net/cast_transport_config.h" #include "media/cast/net/cast_transport_config.h"
#include "media/cast/sender/performance_metrics_overlay.h"
#include "media/cast/sender/video_encoder.h" #include "media/cast/sender/video_encoder.h"
namespace media { namespace media {
...@@ -87,6 +88,8 @@ VideoSender::VideoSender( ...@@ -87,6 +88,8 @@ VideoSender::VideoSender(
frames_in_encoder_(0), frames_in_encoder_(0),
last_bitrate_(0), last_bitrate_(0),
playout_delay_change_cb_(playout_delay_change_cb), playout_delay_change_cb_(playout_delay_change_cb),
last_reported_deadline_utilization_(-1.0),
last_reported_lossy_utilization_(-1.0),
weak_factory_(this) { weak_factory_(this) {
video_encoder_ = VideoEncoder::Create( video_encoder_ = VideoEncoder::Create(
cast_environment_, cast_environment_,
...@@ -194,6 +197,12 @@ void VideoSender::InsertRawVideoFrame( ...@@ -194,6 +197,12 @@ void VideoSender::InsertRawVideoFrame(
return; return;
} }
MaybeRenderPerformanceMetricsOverlay(bitrate,
frames_in_encoder_ + 1,
last_reported_deadline_utilization_,
last_reported_lossy_utilization_,
video_frame.get());
if (video_encoder_->EncodeVideoFrame( if (video_encoder_->EncodeVideoFrame(
video_frame, video_frame,
reference_time, reference_time,
...@@ -233,7 +242,7 @@ void VideoSender::OnAck(uint32 frame_id) { ...@@ -233,7 +242,7 @@ void VideoSender::OnAck(uint32 frame_id) {
void VideoSender::OnEncodedVideoFrame( void VideoSender::OnEncodedVideoFrame(
int encoder_bitrate, int encoder_bitrate,
scoped_ptr<EncodedFrame> encoded_frame) { scoped_ptr<SenderEncodedFrame> encoded_frame) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
frames_in_encoder_--; frames_in_encoder_--;
...@@ -242,6 +251,11 @@ void VideoSender::OnEncodedVideoFrame( ...@@ -242,6 +251,11 @@ void VideoSender::OnEncodedVideoFrame(
duration_in_encoder_ = duration_in_encoder_ =
last_enqueued_frame_reference_time_ - encoded_frame->reference_time; last_enqueued_frame_reference_time_ - encoded_frame->reference_time;
last_reported_deadline_utilization_ = encoded_frame->deadline_utilization;
last_reported_lossy_utilization_ = encoded_frame->lossy_utilization;
// TODO(miu): Plumb-in a utilization feedback signal back to the producer of
// the video frames. http://crbug.com/156767
SendEncodedFrame(encoder_bitrate, encoded_frame.Pass()); SendEncodedFrame(encoder_bitrate, encoded_frame.Pass());
} }
......
...@@ -23,6 +23,7 @@ class VideoFrame; ...@@ -23,6 +23,7 @@ class VideoFrame;
namespace cast { namespace cast {
class CastTransportSender; class CastTransportSender;
struct SenderEncodedFrame;
class VideoEncoder; class VideoEncoder;
class VideoFrameFactory; class VideoFrameFactory;
...@@ -67,7 +68,7 @@ class VideoSender : public FrameSender, ...@@ -67,7 +68,7 @@ class VideoSender : public FrameSender,
private: private:
// Called by the |video_encoder_| with the next EncodedFrame to send. // Called by the |video_encoder_| with the next EncodedFrame to send.
void OnEncodedVideoFrame(int encoder_bitrate, void OnEncodedVideoFrame(int encoder_bitrate,
scoped_ptr<EncodedFrame> encoded_frame); scoped_ptr<SenderEncodedFrame> encoded_frame);
// Encodes media::VideoFrame images into EncodedFrames. Per configuration, // Encodes media::VideoFrame images into EncodedFrames. Per configuration,
// this will point to either the internal software-based encoder or a proxy to // this will point to either the internal software-based encoder or a proxy to
...@@ -90,6 +91,12 @@ class VideoSender : public FrameSender, ...@@ -90,6 +91,12 @@ class VideoSender : public FrameSender,
PlayoutDelayChangeCB playout_delay_change_cb_; PlayoutDelayChangeCB playout_delay_change_cb_;
// The video encoder's performance metrics as of the last call to
// OnEncodedVideoFrame(). See header file comments for SenderEncodedFrame for
// an explanation of these values.
double last_reported_deadline_utilization_;
double last_reported_lossy_utilization_;
// NOTE: Weak pointers must be invalidated before all other member variables. // NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<VideoSender> weak_factory_; base::WeakPtrFactory<VideoSender> weak_factory_;
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#include "base/logging.h" #include "base/logging.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
#include "media/cast/cast_defines.h" #include "media/cast/cast_defines.h"
#include "media/cast/net/cast_transport_config.h"
#include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
namespace media { namespace media {
...@@ -164,10 +163,15 @@ void Vp8Encoder::ConfigureForNewFrameSize(const gfx::Size& frame_size) { ...@@ -164,10 +163,15 @@ void Vp8Encoder::ConfigureForNewFrameSize(const gfx::Size& frame_size) {
void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& reference_time, const base::TimeTicks& reference_time,
EncodedFrame* encoded_frame) { SenderEncodedFrame* encoded_frame) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(encoded_frame); DCHECK(encoded_frame);
// Note: This is used to compute the |deadline_utilization| and so it uses the
// real-world clock instead of the CastEnvironment clock, the latter of which
// might be simulated.
const base::TimeTicks start_time = base::TimeTicks::Now();
// Initialize on-demand. Later, if the video frame size has changed, update // Initialize on-demand. Later, if the video frame size has changed, update
// the encoder configuration. // the encoder configuration.
const gfx::Size frame_size = video_frame->visible_rect().size(); const gfx::Size frame_size = video_frame->visible_rect().size();
...@@ -279,8 +283,37 @@ void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, ...@@ -279,8 +283,37 @@ void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
DCHECK(!encoded_frame->data.empty()) DCHECK(!encoded_frame->data.empty())
<< "BUG: Encoder must provide data since lagged encoding is disabled."; << "BUG: Encoder must provide data since lagged encoding is disabled.";
// Compute deadline utilization as the real-world time elapsed divided by the
// frame duration.
const base::TimeDelta processing_time = base::TimeTicks::Now() - start_time;
encoded_frame->deadline_utilization =
processing_time.InSecondsF() / predicted_frame_duration.InSecondsF();
// Compute lossy utilization. The VP8 encoder took an estimated guess at what
// quantizer value would produce an encoded frame size as close to the target
// as possible. Now that the frame has been encoded and the number of bytes
// is known, the perfect quantizer value (i.e., the one that should have been
// used) can be determined. This perfect quantizer is then normalized and
// used as the lossy utilization.
const double actual_bitrate =
encoded_frame->data.size() * 8.0 / predicted_frame_duration.InSecondsF();
const double target_bitrate = 1000.0 * config_.rc_target_bitrate;
DCHECK_GT(target_bitrate, 0.0);
const double bitrate_utilization = actual_bitrate / target_bitrate;
int quantizer = -1;
CHECK_EQ(vpx_codec_control(&encoder_, VP8E_GET_LAST_QUANTIZER_64, &quantizer),
VPX_CODEC_OK);
const double perfect_quantizer = bitrate_utilization * std::max(0, quantizer);
// Side note: If it was possible for the encoder to encode within the target
// number of bytes, the |perfect_quantizer| will be in the range [0.0,63.0].
// If it was never possible, the value will be greater than 63.0.
encoded_frame->lossy_utilization = perfect_quantizer / 63.0;
DVLOG(2) << "VP8 encoded frame_id " << encoded_frame->frame_id DVLOG(2) << "VP8 encoded frame_id " << encoded_frame->frame_id
<< ", sized:" << encoded_frame->data.size(); << ", sized: " << encoded_frame->data.size()
<< ", deadline_utilization: " << encoded_frame->deadline_utilization
<< ", lossy_utilization: " << encoded_frame->lossy_utilization
<< " (quantizer chosen by the encoder was " << quantizer << ')';
if (encoded_frame->dependency == EncodedFrame::KEY) { if (encoded_frame->dependency == EncodedFrame::KEY) {
key_frame_requested_ = false; key_frame_requested_ = false;
......
...@@ -30,7 +30,7 @@ class Vp8Encoder : public SoftwareVideoEncoder { ...@@ -30,7 +30,7 @@ class Vp8Encoder : public SoftwareVideoEncoder {
void Initialize() final; void Initialize() final;
void Encode(const scoped_refptr<media::VideoFrame>& video_frame, void Encode(const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& reference_time, const base::TimeTicks& reference_time,
EncodedFrame* encoded_frame) final; SenderEncodedFrame* encoded_frame) final;
void UpdateRates(uint32 new_bitrate) final; void UpdateRates(uint32 new_bitrate) final;
void GenerateKeyFrame() final; void GenerateKeyFrame() final;
void LatestFrameIdToReference(uint32 frame_id) final; void LatestFrameIdToReference(uint32 frame_id) final;
......
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