1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "camera_dump.h"
17
18 #include <sys/time.h>
19 #include <unistd.h>
20 #include <fstream>
21 #include <sstream>
22 #include <cstdio>
23 #include <iostream>
24 #include <cstdlib>
25 #include <regex>
26
27 #include "camera.h"
28 #include "v1_0/vdi_types.h"
29
30 using namespace std;
31
32 namespace OHOS::Camera {
33 const std::string DUMP_PATH = "/data/local/tmp/";
34 const std::string DUMP_CONFIG_PATH = "/data/local/tmp/dump.config";
35 constexpr uint32_t ARGS_MAX_NUM = 5;
36 constexpr uint32_t MAX_USAGE_RATE = 70;
37 constexpr int32_t CHECK_DISKINFO_TIME_MS = 10000;
38 const uint32_t TITLEINFO_ARRAY_SIZE = 200;
39
40 const char *g_cameraDumpHelp =
41 " Camera manager dump options:\n"
42 " -h: camera dump help\n"
43 " -m: start dump metadata\n"
44 " -b: start dump buffer\n"
45 " -o: start dump start\n"
46 " -e: exit all dump\n";
47
48 std::map<DumpType, bool> g_dumpInfoMap = {
49 {MedataType, false},
50 {BufferType, false},
51 {OpenType, false}
52 };
53
54 std::map<std::string, std::string> g_dumpToolMap = {
55 {ENABLE_DQ_BUFFER_DUMP, "false"},
56 {ENABLE_UVC_NODE, "false"},
57 {ENABLE_UVC_NODE_CONVERTED, "false"},
58 {ENABLE_EXIF_NODE_CONVERTED, "false"},
59 {ENABLE_FACE_NODE_CONVERTED, "false"},
60 {ENABLE_FORK_NODE_CONVERTED, "false"},
61 {ENABLE_RKFACE_NODE_CONVERTED, "false"},
62 {ENABLE_RKEXIF_NODE_CONVERTED, "false"},
63 {ENABLE_CODEC_NODE_CONVERTED, "false"},
64 {ENABLE_RKCODEC_NODE_CONVERTED, "false"},
65 {ENABLE_STREAM_TUNNEL, "false"},
66 {ENABLE_METADATA, "false"},
67 {PREVIEW_INTERVAL, "1"},
68 {CAPTURE_INTERVAL, "1"}
69 };
70
~CameraDumper()71 CameraDumper::~CameraDumper()
72 {
73 StopCheckDiskInfo();
74 }
75
DumpStart()76 bool CameraDumper::DumpStart()
77 {
78 if (!IsDumpOpened(OpenType)) {
79 return false;
80 }
81 std::stringstream mkdirCmd;
82 mkdirCmd << "mkdir -p " << DUMP_PATH;
83 system(mkdirCmd.str().c_str());
84
85 ReadDumpConfig();
86 return true;
87 }
88
ReadDumpConfig()89 bool CameraDumper::ReadDumpConfig()
90 {
91 std::stringstream ss;
92 ss << DUMP_CONFIG_PATH;
93 std::string configPath = ss.str();
94 if (access(configPath.c_str(), R_OK) != 0) {
95 CAMERA_LOGE("dump config file <%{public}s> not exist or not readable", configPath.c_str());
96 return false;
97 }
98 std::ifstream ifs;
99 ifs.open(configPath, std::ios::in);
100 if (!ifs) {
101 CAMERA_LOGE("open dump config file <%{public}s> failed, error: %{public}s",
102 configPath.c_str(), std::strerror(errno));
103 return false;
104 }
105
106 std::string str;
107 while (!ifs.eof()) {
108 if (ifs >> str) {
109 istringstream istr(str);
110 std::string strTemp;
111 vector<std::string> strVector;
112 while (getline(istr, strTemp, '=')) {
113 strVector.push_back(strTemp);
114 }
115 g_dumpToolMap[strVector[0]] = strVector[1];
116 }
117 }
118
119 ifs.close();
120 return true;
121 }
122
IsDumpCommandOpened(std::string type)123 bool CameraDumper::IsDumpCommandOpened(std::string type)
124 {
125 std::lock_guard<std::mutex> l(dumpStateLock_);
126 if (g_dumpToolMap.find(type) != g_dumpToolMap.end() && g_dumpToolMap[type] == "true") {
127 return true;
128 }
129 return false;
130 }
131
DumpBuffer(std::string name,std::string type,const std::shared_ptr<IBuffer> & buffer,uint32_t width,uint32_t height)132 bool CameraDumper::DumpBuffer(std::string name, std::string type, const std::shared_ptr<IBuffer>& buffer,
133 uint32_t width, uint32_t height)
134 {
135 if (!IsDumpOpened(OpenType) || !IsDumpCommandOpened(type) || (buffer == nullptr)) {
136 return false;
137 }
138 uint32_t defaultWidth = (width == 0) ? buffer->GetCurWidth() : width;
139 uint32_t defaultHeight = (height == 0) ? buffer->GetCurHeight() : height;
140 void* srcAddr = buffer->GetIsValidDataInSurfaceBuffer() ? buffer->GetSuffaceBufferAddr() : buffer->GetVirAddress();
141 uint32_t size = buffer->GetIsValidDataInSurfaceBuffer() ? buffer->GetSuffaceBufferSize() : buffer->GetSize();
142 const std::string DqBufferName = "DQBuffer";
143 if (name != DqBufferName) {
144 size = buffer->GetEsFrameInfo().size > 0 ? static_cast<uint32_t>(buffer->GetEsFrameInfo().size) : size;
145 }
146
147 std::stringstream ss;
148 std::string fileName;
149 ss << name.c_str() << "_captureId[" << buffer->GetCaptureId() << "]_streamId[" << buffer->GetStreamId() <<
150 "]_width[" << defaultWidth << "]_height[" << defaultHeight;
151
152 int32_t previewInterval = 1;
153 std::istringstream ssPreview(g_dumpToolMap[PREVIEW_INTERVAL]);
154 ssPreview >> previewInterval;
155
156 int32_t captureInterval = 1;
157 std::istringstream ssVideo(g_dumpToolMap[CAPTURE_INTERVAL]);
158 ssVideo >> captureInterval;
159
160 ++dumpCount_;
161 if (buffer->GetEncodeType() == VDI::Camera::V1_0::ENCODE_TYPE_JPEG) {
162 if (dumpCount_ % captureInterval != 0) {
163 return true;
164 }
165 ss << "]_" << GetCurrentLocalTimeStamp();
166 ss >> fileName;
167 fileName += ".jpeg";
168 } else if (buffer->GetEncodeType() == VDI::Camera::V1_0::ENCODE_TYPE_H264) {
169 #ifdef CAMERA_BUILT_ON_USB
170 ss << "]_" << GetCurrentLocalTimeStamp();
171 ss >> fileName;
172 fileName += "_umpVideo.yuv";
173 #else
174 fileName += "dumpVideo.h264";
175 #endif
176 } else {
177 if (dumpCount_ % previewInterval != 0) {
178 return true;
179 }
180
181 ss << "]_" << GetCurrentLocalTimeStamp();
182 ss >> fileName;
183 fileName += (buffer->GetCurFormat() == CAMERA_FORMAT_BLOB ? ".jpeg" : ".yuv");
184 }
185 return SaveDataToFile(fileName.c_str(), srcAddr, size);
186 }
187
DumpMetadata(std::string name,std::string type,const std::shared_ptr<CameraMetadata> & metadata)188 bool CameraDumper::DumpMetadata(std::string name, std::string type,
189 const std::shared_ptr<CameraMetadata>& metadata)
190 {
191 if (metadata == nullptr) {
192 CAMERA_LOGE("metadata is nullptr");
193 return false;
194 }
195
196 if (!IsDumpOpened(OpenType) || !IsDumpCommandOpened(type)) {
197 return false;
198 }
199
200 common_metadata_header_t *data = metadata->get();
201 if (data == nullptr) {
202 CAMERA_LOGE("data is nullptr");
203 return false;
204 }
205 std::string metaStr = FormatCameraMetadataToString(data);
206 if (metaStr.size() == 0) {
207 CAMERA_LOGE("metaStr.size is 0");
208 return true;
209 }
210 std::stringstream ss;
211 ss << GetCurrentLocalTimeStamp() << "_" << name << ".meta";
212
213 return SaveDataToFile(ss.str().c_str(), metaStr.c_str(), metaStr.size());
214 }
215
UpdateDumpMode(DumpType type,bool isDump,HdfSBuf * reply)216 void CameraDumper::UpdateDumpMode(DumpType type, bool isDump, HdfSBuf *reply)
217 {
218 std::string upRetStr;
219 {
220 std::lock_guard<std::mutex> l(dumpStateLock_);
221 auto it = g_dumpInfoMap.find(type);
222 if (it != g_dumpInfoMap.end()) {
223 g_dumpInfoMap[type] = isDump;
224 upRetStr += " set dump mode success!\n";
225 }
226 }
227
228 if (reply != nullptr) {
229 (void)HdfSbufWriteString(reply, upRetStr.c_str());
230 }
231
232 if (isDump) {
233 StartCheckDiskInfo();
234 } else {
235 StopCheckDiskInfo();
236 }
237 }
238
IsDumpOpened(DumpType type)239 bool CameraDumper::IsDumpOpened(DumpType type)
240 {
241 std::lock_guard<std::mutex> l(dumpStateLock_);
242 if (g_dumpInfoMap.find(type) != g_dumpInfoMap.end() && g_dumpInfoMap[type]) {
243 return true;
244 }
245 return false;
246 }
247
SaveDataToFile(const char * fileName,const void * data,uint32_t size)248 bool CameraDumper::SaveDataToFile(const char *fileName, const void *data, uint32_t size)
249 {
250 CAMERA_LOGI("save dump file <%{public}s> begin, size: %{public}d", fileName, size);
251 std::stringstream mkdirCmd;
252 mkdirCmd << "mkdir -p " << DUMP_PATH;
253 system(mkdirCmd.str().c_str());
254 if (access(DUMP_PATH.c_str(), F_OK) != 0 || access(DUMP_PATH.c_str(), W_OK) != 0) {
255 CAMERA_LOGE("dump path <%{public}s> not exist, error: %{public}s", DUMP_PATH.c_str(), std::strerror(errno));
256 return false;
257 }
258
259 std::stringstream ss;
260 ss << DUMP_PATH << fileName;
261 std::ofstream ofs(ss.str(), std::ios::app);
262
263 if (!ofs.good()) {
264 CAMERA_LOGE("open dump file <%{public}s> failed, error: %{public}s", ss.str().c_str(), std::strerror(errno));
265 return false;
266 }
267
268 ofs.write(static_cast<const char *>(data), size);
269 ofs.close();
270
271 return true;
272 }
273
GetCurrentLocalTimeStamp()274 uint64_t CameraDumper::GetCurrentLocalTimeStamp()
275 {
276 std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp =
277 std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
278 auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
279 return static_cast<uint64_t>(tmp.count());
280 }
281
ShowDumpMenu(HdfSBuf * reply)282 void CameraDumper::ShowDumpMenu(HdfSBuf *reply)
283 {
284 if (reply != nullptr) {
285 (void)HdfSbufWriteString(reply, g_cameraDumpHelp);
286 }
287 }
288
CameraHostDumpProcess(HdfSBuf * data,HdfSBuf * reply)289 void CameraDumper::CameraHostDumpProcess(HdfSBuf *data, HdfSBuf *reply)
290 {
291 if (data == nullptr || reply == nullptr) {
292 CAMERA_LOGE("%{public}s is nullptr", (data == nullptr) ? "data" : "reply");
293 return;
294 }
295
296 uint32_t argsNum;
297 if (!HdfSbufReadUint32(data, &argsNum)) {
298 CAMERA_LOGE("read argsNum failed!");
299 return;
300 }
301
302 if (argsNum <= 0 || argsNum > ARGS_MAX_NUM) {
303 (void)HdfSbufWriteString(reply, g_cameraDumpHelp);
304 return;
305 }
306
307 for (uint32_t i = 0; i < argsNum; i++) {
308 const char *value = HdfSbufReadString(data);
309 if (value == NULL) {
310 CAMERA_LOGE("arg is invalid i: %{public}u", i);
311 return;
312 }
313 if (strcmp(value, "-m") == 0) {
314 UpdateDumpMode(MedataType, true, reply);
315 } else if (strcmp(value, "-b") == 0) {
316 UpdateDumpMode(BufferType, true, reply);
317 } else if (strcmp(value, "-e") == 0) {
318 UpdateDumpMode(BufferType, false, reply);
319 UpdateDumpMode(MedataType, false, reply);
320 } else if (strcmp(value, "-o") == 0) {
321 UpdateDumpMode(OpenType, true, reply);
322 } else {
323 ShowDumpMenu(reply);
324 }
325 }
326 }
327
CameraDumpEvent(HdfSBuf * data,HdfSBuf * reply)328 int32_t CameraDumpEvent(HdfSBuf *data, HdfSBuf *reply)
329 {
330 CameraDumper& dumper = CameraDumper::GetInstance();
331 dumper.CameraHostDumpProcess(data, reply);
332 return HDF_SUCCESS;
333 }
334
CheckDiskInfo()335 void CameraDumper::CheckDiskInfo()
336 {
337 stringstream ss;
338 ss << "df " << DUMP_PATH;
339
340 FILE *fp = popen(ss.str().c_str(), "r");
341 if (fp == NULL) {
342 CAMERA_LOGE("popen failed, cmd : %{public}s", ss.str().c_str());
343 return;
344 }
345
346 char titleInfo[TITLEINFO_ARRAY_SIZE] = {0};
347 char resultInfo[TITLEINFO_ARRAY_SIZE] = {0};
348 fgets(titleInfo, sizeof(titleInfo) / sizeof(titleInfo[0]) - 1, fp);
349 fgets(resultInfo, sizeof(resultInfo) / sizeof(resultInfo[0]) - 1, fp);
350
351 pclose(fp);
352
353 std::string diskInfoStr(resultInfo);
354 istringstream str(diskInfoStr);
355 string out;
356 std::vector<std::string> infos;
357
358 while (str >> out) {
359 infos.push_back(out);
360 }
361
362 std::string userPerStr = infos[4].substr(0, infos[4].length() - 1);
363 uint32_t usePer = static_cast<uint32_t>(std::atoi(userPerStr.c_str()));
364 if (usePer >= MAX_USAGE_RATE) {
365 CAMERA_LOGE("dump use disk over the limit, stop dump");
366 std::lock_guard<std::mutex> l(dumpStateLock_);
367 for (auto it = g_dumpInfoMap.begin(); it != g_dumpInfoMap.end(); it++) {
368 it->second = false;
369 }
370 }
371 }
372
ThreadWorkFun()373 void CameraDumper::ThreadWorkFun()
374 {
375 while (true) {
376 CheckDiskInfo();
377
378 std::unique_lock<std::mutex> l(terminateLock_);
379 cv_.wait_for(l, std::chrono::milliseconds(CHECK_DISKINFO_TIME_MS),
380 [this]() {
381 return terminate_;
382 }
383 );
384
385 if (terminate_) {
386 break;
387 }
388 }
389 }
390
StartCheckDiskInfo()391 void CameraDumper::StartCheckDiskInfo()
392 {
393 {
394 std::unique_lock<std::mutex> l(terminateLock_);
395 if (terminate_ == false) {
396 CAMERA_LOGD("thread is already start");
397 return;
398 }
399 terminate_ = false;
400 }
401
402 handleThread_ = std::make_unique<std::thread>([this] { this->ThreadWorkFun(); });
403 }
404
StopCheckDiskInfo()405 void CameraDumper::StopCheckDiskInfo()
406 {
407 {
408 std::unique_lock<std::mutex> l(terminateLock_);
409 if (terminate_ == true) {
410 CAMERA_LOGD("thread is already stop");
411 return;
412 }
413 terminate_ = true;
414 cv_.notify_one();
415 }
416 if (handleThread_ != nullptr && handleThread_->joinable()) {
417 handleThread_->join();
418 handleThread_ = nullptr;
419 }
420 }
421
GetInstance()422 CameraDumper& CameraDumper::GetInstance()
423 {
424 static CameraDumper instance_;
425 return instance_;
426 }
427 } // namespace OHOS::Camera
428