/* * 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 #include #include #include #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 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 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 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 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 guard(m_); while (clients_.empty()) { new_client_cv_.wait(guard); } } void BlackBoard::SetOrientation(const VncClientConnection* conn, ScreenOrientation orientation) { std::lock_guard 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 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 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 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 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 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]; }