diff options
author | wez <wez@chromium.org> | 2015-07-10 14:43:33 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-10 21:45:29 +0000 |
commit | 9242f6c195c4b6ed37c1e565362f8324f9edc879 (patch) | |
tree | 1cbb3fd05eba7468c4878ac6ca649502da47cf1f | |
parent | b3da857eaa84821c47719a9ebe9c464aedbe02c7 (diff) | |
download | chromium_src-9242f6c195c4b6ed37c1e565362f8324f9edc879.zip chromium_src-9242f6c195c4b6ed37c1e565362f8324f9edc879.tar.gz chromium_src-9242f6c195c4b6ed37c1e565362f8324f9edc879.tar.bz2 |
Supply empty frames until codec is done with top-off.
Previously the VideoEncoderVpx would pass up to two empty/unchanged
frames to the codec when in lossy-VP9 mode, to allow some quality
top-off even if the desktop is otherwise idle. VideoEncoderVpx now calls
the codec until it reports that no changes were encoded.
This CL also uses a DesktopSize to hold the active-map dimensions.
BUG=134202
Review URL: https://codereview.chromium.org/1213323003
Cr-Commit-Position: refs/heads/master@{#338371}
-rw-r--r-- | remoting/codec/codec_test.cc | 16 | ||||
-rw-r--r-- | remoting/codec/codec_test.h | 4 | ||||
-rw-r--r-- | remoting/codec/video_encoder_vpx.cc | 55 | ||||
-rw-r--r-- | remoting/codec/video_encoder_vpx.h | 8 | ||||
-rw-r--r-- | remoting/codec/video_encoder_vpx_unittest.cc | 10 |
5 files changed, 48 insertions, 45 deletions
diff --git a/remoting/codec/codec_test.cc b/remoting/codec/codec_test.cc index 51b52f0..c5e1dae 100644 --- a/remoting/codec/codec_test.cc +++ b/remoting/codec/codec_test.cc @@ -284,7 +284,8 @@ void TestVideoEncoder(VideoEncoder* encoder, bool strict) { } } -void TestVideoEncoderEmptyFrames(VideoEncoder* encoder, int topoff_frames) { +void TestVideoEncoderEmptyFrames(VideoEncoder* encoder, + int max_topoff_frames) { const DesktopSize kSize(640, 480); scoped_ptr<DesktopFrame> frame(PrepareFrame(kSize)); @@ -292,12 +293,19 @@ void TestVideoEncoderEmptyFrames(VideoEncoder* encoder, int topoff_frames) { webrtc::DesktopRect::MakeSize(kSize)); EXPECT_TRUE(encoder->Encode(*frame)); + int topoff_frames = 0; frame->mutable_updated_region()->Clear(); - for (int i=0; i < topoff_frames; ++i) { - EXPECT_TRUE(encoder->Encode(*frame)); + for (int i = 0; i < max_topoff_frames + 1; ++i) { + if (!encoder->Encode(*frame)) + break; + topoff_frames++; } - EXPECT_FALSE(encoder->Encode(*frame)); + // If top-off is enabled then our random frame contents should always + // trigger it, so expect at least one top-off frame - strictly, though, + // an encoder may not always need to top-off. + EXPECT_GE(topoff_frames, max_topoff_frames ? 1 : 0); + EXPECT_LE(topoff_frames, max_topoff_frames); } static void TestEncodeDecodeRects(VideoEncoder* encoder, diff --git a/remoting/codec/codec_test.h b/remoting/codec/codec_test.h index 0c143b7..e5d5bb6 100644 --- a/remoting/codec/codec_test.h +++ b/remoting/codec/codec_test.h @@ -28,9 +28,9 @@ void TestVideoEncoder(VideoEncoder* encoder, bool strict); // Generate test data and test the encoder for a sequence of one "changed" // frame followed by one or more "unchanged" frames, and verify that the -// encoder sends exactly |topoff_frames| of non-empty data for unchanged +// encoder sends up to |max_topoff_frames| of non-empty data for unchanged // frames, after which it returns null frames. -void TestVideoEncoderEmptyFrames(VideoEncoder* encoder, int topoff_frames); +void TestVideoEncoderEmptyFrames(VideoEncoder* encoder, int max_topoff_frames); // Generate test data and test the encoder and decoder pair. // diff --git a/remoting/codec/video_encoder_vpx.cc b/remoting/codec/video_encoder_vpx.cc index adc6bf9..3c54652 100644 --- a/remoting/codec/video_encoder_vpx.cc +++ b/remoting/codec/video_encoder_vpx.cc @@ -266,21 +266,9 @@ scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( DCHECK_LE(32, frame.size().width()); DCHECK_LE(32, frame.size().height()); - if (!use_vp9_ || lossless_encode_) { - // Neither VP8 nor VP9-lossless support top-off, so ignore unchanged frames. - if (frame.updated_region().is_empty()) - return nullptr; - } else { - // Let VP9-lossy mode top-off, by continuing to pass it unchanged frames - // for a short while. - if (frame.updated_region().is_empty()) { - if (topoff_frame_count_ == 0) - return nullptr; - topoff_frame_count_--; - } else { - topoff_frame_count_ = 2; - } - } + // If there is nothing to encode, and nothing to top-off, then return nothing. + if (frame.updated_region().is_empty() && !encode_unchanged_frame_) + return nullptr; base::TimeTicks encode_start_time = base::TimeTicks::Now(); @@ -300,8 +288,8 @@ scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( // Apply active map to the encoder. vpx_active_map_t act_map; - act_map.rows = active_map_height_; - act_map.cols = active_map_width_; + act_map.rows = active_map_size_.height(); + act_map.cols = active_map_size_.width(); act_map.active_map = active_map_.get(); if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { LOG(ERROR) << "Unable to apply active map"; @@ -322,6 +310,9 @@ scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( << "Failed to fetch active map: " << vpx_codec_err_to_string(ret) << "\n"; UpdateRegionFromActiveMap(&updated_region); + + // If the encoder output no changes then there's nothing left to top-off. + encode_unchanged_frame_ = !updated_region.is_empty(); } // Read the encoded data. @@ -357,7 +348,8 @@ scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( return packet.Pass(); } -VideoEncoderVpx::VideoEncoderVpx(bool use_vp9) : use_vp9_(use_vp9) { +VideoEncoderVpx::VideoEncoderVpx(bool use_vp9) + : use_vp9_(use_vp9), encode_unchanged_frame_(false) { } void VideoEncoderVpx::Configure(const webrtc::DesktopSize& size) { @@ -370,9 +362,11 @@ void VideoEncoderVpx::Configure(const webrtc::DesktopSize& size) { FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_); // Initialize active map. - active_map_width_ = (size.width() + kMacroBlockSize - 1) / kMacroBlockSize; - active_map_height_ = (size.height() + kMacroBlockSize - 1) / kMacroBlockSize; - active_map_.reset(new uint8[active_map_width_ * active_map_height_]); + active_map_size_ = webrtc::DesktopSize( + (size.width() + kMacroBlockSize - 1) / kMacroBlockSize, + (size.height() + kMacroBlockSize - 1) / kMacroBlockSize); + active_map_.reset( + new uint8[active_map_size_.width() * active_map_size_.height()]); // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration. // See https://code.google.com/p/webm/issues/detail?id=912. @@ -507,7 +501,8 @@ void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame, void VideoEncoderVpx::SetActiveMapFromRegion( const webrtc::DesktopRegion& updated_region) { // Clear active map first. - memset(active_map_.get(), 0, active_map_width_ * active_map_height_); + memset(active_map_.get(), 0, + active_map_size_.width() * active_map_size_.height()); // Mark updated areas active. for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); @@ -517,14 +512,14 @@ void VideoEncoderVpx::SetActiveMapFromRegion( int right = (rect.right() - 1) / kMacroBlockSize; int top = rect.top() / kMacroBlockSize; int bottom = (rect.bottom() - 1) / kMacroBlockSize; - DCHECK_LT(right, active_map_width_); - DCHECK_LT(bottom, active_map_height_); + DCHECK_LT(right, active_map_size_.width()); + DCHECK_LT(bottom, active_map_size_.height()); - uint8* map = active_map_.get() + top * active_map_width_; + uint8* map = active_map_.get() + top * active_map_size_.width(); for (int y = top; y <= bottom; ++y) { for (int x = left; x <= right; ++x) map[x] = 1; - map += active_map_width_; + map += active_map_size_.width(); } } } @@ -532,11 +527,11 @@ void VideoEncoderVpx::SetActiveMapFromRegion( void VideoEncoderVpx::UpdateRegionFromActiveMap( webrtc::DesktopRegion* updated_region) { const uint8* map = active_map_.get(); - for (int y = 0; y < active_map_height_; ++y) { - for (int x0 = 0; x0 < active_map_width_;) { + for (int y = 0; y < active_map_size_.height(); ++y) { + for (int x0 = 0; x0 < active_map_size_.width();) { int x1 = x0; - for (; x1 < active_map_width_; ++x1) { - if (map[y * active_map_width_ + x1] == 0) + for (; x1 < active_map_size_.width(); ++x1) { + if (map[y * active_map_size_.width() + x1] == 0) break; } if (x1 > x0) { diff --git a/remoting/codec/video_encoder_vpx.h b/remoting/codec/video_encoder_vpx.h index dbfefeb..7d80592 100644 --- a/remoting/codec/video_encoder_vpx.h +++ b/remoting/codec/video_encoder_vpx.h @@ -73,12 +73,10 @@ class VideoEncoderVpx : public VideoEncoder { // Active map used to optimize out processing of un-changed macroblocks. scoped_ptr<uint8[]> active_map_; - int active_map_width_ = 0; - int active_map_height_ = 0; + webrtc::DesktopSize active_map_size_; - // Number of "top-off" frames we've encoded since we were last passed a - // frame containing an actual change. Used only when encoding with top-off. - int topoff_frame_count_ = 0; + // True if the codec wants unchanged frames to finish topping-off with. + bool encode_unchanged_frame_; // Used to help initialize VideoPackets from DesktopFrames. VideoEncoderHelper helper_; diff --git a/remoting/codec/video_encoder_vpx_unittest.cc b/remoting/codec/video_encoder_vpx_unittest.cc index efe07d2..dfb3627 100644 --- a/remoting/codec/video_encoder_vpx_unittest.cc +++ b/remoting/codec/video_encoder_vpx_unittest.cc @@ -162,21 +162,23 @@ TEST(VideoEncoderVpxTest, DpiPropagation) { EXPECT_EQ(packet->format().y_dpi(), 97); } -TEST(VideoEncoderVerbatimTest, Vp8EncodeUnchangedFrame) { +TEST(VideoEncoderVpxTest, Vp8EncodeUnchangedFrame) { scoped_ptr<VideoEncoderVpx> encoder(VideoEncoderVpx::CreateForVP8()); TestVideoEncoderEmptyFrames(encoder.get(), 0); } -TEST(VideoEncoderVerbatimTest, Vp9LosslessUnchangedFrame) { +TEST(VideoEncoderVpxTest, Vp9LosslessUnchangedFrame) { scoped_ptr<VideoEncoderVpx> encoder(VideoEncoderVpx::CreateForVP9()); encoder->SetLosslessEncode(true); TestVideoEncoderEmptyFrames(encoder.get(), 0); } -TEST(VideoEncoderVerbatimTest, Vp9LossyUnchangedFrame) { +TEST(VideoEncoderVpxTest, Vp9LossyUnchangedFrame) { scoped_ptr<VideoEncoderVpx> encoder(VideoEncoderVpx::CreateForVP9()); encoder->SetLosslessEncode(false); - TestVideoEncoderEmptyFrames(encoder.get(), 2); + // Expect that VP9+CR should generate no more than 10 top-off frames + // per cycle, and take no more than 2 cycles to top-off. + TestVideoEncoderEmptyFrames(encoder.get(), 20); } } // namespace remoting |