1 /*
2 * Copyright (C) 2024 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 <assert.h>
18 #include <fcntl.h>
19 #include <log/log.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/ioctl.h>
24 #include <unistd.h>
25
26 #include "guest/commands/v4l2_streamer/v4l2_helpers.h"
27 #include "guest/commands/v4l2_streamer/vsock_frame_source.h"
28 #include "guest/commands/v4l2_streamer/yuv2rgb.h"
29
30 namespace cuttlefish {
31
~VsockFrameSource()32 VsockFrameSource::~VsockFrameSource() { Stop(); }
33
IsBlob(const std::vector<char> & blob)34 bool VsockFrameSource::IsBlob(const std::vector<char>& blob) {
35 static const char kPng[] = "\x89PNG";
36 static const char kJpeg[] = "\xff\xd8";
37 bool is_png =
38 blob.size() > 4 && std::memcmp(blob.data(), kPng, sizeof(kPng)) == 0;
39 bool is_jpeg =
40 blob.size() > 2 && std::memcmp(blob.data(), kJpeg, sizeof(kJpeg)) == 0;
41 return is_png || is_jpeg;
42 }
43
WriteJsonEventMessage(const std::string & message)44 bool VsockFrameSource::WriteJsonEventMessage(const std::string& message) {
45 Json::Value json_message;
46 json_message["event"] = message;
47 return connection_ && connection_->WriteMessage(json_message);
48 }
49
ReadSettingsFromJson(const Json::Value & json)50 Result<bool> VsockFrameSource::ReadSettingsFromJson(const Json::Value& json) {
51 frame_width_ = json["width"].asInt();
52 frame_height_ = json["height"].asInt();
53 frame_rate_ = json["frame_rate"].asDouble();
54
55 if (frame_width_ > 0 && frame_height_ > 0 && frame_rate_ > 0) {
56 frame_size_ =
57 CF_EXPECT(V4l2GetFrameSize(format_, frame_width_, frame_height_),
58 "Error getting framesize");
59 ALOGI("%s: readSettingsFromJson received: w/h/fps(%d,%d,%d)", __FUNCTION__,
60 frame_width_, frame_height_, frame_rate_);
61 return true;
62 } else {
63 ALOGE("%s: readSettingsFromJson received invalid values: w/h/fps(%d,%d,%d)",
64 __FUNCTION__, frame_width_, frame_height_, frame_rate_);
65 return false;
66 }
67 }
68
Connect()69 bool VsockFrameSource::Connect() {
70 connection_ = std::make_unique<
71 cuttlefish::VsockServerConnection>(); // VsockServerConnection
72 if (connection_->Connect(
73 7600, VMADDR_CID_ANY,
74 std::nullopt /* vhost_user_vsock: because it's guest */)) {
75 auto json_settings = connection_->ReadJsonMessage();
76
77 if (ReadSettingsFromJson(json_settings)) {
78 std::lock_guard<std::mutex> lock(settings_mutex_);
79 ALOGI("%s: VsockFrameSource connected", __FUNCTION__);
80 return true;
81 } else {
82 ALOGE("%s: Could not read settings", __FUNCTION__);
83 }
84 } else {
85 ALOGE("%s: VsockFrameSource connection failed", __FUNCTION__);
86 }
87 return false;
88 }
89
Start(const std::string & v4l2_device_path)90 Result<std::unique_ptr<VsockFrameSource>> VsockFrameSource::Start(
91 const std::string& v4l2_device_path) {
92 auto frame_source = std::unique_ptr<VsockFrameSource>(new VsockFrameSource);
93
94 frame_source->v4l2_device_path_ = v4l2_device_path;
95
96 CF_EXPECT(frame_source->Connect(), "connect failed");
97
98 ALOGI("%s: VsockFrameSource connected", __FUNCTION__);
99
100 frame_source->running_ = true;
101
102 frame_source->WriteJsonEventMessage("VIRTUAL_DEVICE_START_CAMERA_SESSION");
103
104 frame_source->fd_v4l2_device_ = CF_EXPECT(
105 V4l2InitDevice(frame_source->v4l2_device_path_, frame_source->format_,
106 frame_source->frame_width_, frame_source->frame_height_),
107 "Error opening v4l2 device");
108
109 CF_EXPECT(frame_source->fd_v4l2_device_->IsOpen(),
110 "Error: fd_v4l2_device_->IsOpen() failed");
111
112 ALOGI("%s: successful v4l2 device open.", __FUNCTION__);
113
114 return frame_source;
115 }
116
Stop()117 void VsockFrameSource::Stop() {
118 if (running_.exchange(false)) {
119 if (reader_thread_.joinable()) {
120 reader_thread_.join();
121 }
122 WriteJsonEventMessage("VIRTUAL_DEVICE_STOP_CAMERA_SESSION");
123 connection_ = nullptr;
124 fd_v4l2_device_->Close();
125 }
126 }
127
WriteFrame(const std::vector<char> & frame,std::vector<char> & rgb_frame)128 void VsockFrameSource::WriteFrame(const std::vector<char>& frame,
129 std::vector<char>& rgb_frame) {
130 if (rgb_frame.size() != frame_size_) {
131 rgb_frame.resize(frame_size_);
132 }
133 Yuv2Rgb((unsigned char*)frame.data(), (unsigned char*)rgb_frame.data(),
134 frame_width_, frame_height_);
135 fd_v4l2_device_->Write((unsigned char*)rgb_frame.data(), frame_size_);
136 }
137
Running()138 bool VsockFrameSource::Running() { return running_; }
139
FramesizeMatches(const std::vector<char> & data)140 bool VsockFrameSource::FramesizeMatches(const std::vector<char>& data) {
141 return data.size() == 3 * frame_width_ * frame_height_ / 2;
142 }
143
VsockReadLoopThreaded()144 Result<void> VsockFrameSource::VsockReadLoopThreaded() {
145 CF_EXPECT(fd_v4l2_device_->IsOpen(), "Error: v4l2_initdevice == 0");
146
147 reader_thread_ = std::thread([this] { VsockReadLoop(); });
148
149 return {};
150 }
151
VsockReadLoop()152 void VsockFrameSource::VsockReadLoop() {
153 std::vector<char> frame;
154 std::vector<char> next_frame;
155 std::vector<char> rgb_frame;
156
157 while (running_.load() && connection_->ReadMessage(next_frame)) {
158 if (FramesizeMatches(next_frame)) {
159 std::lock_guard<std::mutex> lock(frame_mutex_);
160 timestamp_ = systemTime();
161 frame.swap(next_frame);
162 yuv_frame_updated_.notify_one();
163 WriteFrame(frame, rgb_frame);
164 } else if (IsBlob(next_frame)) {
165 } // TODO
166 else {
167 ALOGE("%s: Unexpected data of %zu bytes", __FUNCTION__,
168 next_frame.size());
169 }
170 }
171 if (!connection_->IsConnected_Unguarded()) {
172 ALOGE("%s: Connection closed - exiting", __FUNCTION__);
173 running_ = false;
174 }
175 }
176
177 } // End namespace cuttlefish