You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
5.6 KiB
168 lines
5.6 KiB
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "host/frontend/vnc_server/blackboard.h"
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
#include <gflags/gflags.h>
|
|
#include <android-base/logging.h>
|
|
#include "host/frontend/vnc_server/frame_buffer_watcher.h"
|
|
|
|
DEFINE_bool(debug_blackboard, false,
|
|
"Turn on detailed logging for the blackboard");
|
|
|
|
#define DLOG(LEVEL) \
|
|
if (FLAGS_debug_blackboard) LOG(LEVEL)
|
|
|
|
using cuttlefish::vnc::BlackBoard;
|
|
using cuttlefish::vnc::Stripe;
|
|
|
|
cuttlefish::vnc::SeqNumberVec cuttlefish::vnc::MakeSeqNumberVec() {
|
|
return SeqNumberVec(FrameBufferWatcher::StripesPerFrame());
|
|
}
|
|
|
|
void BlackBoard::NewStripeReady(int index, StripeSeqNumber seq_num) {
|
|
std::lock_guard<std::mutex> guard(m_);
|
|
DLOG(INFO) << "new stripe arrived from frame watcher";
|
|
auto& current_seq_num = most_recent_stripe_seq_nums_[index];
|
|
current_seq_num = std::max(current_seq_num, seq_num);
|
|
for (auto& client : clients_) {
|
|
if (client.second.ready_to_receive) {
|
|
client.second.new_frame_cv.notify_one();
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlackBoard::Register(const VncClientConnection* conn) {
|
|
{
|
|
std::lock_guard<std::mutex> guard(m_);
|
|
CHECK(!clients_.count(conn));
|
|
clients_[conn]; // constructs new state in place
|
|
}
|
|
new_client_cv_.notify_one();
|
|
}
|
|
|
|
void BlackBoard::Unregister(const VncClientConnection* conn) {
|
|
std::lock_guard<std::mutex> guard(m_);
|
|
CHECK(clients_.count(conn));
|
|
clients_.erase(clients_.find(conn));
|
|
}
|
|
|
|
bool BlackBoard::NoNewStripesFor(const SeqNumberVec& seq_nums) const {
|
|
CHECK(seq_nums.size() == most_recent_stripe_seq_nums_.size());
|
|
for (auto state_seq_num = seq_nums.begin(),
|
|
held_seq_num = most_recent_stripe_seq_nums_.begin();
|
|
state_seq_num != seq_nums.end(); ++state_seq_num, ++held_seq_num) {
|
|
if (*state_seq_num < *held_seq_num) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
cuttlefish::vnc::StripePtrVec BlackBoard::WaitForSenderWork(
|
|
const VncClientConnection* conn) {
|
|
std::unique_lock<std::mutex> guard(m_);
|
|
auto& state = GetStateForClient(conn);
|
|
DLOG(INFO) << "Waiting for stripe...";
|
|
while (!state.closed &&
|
|
(!state.ready_to_receive || NoNewStripesFor(state.stripe_seq_nums))) {
|
|
state.new_frame_cv.wait(guard);
|
|
}
|
|
DLOG(INFO) << "At least one new stripe is available, should unblock " << conn;
|
|
state.ready_to_receive = false;
|
|
auto new_stripes = frame_buffer_watcher_->StripesNewerThan(
|
|
state.orientation, state.stripe_seq_nums);
|
|
for (auto& s : new_stripes) {
|
|
state.stripe_seq_nums[s->index] = s->seq_number;
|
|
}
|
|
return new_stripes;
|
|
}
|
|
|
|
void BlackBoard::WaitForAtLeastOneClientConnection() {
|
|
std::unique_lock<std::mutex> guard(m_);
|
|
while (clients_.empty()) {
|
|
new_client_cv_.wait(guard);
|
|
}
|
|
}
|
|
|
|
void BlackBoard::SetOrientation(const VncClientConnection* conn,
|
|
ScreenOrientation orientation) {
|
|
std::lock_guard<std::mutex> guard(m_);
|
|
auto& state = GetStateForClient(conn);
|
|
state.orientation = orientation;
|
|
// After an orientation change the vnc client will need all stripes from
|
|
// the new orientation, regardless of age.
|
|
ResetToZero(&state.stripe_seq_nums);
|
|
}
|
|
|
|
void BlackBoard::SignalClientNeedsEntireScreen(
|
|
const VncClientConnection* conn) {
|
|
std::lock_guard<std::mutex> guard(m_);
|
|
ResetToZero(&GetStateForClient(conn).stripe_seq_nums);
|
|
}
|
|
|
|
void BlackBoard::ResetToZero(SeqNumberVec* seq_nums) {
|
|
seq_nums->assign(FrameBufferWatcher::StripesPerFrame(), StripeSeqNumber{});
|
|
}
|
|
|
|
void BlackBoard::FrameBufferUpdateRequestReceived(
|
|
const VncClientConnection* conn) {
|
|
std::lock_guard<std::mutex> guard(m_);
|
|
DLOG(INFO) << "Received frame buffer update request";
|
|
auto& state = GetStateForClient(conn);
|
|
state.ready_to_receive = true;
|
|
state.new_frame_cv.notify_one();
|
|
}
|
|
|
|
void BlackBoard::StopWaiting(const VncClientConnection* conn) {
|
|
std::lock_guard<std::mutex> guard(m_);
|
|
auto& state = GetStateForClient(conn);
|
|
state.closed = true;
|
|
// Wake up the thread that might be in WaitForSenderWork()
|
|
state.new_frame_cv.notify_one();
|
|
}
|
|
|
|
void BlackBoard::set_frame_buffer_watcher(
|
|
cuttlefish::vnc::FrameBufferWatcher* frame_buffer_watcher) {
|
|
std::lock_guard<std::mutex> guard(m_);
|
|
frame_buffer_watcher_ = frame_buffer_watcher;
|
|
}
|
|
|
|
void BlackBoard::set_jpeg_quality_level(int quality_level) {
|
|
// NOTE all vnc clients share a common jpeg quality level because the
|
|
// server doesn't compress per-client. The quality level for all clients
|
|
// will be whatever the most recent set was by any client.
|
|
std::lock_guard<std::mutex> guard(m_);
|
|
if (quality_level < kJpegMinQualityEncoding ||
|
|
quality_level > kJpegMaxQualityEncoding) {
|
|
LOG(WARNING) << "Bogus jpeg quality level: " << quality_level
|
|
<< ". Quality must be in range [" << kJpegMinQualityEncoding
|
|
<< ", " << kJpegMaxQualityEncoding << "]";
|
|
return;
|
|
}
|
|
jpeg_quality_level_ = 55 + (5 * (quality_level + 32));
|
|
DLOG(INFO) << "jpeg quality level set to " << jpeg_quality_level_ << "%";
|
|
}
|
|
|
|
BlackBoard::ClientFBUState& BlackBoard::GetStateForClient(
|
|
const VncClientConnection* conn) {
|
|
CHECK(clients_.count(conn));
|
|
return clients_[conn];
|
|
}
|