From: Loren Merritt Date: Thu, 12 May 2005 23:03:49 +0000 (+0000) Subject: CLI option: --visualize X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=036494a60f7850c1613c5084fe9a11c7821cb5a7;p=libx264 CLI option: --visualize Displays the encoded video along with MB types and motion vectors. patch by Tuukka Toivonen. git-svn-id: svn://svn.videolan.org/x264/trunk@227 df754926-b1dd-0310-bc7b-ec298dee348c --- diff --git a/Makefile b/Makefile index 0895d3a3..cf867ae2 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,11 @@ SRCS = common/mc.c common/predict.c common/pixel.c common/macroblock.c \ encoder/set.c encoder/macroblock.c encoder/cabac.c \ encoder/cavlc.c encoder/encoder.c encoder/eval.c +# Visualization sources +ifeq ($(VIS),yes) +SRCS += common/visualize.c common/display-x11.c +endif + # MMX/SSE optims ifeq ($(ARCH),X86) SRCS += common/i386/mc-c.c common/i386/dct-c.c common/i386/predict.c diff --git a/common/common.h b/common/common.h index 75a15d09..cefdde77 100644 --- a/common/common.h +++ b/common/common.h @@ -455,6 +455,10 @@ struct x264_t x264_vlc_table_t *x264_total_zeros_lookup[15]; x264_vlc_table_t *x264_total_zeros_dc_lookup[3]; x264_vlc_table_t *x264_run_before_lookup[7]; + +#if VISUALIZE + struct visualize_t *visualize; +#endif }; #endif diff --git a/common/display-x11.c b/common/display-x11.c new file mode 100644 index 00000000..3607618b --- /dev/null +++ b/common/display-x11.c @@ -0,0 +1,231 @@ +/***************************************************************************** + * x264: h264 encoder + ***************************************************************************** + * Copyright (C) 2005 x264 project + * + * Author: Tuukka Toivonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "display.h" + +static long event_mask = ConfigureNotify|ExposureMask|KeyPressMask|ButtonPressMask|StructureNotifyMask|ResizeRedirectMask; + +static Display *disp_display = NULL; +static struct disp_window { + int init; + Window window; +} disp_window[10]; + +static inline void disp_chkerror(int cond, char *e) { + if (!cond) return; + fprintf(stderr, "error: %s\n", e ? e : "?"); + abort(); +} + +static void disp_init_display(void) { + Visual *visual; + int dpy_class; + int screen; + int dpy_depth; + + if (disp_display != NULL) return; + memset(&disp_window, 0, sizeof(disp_window)); + disp_display = XOpenDisplay(""); + disp_chkerror(!disp_display, "no display"); + screen = DefaultScreen(disp_display); + visual = DefaultVisual(disp_display, screen); + dpy_class = visual->class; + dpy_depth = DefaultDepth(disp_display, screen); + disp_chkerror(!((dpy_class == TrueColor && dpy_depth == 32) + || (dpy_class == TrueColor && dpy_depth == 24) + || (dpy_class == TrueColor && dpy_depth == 16) + || (dpy_class == PseudoColor && dpy_depth == 8)), + "requires 8 bit PseudoColor or 16/24/32 bit TrueColor display"); +} + +static void disp_init_window(int num, int width, int height, const unsigned char *tit) { + XSizeHints *shint; + XSetWindowAttributes xswa; + XEvent xev; + int screen = DefaultScreen(disp_display); + Visual *visual = DefaultVisual (disp_display, screen); + unsigned int fg, bg; + unsigned int mask; + char title[200]; + Window window; + int dpy_depth; + + if (tit) { + snprintf(title, 200, "%s: %i/disp", tit, num); + } else { + snprintf(title, 200, "%i/disp", num); + } + shint = XAllocSizeHints(); + disp_chkerror(!shint, "memerror"); + shint->min_width = shint->max_width = shint->width = width; + shint->min_height = shint->max_height = shint->height = height; + shint->flags = PSize | PMinSize | PMaxSize; + disp_chkerror(num<0 || num>=10, "bad win num"); + if (!disp_window[num].init) { + disp_window[num].init = 1; + bg = WhitePixel(disp_display, screen); + fg = BlackPixel(disp_display, screen); + dpy_depth = DefaultDepth(disp_display, screen); + if (dpy_depth==32 || dpy_depth==24 || dpy_depth==16) { + mask |= CWColormap; + xswa.colormap = XCreateColormap(disp_display, DefaultRootWindow(disp_display), visual, AllocNone); + } + xswa.background_pixel = bg; + xswa.border_pixel = fg; + xswa.backing_store = Always; + xswa.backing_planes = -1; + xswa.bit_gravity = NorthWestGravity; + mask = CWBackPixel | CWBorderPixel | CWBackingStore | CWBackingPlanes | CWBitGravity; + window = XCreateWindow(disp_display, DefaultRootWindow(disp_display), + shint->x, shint->y, shint->width, shint->height, + 1, dpy_depth, InputOutput, visual, mask, &xswa); + disp_window[num].window = window; + + XSelectInput(disp_display, window, event_mask); + XSetStandardProperties(disp_display, window, title, title, None, NULL, 0, shint); /* Tell other applications about this window */ + XMapWindow(disp_display, window); /* Map window. */ + do { /* Wait for map. */ + XNextEvent(disp_display, &xev); + } while (xev.type!=MapNotify || xev.xmap.event!=window); + //XSelectInput(disp_display, window, KeyPressMask); /* XSelectInput(display, window, NoEventMask);*/ + } + window = disp_window[num].window; + XSetStandardProperties(disp_display, window, title, title, None, NULL, 0, shint); /* Tell other applications about this window */ + XResizeWindow(disp_display, window, width, height); + XSync(disp_display, 1); + XFree(shint); +} + +void disp_sync(void) { + XSync(disp_display, 1); +} + +void disp_setcolor(unsigned char *name) { + int screen; + GC gc; + XColor c_exact, c_nearest; + Colormap cm; + Status st; + + screen = DefaultScreen(disp_display); + gc = DefaultGC(disp_display, screen); /* allocate colors */ + cm = DefaultColormap(disp_display, screen); + st = XAllocNamedColor(disp_display, cm, name, &c_nearest, &c_exact); + disp_chkerror(st!=1, "XAllocNamedColor error"); + XSetForeground(disp_display, gc, c_nearest.pixel); +} + +void disp_gray(int num, char *data, int width, int height, int stride, const unsigned char *tit) { + Visual *visual; + XImage *ximage; + unsigned char *image; + int y,x,pixelsize; + char dummy; + int t = 1; + int dpy_depth; + int screen; + GC gc; + //XEvent xev; + + disp_init_display(); + disp_init_window(num, width, height, tit); + screen = DefaultScreen(disp_display); + visual = DefaultVisual(disp_display, screen); + dpy_depth = DefaultDepth(disp_display, screen); + ximage = XCreateImage(disp_display, visual, dpy_depth, ZPixmap, 0, &dummy, width, height, 8, 0); + disp_chkerror(!ximage, "no ximage"); + if (*(char *)&t == 1) { + ximage->byte_order = LSBFirst; + ximage->bitmap_bit_order = LSBFirst; + } else { + ximage->byte_order = MSBFirst; + ximage->bitmap_bit_order = MSBFirst; + } + pixelsize = dpy_depth>8 ? sizeof(int) : sizeof(unsigned char); + image = malloc(width * height * pixelsize); + disp_chkerror(!image, "malloc failed"); + for (y=0; ydata = image; + gc = DefaultGC(disp_display, screen); /* allocate colors */ + +// XUnmapWindow(disp_display, disp_window[num].window); /* Map window. */ +// XMapWindow(disp_display, disp_window[num].window); /* Map window. */ + XPutImage(disp_display, disp_window[num].window, gc, ximage, 0, 0, 0, 0, width, height); +// do { /* Wait for map. */ +// XNextEvent(disp_display, &xev); +// } while (xev.type!=MapNotify || xev.xmap.event!=disp_window[num].window); + XPutImage(disp_display, disp_window[num].window, gc, ximage, 0, 0, 0, 0, width, height); + + XDestroyImage(ximage); + XSync(disp_display, 1); + +} + +void disp_gray_zoom(int num, char *data, int width, int height, int stride, const unsigned char *tit, int zoom) { + unsigned char *dataz; + int y,x,y0,x0; + dataz = malloc(width*zoom * height*zoom); + disp_chkerror(!dataz, "malloc"); + for (y=0; y + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef _DISPLAY_H +#define _DISPLAY_H 1 + +void disp_sync(void); +void disp_setcolor(unsigned char *name); +/* Display a region of byte wide memory as a grayscale image. + * num is the window to use for displaying. */ +void disp_gray(int num, char *data, int width, int height, + int stride, const unsigned char *title); +void disp_gray_zoom(int num, char *data, int width, int height, + int stride, const unsigned char *title, int zoom); +void disp_point(int num, int x1, int y1); +void disp_line(int num, int x1, int y1, int x2, int y2); +void disp_rect(int num, int x1, int y1, int x2, int y2); + +#endif diff --git a/common/visualize.c b/common/visualize.c new file mode 100644 index 00000000..c76db8b4 --- /dev/null +++ b/common/visualize.c @@ -0,0 +1,324 @@ +/***************************************************************************** + * x264: h264 encoder + ***************************************************************************** + * Copyright (C) 2005 x264 project + * + * Author: Tuukka Toivonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +/* + * Some explanation of the symbols used: + * Red/pink: intra block + * Blue: inter block + * Green: skip block + * Yellow: B-block (not visualized properly yet) + * + * The intra blocks have generally lines drawn perpendicular + * to the prediction direction, so for example, if there is a pink block + * with horizontal line at the top of it, it is interpolated by assuming + * luma to be vertically constant. + * DC predicted blocks have both horizontal and vertical lines, + * pink blocks with a diagonal line are predicted using the planar function. + */ + +#ifdef HAVE_STDINT_H +#include +#else +#include +#endif +#include /* NULL */ +#include /* getchar */ +#include /* abort */ + +#include "common.h" +#include "visualize.h" +#include "macroblock.h" +#include "display.h" + +typedef struct { + int i_type; + int i_partition; + int i_sub_partition[4]; + int i_intra16x16_pred_mode; + int intra4x4_pred_mode[4][4]; + int8_t ref[2][4][4]; /* [list][y][x] */ + int16_t mv[2][4][4][2]; /* [list][y][x][mvxy] */ +} visualize_t; + +/* {{{ [fold] char *get_string(const stringlist_t *sl, int entries, int code) */ +/* Return string from stringlist corresponding to the given code */ +#define GET_STRING(sl, code) get_string((sl), sizeof(sl)/sizeof(*(sl)), code) + +typedef struct { + int code; + char *string; +} stringlist_t; + +static char *get_string(const stringlist_t *sl, int entries, int code) +{ + int i; + + for (i=0; i=entries) ? "?" : sl[i].string; +} +/* }}} */ +/* {{{ [fold] void mv(int x0, int y0, int16_t dmv[2], int zoom, char *col) */ +/* Plot motion vector */ +static void mv(int x0, int y0, int16_t dmv[2], int zoom, char *col) +{ + int dx = dmv[0]; + int dy = dmv[1]; + + dx = (dx*zoom)/4; /* Quarter pixel accurate MVs */ + dy = (dy*zoom)/4; + disp_line(0, x0, y0, x0+dx, y0+dy); + disp_setcolor("black"); + disp_point(0, x0, y0); + disp_setcolor(col); +} +/* }}} */ + +/* {{{ [fold] void x264_visualize_init( x264_t *h ) */ +void x264_visualize_init( x264_t *h ) +{ + int mb = h->sps->i_mb_width * h->sps->i_mb_height; + h->visualize = x264_malloc(mb * sizeof(visualize_t)); +} +/* }}} */ +/* {{{ [fold] void x264_visualize_mb( x264_t *h ) */ +void x264_visualize_mb( x264_t *h ) +{ + visualize_t *v = (visualize_t*)h->visualize + h->mb.i_mb_xy; + int i, l, x, y; + + /* Save all data for the MB what we need for drawing the visualization */ + v->i_type = h->mb.i_type; + v->i_partition = h->mb.i_partition; + for (i=0; i<4; i++) v->i_sub_partition[i] = h->mb.i_sub_partition[i]; + for (y=0; y<4; y++) for (x=0; x<4; x++) + v->intra4x4_pred_mode[y][x] = h->mb.cache.intra4x4_pred_mode[X264_SCAN8_0+y*8+x]; + for (l=0; l<2; l++) for (y=0; y<4; y++) for (x=0; x<4; x++) { + for (i=0; i<2; i++) { + v->mv[l][y][x][i] = h->mb.cache.mv[l][X264_SCAN8_0+y*8+x][i]; + } + v->ref[l][y][x] = h->mb.cache.ref[i][X264_SCAN8_0+y*8+x]; + } + v->i_intra16x16_pred_mode = h->mb.i_intra16x16_pred_mode; +} +/* }}} */ +/* {{{ [fold] void x264_visualize_close( x264_t *h ) */ +void x264_visualize_close( x264_t *h ) +{ + x264_free(h->visualize); +} +/* }}} */ +/* {{{ [fold] void x264_visualize( x264_t *h ) */ +/* Display visualization (block types, MVs) of the encoded frame */ +/* FIXME: B-type MBs not handled yet properly + * Reference frame number not visualized */ +void x264_visualize_show( x264_t *h ) +{ + int mb_xy; + static const stringlist_t mb_types[] = { + /* Block types marked as NULL will not be drawn */ + { I_4x4 , "red" }, + { I_16x16 , "pink" }, + { I_PCM , "violet" }, + { P_L0 , "SlateBlue" }, + { P_8x8 , "blue" }, + { P_SKIP , "green" }, + { B_DIRECT, "yellow" }, + { B_L0_L0 , "yellow" }, + { B_L0_L1 , "yellow" }, + { B_L0_BI , "yellow" }, + { B_L1_L0 , "yellow" }, + { B_L1_L1 , "yellow" }, + { B_L1_BI , "yellow" }, + { B_BI_L0 , "yellow" }, + { B_BI_L1 , "yellow" }, + { B_BI_BI , "yellow" }, + { B_8x8 , "yellow" }, + { B_SKIP , "yellow" }, + }; + + static const int waitkey = 1; + static const int drawbox = 1; + static const int borders = 0; + + static const int pad = 32; + static const int zoom = 2; + uint8_t *const frame = h->fdec->plane[0]; + const int width = h->param.i_width; + const int height = h->param.i_height; + const int stride = h->fdec->i_stride[0]; + + if (borders) { + disp_gray_zoom(0, frame - pad*stride - pad, width+2*pad, height+2*pad, stride, "fdec", zoom); + } else { + disp_gray_zoom(0, frame, width, height, stride, "fdec", zoom); + } + + for( mb_xy = 0; mb_xy < h->sps->i_mb_width * h->sps->i_mb_height; mb_xy++ ) + { + visualize_t *v = (visualize_t*)h->visualize + mb_xy; + const int mb_y = mb_xy / h->sps->i_mb_width; + const int mb_x = mb_xy % h->sps->i_mb_width; + int x = mb_x*16*zoom; + int y = mb_y*16*zoom; + if (borders) { + x += pad*zoom; + y += pad*zoom; + } + int l = 0; + char *col = GET_STRING(mb_types, v->i_type); + unsigned int i, j; + if (col==NULL) continue; + disp_setcolor(col); + if (drawbox) disp_rect(0, x, y, x+16*zoom-1, y+16*zoom-1); + + if (v->i_type==P_L0 || v->i_type==P_8x8 || v->i_type==P_SKIP) { + + /* Predicted (inter) mode, with motion vector */ + if (v->i_partition==D_16x16 || v->i_type==P_SKIP) { + mv(x+8*zoom, y+8*zoom, v->mv[l][0][0], zoom, col); + } + if (v->i_partition==D_16x8) { + if (drawbox) disp_rect(0, x, y, x+16*zoom, y+8*zoom); + mv(x+8*zoom, y+4*zoom, v->mv[l][0][0], zoom, col); + if (drawbox) disp_rect(0, x, y+8*zoom, x+16*zoom, y+16*zoom); + mv(x+8*zoom, y+12*zoom, v->mv[l][2][0], zoom, col); + } + if (v->i_partition==D_8x16) { + if (drawbox) disp_rect(0, x, y, x+8*zoom, y+16*zoom); + mv(x+4*zoom, y+8*zoom, v->mv[l][0][0], zoom, col); + if (drawbox) disp_rect(0, x+8*zoom, y, x+16*zoom, y+16*zoom); + mv(x+12*zoom, y+8*zoom, v->mv[l][0][2], zoom, col); + } + if (v->i_partition==D_8x8) { + for (i=0; i<2; i++) for (j=0; j<2; j++) { + int sp = v->i_sub_partition[i*2+j]; + const int x0 = x + j*8*zoom; + const int y0 = y + i*8*zoom; + if (sp==D_L1_8x8) sp = D_L0_8x8; + if (sp==D_L1_4x8) sp = D_L0_4x8; + if (sp==D_L1_8x4) sp = D_L0_8x4; + if (sp==D_L1_4x4) sp = D_L0_4x4; + if (sp==D_L0_8x8) { + if (drawbox) disp_rect(0, x0, y0, x0+8*zoom, y0+8*zoom); + mv(x0+4*zoom, y0+4*zoom, v->mv[l][2*i][2*j], zoom, col); + } + if (sp==D_L0_8x4) { + if (drawbox) disp_rect(0, x0, y0, x0+8*zoom, y0+4*zoom); + if (drawbox) disp_rect(0, x0, y0+4*zoom, x0+8*zoom, y0+8*zoom); + mv(x0+4*zoom, y0+2*zoom, v->mv[l][2*i][2*j], zoom, col); + mv(x0+4*zoom, y0+6*zoom, v->mv[l][2*i+1][2*j], zoom, col); + } + if (sp==D_L0_4x8) { + if (drawbox) disp_rect(0, x0, y0, x0+4*zoom, y0+8*zoom); + if (drawbox) disp_rect(0, x0+4*zoom, y0, x0+8*zoom, y0+8*zoom); + mv(x0+2*zoom, y0+4*zoom, v->mv[l][2*i][2*j], zoom, col); + mv(x0+6*zoom, y0+4*zoom, v->mv[l][2*i][2*j+1], zoom, col); + } + if (sp==D_L0_4x4) { + if (drawbox) disp_rect(0, x0, y0, x0+4*zoom, y0+4*zoom); + if (drawbox) disp_rect(0, x0+4*zoom, y0, x0+8*zoom, y0+4*zoom); + if (drawbox) disp_rect(0, x0, y0+4*zoom, x0+4*zoom, y0+8*zoom); + if (drawbox) disp_rect(0, x0+4*zoom, y0+4*zoom, x0+8*zoom, y0+8*zoom); + mv(x0+2*zoom, y0+2*zoom, v->mv[l][2*i][2*j], zoom, col); + mv(x0+6*zoom, y0+2*zoom, v->mv[l][2*i][2*j+1], zoom, col); + mv(x0+2*zoom, y0+6*zoom, v->mv[l][2*i+1][2*j], zoom, col); + mv(x0+6*zoom, y0+6*zoom, v->mv[l][2*i+1][2*j+1], zoom, col); + } + } + } + } + + if (v->i_type==I_4x4 || v->i_type==I_16x16 || v->i_type==I_PCM) { + /* Intra coded */ + if (v->i_type==I_16x16) { + switch (v->i_intra16x16_pred_mode) { + case I_PRED_16x16_V: + disp_line(0, x+2*zoom, y+2*zoom, x+14*zoom, y+2*zoom); + break; + case I_PRED_16x16_H: + disp_line(0, x+2*zoom, y+2*zoom, x+2*zoom, y+14*zoom); + break; + case I_PRED_16x16_DC: + case I_PRED_16x16_DC_LEFT: + case I_PRED_16x16_DC_TOP: + case I_PRED_16x16_DC_128: + disp_line(0, x+2*zoom, y+2*zoom, x+14*zoom, y+2*zoom); + disp_line(0, x+2*zoom, y+2*zoom, x+2*zoom, y+14*zoom); + break; + case I_PRED_16x16_P: + disp_line(0, x+2*zoom, y+2*zoom, x+8*zoom, y+8*zoom); + break; + default: abort(); + } + } + if (v->i_type==I_4x4) { + for (i=0; i<4; i++) for (j=0; j<4; j++) { + const int x0 = x + j*4*zoom; + const int y0 = y + i*4*zoom; + if (drawbox) disp_rect(0, x0, y0, x0+4*zoom, y0+4*zoom); + switch (v->intra4x4_pred_mode[i][j]) { + case I_PRED_4x4_V: /* Vertical */ + disp_line(0, x0+0*zoom, y0+1*zoom, x0+4*zoom, y0+1*zoom); + break; + case I_PRED_4x4_H: /* Horizontal */ + disp_line(0, x0+1*zoom, y0+0*zoom, x0+1*zoom, y0+4*zoom); + break; + case I_PRED_4x4_DC: /* DC, average from top and left sides */ + case I_PRED_4x4_DC_LEFT: + case I_PRED_4x4_DC_TOP: + case I_PRED_4x4_DC_128: + disp_line(0, x0+1*zoom, y0+1*zoom, x0+4*zoom, y0+1*zoom); + disp_line(0, x0+1*zoom, y0+1*zoom, x0+1*zoom, y0+4*zoom); + break; + case I_PRED_4x4_DDL: /* Topright-downleft */ + disp_line(0, x0+0*zoom, y0+0*zoom, x0+4*zoom, y0+4*zoom); + break; + case I_PRED_4x4_DDR: /* Topleft-downright */ + disp_line(0, x0+0*zoom, y0+4*zoom, x0+4*zoom, y0+0*zoom); + break; + case I_PRED_4x4_VR: /* Mix of topleft-downright and vertical */ + disp_line(0, x0+0*zoom, y0+2*zoom, x0+4*zoom, y0+1*zoom); + break; + case I_PRED_4x4_HD: /* Mix of topleft-downright and horizontal */ + disp_line(0, x0+2*zoom, y0+0*zoom, x0+1*zoom, y0+4*zoom); + break; + case I_PRED_4x4_VL: /* Mix of topright-downleft and vertical */ + disp_line(0, x0+0*zoom, y0+1*zoom, x0+4*zoom, y0+2*zoom); + break; + case I_PRED_4x4_HU: /* Mix of topright-downleft and horizontal */ + disp_line(0, x0+1*zoom, y0+0*zoom, x0+2*zoom, y0+4*zoom); + break; + default: abort(); + } + } + } + } + } + + disp_sync(); + if (waitkey) getchar(); +} +/* }}} */ + +//EOF diff --git a/common/visualize.h b/common/visualize.h new file mode 100644 index 00000000..92c8b008 --- /dev/null +++ b/common/visualize.h @@ -0,0 +1,33 @@ +/***************************************************************************** + * x264: h264 encoder + ***************************************************************************** + * Copyright (C) 2005 x264 project + * + * Author: Tuukka Toivonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef _VISUALIZE_H +#define _VISUALIZE_H 1 + +#include "common/common.h" + +void x264_visualize_init( x264_t *h ); +void x264_visualize_mb( x264_t *h ); +void x264_visualize_show( x264_t *h ); +void x264_visualize_close( x264_t *h ); + +#endif diff --git a/configure b/configure index 8b3dd33d..6128c116 100755 --- a/configure +++ b/configure @@ -11,6 +11,7 @@ echo " --enable-avis-input enables avisynth input (win32 only)" echo " --enable-mp4-output enables mp4 output (using gpac)" echo " --enable-vfw compiles the VfW frontend" echo " --enable-debug adds -g, doesn't strip" +echo " --enable-visualize enables visualization (X11 only)" echo " --extra-cflags=ECFLAGS add ECFLAGS to CFLAGS" echo " --extra-ldflags=ELDFLAGS add ELDFLAGS to LDFLAGS" echo "" @@ -21,6 +22,7 @@ avis_input="no" mp4_output="no" debug="no" vfw="no" +vis="no" CC="gcc" CFLAGS="-Wall -I. -O4 -funroll-loops -ffast-math -D__X264__" @@ -132,6 +134,11 @@ for opt do --enable-vfw) vfw="yes" ;; + --enable-visualize) + LDFLAGS="$LDFLAGS -L/usr/X11R6/lib -lX11" + CFLAGS="$CFLAGS -DVISUALIZE=1" + vis="yes" + ;; *) echo "Unknown option $opt, ignored" ;; @@ -156,6 +163,7 @@ AS=$AS ASFLAGS=$ASFLAGS VFW=$vfw EXE=$EXE +VIS=$vis EOF if [ "$vfw" == "yes" ]; then @@ -173,6 +181,7 @@ echo "avis input: $avis_input" echo "mp4 output: $mp4_output" echo "vfw: $vfw" echo "debug: $debug" +echo "visualize: $vis" echo echo "You can run 'make' now." diff --git a/encoder/encoder.c b/encoder/encoder.c index de9c130e..5797ca53 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -35,6 +35,10 @@ #include "ratecontrol.h" #include "macroblock.h" +#if VISUALIZE +#include "common/visualize.h" +#endif + //#define DEBUG_MB_TYPE //#define DEBUG_DUMP_FRAME //#define DEBUG_BENCHMARK @@ -863,6 +867,11 @@ static inline void x264_slice_write( x264_t *h, int i_nal_type, int i_nal_ref_id h->mb.i_last_qp = h->pps->i_pic_init_qp + h->sh.i_qp_delta; h->mb.i_last_dqp = 0; +#if VISUALIZE + if( h->param.b_visualize ) + x264_visualize_init( h ); +#endif + for( mb_xy = 0, i_skip = 0; mb_xy < h->sps->i_mb_width * h->sps->i_mb_height; mb_xy++ ) { const int i_mb_y = mb_xy / h->sps->i_mb_width; @@ -931,6 +940,11 @@ static inline void x264_slice_write( x264_t *h, int i_nal_type, int i_nal_ref_id } TIMER_STOP( i_mtime_write ); +#if VISUALIZE + if( h->param.b_visualize ) + x264_visualize_mb( h ); +#endif + /* save cache */ x264_macroblock_cache_save( h ); @@ -971,6 +985,14 @@ static inline void x264_slice_write( x264_t *h, int i_nal_type, int i_nal_ref_id x264_nal_end( h ); +#if VISUALIZE + if( h->param.b_visualize ) + { + x264_visualize_show( h ); + x264_visualize_close( h ); + } +#endif + /* Compute misc bits */ h->stat.frame.i_misc_bits = bs_pos( &h->out.bs ) + NALU_OVERHEAD * 8 diff --git a/x264.c b/x264.c index 69256892..f745e809 100644 --- a/x264.c +++ b/x264.c @@ -61,6 +61,9 @@ static int i_ctrl_c = 0; static void SigIntHandler( int a ) { i_ctrl_c = 1; +#if VISUALIZE + exit(0); +#endif } /* input file operation function pointers */ @@ -228,6 +231,7 @@ static void Help( x264_param_t *defaults ) " --quiet Quiet Mode\n" " -v, --verbose Print stats for each frame\n" " --progress Show a progress indicator while encoding\n" + " --visualize Show MB types overlayed on the encoded video\n" " --aud Use access unit delimiters\n" "\n", X264_BUILD, X264_VERSION, @@ -340,6 +344,7 @@ static int Parse( int argc, char **argv, #define OPT_VBVMAXRATE 287 #define OPT_VBVBUFSIZE 288 #define OPT_VBVINIT 289 +#define OPT_VISUALIZE 290 static struct option long_options[] = { @@ -390,6 +395,7 @@ static int Parse( int argc, char **argv, { "quiet", no_argument, NULL, OPT_QUIET }, { "verbose", no_argument, NULL, 'v' }, { "progress",no_argument, NULL, OPT_PROGRESS }, + { "visualize",no_argument, NULL, OPT_VISUALIZE }, { "aud", no_argument, NULL, OPT_AUD }, {0, 0, 0, 0} }; @@ -635,6 +641,13 @@ static int Parse( int argc, char **argv, case OPT_PROGRESS: *b_progress = 1; break; + case OPT_VISUALIZE: +#ifdef VISUALIZE + param->b_visualize = 1; +#else + fprintf( stderr, "not compiled with visualization support\n" ); +#endif + break; default: fprintf( stderr, "unknown option (%c)\n", optopt ); return -1; diff --git a/x264.h b/x264.h index 9ca64971..e9b8a0d0 100644 --- a/x264.h +++ b/x264.h @@ -135,6 +135,7 @@ typedef struct void (*pf_log)( void *, int i_level, const char *psz, va_list ); void *p_log_private; int i_log_level; + int b_visualize; /* Encoder analyser parameters */ struct