blob: 68faa7bb6a3e96549a993cb08d8e47f2de088a1c [file] [log] [blame]
// 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 "remoting/client/software_video_renderer.h"
#include <stdint.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/threading/thread.h"
#include "remoting/client/client_context.h"
#include "remoting/codec/video_encoder_verbatim.h"
#include "remoting/proto/video.pb.h"
#include "remoting/protocol/frame_consumer.h"
#include "remoting/protocol/session_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
using webrtc::DesktopFrame;
namespace remoting {
namespace {
const int kFrameWidth = 200;
const int kFrameHeight = 200;
class TestFrameConsumer : public protocol::FrameConsumer {
public:
TestFrameConsumer() = default;
~TestFrameConsumer() override = default;
std::unique_ptr<DesktopFrame> WaitForNextFrame(
base::Closure* out_done_callback) {
EXPECT_TRUE(thread_checker_.CalledOnValidThread());
frame_run_loop_.reset(new base::RunLoop());
frame_run_loop_->Run();
frame_run_loop_.reset();
*out_done_callback = last_frame_done_callback_;
last_frame_done_callback_.Reset();
return std::move(last_frame_);
}
// FrameConsumer interface.
std::unique_ptr<DesktopFrame> AllocateFrame(
const webrtc::DesktopSize& size) override {
EXPECT_TRUE(thread_checker_.CalledOnValidThread());
return std::make_unique<webrtc::BasicDesktopFrame>(size);
}
void DrawFrame(std::unique_ptr<DesktopFrame> frame,
const base::Closure& done) override {
EXPECT_TRUE(thread_checker_.CalledOnValidThread());
last_frame_ = std::move(frame);
last_frame_done_callback_ = done;
frame_run_loop_->Quit();
}
PixelFormat GetPixelFormat() override {
EXPECT_TRUE(thread_checker_.CalledOnValidThread());
return FORMAT_BGRA;
}
private:
base::ThreadChecker thread_checker_;
std::unique_ptr<base::RunLoop> frame_run_loop_;
std::unique_ptr<DesktopFrame> last_frame_;
base::Closure last_frame_done_callback_;
};
std::unique_ptr<DesktopFrame> CreateTestFrame(int index) {
std::unique_ptr<DesktopFrame> frame(new webrtc::BasicDesktopFrame(
webrtc::DesktopSize(kFrameWidth, kFrameHeight)));
for (int y = 0; y < kFrameHeight; y++) {
for (int x = 0; x < kFrameWidth; x++) {
uint8_t* out = frame->data() + x * DesktopFrame::kBytesPerPixel +
y * frame->stride();
out[0] = index + x + y * kFrameWidth;
out[1] = index + x + y * kFrameWidth + 1;
out[2] = index + x + y * kFrameWidth + 2;
out[3] = 0;
}
}
if (index == 0) {
frame->mutable_updated_region()->SetRect(
webrtc::DesktopRect::MakeWH(kFrameWidth, kFrameHeight));
} else {
frame->mutable_updated_region()->SetRect(
webrtc::DesktopRect::MakeWH(index, index));
}
return frame;
}
// Returns true when frames a and b are equivalent.
bool CompareFrames(const DesktopFrame& a, const DesktopFrame& b) {
if (!a.size().equals(b.size()) ||
!a.updated_region().Equals(b.updated_region())) {
return false;
}
for (webrtc::DesktopRegion::Iterator i(a.updated_region()); !i.IsAtEnd();
i.Advance()) {
for (int row = i.rect().top(); row < i.rect().bottom(); ++row) {
if (memcmp(a.data() + a.stride() * row +
i.rect().left() * DesktopFrame::kBytesPerPixel,
b.data() + b.stride() * row +
i.rect().left() * DesktopFrame::kBytesPerPixel,
i.rect().width() * DesktopFrame::kBytesPerPixel) != 0) {
return false;
}
}
}
return true;
}
// Helper to set value at |out| to 1.
void SetTrue(int* out) {
*out = 1;
}
} // namespace
class SoftwareVideoRendererTest : public ::testing::Test {
public:
SoftwareVideoRendererTest() : context_(nullptr) {
context_.Start();
renderer_.reset(new SoftwareVideoRenderer(&frame_consumer_));
renderer_->Initialize(context_, nullptr);
renderer_->OnSessionConfig(
*protocol::SessionConfig::ForTestWithVerbatimVideo());
}
protected:
base::MessageLoop message_loop_;
ClientContext context_;
TestFrameConsumer frame_consumer_;
std::unique_ptr<SoftwareVideoRenderer> renderer_;
VideoEncoderVerbatim encoder_;
};
TEST_F(SoftwareVideoRendererTest, DecodeFrame) {
const int kFrameCount = 5;
std::vector<std::unique_ptr<DesktopFrame>> test_frames;
// std::vector<bool> doesn't allow to get pointer to individual values, so
// int needs to be used instead.
std::vector<int> callback_called(kFrameCount);
for (int frame_index = 0; frame_index < kFrameCount; frame_index++) {
test_frames.push_back(CreateTestFrame(frame_index));
callback_called[frame_index] = 0;
renderer_->ProcessVideoPacket(
encoder_.Encode(*test_frames[frame_index]),
base::Bind(&SetTrue, &(callback_called[frame_index])));
}
for (int frame_index = 0; frame_index < kFrameCount; frame_index++) {
base::Closure done_callback;
std::unique_ptr<DesktopFrame> decoded_frame =
frame_consumer_.WaitForNextFrame(&done_callback);
EXPECT_FALSE(callback_called[frame_index]);
done_callback.Run();
EXPECT_TRUE(callback_called[frame_index]);
EXPECT_TRUE(CompareFrames(*test_frames[frame_index], *decoded_frame));
}
}
} // namespace remoting