summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorwez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-25 20:03:34 +0000
committerwez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-25 20:03:34 +0000
commit6d30912e73f8f144777d5a01e87cd273dbdfbb38 (patch)
treeb301e2ff524bdf564cf1c669c6b5e33a01ea8085 /remoting
parentc76305371e9f64614031f4d6160be61ba7dbe5aa (diff)
downloadchromium_src-6d30912e73f8f144777d5a01e87cd273dbdfbb38.zip
chromium_src-6d30912e73f8f144777d5a01e87cd273dbdfbb38.tar.gz
chromium_src-6d30912e73f8f144777d5a01e87cd273dbdfbb38.tar.bz2
Add VideoFrameRecorder for use recording test frame sequences.
This will be used to record representative frame sequences for performance evaluation of the new VP9 encoder. BUG=260879 Review URL: https://codereview.chromium.org/339073002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@279795 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/host/video_frame_recorder.cc207
-rw-r--r--remoting/host/video_frame_recorder.h100
-rw-r--r--remoting/host/video_frame_recorder_unittest.cc279
-rw-r--r--remoting/remoting_host.gypi2
-rw-r--r--remoting/remoting_test.gypi1
5 files changed, 589 insertions, 0 deletions
diff --git a/remoting/host/video_frame_recorder.cc b/remoting/host/video_frame_recorder.cc
new file mode 100644
index 0000000..cccfe7c
--- /dev/null
+++ b/remoting/host/video_frame_recorder.cc
@@ -0,0 +1,207 @@
+// Copyright 2014 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 "remoting/host/video_frame_recorder.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/thread_task_runner_handle.h"
+#include "remoting/codec/video_encoder.h"
+#include "remoting/proto/video.pb.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
+
+namespace remoting {
+
+static int64_t FrameContentSize(const webrtc::DesktopFrame* frame) {
+ DCHECK_GT(frame->stride(), 0);
+ return frame->stride() * frame->size().height();
+}
+
+// VideoEncoder wrapper used to intercept frames passed to a real VideoEncoder.
+class VideoFrameRecorder::RecordingVideoEncoder : public VideoEncoder {
+ public:
+ RecordingVideoEncoder(scoped_ptr<VideoEncoder> encoder,
+ scoped_refptr<base::TaskRunner> recorder_task_runner,
+ base::WeakPtr<VideoFrameRecorder> recorder)
+ : encoder_(encoder.Pass()),
+ recorder_task_runner_(recorder_task_runner),
+ recorder_(recorder),
+ enable_recording_(false),
+ weak_factory_(this) {
+ DCHECK(encoder_);
+ DCHECK(recorder_task_runner_);
+ }
+
+ base::WeakPtr<RecordingVideoEncoder> AsWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
+ void SetEnableRecording(bool enable_recording) {
+ DCHECK(!encoder_task_runner_ ||
+ encoder_task_runner_->BelongsToCurrentThread());
+ enable_recording_ = enable_recording;
+ }
+
+ // remoting::VideoEncoder interface.
+ virtual void SetLosslessEncode(bool want_lossless) OVERRIDE {
+ encoder_->SetLosslessEncode(want_lossless);
+ }
+ virtual void SetLosslessColor(bool want_lossless) OVERRIDE {
+ encoder_->SetLosslessColor(want_lossless);
+ }
+ virtual scoped_ptr<VideoPacket> Encode(
+ const webrtc::DesktopFrame& frame) OVERRIDE {
+ // If this is the first Encode() then store the TaskRunner and inform the
+ // VideoFrameRecorder so it can post SetEnableRecording() on it.
+ if (!encoder_task_runner_) {
+ encoder_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ recorder_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&VideoFrameRecorder::SetEncoderTaskRunner,
+ recorder_,
+ encoder_task_runner_));
+ }
+
+ DCHECK(encoder_task_runner_->BelongsToCurrentThread());
+
+ if (enable_recording_) {
+ // Copy the frame and post it to the VideoFrameRecorder to store.
+ scoped_ptr<webrtc::DesktopFrame> frame_copy(
+ new webrtc::BasicDesktopFrame(frame.size()));
+ *frame_copy->mutable_updated_region() = frame.updated_region();
+ frame_copy->set_dpi(frame.dpi());
+ frame_copy->CopyPixelsFrom(frame.data(),
+ frame.stride(),
+ webrtc::DesktopRect::MakeSize(frame.size()));
+ recorder_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&VideoFrameRecorder::RecordFrame,
+ recorder_,
+ base::Passed(&frame_copy)));
+ }
+
+ return encoder_->Encode(frame);
+ }
+
+ private:
+ scoped_ptr<VideoEncoder> encoder_;
+ scoped_refptr<base::TaskRunner> recorder_task_runner_;
+ base::WeakPtr<VideoFrameRecorder> recorder_;
+
+ bool enable_recording_;
+ scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_;
+
+ base::WeakPtrFactory<RecordingVideoEncoder> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(RecordingVideoEncoder);
+};
+
+VideoFrameRecorder::VideoFrameRecorder()
+ : content_bytes_(0),
+ max_content_bytes_(0),
+ enable_recording_(false),
+ weak_factory_(this) {
+}
+
+VideoFrameRecorder::~VideoFrameRecorder() {
+ SetEnableRecording(false);
+ STLDeleteElements(&recorded_frames_);
+}
+
+scoped_ptr<VideoEncoder> VideoFrameRecorder::WrapVideoEncoder(
+ scoped_ptr<VideoEncoder> encoder) {
+ DCHECK(!caller_task_runner_);
+ caller_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+
+ scoped_ptr<RecordingVideoEncoder> recording_encoder(
+ new RecordingVideoEncoder(encoder.Pass(),
+ caller_task_runner_,
+ weak_factory_.GetWeakPtr()));
+ recording_encoder_ = recording_encoder->AsWeakPtr();
+
+ return recording_encoder.PassAs<VideoEncoder>();
+}
+
+void VideoFrameRecorder::SetEnableRecording(bool enable_recording) {
+ DCHECK(!caller_task_runner_ || caller_task_runner_->BelongsToCurrentThread());
+
+ if (enable_recording_ == enable_recording) {
+ return;
+ }
+ enable_recording_ = enable_recording;
+
+ if (encoder_task_runner_) {
+ encoder_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&RecordingVideoEncoder::SetEnableRecording,
+ recording_encoder_,
+ enable_recording_));
+ }
+}
+
+void VideoFrameRecorder::SetMaxContentBytes(int64_t max_content_bytes) {
+ DCHECK(!caller_task_runner_ || caller_task_runner_->BelongsToCurrentThread());
+ DCHECK_GE(max_content_bytes, 0);
+
+ max_content_bytes_ = max_content_bytes;
+}
+
+scoped_ptr<webrtc::DesktopFrame> VideoFrameRecorder::NextFrame() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ scoped_ptr<webrtc::DesktopFrame> frame;
+ if (!recorded_frames_.empty()) {
+ frame.reset(recorded_frames_.front());
+ recorded_frames_.pop_front();
+ content_bytes_ -= FrameContentSize(frame.get());
+ DCHECK_GE(content_bytes_, 0);
+ }
+
+ return frame.Pass();
+}
+
+void VideoFrameRecorder::SetEncoderTaskRunner(
+ scoped_refptr<base::TaskRunner> task_runner) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+ DCHECK(!encoder_task_runner_);
+ DCHECK(task_runner);
+
+ encoder_task_runner_ = task_runner;
+
+ // If the caller already enabled recording, inform the recording encoder.
+ if (enable_recording_ && encoder_task_runner_) {
+ encoder_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&RecordingVideoEncoder::SetEnableRecording,
+ recording_encoder_,
+ enable_recording_));
+ }
+}
+
+void VideoFrameRecorder::RecordFrame(scoped_ptr<webrtc::DesktopFrame> frame) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ int64_t frame_bytes = FrameContentSize(frame.get());
+ DCHECK_GE(frame_bytes, 0);
+
+ // Purge existing frames until there is space for the new one.
+ while (content_bytes_ + frame_bytes > max_content_bytes_ &&
+ !recorded_frames_.empty()) {
+ scoped_ptr<webrtc::DesktopFrame> drop_frame(recorded_frames_.front());
+ recorded_frames_.pop_front();
+ content_bytes_ -= FrameContentSize(drop_frame.get());
+ DCHECK_GE(content_bytes_, 0);
+ }
+
+ // If the frame is still too big, ignore it.
+ if (content_bytes_ + frame_bytes > max_content_bytes_) {
+ return;
+ }
+
+ // Store the frame and update the content byte count.
+ recorded_frames_.push_back(frame.release());
+ content_bytes_ += frame_bytes;
+}
+
+} // namespace remoting
diff --git a/remoting/host/video_frame_recorder.h b/remoting/host/video_frame_recorder.h
new file mode 100644
index 0000000..c204c9a
--- /dev/null
+++ b/remoting/host/video_frame_recorder.h
@@ -0,0 +1,100 @@
+// Copyright 2014 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 REMOTING_HOST_VIDEO_FRAME_RECORDER_H_
+#define REMOTING_HOST_VIDEO_FRAME_RECORDER_H_
+
+#include <stdint.h>
+#include <list>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+
+namespace webrtc {
+class DesktopFrame;
+}
+
+namespace remoting {
+
+class VideoEncoder;
+
+// Allows sequences of DesktopFrames passed to a VideoEncoder to be recorded.
+//
+// VideoFrameRecorder is design to support applications which use a dedicated
+// thread for video encoding, but need to manage that process from a "main"
+// or "control" thread.
+//
+// On the control thread:
+// 1. Create the VideoFrameRecorder on the controlling thread.
+// 2. Specify the amount of memory that may be used for recording.
+// 3. Call WrapVideoEncoder(), passing the actual VideoEncoder that will be
+// used to encode frames.
+// 4. Hand the returned wrapper VideoEncoder of to the video encoding thread,
+// to call in place of the actual VideoEncoder.
+// 5. Start/stop frame recording as necessary.
+// 6. Use NextFrame() to read each recorded frame in sequence.
+//
+// The wrapper VideoEncoder is designed to be handed off to the video encoding
+// thread, and used and torn down there.
+//
+// The VideoFrameRecorder and VideoEncoder may be torn down in any order; frame
+// recording will stop as soon as either is destroyed.
+
+class VideoFrameRecorder {
+ public:
+ VideoFrameRecorder();
+ virtual ~VideoFrameRecorder();
+
+ // Wraps the supplied VideoEncoder, returning a replacement VideoEncoder that
+ // will route frames to the recorder, as well as passing them for encoding.
+ // This may be called at most once on each VideoFrameRecorder instance.
+ scoped_ptr<VideoEncoder> WrapVideoEncoder(scoped_ptr<VideoEncoder> encoder);
+
+ // Enables/disables frame recording. Frame recording is initially disabled.
+ void SetEnableRecording(bool enable_recording);
+
+ // Sets the maximum number of bytes of pixel data that may be recorded.
+ // When this maximum is reached older frames will be discard to make space
+ // for new ones.
+ void SetMaxContentBytes(int64_t max_content_bytes);
+
+ // Pops the next recorded frame in the sequence, and returns it.
+ scoped_ptr<webrtc::DesktopFrame> NextFrame();
+
+ private:
+ class RecordingVideoEncoder;
+ friend class RecordingVideoEncoder;
+
+ void SetEncoderTaskRunner(scoped_refptr<base::TaskRunner> task_runner);
+ void RecordFrame(scoped_ptr<webrtc::DesktopFrame> frame);
+
+ // The recorded frames, in sequence.
+ std::list<webrtc::DesktopFrame*> recorded_frames_;
+
+ // Size of the recorded frames' content, in bytes.
+ int64_t content_bytes_;
+
+ // Size that recorded frames' content must not exceed.
+ int64_t max_content_bytes_;
+
+ // True if recording is started, false otherwise.
+ bool enable_recording_;
+
+ // Task runner on which the wrapper VideoEncoder is being run.
+ scoped_refptr<base::TaskRunner> encoder_task_runner_;
+
+ // Weak reference to the wrapper VideoEncoder, to use to control it.
+ base::WeakPtr<RecordingVideoEncoder> recording_encoder_;
+
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
+ base::WeakPtrFactory<VideoFrameRecorder> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameRecorder);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_VIDEO_FRAME_RECORDER_H_
diff --git a/remoting/host/video_frame_recorder_unittest.cc b/remoting/host/video_frame_recorder_unittest.cc
new file mode 100644
index 0000000..907c4a9
--- /dev/null
+++ b/remoting/host/video_frame_recorder_unittest.cc
@@ -0,0 +1,279 @@
+// Copyright 2014 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 "remoting/host/video_frame_recorder.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "remoting/codec/video_encoder_verbatim.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
+
+namespace webrtc {
+
+// Define equality operator for DesktopFrame to allow use of EXPECT_EQ().
+static bool operator==(const DesktopFrame& a,
+ const DesktopFrame& b) {
+ if ((a.size().equals(b.size())) &&
+ (a.updated_region().Equals(b.updated_region())) &&
+ (a.dpi().equals(b.dpi()))) {
+ for (int i = 0; i < a.size().height(); ++i) {
+ if (memcmp(a.data() + a.stride() * i,
+ b.data() + b.stride() * i,
+ a.size().width() * DesktopFrame::kBytesPerPixel) != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+namespace remoting {
+
+const int64_t kMaxContentBytes = 10 * 1024 * 1024;
+const int kWidth = 640;
+const int kHeight = 480;
+const int kTestFrameCount = 6;
+
+class VideoFrameRecorderTest : public testing::Test {
+ public:
+ VideoFrameRecorderTest();
+
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ void CreateAndWrapEncoder();
+ scoped_ptr<webrtc::DesktopFrame> CreateNextFrame();
+ void CreateTestFrames();
+ void EncodeTestFrames();
+ void EncodeDummyFrame();
+ void StartRecording();
+ void VerifyTestFrames();
+
+ protected:
+ base::MessageLoop message_loop_;
+
+ scoped_ptr<VideoFrameRecorder> recorder_;
+ scoped_ptr<VideoEncoder> encoder_;
+
+ std::list<webrtc::DesktopFrame*> test_frames_;
+ int frame_count_;
+};
+
+VideoFrameRecorderTest::VideoFrameRecorderTest() : frame_count_(0) {}
+
+void VideoFrameRecorderTest::SetUp() {
+ recorder_.reset(new VideoFrameRecorder());
+ recorder_->SetMaxContentBytes(kMaxContentBytes);
+}
+
+void VideoFrameRecorderTest::TearDown() {
+ // Allow events posted to the recorder_, if still valid, to be processed.
+ base::RunLoop().RunUntilIdle();
+
+ // Tear down the recorder, if necessary.
+ recorder_.reset();
+
+ // Process any events resulting from recorder teardown.
+ base::RunLoop().RunUntilIdle();
+}
+
+void VideoFrameRecorderTest::CreateAndWrapEncoder() {
+ scoped_ptr<VideoEncoder> encoder(new VideoEncoderVerbatim());
+ encoder_ = recorder_->WrapVideoEncoder(encoder.Pass());
+
+ // Encode a dummy frame to bind the wrapper to the TaskRunner.
+ EncodeDummyFrame();
+}
+
+scoped_ptr<webrtc::DesktopFrame> VideoFrameRecorderTest::CreateNextFrame() {
+ scoped_ptr<webrtc::DesktopFrame> frame(
+ new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth, kHeight)));
+
+ // Fill content, DPI and updated-region based on |frame_count_| so that each
+ // generated frame is different.
+ memset(frame->data(), frame_count_, frame->stride() * kHeight);
+ frame->set_dpi(webrtc::DesktopVector(frame_count_, frame_count_));
+ frame->mutable_updated_region()->SetRect(
+ webrtc::DesktopRect::MakeWH(frame_count_, frame_count_));
+ ++frame_count_;
+
+ return frame.Pass();
+}
+
+void VideoFrameRecorderTest::CreateTestFrames() {
+ for (int i=0; i < kTestFrameCount; ++i) {
+ test_frames_.push_back(CreateNextFrame().release());
+ }
+}
+
+void VideoFrameRecorderTest::EncodeTestFrames() {
+ std::list<webrtc::DesktopFrame*>::iterator i;
+ for (i = test_frames_.begin(); i != test_frames_.end(); ++i) {
+ scoped_ptr<VideoPacket> packet = encoder_->Encode(*(*i));
+
+ // Process tasks to let the recorder pick up the frame.
+ base::RunLoop().RunUntilIdle();
+ }
+}
+
+void VideoFrameRecorderTest::EncodeDummyFrame() {
+ webrtc::BasicDesktopFrame dummy_frame(webrtc::DesktopSize(kWidth, kHeight));
+ scoped_ptr<VideoPacket> packet = encoder_->Encode(dummy_frame);
+ base::RunLoop().RunUntilIdle();
+}
+
+void VideoFrameRecorderTest::StartRecording() {
+ // Start the recorder and pump events to let things initialize.
+ recorder_->SetEnableRecording(true);
+ base::RunLoop().RunUntilIdle();
+}
+
+void VideoFrameRecorderTest::VerifyTestFrames() {
+ // Verify that the recorded frames match the ones passed to the encoder.
+ while (!test_frames_.empty()) {
+ scoped_ptr<webrtc::DesktopFrame> recorded_frame(recorder_->NextFrame());
+ ASSERT_TRUE(recorded_frame);
+
+ scoped_ptr<webrtc::DesktopFrame> expected_frame(test_frames_.front());
+ test_frames_.pop_front();
+
+ EXPECT_EQ(*recorded_frame, *expected_frame);
+ }
+
+ EXPECT_FALSE(recorder_->NextFrame());
+}
+
+// Basic test that creating & tearing down VideoFrameRecorder doesn't crash.
+TEST_F(VideoFrameRecorderTest, CreateDestroy) {
+}
+
+// Basic test that creating, starting, stopping and destroying a
+// VideoFrameRecorder don't end the world.
+TEST_F(VideoFrameRecorderTest, StartStop) {
+ StartRecording();
+ recorder_->SetEnableRecording(false);
+}
+
+// Test that tearing down the VideoFrameRecorder while the VideoEncoder
+// wrapper exists doesn't crash.
+TEST_F(VideoFrameRecorderTest, DestroyVideoFrameRecorderFirst) {
+ CreateAndWrapEncoder();
+
+ // Start the recorder, so that the wrapper will push frames to it.
+ StartRecording();
+
+ // Tear down the recorder.
+ recorder_.reset();
+
+ // Encode a dummy frame via the wrapper to ensure we don't crash.
+ EncodeDummyFrame();
+}
+
+// Test that creating & tearing down the wrapper while the
+// VideoFrameRecorder still exists doesn't crash.
+TEST_F(VideoFrameRecorderTest, DestroyVideoEncoderWrapperFirst) {
+ CreateAndWrapEncoder();
+
+ // Start the recorder, so that the wrapper will push frames to it.
+ StartRecording();
+
+ // Encode a dummy frame via the wrapper to ensure we don't crash.
+ EncodeDummyFrame();
+
+ // Tear down the encoder wrapper.
+ encoder_.reset();
+
+ // Test teardown will stop the recorder and process pending events.
+}
+
+// Test that when asked to encode a short sequence of frames, those frames are
+// all recorded, in sequence.
+TEST_F(VideoFrameRecorderTest, RecordFrames) {
+ CreateAndWrapEncoder();
+
+ // Start the recorder, so that the wrapper will push frames to it.
+ StartRecording();
+
+ // Create frames, store them and pass them to the encoder.
+ CreateTestFrames();
+ EncodeTestFrames();
+
+ // Verify that the recorded frames match the ones passed to the encoder.
+ VerifyTestFrames();
+}
+
+// Test that when asked to record more frames than the maximum content bytes
+// limit allows, the first encoded frames are dropped.
+TEST_F(VideoFrameRecorderTest, MaxContentBytesEnforced) {
+ CreateAndWrapEncoder();
+
+ // Configure a maximum content size sufficient for five and a half frames.
+ int64 frame_bytes = kWidth * kHeight * webrtc::DesktopFrame::kBytesPerPixel;
+ recorder_->SetMaxContentBytes((frame_bytes * 11) / 2);
+
+ // Start the recorder, so that the wrapper will push frames to it.
+ StartRecording();
+
+ // Create frames, store them and pass them to the encoder.
+ CreateTestFrames();
+ EncodeTestFrames();
+
+ // Only five of the supplied frames should have been recorded.
+ while (test_frames_.size() > 5) {
+ scoped_ptr<webrtc::DesktopFrame> frame(test_frames_.front());
+ test_frames_.pop_front();
+ }
+
+ // Verify that the recorded frames match the ones passed to the encoder.
+ VerifyTestFrames();
+}
+
+// Test that when asked to record more frames than the maximum content bytes
+// limit allows, the first encoded frames are dropped.
+TEST_F(VideoFrameRecorderTest, ContentBytesUpdatedByNextFrame) {
+ CreateAndWrapEncoder();
+
+ // Configure a maximum content size sufficient for kTestFrameCount frames.
+ int64 frame_bytes = kWidth * kHeight * webrtc::DesktopFrame::kBytesPerPixel;
+ recorder_->SetMaxContentBytes(frame_bytes * kTestFrameCount);
+
+ // Start the recorder, so that the wrapper will push frames to it.
+ StartRecording();
+
+ // Encode a frame, to record it, and consume it from the recorder.
+ EncodeDummyFrame();
+ scoped_ptr<webrtc::DesktopFrame> frame = recorder_->NextFrame();
+ EXPECT_TRUE(frame);
+
+ // Create frames, store them and pass them to the encoder.
+ CreateTestFrames();
+ EncodeTestFrames();
+
+ // Verify that the recorded frames match the ones passed to the encoder.
+ VerifyTestFrames();
+}
+
+// Test that when asked to encode a short sequence of frames, none are recorded
+// if recording was not enabled.
+TEST_F(VideoFrameRecorderTest, EncodeButDontRecord) {
+ CreateAndWrapEncoder();
+
+ // Create frames, store them and pass them to the encoder.
+ CreateTestFrames();
+ EncodeTestFrames();
+
+ // Clear the list of expected test frames, since none should be recorded.
+ test_frames_.clear();
+
+ // Verify that the recorded frames match the ones passed to the encoder.
+ VerifyTestFrames();
+}
+
+} // namespace remoting
diff --git a/remoting/remoting_host.gypi b/remoting/remoting_host.gypi
index 47d60fa..d7d87ec 100644
--- a/remoting/remoting_host.gypi
+++ b/remoting/remoting_host.gypi
@@ -244,6 +244,8 @@
'host/usage_stats_consent_win.cc',
'host/username.cc',
'host/username.h',
+ 'host/video_frame_recorder.cc',
+ 'host/video_frame_recorder.h',
'host/video_scheduler.cc',
'host/video_scheduler.h',
'host/win/com_imported_mstscax.tlh',
diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi
index 7fef1c3..35111b4 100644
--- a/remoting/remoting_test.gypi
+++ b/remoting/remoting_test.gypi
@@ -114,6 +114,7 @@
'host/setup/pin_validator_unittest.cc',
'host/shaped_screen_capturer_unittest.cc',
'host/token_validator_factory_impl_unittest.cc',
+ 'host/video_frame_recorder_unittest.cc',
'host/video_scheduler_unittest.cc',
'host/win/rdp_client_unittest.cc',
'host/win/worker_process_launcher.cc',