├── Makefile ├── README.md └── yv.c /Makefile: -------------------------------------------------------------------------------- 1 | DBG = #-ggdb3 2 | OPTFLAGS = -Wall -Wextra -Wstrict-prototypes -Wmissing-prototypes $(DBG) -pedantic 3 | SDL_LIBS := $(shell sdl-config --static-libs) 4 | SDL_CFLAGS := $(shell sdl-config --cflags) 5 | CFLAGS = $(OPTFLAGS) $(SDL_CFLAGS) -std=c99 6 | LDFLAGS = $(SDL_LIBS) #-lefence 7 | 8 | SRC = yv.c 9 | TARGET = yv 10 | OBJ = $(SRC:.c=.o) 11 | 12 | default: $(TARGET) 13 | 14 | %.o: %.c Makefile 15 | $(CC) $(CFLAGS) -c -o $@ $< 16 | 17 | $(TARGET): $(OBJ) 18 | $(CC) -o $@ $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm $(OBJ) $(TARGET) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Simple YCbCr-Viewer (YUV) 2 | ========================= 3 | 4 | Supports the following formats: 5 | 6 | - YV12 7 | - IYUV 8 | - YUVY 9 | - UYVY 10 | - YUY2 11 | - YV1210 12 | - Y42210 13 | 14 | YV1210 is the same as YV12 with 10bpp. 15 | Since SDL does not support this format, I fake it 16 | by converting it to standard 8bpp YV12 prior to viewing. 17 | 18 | Y42210 is YCbCr 4:2:2 planar with 10-bpp. 19 | Since SDL does not support this format, I fake it 20 | by converting it to standard 8bpp YVYU prior to viewing. 21 | 22 | Basically, because that's whats SDL supports. 23 | Other YCbCr (YUV) formats are simple to add as long as 24 | they are 4:2:0 or 4:2:2 8-bpp... 25 | 26 | Features 27 | -------- 28 | 29 | - Play 30 | - Pause 31 | - Rewind 32 | - Single Step Forward 33 | - Single Step Backwards 34 | - Zoom In by a factor of 1..n 35 | - Zoom Out by a factor of 1..n 36 | - Display a 16x16 grid on top of a frame 37 | - Dump Macro-Block-data to stdout for MB pointed 38 | to by mouse 39 | - Only display Luma data 40 | - Only display Cr data 41 | - Only display Cb data 42 | - Diff two files of the same size and format 43 | - PSNR calculation 44 | - Master/Slave mode that allows two instances of 45 | the binary to communicate using a message-queue. 46 | Commands issued in the Master are also executed 47 | in the Slave. Main usage is to single-step two clips 48 | side-by-side to compare them. Works regardless of 49 | format used 50 | - Title reflects mode, feature used, including 51 | frame number and size. 52 | - Histogram for the different color planes, per frame 53 | as csv-data to stdout (for now at least) 54 | 55 | Usage 56 | ----- 57 | 58 | ./yv filename width height format 59 | ./yv foreman_cif.yuv 352 288 YV12 60 | 61 | To use MASTER/SLAVE, type the following 62 | command in two different shells or send them to 63 | the background using a `&` at the end: 64 | 65 | ./yv foreman_1.yuv 352 288 YV12 66 | ./yv foreman_2.yuv 352 288 YV12 67 | 68 | In the first window, press F1 (title should be updated 69 | to show the mode. In the second window, press F2 70 | (title should be updated to show the mode). 71 | Commands given in window1 should be executed in window2. 72 | 73 | To display diff between two files of the same size 74 | and format, just add file as the last argument 75 | (computes and displays differences in luma value only, 76 | PSNR value is written to stdout): 77 | 78 | ./yv filename width height format diff_file 79 | ./yv foreman_cif.yuv 352 288 YV12 foreman_filtered_cif.yuv 80 | 81 | Supported commands 82 | ------------------ 83 | 84 | SPACE - Play clip 85 | RIGHT - Single step 1 frame forward 86 | LEFT - Single step 1 frame backward 87 | r - Rewind 88 | UP - Zoom in 89 | DOWN - Zoom out 90 | g - Enable grid-mode 91 | m - Enable MB-mode, point and click to 92 | print MB-data to stdout 93 | h - histogram, 1 per color plane 94 | F5 - Toggle viewing of Luma data only 95 | F6 - Toggle viewing of Cb data only 96 | F7 - Toggle viewing of Cr data only 97 | F8 - Display all color-planes 98 | q - Quit 99 | F1 - MASTER-mode 100 | F2 - SLAVE-mode 101 | F3 - NONE-mode, i.e. disable MASTER/SLAVE-mode 102 | 103 | Disclaimer 104 | ---------- 105 | 106 | Only verified on a Linux based system... 107 | -------------------------------------------------------------------------------- /yv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "SDL.h" 10 | 11 | /* Supported YUV-formats */ 12 | #define YV12 0 13 | #define IYUV 1 14 | #define YUY2 2 15 | #define UYVY 3 16 | #define YVYU 4 17 | #define YV1210 5 /* 10 bpp YV12 */ 18 | #define Y42210 6 19 | 20 | /* IPC */ 21 | #define NONE 0 22 | #define MASTER 1 23 | #define SLAVE 2 24 | 25 | /* IPC Commands */ 26 | #define NEXT 'a' 27 | #define PREV 'b' 28 | #define REW 'c' 29 | #define ZOOM_IN 'd' 30 | #define ZOOM_OUT 'e' 31 | #define QUIT 'f' 32 | #define Y_ONLY 'g' 33 | #define CB_ONLY 'h' 34 | #define CR_ONLY 'i' 35 | #define ALL_PLANES 'j' 36 | 37 | /* PROTOTYPES */ 38 | Uint32 rd(Uint8* data, Uint32 size); 39 | Uint32 read_yv12(void); 40 | Uint32 read_iyuv(void); 41 | Uint32 read_422(void); 42 | Uint32 read_y42210(void); 43 | Uint32 read_yv1210(void); 44 | Uint32 allocate_memory(void); 45 | void draw_grid422(void); 46 | void draw_grid420(void); 47 | void luma_only(void); 48 | void cb_only(void); 49 | void cr_only(void); 50 | void draw_420(void); 51 | void draw_422(void); 52 | Uint32 diff_mode(void); 53 | void calc_psnr(Uint8* frame0, Uint8* frame1); 54 | void usage(char* name); 55 | void mb_loop(char* str, Uint32 rows, Uint8* data, Uint32 pitch); 56 | void show_mb(Uint32 mouse_x, Uint32 mouse_y); 57 | void draw_frame(void); 58 | Uint32 read_frame(void); 59 | void setup_param(void); 60 | void check_input(void); 61 | Uint32 open_input(void); 62 | Uint32 create_message_queue(void); 63 | void destroy_message_queue(void); 64 | Uint32 connect_message_queue(void); 65 | Uint32 send_message(char cmd); 66 | Uint32 read_message(void); 67 | Uint32 event_dispatcher(void); 68 | Uint32 event_loop(void); 69 | Uint32 parse_input(int argc, char **argv); 70 | Uint32 sdl_init(void); 71 | void set_caption(char *array, Uint32 frame, Uint32 bytes); 72 | void set_zoom_rect(void); 73 | void histogram(void); 74 | Uint32 ten2eight(Uint8* src, Uint8* dst, Uint32 length); 75 | 76 | SDL_Surface *screen; 77 | SDL_Event event; 78 | SDL_Rect video_rect; 79 | SDL_Overlay *my_overlay; 80 | const SDL_VideoInfo* info = NULL; 81 | Uint32 FORMAT = YV12; 82 | FILE* fd; 83 | 84 | struct my_msgbuf { 85 | long mtype; 86 | char mtext[2]; 87 | }; 88 | 89 | struct param { 90 | Uint32 width; /* frame width - in pixels */ 91 | Uint32 height; /* frame height - in pixels */ 92 | Uint32 wh; /* width x height */ 93 | Uint32 frame_size; /* size of 1 frame - in bytes */ 94 | Sint32 zoom; /* zoom-factor */ 95 | Uint32 zoom_width; 96 | Uint32 zoom_height; 97 | Uint32 grid; /* grid-mode - on or off */ 98 | Uint32 hist; /* histogram-mode - on or off */ 99 | Uint32 grid_start_pos; 100 | Uint32 diff; /* diff-mode */ 101 | Uint32 y_start_pos; /* start pos for first Y pel */ 102 | Uint32 cb_start_pos; /* start pos for first Cb pel */ 103 | Uint32 cr_start_pos; /* start pos for first Cr pel */ 104 | Uint32 y_only; /* Grayscale, i.e Luma only */ 105 | Uint32 cb_only; /* Only Cb plane */ 106 | Uint32 cr_only; /* Only Cr plane */ 107 | Uint32 mb; /* macroblock-mode - on or off */ 108 | Uint32 y_size; /* sizeof luma-data for 1 frame - in bytes */ 109 | Uint32 cb_size; /* sizeof croma-data for 1 frame - in bytes */ 110 | Uint32 cr_size; /* sizeof croma-data for 1 frame - in bytes */ 111 | Uint8* raw; /* pointer towards complete frame - frame_size bytes */ 112 | Uint8* y_data; /* pointer towards luma-data */ 113 | Uint8* cb_data; /* pointer towards croma-data */ 114 | Uint8* cr_data; /* pointer towards croma-data */ 115 | char* filename; /* obvious */ 116 | char* fname_diff; /* see above */ 117 | Uint32 overlay_format; /* YV12, IYUV, YUY2, UYVY or YVYU - SDL */ 118 | Uint32 vflags; /* HW support or SW support */ 119 | Uint8 bpp; /* bits per pixel */ 120 | Uint32 mode; /* MASTER, SLAVE or NONE - defaults to NONE */ 121 | struct my_msgbuf buf; 122 | int msqid; 123 | key_t key; 124 | FILE* fd2; /* diff file */ 125 | }; 126 | 127 | /* Global parameter struct */ 128 | struct param P; 129 | 130 | Uint32 rd(Uint8* data, Uint32 size) 131 | { 132 | Uint32 cnt; 133 | 134 | cnt = fread(data, sizeof(Uint8), size, fd); 135 | if (cnt < size) { 136 | fprintf(stderr, "No more data to read!\n"); 137 | return 0; 138 | } 139 | return 1; 140 | } 141 | 142 | Uint32 read_yv12(void) 143 | { 144 | if (!rd(P.y_data, P.y_size)) return 0; 145 | if (!rd(P.cb_data, P.cb_size)) return 0; 146 | if (!rd(P.cr_data, P.cr_size)) return 0; 147 | 148 | return 1; 149 | } 150 | 151 | Uint32 read_iyuv(void) 152 | { 153 | if (!rd(P.y_data, P.y_size)) return 0; 154 | if (!rd(P.cb_data, P.cb_size)) return 0; 155 | if (!rd(P.cr_data, P.cr_size)) return 0; 156 | 157 | return 1; 158 | } 159 | 160 | Uint32 read_422(void) 161 | { 162 | Uint8* y = P.y_data; 163 | Uint8* cb = P.cb_data; 164 | Uint8* cr = P.cr_data; 165 | 166 | if (!rd(P.raw, P.frame_size)) return 0; 167 | 168 | for (Uint32 i = P.y_start_pos; i < P.frame_size; i += 2) *y++ = P.raw[i]; 169 | for (Uint32 i = P.cb_start_pos; i < P.frame_size; i += 4) *cb++ = P.raw[i]; 170 | for (Uint32 i = P.cr_start_pos; i < P.frame_size; i += 4) *cr++ = P.raw[i]; 171 | return 1; 172 | } 173 | 174 | Uint32 read_y42210(void) 175 | { 176 | Uint32 ret = 1; 177 | Uint8* data; 178 | Uint8* tmp; 179 | 180 | data = malloc(sizeof(Uint8) * P.frame_size * 2); 181 | if (!data) { 182 | fprintf(stderr, "Error allocating memory...\n"); 183 | return 0; 184 | } 185 | 186 | tmp = malloc(sizeof(Uint8) * P.frame_size); 187 | if (!tmp) { 188 | fprintf(stderr, "Error allocating memory...\n"); 189 | ret = 0; 190 | goto cleany42210; 191 | } 192 | 193 | if (!rd(data, P.frame_size * 2)) { 194 | ret = 0; 195 | goto cleany42210; 196 | } 197 | ten2eight(data, tmp, P.frame_size * 2); 198 | 199 | /* Y */ 200 | for (Uint32 i = 0, j = 0; i < P.frame_size; i += 2) { 201 | P.raw[i] = tmp[j]; 202 | j++; 203 | } 204 | /* Cb */ 205 | for (Uint32 i = P.cb_start_pos, j = 0 ; i < P.frame_size; i += 4) { 206 | P.raw[i] = tmp[P.wh + j]; 207 | j++; 208 | } 209 | /* Cr */ 210 | for (Uint32 i = P.cr_start_pos, j = 0; i < P.frame_size; i += 4) { 211 | P.raw[i] = tmp[P.wh/2*3 + j]; 212 | j++; 213 | } 214 | 215 | cleany42210: 216 | free(tmp); 217 | free(data); 218 | 219 | return ret; 220 | } 221 | 222 | Uint32 read_yv1210(void) 223 | { 224 | Uint32 ret = 1; 225 | Uint8* data; 226 | 227 | data = malloc(sizeof(Uint8) * P.y_size * 2); 228 | if (!data) { 229 | fprintf(stderr, "Error allocating memory...\n"); 230 | return 0; 231 | } 232 | 233 | if (!rd(data, P.y_size * 2)) { 234 | ret = 0; 235 | goto cleanyv1210; 236 | } 237 | ten2eight(data, P.y_data, P.y_size * 2); 238 | 239 | if (!rd(data, P.cb_size * 2)) { 240 | ret = 0; 241 | goto cleanyv1210; 242 | } 243 | ten2eight(data, P.cb_data, P.cb_size * 2); 244 | 245 | if (!rd(data, P.cr_size * 2)) { 246 | ret = 0; 247 | goto cleanyv1210; 248 | } 249 | ten2eight(data, P.cr_data, P.cr_size * 2); 250 | 251 | cleanyv1210: 252 | free(data); 253 | 254 | return ret; 255 | } 256 | 257 | Uint32 ten2eight(Uint8* src, Uint8* dst, Uint32 length) 258 | { 259 | Uint16 x = 0; 260 | 261 | for (Uint32 i = 0; i < length; i += 2) { 262 | x = (src[i+1] << 8) | src[i]; 263 | x = (x + 2) >> 2; 264 | if (x > 255) { 265 | x = 255; 266 | } 267 | *dst++ = x; 268 | } 269 | 270 | return 1; 271 | } 272 | 273 | Uint32 allocate_memory(void) 274 | { 275 | P.raw = malloc(sizeof(Uint8) * P.frame_size); 276 | P.y_data = malloc(sizeof(Uint8) * P.y_size); 277 | P.cb_data = malloc(sizeof(Uint8) * P.cb_size); 278 | P.cr_data = malloc(sizeof(Uint8) * P.cr_size); 279 | 280 | if (!P.raw || !P.y_data || !P.cb_data || !P.cr_data) { 281 | fprintf(stderr, "Error allocating memory...\n"); 282 | return 0; 283 | } 284 | return 1; 285 | } 286 | 287 | void draw_grid422(void) 288 | { 289 | if (!P.grid) { 290 | return; 291 | } 292 | 293 | /* horizontal grid lines */ 294 | for (Uint32 y = 0; y < P.height; y += 16) { 295 | for (Uint32 x = P.grid_start_pos; x < P.width * 2; x += 16) { 296 | *(my_overlay->pixels[0] + y * my_overlay->pitches[0] + x) = 0xF0; 297 | *(my_overlay->pixels[0] + y * my_overlay->pitches[0] + x + 8) = 0x20; 298 | } 299 | } 300 | /* vertical grid lines */ 301 | for (Uint32 x = P.grid_start_pos; x < P.width * 2; x += 32) { 302 | for (Uint32 y = 0; y < P.height; y += 8) { 303 | *(my_overlay->pixels[0] + y * my_overlay->pitches[0] + x) = 0xF0; 304 | *(my_overlay->pixels[0] + (y + 4) * my_overlay->pitches[0] + x) = 0x20; 305 | } 306 | } 307 | } 308 | 309 | void draw_grid420(void) 310 | { 311 | if (!P.grid) { 312 | return; 313 | } 314 | 315 | /* horizontal grid lines */ 316 | for (Uint32 y = 0; y < P.height; y += 16) { 317 | for (Uint32 x = 0; x < P.width; x += 8) { 318 | *(my_overlay->pixels[0] + y * my_overlay->pitches[0] + x) = 0xF0; 319 | *(my_overlay->pixels[0] + y * my_overlay->pitches[0] + x + 4) = 0x20; 320 | } 321 | } 322 | /* vertical grid lines */ 323 | for (Uint32 x = 0; x < P.width; x += 16) { 324 | for (Uint32 y = 0; y < P.height; y += 8) { 325 | *(my_overlay->pixels[0] + y * my_overlay->pitches[0] + x) = 0xF0; 326 | *(my_overlay->pixels[0] + (y + 4) * my_overlay->pitches[0] + x) = 0x20; 327 | } 328 | } 329 | } 330 | 331 | void luma_only(void) 332 | { 333 | if (!P.y_only) { 334 | return; 335 | } 336 | 337 | if (FORMAT == YV12 || FORMAT == IYUV || FORMAT == YV1210) { 338 | /* Set croma part to 0x80 */ 339 | for (Uint32 i = 0; i < P.cr_size; i++) my_overlay->pixels[1][i] = 0x80; 340 | for (Uint32 i = 0; i < P.cb_size; i++) my_overlay->pixels[2][i] = 0x80; 341 | return; 342 | } 343 | 344 | /* YUY2, UYVY, YVYU */ 345 | for (Uint32 i = P.cb_start_pos; i < P.frame_size; i += 4) { 346 | *(my_overlay->pixels[0] + i) = 0x80; 347 | } 348 | for (Uint32 i = P.cr_start_pos; i < P.frame_size; i += 4) { 349 | *(my_overlay->pixels[0] + i) = 0x80; 350 | } 351 | } 352 | 353 | void cb_only(void) 354 | { 355 | if (!P.cb_only) { 356 | return; 357 | } 358 | 359 | if (FORMAT == YV12 || FORMAT == IYUV || FORMAT == YV1210) { 360 | /* Set Luma part and Cr to 0x80 */ 361 | for (Uint32 i = 0; i < P.y_size; i++) my_overlay->pixels[0][i] = 0x80; 362 | for (Uint32 i = 0; i < P.cr_size; i++) my_overlay->pixels[1][i] = 0x80; 363 | return; 364 | } 365 | 366 | /* YUY2, UYVY, YVYU */ 367 | for (Uint32 i = P.y_start_pos; i < P.frame_size; i += 2) { 368 | *(my_overlay->pixels[0] + i) = 0x80; 369 | } 370 | for (Uint32 i = P.cr_start_pos; i < P.frame_size; i += 4) { 371 | *(my_overlay->pixels[0] + i) = 0x80; 372 | } 373 | } 374 | 375 | void cr_only(void) 376 | { 377 | if (!P.cr_only) { 378 | return; 379 | } 380 | 381 | if (FORMAT == YV12 || FORMAT == IYUV || FORMAT == YV1210) { 382 | /* Set Luma part and Cb to 0x80 */ 383 | for (Uint32 i = 0; i < P.y_size; i++) my_overlay->pixels[0][i] = 0x80; 384 | for (Uint32 i = 0; i < P.cb_size; i++) my_overlay->pixels[2][i] = 0x80; 385 | return; 386 | } 387 | 388 | /* YUY2, UYVY, YVYU */ 389 | for (Uint32 i = P.y_start_pos; i < P.frame_size; i += 2) { 390 | *(my_overlay->pixels[0] + i) = 0x80; 391 | } 392 | for (Uint32 i = P.cb_start_pos; i < P.frame_size; i += 4) { 393 | *(my_overlay->pixels[0] + i) = 0x80; 394 | } 395 | } 396 | 397 | void draw_420(void) 398 | { 399 | memcpy(my_overlay->pixels[0], P.y_data, P.y_size); 400 | memcpy(my_overlay->pixels[1], P.cr_data, P.cr_size); 401 | memcpy(my_overlay->pixels[2], P.cb_data, P.cb_size); 402 | draw_grid420(); 403 | luma_only(); 404 | cb_only(); 405 | cr_only(); 406 | histogram(); 407 | } 408 | 409 | void draw_422(void) 410 | { 411 | memcpy(my_overlay->pixels[0], P.raw, P.frame_size); 412 | draw_grid422(); 413 | luma_only(); 414 | cb_only(); 415 | cr_only(); 416 | histogram(); 417 | } 418 | 419 | void usage(char* name) 420 | { 421 | fprintf(stderr, "Usage:\n"); 422 | fprintf(stderr, "%s filename width height format [diff_filename]\n", name); 423 | } 424 | 425 | void mb_loop(char* str, Uint32 rows, Uint8* data, Uint32 pitch) 426 | { 427 | printf("%s\n", str); 428 | for (Uint32 i = 0; i < rows;i++) { 429 | for (Uint32 j = 0; j < 16; j++) { 430 | printf("%02X ", data[pitch+i]); 431 | } 432 | printf("\n"); 433 | } 434 | } 435 | 436 | void show_mb(Uint32 mouse_x, Uint32 mouse_y) 437 | { 438 | /* TODO: bad code */ 439 | Uint32 MB; 440 | Uint32 pitch[5] = {64, 64, 128, 128, 128}; 441 | Uint32 length[5] = {4, 4, 8, 8, 8}; 442 | 443 | Uint16 y_pitch, cb_pitch, cr_pitch; 444 | 445 | if (!P.mb) { 446 | return; 447 | } 448 | 449 | /* which MB are we in? */ 450 | MB = mouse_x / (16 * P.zoom) + 451 | (P.width / 16) * 452 | (mouse_y / (16 * P.zoom)); 453 | 454 | y_pitch = 16 * MB; 455 | cb_pitch = pitch[FORMAT] * MB; 456 | cr_pitch = pitch[FORMAT] * MB; 457 | printf("\nMB #%d\n", MB); 458 | 459 | mb_loop("= Y =", 16, P.y_data, y_pitch); 460 | mb_loop("= Cb =", length[FORMAT], P.cb_data, cb_pitch); 461 | mb_loop("= Cr =", length[FORMAT], P.cr_data, cr_pitch); 462 | 463 | printf("\n"); 464 | fflush(stdout); 465 | } 466 | 467 | Uint32 (*reader[])(void) = {read_yv12, read_iyuv, read_422, read_422, read_422, read_yv1210, read_y42210}; 468 | void (*drawer[])(void) = {draw_420, draw_420, draw_422, draw_422, draw_422, draw_420, draw_422}; 469 | 470 | void draw_frame(void) 471 | { 472 | SDL_LockYUVOverlay(my_overlay); 473 | (*drawer[FORMAT])(); 474 | set_zoom_rect(); 475 | video_rect.x = 0; 476 | video_rect.y = 0; 477 | video_rect.w = P.zoom_width; 478 | video_rect.h = P.zoom_height; 479 | SDL_UnlockYUVOverlay(my_overlay); 480 | SDL_DisplayYUVOverlay(my_overlay, &video_rect); 481 | } 482 | 483 | Uint32 read_frame(void) 484 | { 485 | if (!P.diff) { 486 | return (*reader[FORMAT])(); 487 | } else { 488 | return diff_mode(); 489 | } 490 | } 491 | 492 | Uint32 diff_mode(void) 493 | { 494 | FILE* fd_tmp; 495 | Uint8* y_tmp; 496 | 497 | /* Perhaps a bit ugly but it seams to work... 498 | * 1. read frame from fd 499 | * 2. store data away 500 | * 3. read frame from P.fd2 501 | * 4. calculate diff 502 | * 5. place result in P.raw or P.y_data depending on FORMAT 503 | * 6. diff works on luma data so clear P.cb_data and P.cr_data 504 | * Fiddle with the file descriptors so that we read 505 | * from correct file. 506 | */ 507 | 508 | if (!(*reader[FORMAT])()) { 509 | return 0; 510 | } 511 | 512 | y_tmp = malloc(sizeof(Uint8) * P.y_size); 513 | 514 | if (!y_tmp) { 515 | fprintf(stderr, "Error allocating memory...\n"); 516 | return 0; 517 | } 518 | 519 | for (Uint32 i = 0; i < P.y_size; i++) { 520 | y_tmp[i] = P.y_data[i]; 521 | } 522 | 523 | fd_tmp = fd; 524 | fd = P.fd2; 525 | 526 | if (!(*reader[FORMAT])()) { 527 | free(y_tmp); 528 | fd = fd_tmp; 529 | return 0; 530 | } 531 | 532 | /* restore file descriptor */ 533 | fd = fd_tmp; 534 | 535 | /* now, P.y_data contains luminance data for fd2 and 536 | * y_tmp contains luma data for fd. 537 | * Calculate diff and place result where it belongs 538 | * Clear croma data */ 539 | 540 | calc_psnr(y_tmp, P.y_data); 541 | 542 | if (FORMAT == YV12 || FORMAT == IYUV) { 543 | for (Uint32 i = 0; i < P.y_size; i++) { 544 | P.y_data[i] = 0x80 - (y_tmp[i] - P.y_data[i]); 545 | } 546 | for (Uint32 i = 0; i < P.cb_size; i++) P.cb_data[i] = 0x80; 547 | for (Uint32 i = 0; i < P.cr_size; i++) P.cr_data[i] = 0x80; 548 | } else { 549 | Uint32 j = 0; 550 | for (Uint32 i = P.y_start_pos; i < P.frame_size; i += 2) { 551 | P.raw[i] = 0x80 - (y_tmp[j] - P.y_data[j]); 552 | j++; 553 | } 554 | for (Uint32 i = P.cb_start_pos; i < P.frame_size; i += 4) P.raw[i] = 0x80; 555 | for (Uint32 i = P.cr_start_pos; i < P.frame_size; i += 4) P.raw[i] = 0x80; 556 | } 557 | 558 | free(y_tmp); 559 | 560 | return 1; 561 | } 562 | 563 | void calc_psnr(Uint8* frame0, Uint8* frame1) 564 | { 565 | double mse = 0.0; 566 | double mse_tmp = 0.0; 567 | double psnr = 0.0; 568 | 569 | for (Uint32 i = 0; i < P.y_size; i++) { 570 | mse_tmp = abs(frame0[i] - frame1[i]); 571 | mse += mse_tmp * mse_tmp; 572 | } 573 | 574 | /* division by zero */ 575 | if (mse == 0) { 576 | fprintf(stdout, "PSNR: NaN\n"); 577 | return; 578 | } 579 | 580 | mse /= P.y_size; 581 | 582 | psnr = 10.0*log10((256 * 256) / mse); 583 | 584 | fprintf(stdout, "PSNR: %f\n", psnr); 585 | } 586 | 587 | void histogram(void) 588 | { 589 | if (!P.hist) { 590 | return; 591 | } 592 | 593 | Uint8 y[256] = {0}; 594 | Uint8 b[256] = {0}; 595 | Uint8 r[256] = {0}; 596 | 597 | for (Uint32 i = 0; i < P.y_size; i++) y[P.y_data[i]]++; 598 | for (Uint32 i = 0; i < P.cb_size; i++) b[P.cb_data[i]]++; 599 | for (Uint32 i = 0; i < P.cr_size; i++) r[P.cr_data[i]]++; 600 | 601 | fprintf(stdout, "\nY,"); 602 | for (Uint32 i = 0; i < 256; i++) fprintf(stdout, "%u,", y[i]); 603 | fprintf(stdout, "\nCb,"); 604 | for (Uint32 i = 0; i < 256; i++) fprintf(stdout, "%u,", b[i]); 605 | fprintf(stdout, "\nCr,"); 606 | for (Uint32 i = 0; i < 256; i++) fprintf(stdout, "%u,", r[i]); 607 | fprintf(stdout, "\n"); 608 | fflush(stdout); 609 | } 610 | 611 | void setup_param(void) 612 | { 613 | P.zoom = 1; 614 | P.wh = P.width * P.height; 615 | 616 | if (FORMAT == YV12 || FORMAT == IYUV) { 617 | P.frame_size = P.wh * 3 / 2; 618 | P.y_size = P.wh; 619 | P.cb_size = P.wh / 4; 620 | P.cr_size = P.wh / 4; 621 | } else if (FORMAT == YUY2 || FORMAT == UYVY || FORMAT == YVYU || FORMAT == Y42210) { 622 | P.grid_start_pos = 0; 623 | P.frame_size = P.wh * 2; 624 | P.y_size = P.wh; 625 | P.cb_size = P.wh / 2; 626 | P.cr_size = P.wh / 2; 627 | } else if (FORMAT == YV1210) { 628 | /* SDL cannot natively display this format; let's fake it 629 | * and pretend it's YV12 */ 630 | P.frame_size = P.wh * 3 / 2; 631 | P.y_size = P.wh; 632 | P.cb_size = P.wh / 4; 633 | P.cr_size = P.wh / 4; 634 | } 635 | 636 | if (FORMAT == YUY2) { 637 | /* Y U Y V 638 | * 0 1 2 3 */ 639 | P.y_start_pos = 0; 640 | P.cb_start_pos = 1; 641 | P.cr_start_pos = 3; 642 | } else if (FORMAT == UYVY) { 643 | /* U Y V Y 644 | * 0 1 2 3 */ 645 | P.y_start_pos = 1; 646 | P.grid_start_pos = 1; 647 | P.cb_start_pos = 0; 648 | P.cr_start_pos = 2; 649 | } else if (FORMAT == YVYU || FORMAT == Y42210) { 650 | /* Y V Y U 651 | * 0 1 2 3 */ 652 | P.y_start_pos = 0; 653 | P.cb_start_pos = 3; 654 | P.cr_start_pos = 1; 655 | } 656 | } 657 | 658 | void check_input(void) 659 | { 660 | Uint32 file_size; 661 | 662 | /* Frame Size is an even multipe of 16x16? */ 663 | if (P.width % 16 != 0) { 664 | fprintf(stderr, "WIDTH not multiple of 16, check input...\n"); 665 | } 666 | if (P.height % 16 != 0) { 667 | fprintf(stderr, "HEIGHT not multiple of 16, check input...\n"); 668 | } 669 | 670 | /* Even number of frames? */ 671 | fseek(fd, 0L, SEEK_END); 672 | file_size = ftell(fd); 673 | fseek(fd, 0L, SEEK_SET); 674 | 675 | if (file_size % P.frame_size != 0) { 676 | fprintf(stderr, "#FRAMES not an integer, check input...\n"); 677 | } 678 | } 679 | 680 | Uint32 create_message_queue(void) 681 | { 682 | /* Should probably use argv[0] or similar as pathname 683 | * when creating the key; 684 | * but let's keep it simple for now. Y for YCbCr 685 | */ 686 | if ((P.key = ftok("/tmp", 'Y')) == -1) { 687 | perror("ftok"); 688 | return 0; 689 | } 690 | 691 | if ((P.msqid = msgget(P.key, 0644 | IPC_CREAT)) == -1) { 692 | perror("msgget"); 693 | return 0; 694 | } 695 | 696 | /* we don't really care in this case */ 697 | P.buf.mtype = 1; 698 | return 1; 699 | } 700 | 701 | Uint32 connect_message_queue(void) 702 | { 703 | if ((P.key = ftok("/tmp", 'Y')) == -1) { 704 | perror("ftok"); 705 | return 0; 706 | } 707 | 708 | /* connect to the queue */ 709 | if ((P.msqid = msgget(P.key, 0644)) == -1) { 710 | perror("msgget"); 711 | return 0; 712 | } 713 | 714 | printf("Ready to receive messages, captain.\n"); 715 | return 1; 716 | } 717 | 718 | Uint32 send_message(char cmd) 719 | { 720 | if (P.mode != MASTER) { 721 | return 1; 722 | } 723 | 724 | P.buf.mtext[0] = cmd; 725 | P.buf.mtext[1] = '\0'; 726 | 727 | if (msgsnd(P.msqid, &P.buf, 2, 0) == -1) { 728 | perror("msgsnd"); 729 | return 0; 730 | } 731 | 732 | return 1; 733 | } 734 | 735 | Uint32 read_message(void) 736 | { 737 | if (msgrcv(P.msqid, &P.buf, sizeof(P.buf.mtext), 0, 0) == -1) { 738 | perror("msgrcv"); 739 | P.mode = NONE; 740 | return 0; 741 | } 742 | 743 | return 1; 744 | } 745 | 746 | void destroy_message_queue(void) 747 | { 748 | if (P.mode == MASTER) { 749 | if (msgctl(P.msqid, IPC_RMID, NULL) == -1) { 750 | perror("msgctl"); 751 | } 752 | printf("queue destroyed\n"); 753 | } 754 | } 755 | 756 | Uint32 event_dispatcher(void) 757 | { 758 | if (read_message()) { 759 | 760 | switch (P.buf.mtext[0]) 761 | { 762 | case NEXT: 763 | event.type = SDL_KEYDOWN; 764 | event.key.keysym.sym = SDLK_RIGHT; 765 | SDL_PushEvent(&event); 766 | break; 767 | 768 | case PREV: 769 | event.type = SDL_KEYDOWN; 770 | event.key.keysym.sym = SDLK_LEFT; 771 | SDL_PushEvent(&event); 772 | break; 773 | 774 | case REW: 775 | event.type = SDL_KEYDOWN; 776 | event.key.keysym.sym = SDLK_r; 777 | SDL_PushEvent(&event); 778 | break; 779 | 780 | case ZOOM_IN: 781 | event.type = SDL_KEYDOWN; 782 | event.key.keysym.sym = SDLK_UP; 783 | SDL_PushEvent(&event); 784 | break; 785 | 786 | case ZOOM_OUT: 787 | event.type = SDL_KEYDOWN; 788 | event.key.keysym.sym = SDLK_DOWN; 789 | SDL_PushEvent(&event); 790 | break; 791 | 792 | case QUIT: 793 | event.type = SDL_KEYDOWN; 794 | event.key.keysym.sym = SDLK_q; 795 | SDL_PushEvent(&event); 796 | break; 797 | 798 | case Y_ONLY: 799 | event.type = SDL_KEYDOWN; 800 | event.key.keysym.sym = SDLK_F5; 801 | SDL_PushEvent(&event); 802 | break; 803 | 804 | case CB_ONLY: 805 | event.type = SDL_KEYDOWN; 806 | event.key.keysym.sym = SDLK_F6; 807 | SDL_PushEvent(&event); 808 | break; 809 | 810 | case CR_ONLY: 811 | event.type = SDL_KEYDOWN; 812 | event.key.keysym.sym = SDLK_F7; 813 | SDL_PushEvent(&event); 814 | break; 815 | 816 | case ALL_PLANES: 817 | event.type = SDL_KEYDOWN; 818 | event.key.keysym.sym = SDLK_F8; 819 | SDL_PushEvent(&event); 820 | break; 821 | 822 | default: 823 | fprintf(stderr, "~TILT\n"); 824 | return 0; 825 | } 826 | } 827 | return 1; 828 | } 829 | 830 | void set_caption(char *array, Uint32 frame, Uint32 bytes) 831 | { 832 | snprintf(array, bytes, "%s - %s%s%s%s%s%s%s%s frame %d, size %dx%d", 833 | P.filename, 834 | (P.mode == MASTER) ? "[MASTER]" : 835 | (P.mode == SLAVE) ? "[SLAVE]": "", 836 | P.grid ? "G" : "", 837 | P.mb ? "M" : "", 838 | P.diff ? "D" : "", 839 | P.hist ? "H" : "", 840 | P.y_only ? "Y" : "", 841 | P.cb_only ? "Cb" : "", 842 | P.cr_only ? "Cr" : "", 843 | frame, 844 | P.zoom_width, 845 | P.zoom_height); 846 | } 847 | 848 | void set_zoom_rect(void) 849 | { 850 | if (P.zoom > 0) { 851 | P.zoom_width = P.width * P.zoom; 852 | P.zoom_height = P.height * P.zoom; 853 | } else if (P.zoom <= 0) { 854 | P.zoom_width = P.width / (abs(P.zoom) + 2); 855 | P.zoom_height = P.height / (abs(P.zoom) + 2); 856 | } else { 857 | fprintf(stderr, "ERROR in zoom:\n"); 858 | } 859 | } 860 | 861 | /* loop inspired by yay 862 | * http://freecode.com/projects/yay 863 | */ 864 | Uint32 event_loop(void) 865 | { 866 | char caption[256]; 867 | Uint16 quit = 0; 868 | Uint32 frame = 0; 869 | int play_yuv = 0; 870 | unsigned int start_ticks = 0; 871 | 872 | while (!quit) { 873 | 874 | set_caption(caption, frame, 256); 875 | SDL_WM_SetCaption(caption, NULL); 876 | 877 | /* wait for SDL event */ 878 | if (P.mode == NONE || P.mode == MASTER) { 879 | SDL_WaitEvent(&event); 880 | } else if (P.mode == SLAVE) { 881 | if (!event_dispatcher()) { 882 | SDL_WaitEvent(&event); 883 | } 884 | } 885 | 886 | switch (event.type) 887 | { 888 | case SDL_KEYDOWN: 889 | switch (event.key.keysym.sym) 890 | { 891 | case SDLK_SPACE: 892 | play_yuv = 1; /* play it, sam! */ 893 | while (play_yuv) { 894 | start_ticks = SDL_GetTicks(); 895 | set_caption(caption, frame, 256); 896 | SDL_WM_SetCaption( caption, NULL ); 897 | 898 | /* check for next frame existing */ 899 | if (read_frame()) { 900 | draw_frame(); 901 | /* insert delay for real time viewing */ 902 | if (SDL_GetTicks() - start_ticks < 40) 903 | SDL_Delay(40 - (SDL_GetTicks() - start_ticks)); 904 | frame++; 905 | send_message(NEXT); 906 | } else { 907 | play_yuv = 0; 908 | } 909 | /* check for any key event */ 910 | if (SDL_PollEvent(&event)) { 911 | if (event.type == SDL_KEYDOWN) { 912 | /* stop playing */ 913 | play_yuv = 0; 914 | } 915 | } 916 | } 917 | break; 918 | case SDLK_RIGHT: /* next frame */ 919 | /* check for next frame existing */ 920 | if (read_frame()) { 921 | draw_frame(); 922 | frame++; 923 | send_message(NEXT); 924 | } 925 | break; 926 | case SDLK_LEFT: /* previous frame */ 927 | if (frame > 1) { 928 | frame--; 929 | fseek(fd, ((frame-1) * P.frame_size), SEEK_SET); 930 | if (P.diff) { 931 | fseek(P.fd2, ((frame-1) * P.frame_size), SEEK_SET); 932 | } 933 | read_frame(); 934 | draw_frame(); 935 | send_message(PREV); 936 | } 937 | break; 938 | case SDLK_UP: /* zoom in */ 939 | P.zoom++; 940 | set_zoom_rect(); 941 | screen = SDL_SetVideoMode(P.zoom_width, 942 | P.zoom_height, 943 | P.bpp, P.vflags); 944 | video_rect.w = P.zoom_width; 945 | video_rect.h = P.zoom_height; 946 | SDL_DisplayYUVOverlay(my_overlay, &video_rect); 947 | send_message(ZOOM_IN); 948 | break; 949 | case SDLK_DOWN: /* zoom out */ 950 | P.zoom--; 951 | set_zoom_rect(); 952 | screen = SDL_SetVideoMode(P.zoom_width, 953 | P.zoom_height, 954 | P.bpp, P.vflags); 955 | video_rect.w = P.zoom_width; 956 | video_rect.h = P.zoom_height; 957 | SDL_DisplayYUVOverlay(my_overlay, &video_rect); 958 | send_message(ZOOM_OUT); 959 | break; 960 | case SDLK_r: /* rewind */ 961 | if (frame > 1) { 962 | frame = 1; 963 | fseek(fd, 0, SEEK_SET); 964 | if (P.diff) { 965 | fseek(P.fd2, 0, SEEK_SET); 966 | } 967 | read_frame(); 968 | draw_frame(); 969 | send_message(REW); 970 | } 971 | break; 972 | case SDLK_g: /* display grid */ 973 | P.grid = ~P.grid; 974 | if (P.zoom < 1) 975 | P.grid = 0; 976 | draw_frame(); 977 | break; 978 | case SDLK_m: /* show mb-data on stdout */ 979 | P.mb = ~P.mb; 980 | if (P.zoom < 1) 981 | P.mb = 0; 982 | draw_frame(); 983 | break; 984 | case SDLK_F5: /* Luma data only */ 985 | P.y_only = ~P.y_only; 986 | P.cb_only = 0; 987 | P.cr_only = 0; 988 | draw_frame(); 989 | send_message(Y_ONLY); 990 | break; 991 | case SDLK_F6: /* Cb data only */ 992 | P.cb_only = ~P.cb_only; 993 | P.y_only = 0; 994 | P.cr_only = 0; 995 | draw_frame(); 996 | send_message(CB_ONLY); 997 | break; 998 | case SDLK_F7: /* Cr data only */ 999 | P.cr_only = ~P.cr_only; 1000 | P.y_only = 0; 1001 | P.cb_only = 0; 1002 | send_message(CR_ONLY); 1003 | draw_frame(); 1004 | break; 1005 | case SDLK_F8: /* display all color planes */ 1006 | P.y_only = 0; 1007 | P.cb_only = 0; 1008 | P.cr_only = 0; 1009 | draw_frame(); 1010 | send_message(ALL_PLANES); 1011 | break; 1012 | case SDLK_h: /* histogram */ 1013 | P.hist = ~P.hist; 1014 | draw_frame(); 1015 | break; 1016 | case SDLK_F1: /* MASTER-mode */ 1017 | if (create_message_queue()) { 1018 | P.mode = MASTER; 1019 | } 1020 | break; 1021 | case SDLK_F2: /* SLAVE-mode */ 1022 | if (P.mode == MASTER) { 1023 | destroy_message_queue(); 1024 | } 1025 | if (connect_message_queue()) { 1026 | P.mode = SLAVE; 1027 | } 1028 | break; 1029 | case SDLK_F3: /* NONE-mode */ 1030 | destroy_message_queue(); 1031 | P.mode = NONE; 1032 | break; 1033 | case SDLK_q: /* quit */ 1034 | quit = 1; 1035 | send_message(QUIT); 1036 | break; 1037 | default: 1038 | break; 1039 | } /* switch key */ 1040 | break; 1041 | case SDL_QUIT: 1042 | quit = 1; 1043 | break; 1044 | case SDL_VIDEOEXPOSE: 1045 | SDL_DisplayYUVOverlay(my_overlay, &video_rect); 1046 | break; 1047 | case SDL_MOUSEBUTTONDOWN: 1048 | /* If the left mouse button was pressed */ 1049 | if (event.button.button == SDL_BUTTON_LEFT ) { 1050 | show_mb(event.button.x, event.button.y); 1051 | } 1052 | break; 1053 | 1054 | default: 1055 | break; 1056 | 1057 | } /* switch event type */ 1058 | 1059 | } /* while */ 1060 | 1061 | return quit; 1062 | } 1063 | 1064 | Uint32 parse_input(int argc, char **argv) 1065 | { 1066 | if (argc != 5 && argc != 6) { 1067 | usage(argv[0]); 1068 | return 0; 1069 | } 1070 | 1071 | if (argc == 6) { 1072 | /* diff mode */ 1073 | P.diff = 1; 1074 | P.fname_diff = argv[5]; 1075 | } 1076 | 1077 | P.filename = argv[1]; 1078 | 1079 | P.width = atoi(argv[2]); 1080 | P.height = atoi(argv[3]); 1081 | 1082 | if (!strncmp(argv[4], "YV1210", 6)) { 1083 | P.overlay_format = SDL_YV12_OVERLAY; 1084 | FORMAT = YV1210; 1085 | } else if (!strncmp(argv[4], "YV12", 4)) { 1086 | P.overlay_format = SDL_YV12_OVERLAY; 1087 | FORMAT = YV12; 1088 | } else if (!strncmp(argv[4], "IYUV", 4)) { 1089 | P.overlay_format = SDL_IYUV_OVERLAY; 1090 | FORMAT = IYUV; 1091 | } else if (!strncmp(argv[4], "YUY2", 4)) { 1092 | P.overlay_format = SDL_YUY2_OVERLAY; 1093 | FORMAT = YUY2; 1094 | } else if (!strncmp(argv[4], "UYVY", 4)) { 1095 | P.overlay_format = SDL_UYVY_OVERLAY; 1096 | FORMAT = UYVY; 1097 | } else if (!strncmp(argv[4], "YVYU", 4)) { 1098 | P.overlay_format = SDL_YVYU_OVERLAY; 1099 | FORMAT = YVYU; 1100 | } else if (!strncmp(argv[4], "Y42210", 6)) { 1101 | /* No support for 422, display it as YVYU */ 1102 | P.overlay_format = SDL_YVYU_OVERLAY; 1103 | FORMAT = Y42210; 1104 | } else { 1105 | fprintf(stderr, "The format option '%s' is not recognized\n", argv[4]); 1106 | return 0; 1107 | } 1108 | 1109 | return 1; 1110 | } 1111 | 1112 | Uint32 open_input(void) 1113 | { 1114 | fd = fopen(P.filename, "rb"); 1115 | if (fd == NULL) { 1116 | fprintf(stderr, "Error opening %s\n", P.filename); 1117 | return 0; 1118 | } 1119 | 1120 | if (P.diff) { 1121 | P.fd2 = fopen(P.fname_diff, "rb"); 1122 | if (P.fd2 == NULL) { 1123 | fprintf(stderr, "Error opening %s\n", P.fname_diff); 1124 | return 0; 1125 | } 1126 | } 1127 | return 1; 1128 | } 1129 | 1130 | Uint32 sdl_init(void) 1131 | { 1132 | /* SDL init */ 1133 | if (SDL_Init(SDL_INIT_VIDEO) < 0) { 1134 | fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError()); 1135 | atexit(SDL_Quit); 1136 | return 0; 1137 | } 1138 | 1139 | info = SDL_GetVideoInfo(); 1140 | if (!info) { 1141 | fprintf(stderr, "SDL ERROR Video query failed: %s\n", SDL_GetError()); 1142 | SDL_Quit(); 1143 | return 0; 1144 | } 1145 | 1146 | P.bpp = info->vfmt->BitsPerPixel; 1147 | 1148 | if (info->hw_available){ 1149 | P.vflags = SDL_HWSURFACE; 1150 | } else { 1151 | P.vflags = SDL_SWSURFACE; 1152 | } 1153 | 1154 | if ((screen = SDL_SetVideoMode(P.width, P.height, P.bpp, P.vflags)) == 0) { 1155 | fprintf(stderr, "SDL ERROR Video mode set failed: %s\n", SDL_GetError()); 1156 | SDL_Quit(); 1157 | return 0; 1158 | } 1159 | 1160 | my_overlay = SDL_CreateYUVOverlay(P.width, P.height, P.overlay_format, screen); 1161 | if (!my_overlay) { 1162 | fprintf(stderr, "Couldn't create overlay\n"); 1163 | return 0; 1164 | } 1165 | return 1; 1166 | } 1167 | 1168 | int main(int argc, char** argv) 1169 | { 1170 | int ret = EXIT_SUCCESS; 1171 | 1172 | /* Initialize param struct to zero */ 1173 | memset(&P, 0, sizeof(P)); 1174 | 1175 | if (!parse_input(argc, argv)) { 1176 | return EXIT_FAILURE; 1177 | } 1178 | 1179 | /* Initialize parameters corresponding to YUV-format */ 1180 | setup_param(); 1181 | 1182 | if (!sdl_init()) { 1183 | return EXIT_FAILURE; 1184 | } 1185 | 1186 | if (!open_input()) { 1187 | return EXIT_FAILURE; 1188 | } 1189 | 1190 | if (!allocate_memory()) { 1191 | ret = EXIT_FAILURE; 1192 | goto cleanup; 1193 | } 1194 | 1195 | /* Lets do some basic consistency check on input */ 1196 | check_input(); 1197 | 1198 | /* send event to display first frame */ 1199 | event.type = SDL_KEYDOWN; 1200 | event.key.keysym.sym = SDLK_RIGHT; 1201 | SDL_PushEvent(&event); 1202 | 1203 | /* while true */ 1204 | event_loop(); 1205 | 1206 | cleanup: 1207 | destroy_message_queue(); 1208 | SDL_FreeYUVOverlay(my_overlay); 1209 | free(P.raw); 1210 | free(P.y_data); 1211 | free(P.cb_data); 1212 | free(P.cr_data); 1213 | if (fd) { 1214 | fclose(fd); 1215 | } 1216 | if (P.fd2) { 1217 | fclose(P.fd2); 1218 | } 1219 | 1220 | return ret; 1221 | } 1222 | --------------------------------------------------------------------------------