1 /*
2  * Copyright (C) 2019 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/libs/screen_connector/socket_based_screen_connector.h"
18 
19 #include <glog/logging.h>
20 
21 #include "common/libs/fs/shared_fd.h"
22 
23 namespace cvd {
24 
SocketBasedScreenConnector(int frames_fd)25 SocketBasedScreenConnector::SocketBasedScreenConnector(int frames_fd) {
26 screen_server_thread_ =
27     std::thread([this, frames_fd]() { ServerLoop(frames_fd); });
28 }
29 
OnFrameAfter(std::uint32_t frame_number,const FrameCallback & frame_callback)30 bool SocketBasedScreenConnector::OnFrameAfter(
31     std::uint32_t frame_number, const FrameCallback& frame_callback) {
32   int buffer_idx = WaitForNewFrameSince(&frame_number);
33   void* buffer = GetBuffer(buffer_idx);
34   frame_callback(frame_number, reinterpret_cast<uint8_t*>(buffer));
35   return true;
36 }
37 
WaitForNewFrameSince(std::uint32_t * seq_num)38 int SocketBasedScreenConnector::WaitForNewFrameSince(std::uint32_t* seq_num) {
39   std::unique_lock<std::mutex> lock(new_frame_mtx_);
40   while (seq_num_ == *seq_num) {
41     new_frame_cond_var_.wait(lock);
42   }
43   *seq_num = seq_num_;
44   return newest_buffer_;
45 }
46 
GetBuffer(int buffer_idx)47 void* SocketBasedScreenConnector::GetBuffer(int buffer_idx) {
48   if (buffer_idx < 0) return nullptr;
49   buffer_idx %= NUM_BUFFERS_;
50   return &buffer_[buffer_idx * ScreenSizeInBytes()];
51 }
52 
ServerLoop(int frames_fd)53 void SocketBasedScreenConnector::ServerLoop(int frames_fd) {
54   if (frames_fd < 0) {
55     LOG(FATAL) << "Invalid file descriptor: " << frames_fd;
56     return;
57   }
58   auto server = SharedFD::Dup(frames_fd);
59   close(frames_fd);
60   if (!server->IsOpen()) {
61     LOG(FATAL) << "Unable to dup screen server: " << server->StrError();
62     return;
63   }
64 
65   int current_buffer = 0;
66 
67   while (1) {
68     LOG(INFO) << "Screen Connector accepting connections...";
69     auto conn = SharedFD::Accept(*server);
70     if (!conn->IsOpen()) {
71       LOG(ERROR) << "Disconnected fd returned from accept";
72       continue;
73     }
74     while (conn->IsOpen()) {
75       int32_t size = 0;
76       if (conn->Read(&size, sizeof(size)) < 0) {
77         LOG(ERROR) << "Failed to read from hwcomposer: " << conn->StrError();
78         break;
79       }
80       auto buff = reinterpret_cast<uint8_t*>(GetBuffer(current_buffer));
81       while (size > 0) {
82         auto read = conn->Read(buff, size);
83         if (read < 0) {
84           LOG(ERROR) << "Failed to read from hwcomposer: " << conn->StrError();
85           conn->Close();
86           break;
87         }
88         size -= read;
89         buff += read;
90       }
91       BroadcastNewFrame(current_buffer);
92       current_buffer = (current_buffer + 1) % NUM_BUFFERS_;
93     }
94   }
95 }
96 
BroadcastNewFrame(int buffer_idx)97 void SocketBasedScreenConnector::BroadcastNewFrame(int buffer_idx) {
98   {
99     std::lock_guard<std::mutex> lock(new_frame_mtx_);
100     seq_num_++;
101     newest_buffer_ = buffer_idx;
102   }
103   new_frame_cond_var_.notify_all();
104 }
105 } // namespace cvd
106