/src/vvenc/source/Lib/CommonLib/PicYuvMD5.cpp
Line | Count | Source |
1 | | /* ----------------------------------------------------------------------------- |
2 | | The copyright in this software is being made available under the Clear BSD |
3 | | License, included below. No patent rights, trademark rights and/or |
4 | | other Intellectual Property Rights other than the copyrights concerning |
5 | | the Software are granted under this license. |
6 | | |
7 | | The Clear BSD License |
8 | | |
9 | | Copyright (c) 2019-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVenC Authors. |
10 | | All rights reserved. |
11 | | |
12 | | Redistribution and use in source and binary forms, with or without modification, |
13 | | are permitted (subject to the limitations in the disclaimer below) provided that |
14 | | the following conditions are met: |
15 | | |
16 | | * Redistributions of source code must retain the above copyright notice, |
17 | | this list of conditions and the following disclaimer. |
18 | | |
19 | | * Redistributions in binary form must reproduce the above copyright |
20 | | notice, this list of conditions and the following disclaimer in the |
21 | | documentation and/or other materials provided with the distribution. |
22 | | |
23 | | * Neither the name of the copyright holder nor the names of its |
24 | | contributors may be used to endorse or promote products derived from this |
25 | | software without specific prior written permission. |
26 | | |
27 | | NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY |
28 | | THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
29 | | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
30 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
31 | | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
32 | | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
33 | | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
34 | | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
35 | | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER |
36 | | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
37 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
38 | | POSSIBILITY OF SUCH DAMAGE. |
39 | | |
40 | | |
41 | | ------------------------------------------------------------------------------------------- */ |
42 | | |
43 | | |
44 | | #include "PicYuvMD5.h" |
45 | | #include "Picture.h" |
46 | | #include "MD5.h" |
47 | | #include "Utilities/MsgLog.h" |
48 | | |
49 | | //! \ingroup CommonLib |
50 | | //! \{ |
51 | | |
52 | | namespace vvenc { |
53 | | |
54 | | /** |
55 | | * Update md5 using n samples from plane, each sample is adjusted to |
56 | | * OUTBIT_BITDEPTH_DIV8. |
57 | | */ |
58 | | template<uint32_t OUTPUT_BITDEPTH_DIV8> |
59 | | static void md5_block(MD5& md5, const Pel* plane, uint32_t n) |
60 | 0 | { |
61 | | /* create a 64 byte buffer for packing Pel's into */ |
62 | 0 | uint8_t buf[64/OUTPUT_BITDEPTH_DIV8][OUTPUT_BITDEPTH_DIV8]; |
63 | 0 | for (uint32_t i = 0; i < n; i++) |
64 | 0 | { |
65 | 0 | Pel pel = plane[i]; |
66 | | /* perform bitdepth and endian conversion */ |
67 | 0 | for (uint32_t d = 0; d < OUTPUT_BITDEPTH_DIV8; d++) |
68 | 0 | { |
69 | 0 | buf[i][d] = pel >> (d*8); |
70 | 0 | } |
71 | 0 | } |
72 | 0 | md5.update((uint8_t*)buf, n * OUTPUT_BITDEPTH_DIV8); |
73 | 0 | } Unexecuted instantiation: PicYuvMD5.cpp:void vvenc::md5_block<1u>(vvenc::MD5&, short const*, unsigned int) Unexecuted instantiation: PicYuvMD5.cpp:void vvenc::md5_block<2u>(vvenc::MD5&, short const*, unsigned int) |
74 | | |
75 | | /** |
76 | | * Update md5 with all samples in plane in raster order, each sample |
77 | | * is adjusted to OUTBIT_BITDEPTH_DIV8. |
78 | | */ |
79 | | template<uint32_t OUTPUT_BITDEPTH_DIV8> |
80 | | static void md5_plane(MD5& md5, const Pel* plane, uint32_t width, uint32_t height, uint32_t stride) |
81 | 0 | { |
82 | | /* N is the number of samples to process per md5 update. |
83 | | * All N samples must fit in buf */ |
84 | 0 | uint32_t N = 32; |
85 | 0 | uint32_t width_modN = width % N; |
86 | 0 | uint32_t width_less_modN = width - width_modN; |
87 | |
|
88 | 0 | for (uint32_t y = 0; y < height; y++) |
89 | 0 | { |
90 | | /* convert pels into unsigned chars in little endian byte order. |
91 | | * NB, for 8bit data, data is truncated to 8bits. */ |
92 | 0 | for (uint32_t x = 0; x < width_less_modN; x += N) |
93 | 0 | { |
94 | 0 | md5_block<OUTPUT_BITDEPTH_DIV8>(md5, &plane[y*stride + x], N); |
95 | 0 | } |
96 | | |
97 | | /* mop up any of the remaining line */ |
98 | 0 | md5_block<OUTPUT_BITDEPTH_DIV8>(md5, &plane[y*stride + width_less_modN], width_modN); |
99 | 0 | } |
100 | 0 | } Unexecuted instantiation: PicYuvMD5.cpp:void vvenc::md5_plane<1u>(vvenc::MD5&, short const*, unsigned int, unsigned int, unsigned int) Unexecuted instantiation: PicYuvMD5.cpp:void vvenc::md5_plane<2u>(vvenc::MD5&, short const*, unsigned int, unsigned int, unsigned int) |
101 | | |
102 | | |
103 | | uint32_t compCRC(int bitdepth, const Pel* plane, uint32_t width, uint32_t height, uint32_t stride, PictureHash &digest) |
104 | 0 | { |
105 | 0 | uint32_t crcMsb; |
106 | 0 | uint32_t bitVal; |
107 | 0 | uint32_t crcVal = 0xffff; |
108 | 0 | uint32_t bitIdx; |
109 | 0 | for (uint32_t y = 0; y < height; y++) |
110 | 0 | { |
111 | 0 | for (uint32_t x = 0; x < width; x++) |
112 | 0 | { |
113 | | // take CRC of first pictureData byte |
114 | 0 | for(bitIdx=0; bitIdx<8; bitIdx++) |
115 | 0 | { |
116 | 0 | crcMsb = (crcVal >> 15) & 1; |
117 | 0 | bitVal = (plane[y*stride+x] >> (7 - bitIdx)) & 1; |
118 | 0 | crcVal = (((crcVal << 1) + bitVal) & 0xffff) ^ (crcMsb * 0x1021); |
119 | 0 | } |
120 | | // take CRC of second pictureData byte if bit depth is greater than 8-bits |
121 | 0 | if(bitdepth > 8) |
122 | 0 | { |
123 | 0 | for(bitIdx=0; bitIdx<8; bitIdx++) |
124 | 0 | { |
125 | 0 | crcMsb = (crcVal >> 15) & 1; |
126 | 0 | bitVal = (plane[y*stride+x] >> (15 - bitIdx)) & 1; |
127 | 0 | crcVal = (((crcVal << 1) + bitVal) & 0xffff) ^ (crcMsb * 0x1021); |
128 | 0 | } |
129 | 0 | } |
130 | 0 | } |
131 | 0 | } |
132 | 0 | for(bitIdx=0; bitIdx<16; bitIdx++) |
133 | 0 | { |
134 | 0 | crcMsb = (crcVal >> 15) & 1; |
135 | 0 | crcVal = ((crcVal << 1) & 0xffff) ^ (crcMsb * 0x1021); |
136 | 0 | } |
137 | |
|
138 | 0 | digest.hash.push_back((crcVal>>8) & 0xff); |
139 | 0 | digest.hash.push_back( crcVal & 0xff); |
140 | 0 | return 2; |
141 | 0 | } |
142 | | |
143 | | uint32_t calcCRC(const CPelUnitBuf& pic, PictureHash &digest, const BitDepths &bitDepths) |
144 | 0 | { |
145 | 0 | uint32_t digestLen=0; |
146 | 0 | digest.hash.clear(); |
147 | 0 | for (uint32_t chan = 0; chan< (uint32_t)pic.bufs.size(); chan++) |
148 | 0 | { |
149 | 0 | const ComponentID compID = ComponentID(chan); |
150 | 0 | const CPelBuf area = pic.get(compID); |
151 | 0 | digestLen = compCRC(bitDepths[toChannelType(compID)], area.bufAt(0, 0), area.width, area.height, area.stride, digest ); |
152 | 0 | } |
153 | 0 | return digestLen; |
154 | 0 | } |
155 | | |
156 | | uint32_t compChecksum(int bitdepth, const Pel* plane, uint32_t width, uint32_t height, uint32_t stride, PictureHash &digest, const BitDepths &/*bitDepths*/) |
157 | 0 | { |
158 | 0 | uint32_t checksum = 0; |
159 | 0 | uint8_t xor_mask; |
160 | |
|
161 | 0 | for (uint32_t y = 0; y < height; y++) |
162 | 0 | { |
163 | 0 | for (uint32_t x = 0; x < width; x++) |
164 | 0 | { |
165 | 0 | xor_mask = (x & 0xff) ^ (y & 0xff) ^ (x >> 8) ^ (y >> 8); |
166 | 0 | checksum = (checksum + ((plane[y*stride+x] & 0xff) ^ xor_mask)) & 0xffffffff; |
167 | |
|
168 | 0 | if(bitdepth > 8) |
169 | 0 | { |
170 | 0 | checksum = (checksum + ((plane[y*stride+x]>>8) ^ xor_mask)) & 0xffffffff; |
171 | 0 | } |
172 | 0 | } |
173 | 0 | } |
174 | |
|
175 | 0 | digest.hash.push_back((checksum>>24) & 0xff); |
176 | 0 | digest.hash.push_back((checksum>>16) & 0xff); |
177 | 0 | digest.hash.push_back((checksum>>8) & 0xff); |
178 | 0 | digest.hash.push_back( checksum & 0xff); |
179 | 0 | return 4; |
180 | 0 | } |
181 | | |
182 | | uint32_t calcChecksum(const CPelUnitBuf& pic, PictureHash &digest, const BitDepths &bitDepths) |
183 | 0 | { |
184 | 0 | uint32_t digestLen=0; |
185 | 0 | digest.hash.clear(); |
186 | 0 | for(uint32_t chan=0; chan< (uint32_t)pic.bufs.size(); chan++) |
187 | 0 | { |
188 | 0 | const ComponentID compID=ComponentID(chan); |
189 | 0 | const CPelBuf area = pic.get(compID); |
190 | 0 | digestLen=compChecksum(bitDepths[toChannelType(compID)], area.bufAt(0,0), area.width, area.height, area.stride, digest, bitDepths); |
191 | 0 | } |
192 | 0 | return digestLen; |
193 | 0 | } |
194 | | /** |
195 | | * Calculate the MD5sum of pic, storing the result in digest. |
196 | | * MD5 calculation is performed on Y' then Cb, then Cr; each in raster order. |
197 | | * Pel data is inserted into the MD5 function in little-endian byte order, |
198 | | * using sufficient bytes to represent the picture bitdepth. Eg, 10bit data |
199 | | * uses little-endian two byte words; 8bit data uses single byte words. |
200 | | */ |
201 | | uint32_t calcMD5(const CPelUnitBuf& pic, PictureHash &digest, const BitDepths &bitDepths) |
202 | 0 | { |
203 | | /* choose an md5_plane packing function based on the system bitdepth */ |
204 | 0 | typedef void (*MD5PlaneFunc)(MD5&, const Pel*, uint32_t, uint32_t, uint32_t); |
205 | 0 | MD5PlaneFunc md5_plane_func; |
206 | |
|
207 | 0 | MD5 md5[MAX_NUM_COMP]; |
208 | |
|
209 | 0 | digest.hash.clear(); |
210 | |
|
211 | 0 | for (uint32_t chan = 0; chan< (uint32_t)pic.bufs.size(); chan++) |
212 | 0 | { |
213 | 0 | const ComponentID compID=ComponentID(chan); |
214 | 0 | const CPelBuf area = pic.get(compID); |
215 | 0 | md5_plane_func = bitDepths[toChannelType(compID)] <= 8 ? (MD5PlaneFunc)md5_plane<1> : (MD5PlaneFunc)md5_plane<2>; |
216 | 0 | uint8_t tmp_digest[MD5_DIGEST_STRING_LENGTH]; |
217 | 0 | md5_plane_func(md5[compID], area.bufAt(0, 0), area.width, area.height, area.stride ); |
218 | 0 | md5[compID].finalize(tmp_digest); |
219 | 0 | for(uint32_t i=0; i<MD5_DIGEST_STRING_LENGTH; i++) |
220 | 0 | { |
221 | 0 | digest.hash.push_back(tmp_digest[i]); |
222 | 0 | } |
223 | 0 | } |
224 | 0 | return 16; |
225 | 0 | } |
226 | | |
227 | | std::string hashToString(const PictureHash &digest, int numChar) |
228 | 0 | { |
229 | 0 | static const char* hex = "0123456789abcdef"; |
230 | 0 | std::string result; |
231 | |
|
232 | 0 | for(int pos=0; pos<int(digest.hash.size()); pos++) |
233 | 0 | { |
234 | 0 | if ((pos % numChar) == 0 && pos!=0 ) |
235 | 0 | { |
236 | 0 | result += ','; |
237 | 0 | } |
238 | 0 | result += hex[digest.hash[pos] >> 4]; |
239 | 0 | result += hex[digest.hash[pos] & 0xf]; |
240 | 0 | } |
241 | |
|
242 | 0 | return result; |
243 | 0 | } |
244 | | |
245 | | int calcAndPrintHashStatus(const CPelUnitBuf& pic, const SEIDecodedPictureHash* pictureHashSEI, const BitDepths &bitDepths, const vvencMsgLevel msgl, MsgLog& msg ) |
246 | 0 | { |
247 | | /* calculate MD5sum for entire reconstructed picture */ |
248 | 0 | PictureHash recon_digest; |
249 | 0 | int numChar=0; |
250 | 0 | const char* hashType = "\0"; |
251 | |
|
252 | 0 | if (pictureHashSEI) |
253 | 0 | { |
254 | 0 | switch (pictureHashSEI->method) |
255 | 0 | { |
256 | 0 | case VVENC_HASHTYPE_MD5: |
257 | 0 | case VVENC_HASHTYPE_MD5_LOG: |
258 | 0 | { |
259 | 0 | hashType = "MD5"; |
260 | 0 | numChar = calcMD5(pic, recon_digest, bitDepths); |
261 | 0 | break; |
262 | 0 | } |
263 | 0 | case VVENC_HASHTYPE_CRC: |
264 | 0 | case VVENC_HASHTYPE_CRC_LOG: |
265 | 0 | { |
266 | 0 | hashType = "CRC"; |
267 | 0 | numChar = calcCRC(pic, recon_digest, bitDepths); |
268 | 0 | break; |
269 | 0 | } |
270 | 0 | case VVENC_HASHTYPE_CHECKSUM: |
271 | 0 | case VVENC_HASHTYPE_CHECKSUM_LOG: |
272 | 0 | { |
273 | 0 | hashType = "Checksum"; |
274 | 0 | numChar = calcChecksum(pic, recon_digest, bitDepths); |
275 | 0 | break; |
276 | 0 | } |
277 | 0 | default: |
278 | 0 | { |
279 | 0 | THROW("Unknown hash type"); |
280 | 0 | break; |
281 | 0 | } |
282 | 0 | } |
283 | 0 | } |
284 | | |
285 | | /* compare digest against received version */ |
286 | 0 | const char* ok = "(unk)"; |
287 | 0 | bool mismatch = false; |
288 | |
|
289 | 0 | if (pictureHashSEI) |
290 | 0 | { |
291 | 0 | ok = "(OK)"; |
292 | 0 | if (recon_digest != pictureHashSEI->pictureHash) |
293 | 0 | { |
294 | 0 | ok = "(***ERROR***)"; |
295 | 0 | mismatch = true; |
296 | 0 | } |
297 | 0 | } |
298 | |
|
299 | 0 | msg.log( msgl, "[%s:%s,%s] ", hashType, hashToString(recon_digest, numChar).c_str(), ok); |
300 | |
|
301 | 0 | if (mismatch) |
302 | 0 | { |
303 | 0 | msg.log( msgl, "[rx%s:%s] ", hashType, hashToString(pictureHashSEI->pictureHash, numChar).c_str()); |
304 | 0 | } |
305 | 0 | return mismatch; |
306 | 0 | } |
307 | | |
308 | | } // namespace vvenc |
309 | | |
310 | | //! \} |
311 | | |