1 /*
2 * Copyright (C) 2022 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 "avmetadata_mock.h"
17 #include <fcntl.h>
18 #include "gtest/gtest.h"
19 #include "media_errors.h"
20
21 using namespace OHOS;
22 using namespace OHOS::Media;
23 using namespace AVMetadataTestParam;
24 namespace OHOS {
25 namespace Media {
AVMetadataMock()26 AVMetadataMock::AVMetadataMock()
27 {
28 }
29
~AVMetadataMock()30 AVMetadataMock::~AVMetadataMock()
31 {
32 }
33
CreateAVMetadataHelper()34 bool AVMetadataMock::CreateAVMetadataHelper()
35 {
36 UNITTEST_INFO_LOG("%s", __FUNCTION__);
37 avMetadataHelper_ = AVMetadataHelperFactory::CreateAVMetadataHelper();
38 if (avMetadataHelper_ == nullptr) {
39 return false;
40 }
41 return true;
42 }
43
SetSource(const std::string & uri,int32_t usage)44 int32_t AVMetadataMock::SetSource(const std::string &uri, int32_t usage)
45 {
46 UNITTEST_INFO_LOG("%s %s", __FUNCTION__, uri.c_str());
47 return avMetadataHelper_->SetSource(uri, usage);
48 }
49
SetSource(const std::string & path,int64_t offset,int64_t size,int32_t usage)50 int32_t AVMetadataMock::SetSource(const std::string &path, int64_t offset, int64_t size, int32_t usage)
51 {
52 UNITTEST_INFO_LOG("%s %s", __FUNCTION__, path.c_str());
53 std::string rawFile = path.substr(strlen("file://"));
54 int32_t fd = open(rawFile.c_str(), O_RDONLY);
55 if (fd <= 0) {
56 std::cout << "Open file failed" << std::endl;
57 return -1;
58 }
59
60 struct stat64 st;
61 if (fstat64(fd, &st) != 0) {
62 std::cout << "Get file state failed" << std::endl;
63 (void)close(fd);
64 return -1;
65 }
66 int64_t length = static_cast<int64_t>(st.st_size);
67 if (size > 0) {
68 length = size;
69 }
70 int32_t ret = avMetadataHelper_->SetSource(fd, offset, length, usage);
71 if (ret != 0) {
72 (void)close(fd);
73 return -1;
74 }
75
76 (void)close(fd);
77 return ret;
78 }
79
PrintMetadata()80 void AVMetadataMock::PrintMetadata()
81 {
82 UNITTEST_INFO_LOG("%s", __FUNCTION__);
83 auto resultMetas = avMetadataHelper_->ResolveMetadata();
84 for (const auto &[key, value]: resultMetas) {
85 std::string prettyValue = value;
86 if (key == AV_KEY_DURATION) {
87 int64_t resultDuration = 0;
88 AVMetadataTestBase::GetInstance().StrToInt64(value, resultDuration);
89 prettyValue = AVMetadataTestBase::GetInstance().GetPrettyDuration(resultDuration);
90 }
91 if (AVMETA_KEY_TO_STRING_MAP.count(key) != 0) {
92 UNITTEST_INFO_LOG("key %s: value %s", AVMETA_KEY_TO_STRING_MAP.at(key).data(), prettyValue.c_str());
93 } else {
94 UNITTEST_INFO_LOG("key %d: value %s", key, prettyValue.c_str());
95 }
96 }
97 }
98
ResolveMetadata()99 std::unordered_map<int32_t, std::string> AVMetadataMock::ResolveMetadata()
100 {
101 UNITTEST_INFO_LOG("%s", __FUNCTION__);
102 return avMetadataHelper_->ResolveMetadata();
103 }
104
ResolveMetadata(int32_t key)105 std::string AVMetadataMock::ResolveMetadata(int32_t key)
106 {
107 UNITTEST_INFO_LOG("%s(%d)", __FUNCTION__, key);
108 return avMetadataHelper_->ResolveMetadata(key);
109 }
110
FetchFrameAtTime(int64_t timeUs,int32_t option,PixelMapParams param)111 std::shared_ptr<PixelMap> AVMetadataMock::FetchFrameAtTime(int64_t timeUs, int32_t option, PixelMapParams param)
112 {
113 UNITTEST_INFO_LOG("%s", __FUNCTION__);
114 return avMetadataHelper_->FetchFrameAtTime(timeUs, option, param);
115 }
116
FetchArtPicture()117 std::shared_ptr<AVSharedMemory> AVMetadataMock::FetchArtPicture()
118 {
119 UNITTEST_INFO_LOG("%s", __FUNCTION__);
120 return avMetadataHelper_->FetchArtPicture();
121 }
122
Release()123 void AVMetadataMock::Release()
124 {
125 UNITTEST_INFO_LOG("%s", __FUNCTION__);
126 return avMetadataHelper_->Release();
127 }
128
129 // only valid for little-endian order.
RGB565ToRGB888(const unsigned short * rgb565Buf,int rgb565Size,unsigned char * rgb888Buf,int rgb888Size)130 int32_t AVMetadataMock::RGB565ToRGB888(const unsigned short *rgb565Buf, int rgb565Size,
131 unsigned char *rgb888Buf, int rgb888Size)
132 {
133 if (rgb565Buf == nullptr || rgb565Size <= 0 || rgb888Buf == nullptr || rgb888Size <= 0) {
134 return -1;
135 }
136
137 if (rgb888Size < rgb565Size * RGB888_PIXEL_BYTES) {
138 return -1;
139 }
140
141 for (int i = 0; i < rgb565Size; i++) {
142 rgb888Buf[i * RGB888_PIXEL_BYTES + R_INDEX] = (rgb565Buf[i] & RGB565_MASK_RED);
143 rgb888Buf[i * RGB888_PIXEL_BYTES + G_INDEX] = (rgb565Buf[i] & RGB565_MASK_GREEN) >> SHIFT_5_BIT;
144 rgb888Buf[i * RGB888_PIXEL_BYTES + B_INDEX] = (rgb565Buf[i] & RGB565_MASK_BLUE) >> SHIFT_11_BIT;
145 rgb888Buf[i * RGB888_PIXEL_BYTES + R_INDEX] <<= SHIFT_3_BIT;
146 rgb888Buf[i * RGB888_PIXEL_BYTES + G_INDEX] <<= SHIFT_2_BIT;
147 rgb888Buf[i * RGB888_PIXEL_BYTES + B_INDEX] <<= SHIFT_3_BIT;
148 }
149
150 return 0;
151 }
152
Rgb888ToJpeg(const std::string_view & filename,const uint8_t * rgbData,int width,int height)153 int32_t AVMetadataMock::Rgb888ToJpeg(const std::string_view &filename,
154 const uint8_t *rgbData, int width, int height)
155 {
156 if (rgbData == nullptr) {
157 std::cout << "rgbData is nullptr" << std::endl;
158 return -1;
159 }
160
161 jpeg.err = jpeg_std_error(&jerr);
162 jpeg_create_compress(&jpeg);
163 jpeg.image_width = width;
164 jpeg.image_height = height;
165 jpeg.input_components = RGB888_PIXEL_BYTES;
166 jpeg.in_color_space = JCS_RGB;
167 jpeg_set_defaults(&jpeg);
168
169 static const int QUALITY = 100;
170 jpeg_set_quality(&jpeg, QUALITY, TRUE);
171
172 FILE *pFile = fopen(filename.data(), "wb");
173 if (!pFile) {
174 jpeg_destroy_compress(&jpeg);
175 return 0;
176 }
177
178 jpeg_stdio_dest(&jpeg, pFile);
179 jpeg_start_compress(&jpeg, TRUE);
180 JSAMPROW row_pointer[1];
181 for (uint32_t i = 0; i < jpeg.image_height; i++) {
182 row_pointer[0] = const_cast<uint8_t *>(rgbData + i * jpeg.image_width * RGB888_PIXEL_BYTES);
183 jpeg_write_scanlines(&jpeg, row_pointer, 1);
184 }
185 jpeg_finish_compress(&jpeg);
186 (void)fclose(pFile);
187 pFile = NULL;
188
189 jpeg_destroy_compress(&jpeg);
190 return 0;
191 }
192
FrameToFile(std::shared_ptr<PixelMap> frame,const char * fileName,int64_t timeUs,int32_t queryOption)193 void AVMetadataMock::FrameToFile(std::shared_ptr<PixelMap> frame,
194 const char *fileName, int64_t timeUs, int32_t queryOption)
195 {
196 const uint8_t *data = frame->GetPixels();
197 EXPECT_NE(data, nullptr);
198 int32_t bufferSize = frame->GetByteCount();
199 const uint8_t MAX_FILE_PATH_LENGTH = 255;
200 char filePath[MAX_FILE_PATH_LENGTH];
201 if (access("/data/test/ThumbnailBak", 0) != F_OK) {
202 mkdir("/data/test/ThumbnailBak", 0777); // 0777 is the file permission.
203 }
204 auto ret = sprintf_s(filePath, MAX_FILE_PATH_LENGTH,
205 "/data/test/ThumbnailBak/%s_time_%" PRIi64 "_option_%d_width_%d_height_%d_color_%d.pixel",
206 fileName, timeUs, queryOption, frame->GetWidth(), frame->GetHeight(), frame->GetPixelFormat());
207 if (ret <= 0) {
208 std::cout << "generate file path failed" << std::endl;
209 return;
210 }
211 FILE *desFile = fopen(filePath, "wb");
212 ASSERT_NE(desFile, nullptr);
213 int64_t dstBufferSize = fwrite(data, bufferSize, 1, desFile);
214 EXPECT_EQ(dstBufferSize, 1);
215 (void)fclose(desFile);
216 }
217
SurfaceToFile(std::shared_ptr<AVSharedMemory> frame,const char * fileName)218 void AVMetadataMock::SurfaceToFile(std::shared_ptr<AVSharedMemory> frame,
219 const char *fileName)
220 {
221 const uint8_t *data = frame->GetBase();
222 EXPECT_NE(data, nullptr);
223 int32_t bufferSize = frame->GetSize();
224 uint32_t flag = frame->GetFlags();
225 const uint8_t MAX_FILE_PATH_LENGTH = 255;
226 char filePath[MAX_FILE_PATH_LENGTH];
227 if (access("/data/test/SurfaceBak", 0) != F_OK) {
228 mkdir("/data/test/SurfaceBak", 0777); // permission 0777
229 }
230 auto ret = sprintf_s(filePath, MAX_FILE_PATH_LENGTH, "/data/test/SurfaceBak/%s.pixel", fileName);
231 if (ret <= 0) {
232 std::cout << "generate file path failed, flag:" << flag << std::endl;
233 return;
234 }
235 FILE *desFile = fopen(filePath, "wb");
236 ASSERT_NE(desFile, nullptr);
237 int64_t dstBufferSize = fwrite(data, bufferSize, 1, desFile);
238 EXPECT_EQ(dstBufferSize, 1);
239 (void)fclose(desFile);
240 }
241
FrameToJpeg(std::shared_ptr<PixelMap> frame,const char * fileName,int64_t timeUs,int32_t queryOption)242 void AVMetadataMock::FrameToJpeg(std::shared_ptr<PixelMap> frame,
243 const char *fileName, int64_t timeUs, int32_t queryOption)
244 {
245 const uint8_t MAX_FILE_PATH_LENGTH = 255;
246 char filePath[MAX_FILE_PATH_LENGTH];
247 if (access("/data/test/ThumbnailBak", 0) != F_OK) {
248 mkdir("/data/test/ThumbnailBak", 0777); // permission 0777
249 }
250 auto ret = sprintf_s(filePath, MAX_FILE_PATH_LENGTH,
251 "/data/test/ThumbnailBak/%s_time_%" PRIi64 "_option_%d_width_%d_height_%d_color_%d.jpg",
252 fileName, timeUs, queryOption, frame->GetWidth(), frame->GetHeight(), frame->GetPixelFormat());
253 if (ret <= 0) {
254 std::cout << "generate file path failed" << std::endl;
255 return;
256 }
257 if (frame->GetPixelFormat() == PixelFormat::RGB_565) {
258 uint32_t rgb888Size = (frame->GetByteCount() / RGB565_PIXEL_BYTES) * RGB888_PIXEL_BYTES;
259 if (rgb888Size <= 0) {
260 std::cout << "invalid rgb888Size" << std::endl;
261 return;
262 }
263 uint8_t *rgb888 = new (std::nothrow) uint8_t[rgb888Size];
264 if (rgb888 == nullptr) {
265 std::cout << "alloc mem failed" << std::endl;
266 return;
267 }
268 const uint16_t *rgb565Data = reinterpret_cast<const uint16_t *>(frame->GetPixels());
269 ret = RGB565ToRGB888(rgb565Data, frame->GetByteCount() / RGB565_PIXEL_BYTES, rgb888, rgb888Size);
270 if (ret != 0) {
271 std::cout << "convert rgb565 to rgb888 failed" << std::endl;
272 delete [] rgb888;
273 return;
274 }
275 ret = Rgb888ToJpeg(filePath, rgb888, frame->GetWidth(), frame->GetHeight());
276 delete [] rgb888;
277 } else if (frame->GetPixelFormat() == PixelFormat::RGB_888) {
278 ret = Rgb888ToJpeg(filePath, frame->GetPixels(), frame->GetWidth(), frame->GetHeight());
279 } else {
280 std::cout << "invalid pixel format" << std::endl;
281 return;
282 }
283 if (ret != 0) {
284 std::cout << "pack image failed" << std::endl;
285 }
286 std::cout << "save to " << filePath << std::endl;
287 }
288
AVMetadataTestBase()289 AVMetadataTestBase::AVMetadataTestBase()
290 {
291 }
292
~AVMetadataTestBase()293 AVMetadataTestBase::~AVMetadataTestBase()
294 {
295 }
296
StrToInt64(const std::string & str,int64_t & value)297 bool AVMetadataTestBase::StrToInt64(const std::string &str, int64_t &value)
298 {
299 if (str.empty() || (!isdigit(str.front()) && (str.front() != '-'))) {
300 return false;
301 }
302
303 char *end = nullptr;
304 errno = 0;
305 auto addr = str.c_str();
306 auto result = strtoll(addr, &end, 10); /* 10 means decimal */
307 if (result == 0) {
308 return false;
309 }
310 if ((end == addr) || (end[0] != '\0') || (errno == ERANGE)) {
311 UNITTEST_INFO_LOG("call StrToInt func false, input str is: %s!", str.c_str());
312 return false;
313 }
314
315 value = result;
316 return true;
317 }
318
CompareMetadata(int32_t key,const std::string & result,const std::string & expected)319 bool AVMetadataTestBase::CompareMetadata(int32_t key, const std::string &result, const std::string &expected)
320 {
321 std::string keyStr = (AVMETA_KEY_TO_STRING_MAP.count(key) == 0) ?
322 std::string(AVMETA_KEY_TO_STRING_MAP.at(key)) : std::to_string(key);
323
324 do {
325 if (key == AV_KEY_DURATION) {
326 int64_t resultDuration = 0;
327 int64_t expectedDuration = 0;
328 if (result.compare(expected) == 0) {
329 return true;
330 }
331 if (!StrToInt64(result, resultDuration) || !StrToInt64(expected, expectedDuration)) {
332 break;
333 }
334 if (std::abs(resultDuration - expectedDuration) > 1000) { // max allowed time margin is 1000ms
335 break;
336 }
337 } else {
338 if (result.compare(expected) != 0) {
339 break;
340 }
341 }
342 return true;
343 } while (0);
344
345 UNITTEST_INFO_LOG(">>>>>>>>>>>>>>>>>>>>>>>>>>[resolve failed] key = %s, result = %s, expected = %s",
346 keyStr.c_str(), result.c_str(), expected.c_str());
347 return false;
348 }
349
CompareMetadata(const std::unordered_map<int32_t,std::string> & result,const std::unordered_map<int32_t,std::string> & expected)350 bool AVMetadataTestBase::CompareMetadata(const std::unordered_map<int32_t, std::string> &result,
351 const std::unordered_map<int32_t, std::string> &expected)
352 {
353 std::string resultValue;
354 bool success = true;
355
356 for (const auto &[key, expectedValue] : expected) {
357 if (result.count(key) == 0) {
358 resultValue = "";
359 } else {
360 resultValue = result.at(key);
361 }
362
363 success = success && CompareMetadata(key, resultValue, expectedValue);
364 }
365
366 return success;
367 }
368
GetPrettyDuration(int64_t duration)369 std::string AVMetadataTestBase::GetPrettyDuration(int64_t duration) // ms
370 {
371 static const int32_t msPerSec = 1000;
372 static const int32_t msPerMin = 60 * msPerSec;
373 static const int32_t msPerHour = 60 * msPerMin;
374
375 int64_t hour = duration / msPerHour;
376 int64_t min = (duration % msPerHour) / msPerMin;
377 int64_t sec = (duration % msPerMin) / msPerSec;
378 int64_t milliSec = duration % msPerSec;
379
380 std::ostringstream oss;
381 oss << std::setfill('0')
382 << std::setw(2) << hour << ":" // Set the width of the output field to 2 for hour.
383 << std::setw(2) << min << ":" // Set the width of the output field to 2 for min.
384 << std::setw(2) << sec << "." // Set the width of the output field to 2 for sec.
385 << std::setw(3) << milliSec; // Set the width of the output field to 3 for milliSec.
386
387 return oss.str();
388 }
389 } // namespace Media
390 } // namespace OHOS