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