1 /*
2 * Copyright (C) 2020 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/webrtc/display_handler.h"
18
19 #include <chrono>
20 #include <functional>
21 #include <memory>
22
23 #include <libyuv.h>
24
25 namespace cuttlefish {
DisplayHandler(std::shared_ptr<webrtc_streaming::VideoSink> display_sink,ScreenConnector & screen_connector)26 DisplayHandler::DisplayHandler(
27 std::shared_ptr<webrtc_streaming::VideoSink> display_sink,
28 ScreenConnector& screen_connector)
29 : display_sink_(display_sink), screen_connector_(screen_connector) {
30 screen_connector_.SetCallback(std::move(GetScreenConnectorCallback()));
31 }
32
GetScreenConnectorCallback()33 DisplayHandler::GenerateProcessedFrameCallback DisplayHandler::GetScreenConnectorCallback() {
34 // only to tell the producer how to create a ProcessedFrame to cache into the queue
35 DisplayHandler::GenerateProcessedFrameCallback callback =
36 [](std::uint32_t display_number, std::uint8_t* frame_pixels,
37 WebRtcScProcessedFrame& processed_frame) {
38 processed_frame.display_number_ = display_number;
39 // TODO(171305898): handle multiple displays.
40 if (display_number != 0) {
41 // BUG 186580833: display_number comes from surface_id in crosvm
42 // create_surface from virtio_gpu.rs set_scanout. We cannot use it as
43 // the display number. Either crosvm virtio-gpu is incorrectly ignoring
44 // scanout id and instead using a monotonically increasing surface id
45 // number as the scanout resource is replaced over time, or frontend code
46 // here is incorrectly assuming surface id == display id.
47 display_number = 0;
48 }
49 const int display_w =
50 ScreenConnectorInfo::ScreenWidth(display_number);
51 const int display_h =
52 ScreenConnectorInfo::ScreenHeight(display_number);
53 const int display_stride_bytes =
54 ScreenConnectorInfo::ScreenStrideBytes(display_number);
55 processed_frame.display_number_ = display_number;
56 processed_frame.buf_ =
57 std::make_unique<CvdVideoFrameBuffer>(display_w, display_h);
58 libyuv::ABGRToI420(
59 frame_pixels, display_stride_bytes, processed_frame.buf_->DataY(),
60 processed_frame.buf_->StrideY(), processed_frame.buf_->DataU(),
61 processed_frame.buf_->StrideU(), processed_frame.buf_->DataV(),
62 processed_frame.buf_->StrideV(), display_w, display_h);
63 processed_frame.is_success_ = true;
64 };
65 return callback;
66 }
67
Loop()68 [[noreturn]] void DisplayHandler::Loop() {
69 for (;;) {
70 auto processed_frame = screen_connector_.OnNextFrame();
71 // processed_frame has display number from the guest
72 {
73 std::lock_guard<std::mutex> lock(last_buffer_mutex_);
74 std::shared_ptr<CvdVideoFrameBuffer> buffer = std::move(processed_frame.buf_);
75 last_buffer_ =
76 std::static_pointer_cast<webrtc_streaming::VideoFrameBuffer>(buffer);
77 }
78 if (processed_frame.is_success_) {
79 SendLastFrame();
80 }
81 }
82 }
83
SendLastFrame()84 void DisplayHandler::SendLastFrame() {
85 std::shared_ptr<webrtc_streaming::VideoFrameBuffer> buffer;
86 {
87 std::lock_guard<std::mutex> lock(last_buffer_mutex_);
88 buffer = last_buffer_;
89 }
90 if (!buffer) {
91 // If a connection request arrives before the first frame is available don't
92 // send any frame.
93 return;
94 }
95 {
96 // SendLastFrame can be called from multiple threads simultaneously, locking
97 // here avoids injecting frames with the timestamps in the wrong order.
98 std::lock_guard<std::mutex> lock(next_frame_mutex_);
99 int64_t time_stamp =
100 std::chrono::duration_cast<std::chrono::microseconds>(
101 std::chrono::system_clock::now().time_since_epoch())
102 .count();
103 display_sink_->OnFrame(buffer, time_stamp);
104 }
105 }
106
IncClientCount()107 void DisplayHandler::IncClientCount() {
108 client_count_++;
109 if (client_count_ == 1) {
110 screen_connector_.ReportClientsConnected(true);
111 }
112 }
113
DecClientCount()114 void DisplayHandler::DecClientCount() {
115 client_count_--;
116 if (client_count_ == 0) {
117 screen_connector_.ReportClientsConnected(false);
118 }
119 }
120
121 } // namespace cuttlefish
122