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,uint32_t width,uint32_t height)126 bool CameraDumper::DumpBuffer(std::string name, std::string type, const std::shared_ptr<IBuffer>& buffer,
127 uint32_t width, uint32_t height)
128 {
129 if (!IsDumpOpened(OpenType) || !IsDumpCommandOpened(type) || (buffer == nullptr)) {
130 return false;
131 }
132 uint32_t defaultWidth = (width == 0) ? buffer->GetCurWidth() : width;
133 uint32_t defaultHeight = (height == 0) ? buffer->GetCurHeight() : height;
134 void* srcAddr = buffer->GetIsValidDataInSurfaceBuffer() ? buffer->GetSuffaceBufferAddr() : buffer->GetVirAddress();
135 uint32_t size = buffer->GetIsValidDataInSurfaceBuffer() ? buffer->GetSuffaceBufferSize() : buffer->GetSize();
136 const std::string DqBufferName = "DQBuffer";
137 if (name != DqBufferName) {
138 size = buffer->GetEsFrameInfo().size > 0 ? buffer->GetEsFrameInfo().size : size;
139 }
140
141 std::stringstream ss;
142 std::string fileName;
143 ss << name.c_str() << "_captureId[" << buffer->GetCaptureId() << "]_streamId[" << buffer->GetStreamId() <<
144 "]_width[" << defaultWidth << "]_height[" << defaultHeight;
145
146 int32_t previewInterval = 1;
147 std::istringstream ssPreview(g_dumpToolMap[PREVIEW_INTERVAL]);
148 ssPreview >> previewInterval;
149
150 int32_t captureInterval = 1;
151 std::istringstream ssVideo(g_dumpToolMap[CAPTURE_INTERVAL]);
152 ssVideo >> captureInterval;
153
154 ++dumpCount_;
155 if (buffer->GetEncodeType() == VDI::Camera::V1_0::ENCODE_TYPE_JPEG) {
156 if (dumpCount_ % captureInterval != 0) {
157 return true;
158 }
159 ss << "]_" << GetCurrentLocalTimeStamp();
160 ss >> fileName;
161 fileName += ".jpeg";
162 } else if (buffer->GetEncodeType() == VDI::Camera::V1_0::ENCODE_TYPE_H264) {
163 #ifdef CAMERA_BUILT_ON_USB
164 ss << "]_" << GetCurrentLocalTimeStamp();
165 ss >> fileName;
166 fileName += "_umpVideo.yuv";
167 #else
168 fileName += "dumpVideo.h264";
169 #endif
170 } else {
171 if (dumpCount_ % previewInterval != 0) {
172 return true;
173 }
174
175 ss << "]_" << GetCurrentLocalTimeStamp();
176 ss >> fileName;
177 fileName += ".yuv";
178 }
179 return SaveDataToFile(fileName.c_str(), srcAddr, size);
180 }
181
DumpMetadata(std::string name,std::string type,const std::shared_ptr<CameraMetadata> & metadata)182 bool CameraDumper::DumpMetadata(std::string name, std::string type,
183 const std::shared_ptr<CameraMetadata>& metadata)
184 {
185 if (metadata == nullptr) {
186 CAMERA_LOGE("metadata is nullptr");
187 return false;
188 }
189
190 if (!IsDumpOpened(OpenType) || !IsDumpCommandOpened(type)) {
191 return false;
192 }
193
194 common_metadata_header_t *data = metadata->get();
195 if (data == nullptr) {
196 CAMERA_LOGE("data is nullptr");
197 return false;
198 }
199 std::string metaStr = FormatCameraMetadataToString(data);
200 if (metaStr.size() == 0) {
201 CAMERA_LOGE("metaStr.size is 0");
202 return true;
203 }
204 std::stringstream ss;
205 ss << GetCurrentLocalTimeStamp() << "_" << name << ".meta";
206
207 return SaveDataToFile(ss.str().c_str(), metaStr.c_str(), metaStr.size());
208 }
209
UpdateDumpMode(DumpType type,bool isDump,HdfSBuf * reply)210 void CameraDumper::UpdateDumpMode(DumpType type, bool isDump, HdfSBuf *reply)
211 {
212 std::string upRetStr;
213 {
214 std::lock_guard<std::mutex> l(dumpStateLock_);
215 auto it = g_dumpInfoMap.find(type);
216 if (it != g_dumpInfoMap.end()) {
217 g_dumpInfoMap[type] = isDump;
218 upRetStr += " set dump mode success!\n";
219 }
220 }
221
222 if (reply != nullptr) {
223 (void)HdfSbufWriteString(reply, upRetStr.c_str());
224 }
225
226 if (isDump) {
227 StartCheckDiskInfo();
228 } else {
229 StopCheckDiskInfo();
230 }
231 }
232
IsDumpOpened(DumpType type)233 bool CameraDumper::IsDumpOpened(DumpType type)
234 {
235 std::lock_guard<std::mutex> l(dumpStateLock_);
236 if (g_dumpInfoMap.find(type) != g_dumpInfoMap.end() && g_dumpInfoMap[type]) {
237 return true;
238 }
239 return false;
240 }
241
SaveDataToFile(const char * fileName,const void * data,uint32_t size)242 bool CameraDumper::SaveDataToFile(const char *fileName, const void *data, uint32_t size)
243 {
244 CAMERA_LOGI("save dump file <%{public}s> begin, size: %{public}d", fileName, size);
245 std::stringstream mkdirCmd;
246 mkdirCmd << "mkdir -p " << DUMP_PATH;
247 system(mkdirCmd.str().c_str());
248
249 std::stringstream ss;
250 ss << DUMP_PATH << fileName;
251 std::ofstream ofs(ss.str(), std::ios::app);
252
253 if (!ofs.good()) {
254 CAMERA_LOGE("open dump file <%{public}s> failed, error: %{public}s", ss.str().c_str(), std::strerror(errno));
255 return false;
256 }
257
258 ofs.write(static_cast<const char *>(data), size);
259 ofs.close();
260
261 return true;
262 }
263
GetCurrentLocalTimeStamp()264 uint64_t CameraDumper::GetCurrentLocalTimeStamp()
265 {
266 std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp =
267 std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
268 auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
269 return static_cast<uint64_t>(tmp.count());
270 }
271
ShowDumpMenu(HdfSBuf * reply)272 void CameraDumper::ShowDumpMenu(HdfSBuf *reply)
273 {
274 if (reply != nullptr) {
275 (void)HdfSbufWriteString(reply, g_cameraDumpHelp);
276 }
277 }
278
CameraHostDumpProcess(HdfSBuf * data,HdfSBuf * reply)279 void CameraDumper::CameraHostDumpProcess(HdfSBuf *data, HdfSBuf *reply)
280 {
281 if (data == nullptr || reply == nullptr) {
282 CAMERA_LOGE("%{public}s is nullptr", (data == nullptr) ? "data" : "reply");
283 return;
284 }
285
286 uint32_t argsNum;
287 if (!HdfSbufReadUint32(data, &argsNum)) {
288 CAMERA_LOGE("read argsNum failed!");
289 return;
290 }
291
292 if (argsNum <= 0 || argsNum > ARGS_MAX_NUM) {
293 (void)HdfSbufWriteString(reply, g_cameraDumpHelp);
294 return;
295 }
296
297 for (uint32_t i = 0; i < argsNum; i++) {
298 const char *value = HdfSbufReadString(data);
299 if (value == NULL) {
300 CAMERA_LOGE("arg is invalid i: %{public}u", i);
301 return;
302 }
303 if (strcmp(value, "-m") == 0) {
304 UpdateDumpMode(MedataType, true, reply);
305 } else if (strcmp(value, "-b") == 0) {
306 UpdateDumpMode(BufferType, true, reply);
307 } else if (strcmp(value, "-e") == 0) {
308 UpdateDumpMode(BufferType, false, reply);
309 UpdateDumpMode(MedataType, false, reply);
310 } else if (strcmp(value, "-o") == 0) {
311 UpdateDumpMode(OpenType, true, reply);
312 } else {
313 ShowDumpMenu(reply);
314 }
315 }
316 }
317
CameraDumpEvent(HdfSBuf * data,HdfSBuf * reply)318 int32_t CameraDumpEvent(HdfSBuf *data, HdfSBuf *reply)
319 {
320 CameraDumper& dumper = CameraDumper::GetInstance();
321 dumper.CameraHostDumpProcess(data, reply);
322 return HDF_SUCCESS;
323 }
324
CheckDiskInfo()325 void CameraDumper::CheckDiskInfo()
326 {
327 stringstream ss;
328 ss << "df " << DUMP_PATH;
329
330 FILE *fp = popen(ss.str().c_str(), "r");
331 if (fp == NULL) {
332 CAMERA_LOGE("popen failed, cmd : %{public}s", ss.str().c_str());
333 return;
334 }
335
336 char titleInfo[TITLEINFO_ARRAY_SIZE] = {0};
337 char resultInfo[TITLEINFO_ARRAY_SIZE] = {0};
338 fgets(titleInfo, sizeof(titleInfo) / sizeof(titleInfo[0]) - 1, fp);
339 fgets(resultInfo, sizeof(resultInfo) / sizeof(resultInfo[0]) - 1, fp);
340
341 pclose(fp);
342
343 std::string diskInfoStr(resultInfo);
344 istringstream str(diskInfoStr);
345 string out;
346 std::vector<std::string> infos;
347
348 while (str >> out) {
349 infos.push_back(out);
350 }
351
352 std::string userPerStr = infos[4].substr(0, infos[4].length() - 1);
353 uint32_t usePer = std::atoi(userPerStr.c_str());
354 if (usePer >= MAX_USAGE_RATE) {
355 CAMERA_LOGE("dump use disk over the limit, stop dump");
356 std::lock_guard<std::mutex> l(dumpStateLock_);
357 for (auto it = g_dumpInfoMap.begin(); it != g_dumpInfoMap.end(); it++) {
358 it->second = false;
359 }
360 }
361 }
362
ThreadWorkFun()363 void CameraDumper::ThreadWorkFun()
364 {
365 while (true) {
366 CheckDiskInfo();
367
368 std::unique_lock<std::mutex> l(terminateLock_);
369 cv_.wait_for(l, std::chrono::milliseconds(CHECK_DISKINFO_TIME_MS),
370 [this]() {
371 return terminate_;
372 }
373 );
374
375 if (terminate_) {
376 break;
377 }
378 }
379 }
380
StartCheckDiskInfo()381 void CameraDumper::StartCheckDiskInfo()
382 {
383 {
384 std::unique_lock<std::mutex> l(terminateLock_);
385 if (terminate_ == false) {
386 CAMERA_LOGD("thread is already start");
387 return;
388 }
389 terminate_ = false;
390 }
391
392 handleThread_ = std::make_unique<std::thread>([this] { this->ThreadWorkFun(); });
393 }
394
StopCheckDiskInfo()395 void CameraDumper::StopCheckDiskInfo()
396 {
397 {
398 std::unique_lock<std::mutex> l(terminateLock_);
399 if (terminate_ == true) {
400 CAMERA_LOGD("thread is already stop");
401 return;
402 }
403 terminate_ = true;
404 cv_.notify_one();
405 }
406 if (handleThread_ != nullptr && handleThread_->joinable()) {
407 handleThread_->join();
408 handleThread_ = nullptr;
409 }
410 }
411
GetInstance()412 CameraDumper& CameraDumper::GetInstance()
413 {
414 static CameraDumper instance_;
415 return instance_;
416 }
417 } // namespace OHOS::Camera
418