1/* 2 * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at> 3 * 4 * This file is part of FFmpeg. 5 * 6 * FFmpeg is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * FFmpeg is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with FFmpeg; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21#include <stdio.h> 22#include <stdlib.h> 23#include <string.h> 24#include <inttypes.h> 25#include <math.h> 26#include <float.h> 27#include <limits.h> 28 29#include "libavutil/intfloat.h" 30#include "libavutil/intreadwrite.h" 31 32#define FFMIN(a, b) ((a) > (b) ? (b) : (a)) 33#define F 100 34#define SIZE 2048 35 36uint64_t exp16_table[21] = { 37 65537, 38 65538, 39 65540, 40 65544, 41 65552, 42 65568, 43 65600, 44 65664, 45 65793, 46 66050, 47 66568, 48 67616, 49 69763, 50 74262, 51 84150, 52 108051, 53 178145, 54 484249, 55 3578144, 56 195360063, 57 582360139072LL, 58}; 59 60#if 0 61// 16.16 fixpoint exp() 62static unsigned int exp16(unsigned int a){ 63 int i; 64 int out= 1<<16; 65 66 for(i=19;i>=0;i--){ 67 if(a&(1<<i)) 68 out= (out*exp16_table[i] + (1<<15))>>16; 69 } 70 71 return out; 72} 73#endif 74 75// 16.16 fixpoint log() 76static int64_t log16(uint64_t a) 77{ 78 int i; 79 int out = 0; 80 81 if (a < 1 << 16) 82 return -log16((1LL << 32) / a); 83 a <<= 16; 84 85 for (i = 20; i >= 0; i--) { 86 int64_t b = exp16_table[i]; 87 if (a < (b << 16)) 88 continue; 89 out |= 1 << i; 90 a = ((a / b) << 16) + (((a % b) << 16) + b / 2) / b; 91 } 92 return out; 93} 94 95static uint64_t int_sqrt(uint64_t a) 96{ 97 uint64_t ret = 0; 98 uint64_t ret_sq = 0; 99 int s; 100 101 for (s = 31; s >= 0; s--) { 102 uint64_t b = ret_sq + (1ULL << (s * 2)) + (ret << s) * 2; 103 if (b <= a) { 104 ret_sq = b; 105 ret += 1ULL << s; 106 } 107 } 108 return ret; 109} 110 111static int16_t get_s16l(uint8_t *p) 112{ 113 union { 114 uint16_t u; 115 int16_t s; 116 } v; 117 v.u = p[0] | p[1] << 8; 118 return v.s; 119} 120 121static float get_f32l(uint8_t *p) 122{ 123 union av_intfloat32 v; 124 v.i = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; 125 return v.f; 126} 127 128static double get_f64l(uint8_t *p) 129{ 130 return av_int2double(AV_RL64(p)); 131} 132 133static int run_psnr(FILE *f[2], int len, int shift, int skip_bytes) 134{ 135 uint64_t i, j; 136 uint64_t sse = 0; 137 double sse_d = 0.0; 138 uint8_t buf[2][SIZE]; 139 int64_t max = (1LL << (8 * len)) - 1; 140 uint64_t size0 = 0; 141 uint64_t size1 = 0; 142 uint64_t maxdist = 0; 143 double maxdist_d = 0.0; 144 int noseek; 145 146 noseek = fseek(f[0], 0, SEEK_SET) || 147 fseek(f[1], 0, SEEK_SET); 148 149 if (!noseek) { 150 for (i = 0; i < 2; i++) { 151 uint8_t *p = buf[i]; 152 if (fread(p, 1, 12, f[i]) != 12) 153 return -1; 154 if (!memcmp(p, "RIFF", 4) && 155 !memcmp(p + 8, "WAVE", 4)) { 156 if (fread(p, 1, 8, f[i]) != 8) 157 return -1; 158 while (memcmp(p, "data", 4)) { 159 int s = p[4] | p[5] << 8 | p[6] << 16 | p[7] << 24; 160 fseek(f[i], s, SEEK_CUR); 161 if (fread(p, 1, 8, f[i]) != 8) 162 return -1; 163 } 164 } else { 165 fseek(f[i], -12, SEEK_CUR); 166 } 167 } 168 169 fseek(f[shift < 0], abs(shift), SEEK_CUR); 170 171 fseek(f[0], skip_bytes, SEEK_CUR); 172 fseek(f[1], skip_bytes, SEEK_CUR); 173 } 174 175 for (;;) { 176 int s0 = fread(buf[0], 1, SIZE, f[0]); 177 int s1 = fread(buf[1], 1, SIZE, f[1]); 178 179 for (j = 0; j < FFMIN(s0, s1); j += len) { 180 switch (len) { 181 case 1: 182 case 2: { 183 int64_t a, b; 184 int dist; 185 if (len == 2) { 186 a = get_s16l(buf[0] + j); 187 b = get_s16l(buf[1] + j); 188 } else { 189 a = buf[0][j]; 190 b = buf[1][j]; 191 } 192 sse += (a - b) * (a - b); 193 dist = llabs(a - b); 194 if (dist > maxdist) 195 maxdist = dist; 196 break; 197 } 198 case 4: 199 case 8: { 200 double dist, a, b; 201 if (len == 8) { 202 a = get_f64l(buf[0] + j); 203 b = get_f64l(buf[1] + j); 204 } else { 205 a = get_f32l(buf[0] + j); 206 b = get_f32l(buf[1] + j); 207 } 208 dist = fabs(a - b); 209 sse_d += (a - b) * (a - b); 210 if (dist > maxdist_d) 211 maxdist_d = dist; 212 break; 213 } 214 } 215 } 216 size0 += s0; 217 size1 += s1; 218 if (s0 + s1 <= 0) 219 break; 220 } 221 222 i = FFMIN(size0, size1) / len; 223 if (!i) 224 i = 1; 225 switch (len) { 226 case 1: 227 case 2: { 228 uint64_t psnr; 229 uint64_t dev = int_sqrt(((sse / i) * F * F) + (((sse % i) * F * F) + i / 2) / i); 230 if (sse) 231 psnr = ((2 * log16(max << 16) + log16(i) - log16(sse)) * 232 284619LL * F + (1LL << 31)) / (1LL << 32); 233 else 234 psnr = 1000 * F - 1; // floating point free infinity :) 235 236 printf("stddev:%5d.%02d PSNR:%3d.%02d MAXDIFF:%5"PRIu64" bytes:%9"PRIu64"/%9"PRIu64"\n", 237 (int)(dev / F), (int)(dev % F), 238 (int)(psnr / F), (int)(psnr % F), 239 maxdist, size0, size1); 240 return psnr; 241 } 242 case 4: 243 case 8: { 244 char psnr_str[64]; 245 double psnr = INT_MAX; 246 double dev = sqrt(sse_d / i); 247 uint64_t scale = (len == 4) ? (1ULL << 24) : (1ULL << 32); 248 249 if (sse_d) { 250 psnr = 2 * log(DBL_MAX) - log(i / sse_d); 251 snprintf(psnr_str, sizeof(psnr_str), "%5.02f", psnr); 252 } else 253 snprintf(psnr_str, sizeof(psnr_str), "inf"); 254 255 maxdist = maxdist_d * scale; 256 257 printf("stddev:%10.2f PSNR:%s MAXDIFF:%10"PRIu64" bytes:%9"PRIu64"/%9"PRIu64"\n", 258 dev * scale, psnr_str, maxdist, size0, size1); 259 return psnr; 260 } 261 } 262 return -1; 263} 264 265int main(int argc, char *argv[]) 266{ 267 FILE *f[2]; 268 int len = 1; 269 int shift_first= argc < 5 ? 0 : atoi(argv[4]); 270 int skip_bytes = argc < 6 ? 0 : atoi(argv[5]); 271 int shift_last = shift_first + (argc < 7 ? 0 : atoi(argv[6])); 272 int shift; 273 int max_psnr = -1; 274 int max_psnr_shift = 0; 275 276 if (shift_last > shift_first) 277 shift_first -= shift_last - shift_first; 278 279 if (argc > 3) { 280 if (!strcmp(argv[3], "u8")) { 281 len = 1; 282 } else if (!strcmp(argv[3], "s16")) { 283 len = 2; 284 } else if (!strcmp(argv[3], "f32")) { 285 len = 4; 286 } else if (!strcmp(argv[3], "f64")) { 287 len = 8; 288 } else { 289 char *end; 290 len = strtol(argv[3], &end, 0); 291 if (*end || len < 1 || len > 2) { 292 fprintf(stderr, "Unsupported sample format: %s\nSupported: u8, s16, f32, f64\n", argv[3]); 293 return 1; 294 } 295 } 296 } 297 298 if (argc < 3) { 299 printf("tiny_psnr <file1> <file2> [<elem size>|u8|s16|f32|f64 [<shift> [<skip bytes> [<shift search range>]]]]\n"); 300 printf("WAV headers are skipped automatically.\n"); 301 return 1; 302 } 303 304 f[0] = fopen(argv[1], "rb"); 305 f[1] = fopen(argv[2], "rb"); 306 if (!f[0] || !f[1]) { 307 fprintf(stderr, "Could not open input files.\n"); 308 return 1; 309 } 310 311 for (shift = shift_first; shift <= shift_last; shift++) { 312 int psnr = run_psnr(f, len, shift, skip_bytes); 313 if (psnr > max_psnr || (shift < 0 && psnr == max_psnr)) { 314 max_psnr = psnr; 315 max_psnr_shift = shift; 316 } 317 } 318 if (max_psnr < 0) 319 return 2; 320 321 if (shift_last > shift_first) 322 printf("Best PSNR is %3d.%02d for shift %i\n", (int)(max_psnr / F), (int)(max_psnr % F), max_psnr_shift); 323 return 0; 324} 325