1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "host/frontend/vnc_server/blackboard.h" 18 19 #include <algorithm> 20 #include <utility> 21 22 #include <gflags/gflags.h> 23 #include <glog/logging.h> 24 #include "host/frontend/vnc_server/frame_buffer_watcher.h" 25 26 DEFINE_bool(debug_blackboard, false, 27 "Turn on detailed logging for the blackboard"); 28 29 #define DLOG(LEVEL) \ 30 if (FLAGS_debug_blackboard) LOG(LEVEL) 31 32 using cvd::vnc::BlackBoard; 33 using cvd::vnc::Stripe; 34 35 cvd::vnc::SeqNumberVec cvd::vnc::MakeSeqNumberVec() { 36 return SeqNumberVec(FrameBufferWatcher::StripesPerFrame()); 37 } 38 39 void BlackBoard::NewStripeReady(int index, StripeSeqNumber seq_num) { 40 std::lock_guard<std::mutex> guard(m_); 41 DLOG(INFO) << "new stripe arrived from frame watcher"; 42 auto& current_seq_num = most_recent_stripe_seq_nums_[index]; 43 current_seq_num = std::max(current_seq_num, seq_num); 44 for (auto& client : clients_) { 45 if (client.second.ready_to_receive) { 46 client.second.new_frame_cv.notify_one(); 47 } 48 } 49 } 50 51 void BlackBoard::Register(const VncClientConnection* conn) { 52 { 53 std::lock_guard<std::mutex> guard(m_); 54 CHECK(!clients_.count(conn)); 55 clients_[conn]; // constructs new state in place 56 } 57 new_client_cv_.notify_one(); 58 } 59 60 void BlackBoard::Unregister(const VncClientConnection* conn) { 61 std::lock_guard<std::mutex> guard(m_); 62 CHECK(clients_.count(conn)); 63 clients_.erase(clients_.find(conn)); 64 } 65 66 bool BlackBoard::NoNewStripesFor(const SeqNumberVec& seq_nums) const { 67 CHECK(seq_nums.size() == most_recent_stripe_seq_nums_.size()); 68 for (auto state_seq_num = seq_nums.begin(), 69 held_seq_num = most_recent_stripe_seq_nums_.begin(); 70 state_seq_num != seq_nums.end(); ++state_seq_num, ++held_seq_num) { 71 if (*state_seq_num < *held_seq_num) { 72 return false; 73 } 74 } 75 return true; 76 } 77 78 cvd::vnc::StripePtrVec BlackBoard::WaitForSenderWork( 79 const VncClientConnection* conn) { 80 std::unique_lock<std::mutex> guard(m_); 81 auto& state = GetStateForClient(conn); 82 DLOG(INFO) << "Waiting for stripe..."; 83 while (!state.closed && 84 (!state.ready_to_receive || NoNewStripesFor(state.stripe_seq_nums))) { 85 state.new_frame_cv.wait(guard); 86 } 87 DLOG(INFO) << "At least one new stripe is available, should unblock " << conn; 88 state.ready_to_receive = false; 89 auto new_stripes = frame_buffer_watcher_->StripesNewerThan( 90 state.orientation, state.stripe_seq_nums); 91 for (auto& s : new_stripes) { 92 state.stripe_seq_nums[s->index] = s->seq_number; 93 } 94 return new_stripes; 95 } 96 97 void BlackBoard::WaitForAtLeastOneClientConnection() { 98 std::unique_lock<std::mutex> guard(m_); 99 while (clients_.empty()) { 100 new_client_cv_.wait(guard); 101 } 102 } 103 104 void BlackBoard::SetOrientation(const VncClientConnection* conn, 105 ScreenOrientation orientation) { 106 std::lock_guard<std::mutex> guard(m_); 107 auto& state = GetStateForClient(conn); 108 state.orientation = orientation; 109 // After an orientation change the vnc client will need all stripes from 110 // the new orientation, regardless of age. 111 ResetToZero(&state.stripe_seq_nums); 112 } 113 114 void BlackBoard::SignalClientNeedsEntireScreen( 115 const VncClientConnection* conn) { 116 std::lock_guard<std::mutex> guard(m_); 117 ResetToZero(&GetStateForClient(conn).stripe_seq_nums); 118 } 119 120 void BlackBoard::ResetToZero(SeqNumberVec* seq_nums) { 121 seq_nums->assign(FrameBufferWatcher::StripesPerFrame(), StripeSeqNumber{}); 122 } 123 124 void BlackBoard::FrameBufferUpdateRequestReceived( 125 const VncClientConnection* conn) { 126 std::lock_guard<std::mutex> guard(m_); 127 DLOG(INFO) << "Received frame buffer update request"; 128 auto& state = GetStateForClient(conn); 129 state.ready_to_receive = true; 130 state.new_frame_cv.notify_one(); 131 } 132 133 void BlackBoard::StopWaiting(const VncClientConnection* conn) { 134 std::lock_guard<std::mutex> guard(m_); 135 auto& state = GetStateForClient(conn); 136 state.closed = true; 137 // Wake up the thread that might be in WaitForSenderWork() 138 state.new_frame_cv.notify_one(); 139 } 140 141 void BlackBoard::set_frame_buffer_watcher( 142 cvd::vnc::FrameBufferWatcher* frame_buffer_watcher) { 143 std::lock_guard<std::mutex> guard(m_); 144 frame_buffer_watcher_ = frame_buffer_watcher; 145 } 146 147 void BlackBoard::set_jpeg_quality_level(int quality_level) { 148 // NOTE all vnc clients share a common jpeg quality level because the 149 // server doesn't compress per-client. The quality level for all clients 150 // will be whatever the most recent set was by any client. 151 std::lock_guard<std::mutex> guard(m_); 152 if (quality_level < kJpegMinQualityEncoding || 153 quality_level > kJpegMaxQualityEncoding) { 154 LOG(WARNING) << "Bogus jpeg quality level: " << quality_level 155 << ". Quality must be in range [" << kJpegMinQualityEncoding 156 << ", " << kJpegMaxQualityEncoding << "]"; 157 return; 158 } 159 jpeg_quality_level_ = 55 + (5 * (quality_level + 32)); 160 DLOG(INFO) << "jpeg quality level set to " << jpeg_quality_level_ << "%"; 161 } 162 163 BlackBoard::ClientFBUState& BlackBoard::GetStateForClient( 164 const VncClientConnection* conn) { 165 CHECK(clients_.count(conn)); 166 return clients_[conn]; 167 } 168