Home | History | Annotate | Download | only in vnc_server
      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/frame_buffer_watcher.h"
     18 
     19 #include <algorithm>
     20 #include <cstdint>
     21 #include <cstring>
     22 #include <iterator>
     23 #include <memory>
     24 #include <mutex>
     25 #include <thread>
     26 #include <utility>
     27 
     28 #include <glog/logging.h>
     29 #include "host/frontend/vnc_server/vnc_utils.h"
     30 
     31 using cvd::vnc::FrameBufferWatcher;
     32 
     33 FrameBufferWatcher::FrameBufferWatcher(BlackBoard* bb)
     34     : bb_{bb}, hwcomposer{bb_} {
     35   for (auto& stripes_vec : stripes_) {
     36     std::generate_n(std::back_inserter(stripes_vec),
     37                     SimulatedHWComposer::NumberOfStripes(),
     38                     std::make_shared<Stripe>);
     39   }
     40   bb_->set_frame_buffer_watcher(this);
     41   auto num_workers = std::max(std::thread::hardware_concurrency(), 1u);
     42   std::generate_n(std::back_inserter(workers_), num_workers, [this] {
     43     return std::thread{&FrameBufferWatcher::Worker, this};
     44   });
     45 }
     46 
     47 FrameBufferWatcher::~FrameBufferWatcher() {
     48   {
     49     std::lock_guard<std::mutex> guard(m_);
     50     closed_ = true;
     51   }
     52   for (auto& tid : workers_) {
     53     tid.join();
     54   }
     55 }
     56 
     57 bool FrameBufferWatcher::closed() const {
     58   std::lock_guard<std::mutex> guard(m_);
     59   return closed_;
     60 }
     61 
     62 cvd::vnc::Stripe FrameBufferWatcher::Rotated(Stripe stripe) {
     63   if (stripe.orientation == ScreenOrientation::Landscape) {
     64     LOG(FATAL) << "Rotating a landscape stripe, this is a mistake";
     65   }
     66   auto w = stripe.width;
     67   auto h = stripe.height;
     68   const auto& raw = stripe.raw_data;
     69   Message rotated(raw.size(), 0xAA);
     70   for (std::uint16_t i = 0; i < w; ++i) {
     71     for (std::uint16_t j = 0; j < h; ++j) {
     72       size_t to = (i * h + j) * BytesPerPixel();
     73       size_t from = (w - (i + 1) + w * j) * BytesPerPixel();
     74       CHECK(from < raw.size());
     75       CHECK(to < rotated.size());
     76       std::memcpy(&rotated[to], &raw[from], BytesPerPixel());
     77     }
     78   }
     79   std::swap(stripe.x, stripe.y);
     80   std::swap(stripe.width, stripe.height);
     81   stripe.raw_data = std::move(rotated);
     82   stripe.orientation = ScreenOrientation::Landscape;
     83   return stripe;
     84 }
     85 
     86 bool FrameBufferWatcher::StripeIsDifferentFromPrevious(
     87     const Stripe& stripe) const {
     88   return Stripes(stripe.orientation)[stripe.index]->raw_data != stripe.raw_data;
     89 }
     90 
     91 cvd::vnc::StripePtrVec FrameBufferWatcher::StripesNewerThan(
     92     ScreenOrientation orientation, const SeqNumberVec& seq_numbers) const {
     93   std::lock_guard<std::mutex> guard(stripes_lock_);
     94   const auto& stripes = Stripes(orientation);
     95   CHECK(seq_numbers.size() == stripes.size());
     96   StripePtrVec new_stripes;
     97   auto seq_number_it = seq_numbers.begin();
     98   std::copy_if(stripes.begin(), stripes.end(), std::back_inserter(new_stripes),
     99                [seq_number_it](const StripePtrVec::value_type& s) mutable {
    100                  return *(seq_number_it++) < s->seq_number;
    101                });
    102   return new_stripes;
    103 }
    104 
    105 cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
    106     ScreenOrientation orientation) {
    107   return stripes_[static_cast<int>(orientation)];
    108 }
    109 
    110 const cvd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
    111     ScreenOrientation orientation) const {
    112   return stripes_[static_cast<int>(orientation)];
    113 }
    114 
    115 bool FrameBufferWatcher::UpdateMostRecentSeqNumIfStripeIsNew(
    116     const Stripe& stripe) {
    117   if (most_recent_identical_stripe_seq_nums_[stripe.index] <=
    118       stripe.seq_number) {
    119     most_recent_identical_stripe_seq_nums_[stripe.index] = stripe.seq_number;
    120     return true;
    121   }
    122   return false;
    123 }
    124 
    125 bool FrameBufferWatcher::UpdateStripeIfStripeIsNew(
    126     const std::shared_ptr<const Stripe>& stripe) {
    127   std::lock_guard<std::mutex> guard(stripes_lock_);
    128   if (UpdateMostRecentSeqNumIfStripeIsNew(*stripe)) {
    129     Stripes(stripe->orientation)[stripe->index] = stripe;
    130     return true;
    131   }
    132   return false;
    133 }
    134 
    135 void FrameBufferWatcher::CompressStripe(JpegCompressor* jpeg_compressor,
    136                                         Stripe* stripe) {
    137   stripe->jpeg_data = jpeg_compressor->Compress(
    138       stripe->raw_data, bb_->jpeg_quality_level(), 0, 0, stripe->width,
    139       stripe->height, stripe->width);
    140 }
    141 
    142 void FrameBufferWatcher::Worker() {
    143   JpegCompressor jpeg_compressor;
    144 #ifdef FUZZ_TEST_VNC
    145   std::default_random_engine e{std::random_device{}()};
    146   std::uniform_int_distribution<int> random{0, 2};
    147 #endif
    148   while (!closed()) {
    149     auto portrait_stripe = hwcomposer.GetNewStripe();
    150     if (closed()) {
    151       break;
    152     }
    153     {
    154       // TODO(haining) use if (with init) and else for c++17 instead of extra
    155       // scope and continue
    156       // if (std::lock_guard guard(stripes_lock_); /*condition*/) { }
    157       std::lock_guard<std::mutex> guard(stripes_lock_);
    158       if (!StripeIsDifferentFromPrevious(portrait_stripe)) {
    159         UpdateMostRecentSeqNumIfStripeIsNew(portrait_stripe);
    160         continue;
    161       }
    162     }
    163     auto seq_num = portrait_stripe.seq_number;
    164     auto index = portrait_stripe.index;
    165     auto landscape_stripe = Rotated(portrait_stripe);
    166     auto stripes = {std::make_shared<Stripe>(std::move(portrait_stripe)),
    167                     std::make_shared<Stripe>(std::move(landscape_stripe))};
    168     for (auto& stripe : stripes) {
    169 #ifdef FUZZ_TEST_VNC
    170       if (random(e)) {
    171         usleep(10000);
    172       }
    173 #endif
    174       CompressStripe(&jpeg_compressor, stripe.get());
    175     }
    176     bool any_new_stripes = false;
    177     for (auto& stripe : stripes) {
    178       any_new_stripes = UpdateStripeIfStripeIsNew(stripe) || any_new_stripes;
    179     }
    180     if (any_new_stripes) {
    181       bb_->NewStripeReady(index, seq_num);
    182     }
    183   }
    184 }
    185 
    186 int FrameBufferWatcher::StripesPerFrame() {
    187   return SimulatedHWComposer::NumberOfStripes();
    188 }
    189