• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 "avmetadatahelper_demo.h"
17 #include <iostream>
18 #include <string>
19 #include <string_view>
20 #include <sys/stat.h>
21 #include <vector>
22 #include <cstdint>
23 #include <cstdlib>
24 #include <cstdio>
25 #include <fstream>
26 #include "jpeglib.h"
27 #include "string_ex.h"
28 #include "securec.h"
29 #include "uri_helper.h"
30 
31 namespace OHOS {
32 namespace Media {
33 static constexpr int32_t RGBA8888_PIXEL_BYTES = 4;
34 static constexpr int32_t RGB888_PIXEL_BYTES = 3;
35 static constexpr int32_t RGB565_PIXEL_BYTES = 2;
36 static constexpr uint16_t RGB565_MASK_RED = 0x001F;
37 static constexpr uint16_t RGB565_MASK_GREEN = 0x07E0;
38 static constexpr uint16_t RGB565_MASK_BLUE = 0xF800;
39 static constexpr uint32_t RGBA8888_MASK_RED = 0x00FF0000;
40 static constexpr uint32_t RGBA8888_MASK_GREEN = 0x0000FF00;
41 static constexpr uint32_t RGBA8888_MASK_BLUE = 0x000000FF;
42 static constexpr uint8_t SHIFT_2_BIT = 2;
43 static constexpr uint8_t SHITF_3_BIT = 3;
44 static constexpr uint8_t SHIFT_5_BIT = 5;
45 static constexpr uint8_t SHIFT_8_BIT = 8;
46 static constexpr uint8_t SHIFT_11_BIT = 11;
47 static constexpr uint8_t SHIFT_16_BIT = 16;
48 static constexpr uint8_t R_INDEX = 2;
49 static constexpr uint8_t G_INDEX = 1;
50 static constexpr uint8_t B_INDEX = 0;
51 
52 #define AVMETA_KEY_TO_STRING_MAP_ITEM(key) { key, #key }
53 static const std::unordered_map<int32_t, std::string_view> AVMETA_KEY_TO_STRING_MAP = {
54     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_ALBUM),
55     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_ALBUM_ARTIST),
56     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_ARTIST),
57     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_AUTHOR),
58     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_COMPOSER),
59     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_DURATION),
60     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_GENRE),
61     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_HAS_AUDIO),
62     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_HAS_VIDEO),
63     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_MIME_TYPE),
64     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_NUM_TRACKS),
65     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_SAMPLE_RATE),
66     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_TITLE),
67     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_VIDEO_HEIGHT),
68     AVMETA_KEY_TO_STRING_MAP_ITEM(AV_KEY_VIDEO_WIDTH),
69 };
70 
71 static struct jpeg_compress_struct jpeg;
72 static struct jpeg_error_mgr jerr;
73 
Rgb888ToJpeg(const std::string_view & filename,const uint8_t * rgbData,int32_t width,int32_t height)74 static int32_t Rgb888ToJpeg(const std::string_view &filename, const uint8_t *rgbData, int32_t width, int32_t height)
75 {
76     if (rgbData == nullptr) {
77         std::cout << "rgbData is nullptr" << std::endl;
78         return -1;
79     }
80 
81     jpeg.err = jpeg_std_error(&jerr);
82     jpeg_create_compress(&jpeg);
83     jpeg.image_width = static_cast<uint32_t>(width);
84     jpeg.image_height = static_cast<uint32_t>(height);
85     jpeg.input_components = RGB888_PIXEL_BYTES;
86     jpeg.in_color_space = JCS_RGB;
87     jpeg_set_defaults(&jpeg);
88 
89     static constexpr int32_t quality = 100;
90     jpeg_set_quality(&jpeg, quality, TRUE);
91 
92     FILE *file = fopen(filename.data(), "wb");
93     if (file == nullptr) {
94         jpeg_destroy_compress(&jpeg);
95         return 0;
96     }
97 
98     jpeg_stdio_dest(&jpeg, file);
99     jpeg_start_compress(&jpeg, TRUE);
100     JSAMPROW rowPointer[1];
101     for (uint32_t i = 0; i < jpeg.image_height; i++) {
102         rowPointer[0] = const_cast<uint8_t *>(rgbData + i * jpeg.image_width * RGB888_PIXEL_BYTES);
103         (void)jpeg_write_scanlines(&jpeg, rowPointer, 1);
104     }
105     jpeg_finish_compress(&jpeg);
106     (void)fclose(file);
107     file = nullptr;
108 
109     jpeg_destroy_compress(&jpeg);
110     return 0;
111 }
112 
113 // only valid for little-endian order.
RGB565ToRGB888(const uint16_t * rgb565Buf,int32_t rgb565Size,uint8_t * rgb888Buf,int32_t rgb888Size)114 static int32_t RGB565ToRGB888(const uint16_t *rgb565Buf, int32_t rgb565Size, uint8_t *rgb888Buf, int32_t rgb888Size)
115 {
116     if (rgb565Buf == nullptr || rgb565Size <= 0 || rgb888Buf == nullptr || rgb888Size <= 0) {
117         return -1;
118     }
119 
120     if (rgb888Size < rgb565Size * RGB888_PIXEL_BYTES) {
121         return -1;
122     }
123 
124     for (int32_t i = 0; i < rgb565Size; i++) {
125         rgb888Buf[i * RGB888_PIXEL_BYTES + R_INDEX] = (rgb565Buf[i] & RGB565_MASK_RED);
126         rgb888Buf[i * RGB888_PIXEL_BYTES + G_INDEX] = (rgb565Buf[i] & RGB565_MASK_GREEN) >> SHIFT_5_BIT;
127         rgb888Buf[i * RGB888_PIXEL_BYTES + B_INDEX] = (rgb565Buf[i] & RGB565_MASK_BLUE) >> SHIFT_11_BIT;
128         rgb888Buf[i * RGB888_PIXEL_BYTES + R_INDEX] <<= SHITF_3_BIT;
129         rgb888Buf[i * RGB888_PIXEL_BYTES + G_INDEX] <<= SHIFT_2_BIT;
130         rgb888Buf[i * RGB888_PIXEL_BYTES + B_INDEX] <<= SHITF_3_BIT;
131     }
132 
133     return 0;
134 }
135 
RGBA8888ToRGB888(const uint32_t * rgba8888Buf,int32_t rgba8888Size,uint8_t * rgb888Buf,int32_t rgb888Size)136 static int32_t RGBA8888ToRGB888(const uint32_t *rgba8888Buf, int32_t rgba8888Size,
137     uint8_t *rgb888Buf, int32_t rgb888Size)
138 {
139     if (rgba8888Buf == nullptr || rgba8888Size <= 0 || rgb888Buf == nullptr || rgb888Size <= 0) {
140         return -1;
141     }
142 
143     if (rgb888Size < rgba8888Size * RGB888_PIXEL_BYTES) {
144         return -1;
145     }
146 
147     for (int32_t i = 0; i < rgba8888Size; i++) {
148         rgb888Buf[i * RGB888_PIXEL_BYTES + R_INDEX] = (rgba8888Buf[i] & RGBA8888_MASK_RED) >> SHIFT_16_BIT;
149         rgb888Buf[i * RGB888_PIXEL_BYTES + G_INDEX] = (rgba8888Buf[i] & RGBA8888_MASK_GREEN) >> SHIFT_8_BIT;
150         rgb888Buf[i * RGB888_PIXEL_BYTES + B_INDEX] = rgba8888Buf[i] & RGBA8888_MASK_BLUE;
151     }
152 
153     return 0;
154 }
155 
StrToInt64(const std::string & str,int64_t & value)156 bool StrToInt64(const std::string &str, int64_t &value)
157 {
158     if (str.empty() || (!isdigit(str.front()) && (str.front() != '-'))) {
159         return false;
160     }
161 
162     char *end = nullptr;
163     errno = 0;
164     auto addr = str.data();
165     auto result = strtoll(addr, &end, 10); /* 10 means decimal */
166     if ((end == addr) || (end[0] != '\0') || (errno == ERANGE)) {
167         std::cout << "call StrToInt func false, input str is: " << str << std::endl;
168         return false;
169     }
170 
171     value = result;
172     return true;
173 }
174 
TrimStr(const std::string_view & str,const char cTrim=' ')175 std::string_view TrimStr(const std::string_view& str, const char cTrim = ' ')
176 {
177     std::string_view strTmp = str.substr(str.find_first_not_of(cTrim));
178     strTmp = strTmp.substr(0, strTmp.find_last_not_of(cTrim) + sizeof(char));
179     return strTmp;
180 }
181 
MySplitStr(const std::string_view & str,const std::string_view & sep,std::queue<std::string_view> & strs)182 void MySplitStr(const std::string_view& str, const std::string_view &sep, std::queue<std::string_view> &strs)
183 {
184     std::string_view strTmp = TrimStr(str);
185     std::string_view strPart;
186     while (true) {
187         std::string::size_type pos = strTmp.find(sep);
188         if (pos == std::string::npos || sep.empty()) {
189             strPart = TrimStr(strTmp);
190             if (!strPart.empty()) {
191                 strs.push(strPart);
192             }
193             break;
194         } else {
195             strPart = TrimStr(strTmp.substr(0, pos));
196             if (!strPart.empty()) {
197                 strs.push(strPart);
198             }
199             strTmp = strTmp.substr(sep.size() + pos, strTmp.size() - sep.size() - pos);
200         }
201     }
202 }
203 
GetMetadata(std::queue<std::string_view> & options)204 void AVMetadataHelperDemo::GetMetadata(std::queue<std::string_view> &options)
205 {
206     if (options.empty()) {
207         std::unordered_map<int32_t, std::string> metadataMap = avMetadataHelper_->ResolveMetadata();
208         for (const auto &[key, value] : metadataMap) {
209             std::string keyPrettyStr = "unknown key";
210             if (AVMETA_KEY_TO_STRING_MAP.find(key) != AVMETA_KEY_TO_STRING_MAP.end()) {
211                 keyPrettyStr = AVMETA_KEY_TO_STRING_MAP.at(key);
212             }
213             std::cout << "key: " << keyPrettyStr << " metadata: " << value.c_str() << std::endl;
214         }
215     } else {
216         std::string keyStr = std::string(options.front());
217         options.pop();
218 
219         int32_t key = -1;
220         if (!StrToInt(keyStr, key) || key < 0) {
221             std::cout << "You need to configure the key parameter  properly" << std::endl;
222             return;
223         }
224 
225         std::string keyPrettyStr = "unknown key";
226         if (AVMETA_KEY_TO_STRING_MAP.find(key) != AVMETA_KEY_TO_STRING_MAP.end()) {
227             keyPrettyStr = AVMETA_KEY_TO_STRING_MAP.at(key);
228         }
229         std::string metadata = avMetadataHelper_->ResolveMetadata(key);
230         std::cout << "key: " << keyPrettyStr << ", metadata: " << metadata.c_str() << std::endl;
231     }
232 }
233 
SaveRGB565Image(const std::shared_ptr<PixelMap> & frame,const std::string_view & filepath)234 static int32_t SaveRGB565Image(const std::shared_ptr<PixelMap> &frame, const std::string_view &filepath)
235 {
236     int32_t rgb888Size = (frame->GetByteCount() / RGB565_PIXEL_BYTES) * RGB888_PIXEL_BYTES;
237     uint8_t *rgb888 = new (std::nothrow) uint8_t[rgb888Size];
238     if (rgb888 == nullptr) {
239         std::cout << "alloc mem failed" << std::endl;
240         return -1;
241     }
242     const uint16_t *rgb565Data = reinterpret_cast<const uint16_t *>(frame->GetPixels());
243     int32_t ret = RGB565ToRGB888(rgb565Data, frame->GetByteCount() / RGB565_PIXEL_BYTES, rgb888, rgb888Size);
244     if (ret != 0) {
245         std::cout << "convert rgb565 to rgb888 failed" << std::endl;
246         delete [] rgb888;
247         return ret;
248     }
249 
250     ret = Rgb888ToJpeg(filepath, rgb888, frame->GetWidth(), frame->GetHeight());
251     delete [] rgb888;
252 
253     return ret;
254 }
255 
SaveRGBA8888Image(const std::shared_ptr<PixelMap> & frame,const std::string_view & filepath)256 static int32_t SaveRGBA8888Image(const std::shared_ptr<PixelMap> &frame, const std::string_view &filepath)
257 {
258     int32_t rgb888Size = (frame->GetByteCount() / RGBA8888_PIXEL_BYTES) * RGB888_PIXEL_BYTES;
259     uint8_t *rgb888 = new (std::nothrow) uint8_t[rgb888Size];
260     if (rgb888 == nullptr) {
261         std::cout << "alloc mem failed" << std::endl;
262         return -1;
263     }
264     const uint32_t *rgba8888Data = reinterpret_cast<const uint32_t *>(frame->GetPixels());
265     int32_t ret = RGBA8888ToRGB888(rgba8888Data, frame->GetByteCount() / RGBA8888_PIXEL_BYTES, rgb888, rgb888Size);
266     if (ret != 0) {
267         std::cout << "convert rgba8888 to rgb888 failed" << std::endl;
268         delete [] rgb888;
269         return ret;
270     }
271 
272     ret = Rgb888ToJpeg(filepath, rgb888, frame->GetWidth(), frame->GetHeight());
273     delete [] rgb888;
274 
275     return ret;
276 }
277 
DoFetchFrame(int64_t timeUs,int32_t queryOption,const PixelMapParams & param)278 void AVMetadataHelperDemo::DoFetchFrame(int64_t timeUs, int32_t queryOption, const PixelMapParams &param)
279 {
280     std::shared_ptr<PixelMap> frame = avMetadataHelper_->FetchFrameAtTime(timeUs, queryOption, param);
281     if (frame == nullptr) {
282         std::cout << "Fetch Frame failed" << std::endl;
283         return;
284     }
285 
286     constexpr uint8_t maxFilePathLength = 255;
287     char filePath[maxFilePathLength];
288     auto ret = sprintf_s(filePath, maxFilePathLength,
289         "/data/media/test/time_%" PRIi64 "_option_%d_width_%d_height_%d_color_%d.jpg",
290         timeUs, queryOption, param.dstWidth, param.dstHeight, param.colorFormat);
291     if (ret <= 0) {
292         std::cout << "generate file path failed" << std::endl;
293         return;
294     }
295 
296     if (param.colorFormat == PixelFormat::RGB_565) {
297         ret = SaveRGB565Image(frame, filePath);
298     } else if (param.colorFormat == PixelFormat::RGBA_8888) {
299         ret = SaveRGBA8888Image(frame, filePath);
300     } else if (param.colorFormat == PixelFormat::RGB_888) {
301         ret = Rgb888ToJpeg(filePath, frame->GetPixels(), frame->GetWidth(), frame->GetHeight());
302     } else {
303         std::cout << "invalid pixel format" << std::endl;
304         return;
305     }
306 
307     if (ret != 0) {
308         std::cout << "pack image failed" << std::endl;
309     }
310     std::cout << "save to " << filePath << std::endl;
311 }
312 
FetchFrame(std::queue<std::string_view> & options)313 void AVMetadataHelperDemo::FetchFrame(std::queue<std::string_view> &options)
314 {
315     PixelMapParams param;
316     int32_t queryOption = 0;
317     int64_t timeUs = 0;
318 
319     while (!options.empty()) {
320         auto option = options.front();
321         options.pop();
322 
323         std::queue<std::string_view> group;
324         MySplitStr(option, ":", group);
325 
326         static const size_t OPTION_GROUP_ITEM_SIZE = 2;
327         if (group.size() != OPTION_GROUP_ITEM_SIZE) {
328             continue;
329         }
330 
331         auto name = group.front();
332         auto val = group.back();
333 
334         if (name.compare("time") == 0) {
335             (void)StrToInt64(std::string(val), timeUs);
336             continue;
337         }
338 
339         if (name.compare("option") == 0) {
340             (void)StrToInt(std::string(val), queryOption);
341             continue;
342         }
343 
344         if (name.compare("width") == 0) {
345             (void)StrToInt(std::string(val), param.dstWidth);
346             continue;
347         }
348 
349         if (name.compare("height") == 0) {
350             (void)StrToInt(std::string(val), param.dstHeight);
351             continue;
352         }
353 
354         if (name.compare("color") == 0) {
355             if (val.compare("rgb565") == 0) {
356                 param.colorFormat = PixelFormat::RGB_565;
357             }
358             if (val.compare("rgb888") == 0) {
359                 param.colorFormat = PixelFormat::RGB_888;
360             }
361             if (val.compare("rgba8888") == 0) {
362                 param.colorFormat = PixelFormat::RGBA_8888;
363             }
364             continue;
365         }
366     }
367     std::cout << "time: " << timeUs << " option: " << queryOption << " width: " << param.dstWidth
368          << " height:" << param.dstHeight << " color: " << (int32_t)param.colorFormat << std::endl;
369 
370     DoFetchFrame(timeUs, queryOption, param);
371 }
372 
FetchArtPicture(std::queue<std::string_view> & options)373 void AVMetadataHelperDemo::FetchArtPicture(std::queue<std::string_view> &options)
374 {
375     (void)options;
376     auto result = avMetadataHelper_->FetchArtPicture();
377     if (result == nullptr) {
378         std::cout << "Fetch art picture failed" << std::endl;
379         return;
380     }
381 
382     std::ofstream ofs("/data/media/cover.img");
383     if (!ofs.is_open()) {
384         std::cout << "open /data/media/cover.img failed" << std::endl;
385         return;
386     }
387 
388     ofs.write(reinterpret_cast<char *>(result->GetBase()), result->GetSize());
389     ofs.close();
390     std::cout << "save art picture to /data/media/cover.img" << std::endl;
391 }
392 
DoNext()393 void AVMetadataHelperDemo::DoNext()
394 {
395     std::string cmd;
396     do {
397         std::cout << "Enter your step:" << std::endl;
398         (void)std::getline(std::cin, cmd);
399 
400         std::queue<std::string_view> options;
401         MySplitStr(cmd, " ", options);
402 
403         if (options.empty()) {
404             continue;
405         }
406 
407         std::string_view funcName = options.front();
408         options.pop();
409 
410         if (funcName.compare("metadata") == 0) {
411             GetMetadata(options);
412             continue;
413         }
414 
415         if (funcName.compare("fetchframe") == 0) {
416             FetchFrame(options);
417             continue;
418         }
419 
420         if (funcName.compare("artpicture") == 0) {
421             FetchArtPicture(options);
422             continue;
423         }
424 
425         if (funcName.compare("quit") == 0 || funcName.compare("q") == 0) {
426             avMetadataHelper_->Release();
427             break;
428         }
429     } while (1);
430 }
431 
SetSource(const std::string & pathOuter)432 int32_t AVMetadataHelperDemo::SetSource(const std::string &pathOuter)
433 {
434     std::string path;
435     if (pathOuter == "") {
436         std::cout << "Please enter the video/audio path: " << std::endl;
437         (void)getline(std::cin, path);
438     } else {
439         path = pathOuter;
440     }
441     std::cout << "Path is " << path << std::endl;
442 
443     UriHelper uriHelper(path);
444     if (uriHelper.UriType() != UriHelper::URI_TYPE_FILE && !uriHelper.AccessCheck(UriHelper::URI_READ)) {
445         std::cout << "Invalid file Path" << std::endl;
446         return -1;
447     }
448 
449     std::string rawFile = uriHelper.FormattedUri();
450     rawFile = rawFile.substr(strlen("file://"));
451     int32_t fd = open(rawFile.c_str(), O_RDONLY);
452     if (fd <= 0) {
453         std::cout << "Open file failed" << std::endl;
454         return -1;
455     }
456 
457     struct stat64 st;
458     if (fstat64(fd, &st) != 0) {
459         std::cout << "Get file state failed" << std::endl;
460         (void)close(fd);
461         return -1;
462     }
463     int64_t length = static_cast<int64_t>(st.st_size);
464 
465     int32_t ret = avMetadataHelper_->SetSource(fd, 0, length, AVMetadataUsage::AV_META_USAGE_PIXEL_MAP);
466     if (ret != 0) {
467         std::cout << "SetSource fail" << std::endl;
468         (void)close(fd);
469         return -1;
470     }
471 
472     (void)close(fd);
473     return 0;
474 }
475 
RunCase(const std::string & pathOuter)476 void AVMetadataHelperDemo::RunCase(const std::string &pathOuter)
477 {
478     avMetadataHelper_ = OHOS::Media::AVMetadataHelperFactory::CreateAVMetadataHelper();
479     if (avMetadataHelper_ == nullptr) {
480         std::cout << "avMetadataHelper_ is null" << std::endl;
481         return;
482     }
483 
484     if (SetSource(pathOuter) != 0) {
485         return;
486     }
487 
488     DoNext();
489 }
490 } // namespace Media
491 } // namespace OHOS
492