• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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