To re-capitulate: XvShmPutImage seems to be slow, can anyone see why that might be so, am I initiating Xv and its buffers correctly?
(p4 2ghz, 512 MB ram, 32Mb nvideo gforce go, calling XvShmPutImage on a 512x256 YUV422 image takes around 4msecs)
etienne
/* * jMax * Copyright (C) 1994, 1995, 1998, 1999 by IRCAM-Centre Georges Pompidou, Paris, France. * * 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. * * See file LICENSE for further informations on licensing terms. * * 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-1307, USA. * * Based on Max/ISPW by Miller Puckette. * * Author: Etienne Deleflie based * */
#include <fts/fts.h> #include <sys/time.h> #include "../../vdsp/h/vdsp.h" #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <time.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xatom.h> #include <X11/extensions/Xv.h> #include <X11/extensions/Xvlib.h> #include <X11/extensions/XShm.h> #include "split_channel.h" #define vmonitor_VERSION "0.1" #define DOUBLEW #define YV12 0x32315659 //(YV12) PLANAR #define I420 0x30323449 //(I420) PLANAR #define YUY2 0x32595559 //(YUY2) PACKED #define UYVY 0x59565955 //(UYVY) PACKED extern int XShmQueryExtension(Display*); extern int XShmGetEventBase(Display*); extern XvImage *XvShmCreateImage(Display*, XvPortID, int, char*, int, int, XShmSegmentInfo*); static void vmonitor_drawTestPattern(XvImage *img); static void vmonitor_send_drawable(fts_object_t *o); /* ---------------------------------------------------------------------- */ /* The fts_videoport_t derived class */ /* ---------------------------------------------------------------------- */ typedef struct { fts_videoport_t head; // X object attributes Display *dpy; Window window, _dw; int _d, _w, _h; int xv_port; GC gc; XvImage *yuv_image; XShmSegmentInfo yuv_shminfo; char constrained; char centered; char scaled; // on off int run; // flag if output image proportions have changed (in which case we have to background colour) int image_parameters_changed; // the default background image unsigned char *background_image; // whether the X stuff has been called or not char x_has_been_setup; } vmonitor_t; static void vmonitor_output( fts_word_t *argv) { // this next line is for timing exclusively struct timeval tv_s, tv_e, result; vmonitor_t *this = (vmonitor_t *)fts_word_get_ptr( argv+0); long n = fts_word_get_int(argv + 1); char *in_Y = (char *) fts_word_get_ptr( argv + 2); char *in_UV = (char *) fts_word_get_ptr( argv + 3); long i; // (OLD VERSION) copy the current incoming data to the buffer //v_byte_copy(in_Y, (this->yuv_image->data) + pdata[0].count*4, VTICK_SIZE*4); // START TIMING CODE gettimeofday(&tv_s, NULL); // END TIMING CODE /* begin SSE2 assembly function call */ // asm_interleave(in_Y, in_UV, this->yuv_image->data, /* size */); /* begin SSE2 assembly function call */ n = n*4; for(i=0; i<n; i+=2) //n is 1 pixel { this->yuv_image->data[i*2] = in_Y[i]; this->yuv_image->data[(i+1)*2] = in_Y[i+1]; } for(i=0; i<n; i+=2) //n is 1 pixel { this->yuv_image->data[i*2 + 1] = in_UV[i]; this->yuv_image->data[(i+1)*2 + 1] = in_UV[i+1]; } // START TIMING CODE gettimeofday(&tv_e, NULL); timeval_subtract (&result, &tv_e, &tv_s); fprintf(stderr, "Took %d micro seconds to write data into Hardware buffer.\n", (&result)->tv_usec ); // END TIMING CODE // START TIMING CODE gettimeofday(&tv_s, NULL); // END TIMING CODE vmonitor_send_drawable(this); // START TIMING CODE gettimeofday(&tv_e, NULL); timeval_subtract (&result, &tv_e, &tv_s); fprintf(stderr, "Took %d micro seconds to send drawable to Xv.\n", (&result)->tv_usec ); // END TIMING CODE //XFlush(this->dpy); } static void vmonitor_nullinput( fts_word_t *argv) { vmonitor_t *port = (vmonitor_t *)fts_word_get_ptr( argv+0); int32 *out = (int32 *) fts_word_get_ptr( argv + 2); //v_vec_const(out, 0x00FF0000, VTICK_SIZE); } //////////////////////////////////////////////////////////////// ////////////// 2 //////////////// //////////////////////////////////////////////////////////////// static void vmonitor_init( fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *att) { vmonitor_t *this = (vmonitor_t *)o; fts_symbol_t which_display; // variables necessary for checking x windows and the like int screen, encodings, attributes, formats; XSizeHints hint; XSetWindowAttributes xswa; XVisualInfo vinfo; unsigned long mask; XEvent event; /** for shm */ int shmem_flag = 0; int CompletionType; /** for xv */ int ret, i, p, j; unsigned int p_version, p_release,p_request_base, p_event_base, p_error_base; int p_num_adaptors; XvAdaptorInfo *ai; XvEncodingInfo *ei; XvAttribute *at; XvImageFormatValues *fo; /** for user defnied parameters */ fts_symbol_t first_arg; //geometry string fts_symbol_t second_arg; //scalable or not fts_symbol_t third_arg; //centered or not fts_symbol_t fourth_arg; //scaled or not fts_symbol_t fifth_arg; //name of window char * window_name = "video monitor"; //name as string /** for printing out the first image */ int drawable_width, drawable_height, drawable_origin_x, drawable_origin_y; /** for window geometry */ char * mainGeometry=""; char defaultMainGeometry[13] = ""; struct coordinates { int x, y; }; struct coordinates total_size; int x_negative = 0; int y_negative = 0; int result = 0; int gravity = 0; total_size.x = total_size.y = 54; // initiate some of the basic variables this->constrained = 0; this->centered = 0; this->scaled = 0; this->xv_port = -1; this->x_has_been_setup=0; // the flag this->image_parameters_changed = 0; fts_videoport_init( &this->head); // retrieve user defined variables (window size + scalable) first_arg = fts_get_symbol_arg(ac, att, 1, 0); if (first_arg) { mainGeometry = fts_symbol_name(first_arg); } second_arg = fts_get_symbol_arg(ac, att, 2, 0); if (second_arg) this->constrained = 1; third_arg = fts_get_symbol_arg(ac, att, 3, 0); if (third_arg) this->centered = 1; fourth_arg = fts_get_symbol_arg(ac, att, 4, 0); if (fourth_arg) this->scaled = 1; fifth_arg = fts_get_symbol_arg(ac, att, 5, 0); if (fifth_arg) window_name = fts_symbol_name (fifth_arg); // set up and test what is available for X // can we open an X display ? this->dpy = XOpenDisplay(NULL); //this->dpy = XOpenDisplay(":1.0"); if (this->dpy == NULL) { post("Cannot open X Display.\n"); return; } screen = DefaultScreen(this->dpy); /** find best display */ if (XMatchVisualInfo(this->dpy, screen, 24, TrueColor, &vinfo)) {post("vmonitor found 24bit TrueColor display\n");} else if (XMatchVisualInfo(this->dpy, screen, 16, TrueColor, &vinfo)) {post("vmonitor found 16bit TrueColor display\n");} else if (XMatchVisualInfo(this->dpy, screen, 15, TrueColor, &vinfo)) {post("vmonitor found 15bit TrueColor display\n");} else if (XMatchVisualInfo(this->dpy, screen, 8, PseudoColor, &vinfo)) {post("vmonitor found 8bit PseudoColor display\n");} else if (XMatchVisualInfo(this->dpy, screen, 8, GrayScale, &vinfo)) {post("vmonitor found 8bit GrayScale display\n");} else if (XMatchVisualInfo(this->dpy, screen, 8, StaticGray, &vinfo)) {post("vmonitor found 8bit StaticGray display\n");} else if (XMatchVisualInfo(this->dpy, screen, 1, StaticGray, &vinfo)) {post("vmonitor found 1bit StaticGray display\n");} else { post("requires 16 bit display\n"); return ;} CompletionType = -1; /** hints **/ // window size and position. if (!strlen(mainGeometry)) { char width[4], height[4]; char by[] = "x"; char position[] = "+20+60"; sprintf (width, "%d",V_WIDTH); sprintf (height, "%d",V_HEIGHT); //post ("width is -%s-\n", width); strcat (defaultMainGeometry, width); strcat (defaultMainGeometry, by); strcat (defaultMainGeometry, height); strcat (defaultMainGeometry, position); mainGeometry = defaultMainGeometry; } // set up and test what is available for X // can we open an X display ? this->dpy = XOpenDisplay(NULL); //this->dpy = XOpenDisplay(":1.0"); if (this->dpy == NULL) { post("Cannot open X Display.\n"); return; } screen = DefaultScreen(this->dpy); /** find best display */ if (XMatchVisualInfo(this->dpy, screen, 24, TrueColor, &vinfo)) {post("vmonitor found 24bit TrueColor display\n");} else if (XMatchVisualInfo(this->dpy, screen, 16, TrueColor, &vinfo)) {post("vmonitor found 16bit TrueColor display\n");} else if (XMatchVisualInfo(this->dpy, screen, 15, TrueColor, &vinfo)) {post("vmonitor found 15bit TrueColor display\n");} else if (XMatchVisualInfo(this->dpy, screen, 8, PseudoColor, &vinfo)) {post("vmonitor found 8bit PseudoColor display\n");} else if (XMatchVisualInfo(this->dpy, screen, 8, GrayScale, &vinfo)) {post("vmonitor found 8bit GrayScale display\n");} else if (XMatchVisualInfo(this->dpy, screen, 8, StaticGray, &vinfo)) {post("vmonitor found 8bit StaticGray display\n");} else if (XMatchVisualInfo(this->dpy, screen, 1, StaticGray, &vinfo)) {post("vmonitor found 1bit StaticGray display\n");} else { post("requires 16 bit display\n"); return ;} CompletionType = -1; /** hints **/ // window size and position. // Check the user-specified size result = XParseGeometry(mainGeometry, &hint.x, &hint.y, &hint.width, &hint.height); if (result & WidthValue) total_size.x = hint.width; if (result & HeightValue) total_size.y = hint.height; if (result & XNegative) x_negative = 1; if (result & YNegative) y_negative = 1; hint.flags = USSize | USPosition; hint.x = 0; hint.y = 0; hint.width = V_WIDTH; hint.height = V_HEIGHT; XWMGeometry(this->dpy, screen, mainGeometry, NULL, 0, &hint, &hint.x, &hint.y, &hint.width, &hint.height, &gravity); /** take care of the gravity */ hint.win_gravity = StaticGravity; hint.flags |= PWinGravity; /** set some X window attributes **/ xswa.colormap = XCreateColormap(this->dpy, DefaultRootWindow(this->dpy), vinfo.visual, AllocNone); xswa.event_mask = StructureNotifyMask | ExposureMask; xswa.background_pixel = 0; xswa.border_pixel = 0; /** some mask business **/ mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; /** initialise the window **/ this->window = XCreateWindow( this->dpy, DefaultRootWindow(this->dpy), hint.x, hint.y, hint.width, hint.height, 0, vinfo.depth, InputOutput, vinfo.visual, mask, &xswa); /** set the window manager hints */ XSetWMNormalHints(this->dpy, this->window, &hint); /** a bit more about the window **/ XStoreName(this->dpy, this->window, window_name); XSetIconName(this->dpy, this->window, window_name); XSelectInput(this->dpy, this->window, StructureNotifyMask); /** Map window */ XMapWindow(this->dpy, this->window); /** Wait for map. (this opens an X window) */ do { XNextEvent(this->dpy, &event); } while (event.type != MapNotify || event.xmap.event != this->window); /** test that Shm is ready ?**/ if (XShmQueryExtension(this->dpy)) shmem_flag = 1; if (!shmem_flag) { printf("no shmem available.\n"); return; } if (shmem_flag==1) CompletionType = XShmGetEventBase(this->dpy) + ShmCompletion; /**--------------------------------- XV ------------------------------------*/ /** query Xvideo Extension */ ret = XvQueryExtension(this->dpy, &p_version, &p_release, &p_request_base, &p_event_base, &p_error_base); if (ret != Success) { if (ret == XvBadExtension) post ("XvBadExtension returned at XvQueryExtension.\n"); else if (ret == XvBadAlloc) post ("XvBadAlloc returned at XvQueryExtension.\n"); else post ("other error happened at XvQueryExtension.\n"); } /* post("========================================\n"); post("XvQueryExtension returned the following:\n"); post("p_version : %u\n", p_version); post("p_release : %u\n", p_release); post("p_request_base : %u\n", p_request_base); post("p_event_base : %u\n", p_event_base); post("p_error_base : %u\n", p_error_base); post("========================================\n"); */ /** query Xvideo Adaptors */ ret = XvQueryAdaptors(this->dpy, DefaultRootWindow(this->dpy), &p_num_adaptors, &ai); if (ret != Success) { if (ret == XvBadExtension) post("XvBadExtension returned at XvQueryExtension.\n"); else if (ret == XvBadAlloc) post("XvBadAlloc returned at XvQueryExtension.\n"); else post("other error happaned at XvQueryAdaptors.\n"); } // post ("%d xv adaptors available.\n", p_num_adaptors); /** loop through all the adaptors */ for (i = 0; i < p_num_adaptors; i++) { this->xv_port = ai[i].base_id; for (p = ai[i].base_id; p < ai[i].base_id+ai[i].num_ports; p++) { if (XvQueryEncodings(this->dpy, p, &encodings, &ei) != Success) continue; XvFreeEncodingInfo(ei); at = XvQueryPortAttributes(this->dpy, p, &attributes); if (at) XFree(at); fo = XvListImageFormats(this->dpy, p, &formats); /* UNCOMMENT TO FIND OUT WHAT COLOUR MODELS THE GRAPHICS CARD CAN HANDLE // to find out what colour models the card accepts for (j = 0; j < formats; j++) { post(" 0x%x (%4.4s) %s\n", fo[j].id, (char *)&fo[j].id, (fo[j].format == XvPacked) ? "packed" : "planar"); } */ if (fo) XFree(fo); } } if (p_num_adaptors > 0) XvFreeAdaptorInfo(ai); if (this->xv_port == -1) { post ("could not find any xv_ports ..... does your graphics card have overlay support?"); return; } this->gc = XCreateGC(this->dpy, this->window, 0, 0); // create the Xv image this->yuv_image = XvShmCreateImage(this->dpy, this->xv_port, YUY2 , 0, V_WIDTH, V_HEIGHT, &(this->yuv_shminfo)); this->yuv_shminfo.shmid = shmget(IPC_PRIVATE, this->yuv_image->data_size, IPC_CREAT | 0777); this->yuv_shminfo.shmaddr = this->yuv_image->data = shmat(this->yuv_shminfo.shmid, 0, 0); this->yuv_shminfo.readOnly = True; XSync (this->dpy, False); if (!XShmAttach(this->dpy, &(this->yuv_shminfo))) { post("XShmAttach failed !\n"); return; } // TODO: the background image needs to be an XV image // // allocate space for the background image that will be re-drawn any time the aspect ratio of the image changes. // this image is the size of the X output window this->background_image = (unsigned char *) fts_malloc( (size_t)(this->_w * this->_h * 2)*sizeof(unsigned char *)); // populate it with black (for now) for (i = (this->_w * this->_h * 2) -1 ; i >= 0; i-- ) { this->background_image[i] = 100; } //vmonitor_setup_X_window_geometry(this); // print out a default image vmonitor_drawTestPattern(this->yuv_image); // set up the window geometry vmonitor_send_drawable(this); XFlush(this->dpy); // object is on by default this->run = 1; // define the number of input channels (which is 2 in the case of YUV) fts_videoport_set_output_function( (fts_videoport_t *)this, vmonitor_output); fts_videoport_set_output_channels( (fts_videoport_t *)this, 2); fts_videoport_set_input_channels( (fts_videoport_t *)this, 2); fts_videoport_set_input_function( (fts_videoport_t *)this, vmonitor_nullinput); } /* CLEAN RESOURCES */ static void vmonitor_delete( fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at) { vmonitor_t *this = (vmonitor_t *)o; free(this->background_image); if (&(this->yuv_shminfo)) { //fprintf(stderr, "cleaning, found some shminfo"); XShmDetach(this->dpy, &(this->yuv_shminfo)); XFree(this->yuv_image); this->yuv_image = NULL; shmdt( this->yuv_shminfo.shmaddr); shmctl( this->yuv_shminfo.shmid, IPC_RMID, 0); } else XFree(this->yuv_image); XCloseDisplay(this->dpy); fts_videoport_delete( (fts_videoport_t *)o); } /* GET STATE (?) */ static void vmonitor_get_state( fts_daemon_action_t action, fts_object_t *o, fts_symbol_t property, fts_atom_t *value) { fts_set_object( value, o); } static void vmonitor_constrained( fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at) { vmonitor_t *this = (vmonitor_t *)o; this->constrained = fts_get_int_arg( ac, at, 0, 0); //fts_get_number_int(at); this->image_parameters_changed = 1; } static void vmonitor_centered( fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at) { vmonitor_t *this = (vmonitor_t *)o; this->centered = fts_get_int_arg( ac, at, 0, 0); //fts_get_number_int(at); this->image_parameters_changed = 1; } static void vmonitor_scaled( fts_object_t *o, int winlet, fts_symbol_t s, int ac, const fts_atom_t *at) { vmonitor_t *this = (vmonitor_t *)o; this->scaled = fts_get_int_arg( ac, at, 0, 0); //fts_get_number_int(at); this->image_parameters_changed = 1; } //////////////////////////////////////////////////////////////// ////////////// 1 //////////////// //////////////////////////////////////////////////////////////// static fts_status_t vmonitor_instantiate(fts_class_t *cl, int ac, const fts_atom_t *at) { fts_symbol_t a[6]; a[0] = fts_s_symbol; /* class */ a[1] = fts_s_symbol; /* X11 params */ a[2] = fts_s_int; /* constrained or not */ a[3] = fts_s_int; /* cenetered or not */ a[4] = fts_s_int; /* scaled or not */ a[5] = fts_s_symbol; /* name of output window */ fts_class_init( cl, sizeof( vmonitor_t), 1, 0, 0); // takes up to 5 arguments ......... but at least 2 are required fts_method_define_optargs( cl, fts_SystemInlet, fts_s_init, vmonitor_init, 6, a, 1); fts_method_define_varargs( cl, fts_SystemInlet, fts_s_delete, vmonitor_delete); // user methods a[0] = fts_t_int; fts_method_define_optargs(cl, 0, fts_new_symbol("constrained"), vmonitor_constrained, 1, a, 1); fts_method_define_optargs(cl, 0, fts_new_symbol("centered"), vmonitor_centered, 1, a, 1); fts_method_define_optargs(cl, 0, fts_new_symbol("scaled"), vmonitor_scaled, 1, a, 1); fts_class_add_daemon ( cl, obj_property_get, fts_s_state, vmonitor_get_state); /* * NOTE: there are no vdsp inlets decalred, because this cass uses a metaclass (is that what they are called ?) * */ return fts_Success; } static void vmonitor_drawTestPattern(XvImage *img) { int i,j, n; int unit, num_in_width; int last_width, last_height; n = V_SIZE*2; for(i=0; i<n; i+=4) //n is 1 pixel { img->data[i] = 0; // Y1 img->data[i+2] = 255; // Y2 img->data[i+1] = 100; // U img->data[i+3] = 100; // V } } static void vmonitor_send_drawable(fts_object_t *o) { vmonitor_t *this = (vmonitor_t *)o; int drawable_width, drawable_height, drawable_origin_x, drawable_origin_y; XGetGeometry(this->dpy, this->window, &(this->_dw), &(this->_d), &(this->_d), &(this->_w), &(this->_h), &(this->_d), &(this->_d)); if (this->image_parameters_changed) { // TODO: if the image parameters change, make screen go back to black, to avoid part of the image lying around. this->image_parameters_changed = o; } // now work out what is the drawable area within the X window if (this->scaled) { if (this->constrained) { // check to see if the shortest side is length or height float proport_w = (float)this->_w / this->_h; float proport_v = (float)V_WIDTH / V_HEIGHT; if (proport_w < proport_v) { // if we get here then the window width is skinny.... we have to scale down height drawable_width = this->_w; drawable_height = (rint)(drawable_width / proport_v); // position if (this->centered) { drawable_origin_x = 0; drawable_origin_y = (this->_h - drawable_height)/2; } else drawable_origin_x = drawable_origin_y = 0; } else { // if we get here then the window height is skinny drawable_height = this->_h; drawable_width = (int)(drawable_height * proport_v); // position if (this->centered) { drawable_origin_y = 0; drawable_origin_x = (this->_w - drawable_width)/2; } else drawable_origin_x = drawable_origin_y = 0; } } else { // if we get here then we are scaled but not constrained. drawable_width = this->_w; drawable_height = this->_h; drawable_origin_x = drawable_origin_y = 0; } } else { // if we get here then we are not constrained drawable_width = V_WIDTH; drawable_height = V_HEIGHT; if (this->centered) { drawable_origin_x = (this->_w - V_WIDTH)/2; drawable_origin_y = (this->_h - V_HEIGHT)/2; } else { drawable_origin_x = 0; drawable_origin_y = 0; } } XvShmPutImage( this->dpy //The connection to the X-Server , this->xv_port //The port id of a port on an XvImage capable adaptor , this->window //The target drawable , this->gc //the graphics context specifying the clip mask to use, if any. , this->yuv_image //A pointer to the XvImage to be displayed , 0, 0 //The portion of the XvImage to be displayed. , this->yuv_image->width , this->yuv_image->height , drawable_origin_x //The portion of the destination drawable to be filled by the image. , drawable_origin_y , drawable_width, drawable_height , True); //Indicates whether or not an XShmCompletionEvent should be sent. } void vmonitor_config( void) { fts_class_install( fts_new_symbol("monitor*"), vmonitor_instantiate); }