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