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 "heif_format_agent.h"
17
18 #include "image_log.h"
19 #include "plugin_service.h"
20 #include "string"
21 #include "securec.h"
22
23 #undef LOG_DOMAIN
24 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_PLUGIN
25
26 #undef LOG_TAG
27 #define LOG_TAG "HeifFormatAgent"
28
29 namespace OHOS {
30 namespace ImagePlugin {
31 using namespace MultimediaPlugin;
32
33 const std::string FORMAT_TYPE = "image/heif";
34 constexpr uint32_t HEADER_SIZE = 32;
35 constexpr uint32_t HEADER_LEAST_SIZE = 8;
36 constexpr size_t HEADER_NEXT_SIZE = 16;
37 constexpr uint32_t OFFSET_SIZE = 8;
38
39 constexpr uint32_t SHIFT_BASE = 8;
40 constexpr uint32_t TIMES_SEVEN = 7;
41 constexpr uint32_t TIMES_FIVE = 5;
42 constexpr uint32_t TIMES_THREE = 3;
43 constexpr uint32_t TIMES_TWO = 2;
44 constexpr uint32_t MAX_LOOP_SIZE = 5;
45
GetFormatType()46 std::string HeifFormatAgent::GetFormatType()
47 {
48 return FORMAT_TYPE;
49 }
50
GetHeaderSize()51 uint32_t HeifFormatAgent::GetHeaderSize()
52 {
53 return HEADER_SIZE;
54 }
55
CheckFormatHead(const void * headerData,uint32_t dataSize)56 bool CheckFormatHead(const void *headerData, uint32_t dataSize)
57 {
58 if (headerData == nullptr) {
59 IMAGE_LOGE("check format failed: header data is null.");
60 return false;
61 }
62 // Any valid ftyp box should have at least 8 bytes.
63 if (dataSize < HEADER_LEAST_SIZE) {
64 IMAGE_LOGE("data size[%{public}u] less than eight.", dataSize);
65 return false;
66 }
67 return true;
68 }
69
CheckFormat(const void * headerData,uint32_t dataSize)70 bool HeifFormatAgent::CheckFormat(const void *headerData, uint32_t dataSize)
71 {
72 if (!CheckFormatHead(headerData, dataSize)) {
73 return false;
74 }
75 uint32_t tmpBuff[HEADER_SIZE];
76 if (memcpy_s(tmpBuff, HEADER_SIZE, headerData, dataSize) != 0) {
77 IMAGE_LOGE("memcpy headerData data size:[%{public}d] error.", dataSize);
78 return false;
79 }
80
81 const uint32_t *ptr = reinterpret_cast<const uint32_t *>(tmpBuff);
82 uint64_t chunkSize = EndianSwap32(ptr[0]); // first item
83 uint32_t chunkType = EndianSwap32(ptr[1]); // second item
84 if (chunkType != Fourcc('f', 't', 'y', 'p')) {
85 IMAGE_LOGD("head type is not ftyp.");
86 return false;
87 }
88
89 int64_t offset = OFFSET_SIZE;
90 if (!IsHeif64(tmpBuff, dataSize, offset, chunkSize)) {
91 return false;
92 }
93 int64_t chunkDataSize = static_cast<int64_t>(chunkSize) - offset;
94 // It should at least have major brand (4-byte) and minor version (4-bytes).
95 // The rest of the chunk (if any) is a list of (4-byte) compatible brands.
96 if (chunkDataSize < HEADER_LEAST_SIZE) {
97 IMAGE_LOGE("chunk data size [%{public}lld] less than eight.", static_cast<long long>(chunkDataSize));
98 return false;
99 }
100 uint32_t numCompatibleBrands = (chunkDataSize - OFFSET_SIZE) / sizeof(uint32_t);
101 if (numCompatibleBrands != 0 && numCompatibleBrands + TIMES_TWO < HEADER_SIZE) {
102 for (size_t i = 0; i < numCompatibleBrands + 2; ++i) { // need next 2 item
103 if (i == 1) {
104 // Skip this index, it refers to the minorVersion, not a brand.
105 continue;
106 }
107 if (i == MAX_LOOP_SIZE) {
108 // When numCompatibleBrands is 4, i equals 5, and there is no heif brand, it will be read out of bounds.
109 IMAGE_LOGI("check heif format failed, the number of cycles exceeded expectations");
110 return false;
111 }
112 auto *brandPtr = static_cast<const uint32_t *>(tmpBuff) + (numCompatibleBrands + i);
113 uint32_t brand = EndianSwap32(*brandPtr);
114 if (brand == Fourcc('m', 'i', 'f', '1') || brand == Fourcc('h', 'e', 'i', 'c') ||
115 brand == Fourcc('m', 's', 'f', '1') || brand == Fourcc('h', 'e', 'v', 'c')) {
116 return true;
117 }
118 }
119 }
120 IMAGE_LOGI("check heif format failed.");
121 return false;
122 }
123
IsHeif64(const void * buffer,const size_t bytesRead,int64_t & offset,uint64_t & chunkSize)124 bool HeifFormatAgent::IsHeif64(const void *buffer, const size_t bytesRead, int64_t &offset, uint64_t &chunkSize)
125 {
126 // If it is 1, a 64-bit check is required.
127 if (chunkSize == 1) {
128 // This indicates that the next 8 bytes represent the chunk size,
129 // and chunk data comes after that.
130 if (bytesRead < HEADER_NEXT_SIZE) {
131 IMAGE_LOGE("bytes read [%{public}zd] less than sixteen.", bytesRead);
132 return false;
133 }
134 auto *chunkSizePtr = static_cast<const uint64_t *>(buffer) + (offset / sizeof(uint64_t));
135 chunkSize = EndianSwap64(*chunkSizePtr);
136 if (chunkSize < HEADER_NEXT_SIZE) {
137 // The smallest valid chunk is 16 bytes long in this case.
138 IMAGE_LOGE("chunk size [%{public}llu] less than sixteen.", static_cast<unsigned long long>(chunkSize));
139 return false;
140 }
141 offset += OFFSET_SIZE;
142 } else if (chunkSize < HEADER_LEAST_SIZE) {
143 // The smallest valid chunk is 8 bytes long.
144 IMAGE_LOGE("chunk size [%{public}llu] less than eight.", static_cast<unsigned long long>(chunkSize));
145 return false;
146 }
147
148 if (chunkSize > bytesRead) {
149 chunkSize = bytesRead;
150 }
151 return true;
152 }
153
Fourcc(uint8_t c1,uint8_t c2,uint8_t c3,uint8_t c4)154 uint32_t HeifFormatAgent::Fourcc(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4)
155 {
156 return (c1 << (SHIFT_BASE * TIMES_THREE)) | (c2 << (SHIFT_BASE * TIMES_TWO)) | (c3 << SHIFT_BASE) | (c4);
157 }
158
EndianSwap32(uint32_t value)159 uint32_t HeifFormatAgent::EndianSwap32(uint32_t value)
160 {
161 return ((value & 0xFF) << (SHIFT_BASE * TIMES_THREE)) | ((value & 0xFF00) << SHIFT_BASE) |
162 ((value & 0xFF0000) >> SHIFT_BASE) | (value >> (SHIFT_BASE * TIMES_THREE));
163 }
164
EndianSwap64(uint64_t value)165 uint64_t HeifFormatAgent::EndianSwap64(uint64_t value)
166 {
167 return (((value & 0x00000000000000FFULL) << (SHIFT_BASE * TIMES_SEVEN)) |
168 ((value & 0x000000000000FF00ULL) << (SHIFT_BASE * TIMES_FIVE)) |
169 ((value & 0x0000000000FF0000ULL) << (SHIFT_BASE * TIMES_THREE)) |
170 ((value & 0x00000000FF000000ULL) << (SHIFT_BASE)) | ((value & 0x000000FF00000000ULL) >> (SHIFT_BASE)) |
171 ((value & 0x0000FF0000000000ULL) >> (SHIFT_BASE * TIMES_THREE)) |
172 ((value & 0x00FF000000000000ULL) >> (SHIFT_BASE * TIMES_FIVE)) | ((value) >> (SHIFT_BASE * TIMES_SEVEN)));
173 }
174 } // namespace ImagePlugin
175 } // namespace OHOS
176