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 "mimetype_utils.h"
17
18 #include <algorithm>
19 #include <fstream>
20 #include <sys/stat.h>
21 #include <sstream>
22 #include <fcntl.h>
23
24 #include "avmetadatahelper.h"
25 #include "directory_ex.h"
26 #include "image_source.h"
27 #include "media_log.h"
28 #include "medialibrary_errno.h"
29 #include "medialibrary_tracer.h"
30 #include "media_file_utils.h"
31
32 #define O_RDONLY 00
33
34 using std::string;
35 using std::vector;
36 using std::unordered_map;
37 using namespace nlohmann;
38
39 namespace OHOS {
40 namespace Media {
41 using MimeTypeMap = unordered_map<string, vector<string>>;
42
43 MimeTypeMap MimeTypeUtils::mediaJsonMap_;
44 const string MIMETYPE_JSON_PATH = "/system/etc/userfilemanager/userfilemanager_mimetypes.json";
45 const string DEFAULT_MIME_TYPE = "application/octet-stream";
46
47 /**
48 * The format of the target json file:
49 * First floor: Media type string, such as image, video, audio, etc.
50 * Second floor: Mime type string
51 * Third floor: Extension array.
52 */
CreateMapFromJson()53 void MimeTypeUtils::CreateMapFromJson()
54 {
55 std::ifstream jFile(MIMETYPE_JSON_PATH);
56 if (!jFile.is_open()) {
57 MEDIA_ERR_LOG("Failed to open: %{private}s", MIMETYPE_JSON_PATH.c_str());
58 return;
59 }
60 json firstFloorObjs;
61 jFile >> firstFloorObjs;
62 jFile.close();
63 for (auto& firstFloorObj : firstFloorObjs.items()) {
64 json secondFloorJsons = json::parse(firstFloorObj.value().dump(), nullptr, false);
65 if (secondFloorJsons.is_discarded() || secondFloorJsons.empty()) {
66 MEDIA_ERR_LOG("parse secondFloorJsons failed");
67 return;
68 }
69 for (auto& secondFloorJson : secondFloorJsons.items()) {
70 json thirdFloorJsons = json::parse(secondFloorJson.value().dump(), nullptr, false);
71 if (thirdFloorJsons.is_discarded() || thirdFloorJsons.empty()) {
72 MEDIA_ERR_LOG("parse thirdFloorJsons failed");
73 return;
74 }
75 // Key: MimeType, Value: Extension array.
76 mediaJsonMap_.insert(std::pair<string, vector<string>>(secondFloorJson.key(), thirdFloorJsons));
77 }
78 }
79 }
80
IsMimeTypeMapEmpty()81 bool MimeTypeUtils::IsMimeTypeMapEmpty()
82 {
83 return mediaJsonMap_.empty();
84 }
85
InitMimeTypeMap()86 int32_t MimeTypeUtils::InitMimeTypeMap()
87 {
88 CreateMapFromJson();
89 if (mediaJsonMap_.empty()) {
90 return E_FAIL;
91 }
92 return E_OK;
93 }
94
GetImageMimetype(const string & filePath,string & mimeType)95 int32_t MimeTypeUtils::GetImageMimetype(const string &filePath, string &mimeType)
96 {
97 SourceOptions opts;
98 uint32_t err = E_OK;
99 MediaLibraryTracer tracer;
100 tracer.Start("ImageSource::CreateImageSource");
101 std::unique_ptr<ImageSource> imageSource = ImageSource::CreateImageSource(filePath, opts, err);
102 tracer.Finish();
103 CHECK_AND_RETURN_RET_LOG(imageSource != nullptr, E_INVALID_VALUES,
104 "CreateImageSource failed, err: %{public}d", err);
105 tracer.Start("imageSource->GetImageInfo");
106 ImageInfo imageInfo;
107 err = imageSource->GetImageInfo(0, imageInfo);
108 tracer.Finish();
109 CHECK_AND_RETURN_RET_LOG(err == E_OK, E_INVALID_VALUES, "GetImageInfo failed, err: %{public}d", err);
110 mimeType = imageInfo.encodedFormat;
111 CHECK_AND_RETURN_RET_LOG(mimeType != "", E_INVALID_VALUES, "failed to get encodedFormat");
112 return E_OK;
113 }
114
GetVideoMimetype(const string & filePath,string & mimeType)115 int32_t MimeTypeUtils::GetVideoMimetype(const string &filePath, string &mimeType)
116 {
117 MediaLibraryTracer tracer;
118 tracer.Start("avMetadataHelper->open");
119 std::shared_ptr<AVMetadataHelper> avMetadataHelper = AVMetadataHelperFactory::CreateAVMetadataHelper();
120 CHECK_AND_RETURN_RET_LOG(avMetadataHelper != nullptr, E_INVALID_VALUES,
121 "CreateAVMetadataHelper fail!");
122 int32_t fd = open(filePath.c_str(), O_RDONLY);
123 tracer.Finish();
124 CHECK_AND_RETURN_RET_LOG(fd > 0, E_SYSCALL, "Open file descriptor failed, errno = %{public}d", errno);
125
126 struct stat64 st;
127 if (fstat64(fd, &st) != 0) {
128 MEDIA_ERR_LOG("Get file state failed for the given fd");
129 (void)close(fd);
130 return E_SYSCALL;
131 }
132
133 tracer.Start("avMetadataHelper->SetSource");
134 int32_t err = avMetadataHelper->SetSource(fd, 0, static_cast<int64_t>(st.st_size), AV_META_USAGE_META_ONLY);
135 tracer.Finish();
136 if (err != E_OK) {
137 MEDIA_ERR_LOG("SetSource failed for file descriptor, err: %{public}d", err);
138 (void)close(fd);
139 return E_AVMETADATA;
140 }
141 tracer.Start("avMetadataHelper->ResolveMetadata");
142 std::unordered_map<int32_t, std::string> resultMap = avMetadataHelper->ResolveMetadata();
143 tracer.Finish();
144 (void)close(fd);
145 CHECK_AND_RETURN_RET_LOG(resultMap.find(AV_KEY_MIME_TYPE) != resultMap.end(), E_INVALID_VALUES,
146 "failed to get AV_KEY_MIME_TYPE");
147 mimeType = resultMap.at(AV_KEY_MIME_TYPE);
148 CHECK_AND_RETURN_RET_LOG(mimeType != "", E_INVALID_VALUES, "AV_KEY_MIME_TYPE empty");
149 return E_OK;
150 }
151
GetMimeTypeFromContent(const string & filePath)152 string MimeTypeUtils::GetMimeTypeFromContent(const string &filePath)
153 {
154 CHECK_AND_RETURN_RET_LOG(filePath != "", "", "empty file path");
155 string extension = MediaFileUtils::GetExtensionFromPath(filePath);
156 string mimeType = GetMimeTypeFromExtension(extension);
157 string absFilePath;
158 CHECK_AND_RETURN_RET_LOG(PathToRealPath(filePath, absFilePath),
159 mimeType, "failed to transfer realpath: %{private}s", filePath.c_str());
160 int32_t mediaType = GetMediaTypeFromMimeType(mimeType);
161 int32_t err = -1;
162 if (mediaType == MEDIA_TYPE_IMAGE) {
163 err = GetImageMimetype(absFilePath, mimeType);
164 } else if (mediaType == MEDIA_TYPE_VIDEO) {
165 err = GetVideoMimetype(absFilePath, mimeType);
166 } else {
167 MEDIA_ERR_LOG("Invalid mediaType: %{public}d", mediaType);
168 return mimeType;
169 }
170 if (err != E_OK) {
171 mimeType = GetMimeTypeFromExtension(extension);
172 }
173 MEDIA_INFO_LOG("GetMimeTypeFromContent mimeType: %{public}s, err: %{public}d",
174 mimeType.c_str(), err);
175 return mimeType;
176 }
177
GetMimeTypeFromExtension(const string & extension)178 string MimeTypeUtils::GetMimeTypeFromExtension(const string &extension)
179 {
180 return GetMimeTypeFromExtension(extension, mediaJsonMap_);
181 }
182
GetMimeTypeFromExtension(const string & extension,const MimeTypeMap & mimeTypeMap)183 string MimeTypeUtils::GetMimeTypeFromExtension(const string &extension,
184 const MimeTypeMap &mimeTypeMap)
185 {
186 std::string tmp = std::move(extension);
187 std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
188 for (auto &item : mimeTypeMap) {
189 for (auto &ext : item.second) {
190 if (ext == tmp) {
191 return item.first;
192 }
193 }
194 }
195 return DEFAULT_MIME_TYPE;
196 }
197
GetMediaTypeFromMimeType(const string & mimeType)198 MediaType MimeTypeUtils::GetMediaTypeFromMimeType(const string &mimeType)
199 {
200 size_t pos = mimeType.find_first_of("/");
201 if (pos == string::npos) {
202 MEDIA_ERR_LOG("Invalid mime type: %{private}s", mimeType.c_str());
203 return MEDIA_TYPE_FILE;
204 }
205 string prefix = mimeType.substr(0, pos);
206 if (prefix == "audio") {
207 return MEDIA_TYPE_AUDIO;
208 } else if (prefix == "video") {
209 return MEDIA_TYPE_VIDEO;
210 } else if (prefix == "image") {
211 return MEDIA_TYPE_IMAGE;
212 } else {
213 return MEDIA_TYPE_FILE;
214 }
215 }
216 }
217 }
218