/src/vvdec/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) 2018-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVdeC 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 | | #include "Picture.h" |
44 | | #include "libmd5/MD5.h" |
45 | | #include "SEI_internal.h" |
46 | | |
47 | | namespace vvdec |
48 | | { |
49 | | |
50 | | /** |
51 | | * Update md5 using n samples from plane, each sample is adjusted to |
52 | | * OUTBIT_BITDEPTH_DIV8. |
53 | | */ |
54 | | template<uint32_t OUTPUT_BITDEPTH_DIV8> |
55 | | static void md5_block( libmd5::MD5& md5, const Pel* plane, uint32_t n) |
56 | 0 | { |
57 | | /* create a 64 byte buffer for packing Pel's into */ |
58 | 0 | uint8_t buf[64/OUTPUT_BITDEPTH_DIV8][OUTPUT_BITDEPTH_DIV8]; |
59 | 0 | for (uint32_t i = 0; i < n; i++) |
60 | 0 | { |
61 | 0 | Pel pel = plane[i]; |
62 | | /* perform bitdepth and endian conversion */ |
63 | 0 | for (uint32_t d = 0; d < OUTPUT_BITDEPTH_DIV8; d++) |
64 | 0 | { |
65 | 0 | buf[i][d] = pel >> (d*8); |
66 | 0 | } |
67 | 0 | } |
68 | 0 | md5.update((uint8_t*)buf, n * OUTPUT_BITDEPTH_DIV8); |
69 | 0 | } Unexecuted instantiation: PicYuvMD5.cpp:void vvdec::md5_block<1u>(vvdec::libmd5::MD5&, short const*, unsigned int) Unexecuted instantiation: PicYuvMD5.cpp:void vvdec::md5_block<2u>(vvdec::libmd5::MD5&, short const*, unsigned int) |
70 | | |
71 | | /** |
72 | | * Update md5 with all samples in plane in raster order, each sample |
73 | | * is adjusted to OUTBIT_BITDEPTH_DIV8. |
74 | | */ |
75 | | template<uint32_t OUTPUT_BITDEPTH_DIV8> |
76 | | static void md5_plane( libmd5::MD5& md5, const Pel* plane, uint32_t width, uint32_t height, ptrdiff_t stride) |
77 | 0 | { |
78 | | /* N is the number of samples to process per md5 update. |
79 | | * All N samples must fit in buf */ |
80 | 0 | uint32_t N = 32; |
81 | 0 | uint32_t width_modN = width % N; |
82 | 0 | uint32_t width_less_modN = width - width_modN; |
83 | |
|
84 | 0 | for (uint32_t y = 0; y < height; y++) |
85 | 0 | { |
86 | | /* convert pels into unsigned chars in little endian byte order. |
87 | | * NB, for 8bit data, data is truncated to 8bits. */ |
88 | 0 | for (uint32_t x = 0; x < width_less_modN; x += N) |
89 | 0 | { |
90 | 0 | md5_block<OUTPUT_BITDEPTH_DIV8>(md5, &plane[y*stride + x], N); |
91 | 0 | } |
92 | | |
93 | | /* mop up any of the remaining line */ |
94 | 0 | md5_block<OUTPUT_BITDEPTH_DIV8>(md5, &plane[y*stride + width_less_modN], width_modN); |
95 | 0 | } |
96 | 0 | } Unexecuted instantiation: PicYuvMD5.cpp:void vvdec::md5_plane<1u>(vvdec::libmd5::MD5&, short const*, unsigned int, unsigned int, long) Unexecuted instantiation: PicYuvMD5.cpp:void vvdec::md5_plane<2u>(vvdec::libmd5::MD5&, short const*, unsigned int, unsigned int, long) |
97 | | |
98 | | |
99 | | uint32_t compCRC(int bitdepth, const Pel* plane, uint32_t width, uint32_t height, ptrdiff_t stride, PictureHash &digest) |
100 | 0 | { |
101 | 0 | uint32_t crcMsb; |
102 | 0 | uint32_t bitVal; |
103 | 0 | uint32_t crcVal = 0xffff; |
104 | 0 | uint32_t bitIdx; |
105 | 0 | for (uint32_t y = 0; y < height; y++) |
106 | 0 | { |
107 | 0 | for (uint32_t x = 0; x < width; x++) |
108 | 0 | { |
109 | | // take CRC of first pictureData byte |
110 | 0 | for(bitIdx=0; bitIdx<8; bitIdx++) |
111 | 0 | { |
112 | 0 | crcMsb = (crcVal >> 15) & 1; |
113 | 0 | bitVal = (plane[y*stride+x] >> (7 - bitIdx)) & 1; |
114 | 0 | crcVal = (((crcVal << 1) + bitVal) & 0xffff) ^ (crcMsb * 0x1021); |
115 | 0 | } |
116 | | // take CRC of second pictureData byte if bit depth is greater than 8-bits |
117 | 0 | if(bitdepth > 8) |
118 | 0 | { |
119 | 0 | for(bitIdx=0; bitIdx<8; bitIdx++) |
120 | 0 | { |
121 | 0 | crcMsb = (crcVal >> 15) & 1; |
122 | 0 | bitVal = (plane[y*stride+x] >> (15 - bitIdx)) & 1; |
123 | 0 | crcVal = (((crcVal << 1) + bitVal) & 0xffff) ^ (crcMsb * 0x1021); |
124 | 0 | } |
125 | 0 | } |
126 | 0 | } |
127 | 0 | } |
128 | 0 | for(bitIdx=0; bitIdx<16; bitIdx++) |
129 | 0 | { |
130 | 0 | crcMsb = (crcVal >> 15) & 1; |
131 | 0 | crcVal = ((crcVal << 1) & 0xffff) ^ (crcMsb * 0x1021); |
132 | 0 | } |
133 | |
|
134 | 0 | digest.hash.push_back((crcVal>>8) & 0xff); |
135 | 0 | digest.hash.push_back( crcVal & 0xff); |
136 | 0 | return 2; |
137 | 0 | } |
138 | | |
139 | | uint32_t calcCRC(const CPelUnitBuf& pic, PictureHash &digest, const BitDepths &bitDepths) |
140 | 0 | { |
141 | 0 | uint32_t digestLen=0; |
142 | 0 | digest.hash.clear(); |
143 | 0 | for (uint32_t chan = 0; chan< (uint32_t)pic.bufs.size(); chan++) |
144 | 0 | { |
145 | 0 | const ComponentID compID = ComponentID(chan); |
146 | 0 | const CPelBuf area = pic.get(compID); |
147 | 0 | digestLen = compCRC(bitDepths.recon, area.bufAt(0, 0), area.width, area.height, area.stride, digest ); |
148 | 0 | } |
149 | 0 | return digestLen; |
150 | 0 | } |
151 | | |
152 | | uint32_t compChecksum(int bitdepth, const Pel* plane, uint32_t width, uint32_t height, ptrdiff_t stride, PictureHash &digest, const BitDepths &/*bitDepths*/) |
153 | 0 | { |
154 | 0 | uint32_t checksum = 0; |
155 | 0 | uint8_t xor_mask; |
156 | |
|
157 | 0 | for (uint32_t y = 0; y < height; y++) |
158 | 0 | { |
159 | 0 | for (uint32_t x = 0; x < width; x++) |
160 | 0 | { |
161 | 0 | xor_mask = (x & 0xff) ^ (y & 0xff) ^ (x >> 8) ^ (y >> 8); |
162 | 0 | checksum = (checksum + ((plane[y*stride+x] & 0xff) ^ xor_mask)) & 0xffffffff; |
163 | |
|
164 | 0 | if(bitdepth > 8) |
165 | 0 | { |
166 | 0 | checksum = (checksum + ((plane[y*stride+x]>>8) ^ xor_mask)) & 0xffffffff; |
167 | 0 | } |
168 | 0 | } |
169 | 0 | } |
170 | |
|
171 | 0 | digest.hash.push_back((checksum>>24) & 0xff); |
172 | 0 | digest.hash.push_back((checksum>>16) & 0xff); |
173 | 0 | digest.hash.push_back((checksum>>8) & 0xff); |
174 | 0 | digest.hash.push_back( checksum & 0xff); |
175 | 0 | return 4; |
176 | 0 | } |
177 | | |
178 | | uint32_t calcChecksum(const CPelUnitBuf& pic, PictureHash &digest, const BitDepths &bitDepths) |
179 | 0 | { |
180 | 0 | uint32_t digestLen=0; |
181 | 0 | digest.hash.clear(); |
182 | 0 | for(uint32_t chan=0; chan< (uint32_t)pic.bufs.size(); chan++) |
183 | 0 | { |
184 | 0 | const ComponentID compID=ComponentID(chan); |
185 | 0 | const CPelBuf area = pic.get(compID); |
186 | 0 | digestLen=compChecksum(bitDepths.recon, area.bufAt(0,0), area.width, area.height, area.stride, digest, bitDepths); |
187 | 0 | } |
188 | 0 | return digestLen; |
189 | 0 | } |
190 | | /** |
191 | | * Calculate the MD5sum of pic, storing the result in digest. |
192 | | * MD5 calculation is performed on Y' then Cb, then Cr; each in raster order. |
193 | | * Pel data is inserted into the MD5 function in little-endian byte order, |
194 | | * using sufficient bytes to represent the picture bitdepth. Eg, 10bit data |
195 | | * uses little-endian two byte words; 8bit data uses single byte words. |
196 | | */ |
197 | | uint32_t calcMD5(const CPelUnitBuf& pic, PictureHash &digest, const BitDepths &bitDepths) |
198 | 0 | { |
199 | | /* choose an md5_plane packing function based on the system bitdepth */ |
200 | 0 | typedef void (*MD5PlaneFunc)(libmd5::MD5&, const Pel*, uint32_t, uint32_t, ptrdiff_t ); |
201 | 0 | MD5PlaneFunc md5_plane_func; |
202 | |
|
203 | 0 | libmd5::MD5 md5[MAX_NUM_COMPONENT]; |
204 | |
|
205 | 0 | digest.hash.clear(); |
206 | |
|
207 | 0 | for (uint32_t chan = 0; chan< (uint32_t)pic.bufs.size(); chan++) |
208 | 0 | { |
209 | 0 | const ComponentID compID=ComponentID(chan); |
210 | 0 | const CPelBuf area = pic.get(compID); |
211 | 0 | md5_plane_func = bitDepths.recon <= 8 ? (MD5PlaneFunc)md5_plane<1> : (MD5PlaneFunc)md5_plane<2>; |
212 | 0 | uint8_t tmp_digest[MD5_DIGEST_STRING_LENGTH]; |
213 | 0 | md5_plane_func(md5[compID], area.bufAt(0, 0), area.width, area.height, area.stride ); |
214 | 0 | md5[compID].finalize(tmp_digest); |
215 | 0 | for(uint32_t i=0; i<MD5_DIGEST_STRING_LENGTH; i++) |
216 | 0 | { |
217 | 0 | digest.hash.push_back(tmp_digest[i]); |
218 | 0 | } |
219 | 0 | } |
220 | 0 | return 16; |
221 | 0 | } |
222 | | |
223 | | std::string hashToString(const PictureHash &digest, int numChar) |
224 | 0 | { |
225 | 0 | static const char* hex = "0123456789abcdef"; |
226 | 0 | std::string result; |
227 | |
|
228 | 0 | CHECK_FATAL(numChar<=0, "numChar needs to be >0"); |
229 | |
|
230 | 0 | for(int pos=0; pos<int(digest.hash.size()); pos++) |
231 | 0 | { |
232 | 0 | if ((pos % numChar) == 0 && pos!=0 ) |
233 | 0 | { |
234 | 0 | result += ','; |
235 | 0 | } |
236 | 0 | result += hex[digest.hash[pos] >> 4]; |
237 | 0 | result += hex[digest.hash[pos] & 0xf]; |
238 | 0 | } |
239 | |
|
240 | 0 | return result; |
241 | 0 | } |
242 | | |
243 | | std::string hashToString(const vvdecSEIDecodedPictureHash* digest, int numChar) |
244 | 0 | { |
245 | 0 | static const char* hex = "0123456789abcdef"; |
246 | 0 | std::string result; |
247 | |
|
248 | 0 | CHECK_FATAL(numChar<=0, "numChar needs to be >0"); |
249 | |
|
250 | 0 | for(int pos=0; pos<int(digest->digest_length); pos++) |
251 | 0 | { |
252 | 0 | if ((pos % numChar) == 0 && pos!=0 ) |
253 | 0 | { |
254 | 0 | result += ','; |
255 | 0 | } |
256 | 0 | result += hex[digest->digest[pos] >> 4]; |
257 | 0 | result += hex[digest->digest[pos] & 0xf]; |
258 | 0 | } |
259 | |
|
260 | 0 | return result; |
261 | 0 | } |
262 | | |
263 | | int calcAndPrintHashStatus(const CPelUnitBuf& pic, const vvdecSEIDecodedPictureHash* pictureHashSEI, const BitDepths &bitDepths, const MsgLevel msgl) |
264 | 0 | { |
265 | | /* calculate MD5sum for entire reconstructed picture */ |
266 | 0 | PictureHash recon_digest; |
267 | 0 | int numChar=0; |
268 | 0 | const char* hashType = "\0"; |
269 | |
|
270 | 0 | if (pictureHashSEI) |
271 | 0 | { |
272 | 0 | switch (pictureHashSEI->method) |
273 | 0 | { |
274 | 0 | case vvdecHashType::VVDEC_HASHTYPE_MD5: |
275 | 0 | { |
276 | 0 | hashType = "MD5"; |
277 | 0 | numChar = calcMD5(pic, recon_digest, bitDepths); |
278 | 0 | break; |
279 | 0 | } |
280 | 0 | case vvdecHashType::VVDEC_HASHTYPE_CRC: |
281 | 0 | { |
282 | 0 | hashType = "CRC"; |
283 | 0 | numChar = calcCRC(pic, recon_digest, bitDepths); |
284 | 0 | break; |
285 | 0 | } |
286 | 0 | case vvdecHashType::VVDEC_HASHTYPE_CHECKSUM: |
287 | 0 | { |
288 | 0 | hashType = "Checksum"; |
289 | 0 | numChar = calcChecksum(pic, recon_digest, bitDepths); |
290 | 0 | break; |
291 | 0 | } |
292 | 0 | default: |
293 | 0 | { |
294 | 0 | THROW_FATAL("Unknown hash type"); |
295 | 0 | break; |
296 | 0 | } |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | /* compare digest against received version */ |
301 | 0 | const char* ok = "(unk)"; |
302 | 0 | bool mismatch = false; |
303 | |
|
304 | 0 | if (pictureHashSEI) |
305 | 0 | { |
306 | 0 | ok = "(OK)"; |
307 | |
|
308 | 0 | if ( !recon_digest.equal( *pictureHashSEI )) |
309 | 0 | { |
310 | 0 | ok = "(***ERROR***)"; |
311 | 0 | mismatch = true; |
312 | 0 | } |
313 | 0 | } |
314 | |
|
315 | 0 | msg( msgl, "[%s:%s,%s] ", hashType, hashToString(recon_digest, numChar).c_str(), ok); |
316 | |
|
317 | 0 | if (mismatch) |
318 | 0 | { |
319 | 0 | msg( msgl, "[rx%s:%s] ", hashType, hashToString(pictureHashSEI, numChar).c_str()); |
320 | 0 | } |
321 | 0 | return mismatch; |
322 | 0 | } |
323 | | |
324 | | } |