Please consider a donation to the Higher Intellect project. See https://preterhuman.net/donate.php or the Donate to Higher Intellect page for more info. |
Composite.c
Jump to navigation
Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
/*********************************************************************** ** ** composite.c ** ** This example program demonstrates the use of DMbuffers to ** do simple compositing. It does "picture in picture", using two ** live video inputs. ** ** Two video streams are captured. The first one is captured in ** "pbuffer" format so that it can be attached to a GLXDMbuffer and ** used as a drawable with GLX. The second stream is captured (as a ** texture) and drawn as a "picture in picture" on top of an image ** from the first stream. ** ** The resulting composite image is sent to video out. ** ** ** Copyright 1996, Silicon Graphics, Inc. ** ALL RIGHTS RESERVED ** ** UNPUBLISHED -- Rights reserved under the copyright laws of the United ** States. Use of a copyright notice is precautionary only and does not ** imply publication or disclosure. ** ** U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND: ** Use, duplication or disclosure by the Government is subject to restrictions ** as set forth in FAR 52.227.19(c)(2) or subparagraph (c)(1)(ii) of the Rights ** in Technical Data and Computer Software clause at DFARS 252.227-7013 and*or ** in similar or successor clauses in the FAR, or the DOD or NASA FAR ** Supplement. Contractor*manufacturer is Silicon Graphics, Inc., ** 2011 N. Shoreline Blvd. Mountain View, CA 94039-7311. ** ** THE CONTENT OF THIS WORK CONTAINS CONFIDENTIAL AND PROPRIETARY ** INFORMATION OF SILICON GRAPHICS, INC. ANY DUPLICATION, MODIFICATION, ** DISTRIBUTION, OR DISCLOSURE IN ANY FORM, IN WHOLE, OR IN PART, IS STRICTLY ** PROHIBITED WITHOUT THE PRIOR EXPRESS WRITTEN PERMISSION OF SILICON ** GRAPHICS, INC. ** ***********************************************************************/ #include <dmedia/dm_buffer.h> #include <dmedia/dm_image.h> #include <dmedia/vl.h> #include <dmedia/vl_mvp.h> #include <GL/gl.h> #include <GL/glu.h> #include <GL/glx.h> #include <math.h> #include <unistd.h> #define CAMERA_Z 1000 #define MY_WIDTH 640 #define MY_HEIGHT 480 #define MY_TEX_WIDTH 512 #define MY_TEX_HEIGHT 256 #define MY_VL_PACKING VL_PACKING_ARGB_1555 #define MY_VL_FORMAT VL_FORMAT_RGB #define MY_VL_RATE 30 #define MY_VL_TIMING VL_TIMING_525_SQ_PIX /********************************************************************** * * This is just a handy little routine for debugging. * **********************************************************************/ void PrintParams( const DMparams* p, int indent ) { int len = dmParamsGetNumElems( p ); int i; int j; for ( i = 0; i < len; i++ ) { const char* name = dmParamsGetElem ( p, i ); DMparamtype type = dmParamsGetElemType( p, i ); for ( j = 0; j < indent; j++ ) { printf( " " ); } printf( "%8s: ", name ); switch( type ) { case DM_TYPE_ENUM: printf( "%d", dmParamsGetEnum( p, name ) ); break; case DM_TYPE_INT: printf( "%d", dmParamsGetInt( p, name ) ); break; case DM_TYPE_STRING: printf( "%s", dmParamsGetString( p, name ) ); break; case DM_TYPE_FLOAT: printf( "%f", dmParamsGetFloat( p, name ) ); break; case DM_TYPE_FRACTION: { DMfraction f; f = dmParamsGetFract( p, name ); printf( "%d/%d", f.numerator, f.denominator ); } break; case DM_TYPE_PARAMS: PrintParams( dmParamsGetParams( p, name ), indent + 4 ); break; case DM_TYPE_ENUM_ARRAY: { int i; const DMenumarray* array = dmParamsGetEnumArray( p, name ); for ( i = 0; i < array->elemCount; i++ ) { printf( "%d ", array->elems[i] ); } } break; case DM_TYPE_INT_ARRAY: { int i; const DMintarray* array = dmParamsGetIntArray( p, name ); for ( i = 0; i < array->elemCount; i++ ) { printf( "%d ", array->elems[i] ); } } break; case DM_TYPE_STRING_ARRAY: { int i; const DMstringarray* array = dmParamsGetStringArray( p, name ); for ( i = 0; i < array->elemCount; i++ ) { printf( "%s ", array->elems[i] ); } } break; case DM_TYPE_FLOAT_ARRAY: { int i; const DMfloatarray* array = dmParamsGetFloatArray( p, name ); for ( i = 0; i < array->elemCount; i++ ) { printf( "%f ", array->elems[i] ); } } break; case DM_TYPE_FRACTION_ARRAY: { int i; const DMfractionarray* array = dmParamsGetFractArray( p, name ); for ( i = 0; i < array->elemCount; i++ ) { printf( "%d/%d ", array->elems[i].numerator, array->elems[i].denominator ); } } break; case DM_TYPE_INT_RANGE: { const DMintrange* range = dmParamsGetIntRange( p, name ); printf( "%d ... %d", range->low, range->high ); } break; case DM_TYPE_FLOAT_RANGE: { const DMfloatrange* range = dmParamsGetFloatRange( p, name ); printf( "%f ... %f", range->low, range->high ); } break; case DM_TYPE_FRACTION_RANGE: { const DMfractionrange* range = dmParamsGetFractRange( p, name ); printf( "%d/%d ... %d/%d", range->low.numerator, range->low.denominator, range->high.numerator, range->high.denominator ); } break; defualt: printf( "UNKNOWN TYPE" ); } printf( "\n" ); } } /********************************************************************** * * Global Variables * **********************************************************************/ /******** * * theDisplay * ********/ Display* theDisplay; /******** * * theVideoServer * * This is that handle that is used to access the video library. * ********/ VLServer theVideoServer = NULL; /********************************************************************** * * Utility Functions * **********************************************************************/ /******** * * CHECK * * This is a simple error-checking macro that just exits the program if * anything goes wrong. * ********/ #define CHECK(condition,message) \ { \ if ( ! ( condition ) ) \ { \ fprintf( stderr, "Error at line %d of %s:\n", \ __LINE__, __FILE__ ); \ fprintf( stderr, " %s\n", message ); \ exit( 1 ); \ } \ } /******** * * CHECK_VL * * This is similar to CHECK, but also prints a VL error message. * ********/ #define CHECK_VL(condition,message) \ { \ if ( ! ( condition ) ) \ { \ fprintf( stderr, "Error at line %d of %s:\n", \ __LINE__, __FILE__ ); \ fprintf( stderr, " %s\n", message ); \ fprintf( stderr, " VL error message: %s\n", \ vlStrError( vlGetErrno() ) ); \ exit( 1 ); \ } \ } /******** * * CHECK_DM * * This is similar to CHECK, but also prints a DM error message. * ********/ #define CHECK_DM(condition,message) \ { \ if ( ! ( condition ) ) \ { \ const char* msg; \ int errornum; \ char detail[DM_MAX_ERROR_DETAIL]; \ fprintf( stderr, "Error at line %d of %s:\n", \ __LINE__, __FILE__ ); \ fprintf( stderr, " %s\n", message ); \ msg = dmGetError( &errornum, detail ); \ fprintf( stderr, " DM error message: %s\n", detail ); \ exit( 1 ); \ } \ } /******** * * getControlName * ********/ const char* getControlName( VLPath path, VLNode node, VLControlType type ) { static char name[VL_NAME_SIZE]; VLControlInfo* info; info = vlGetControlInfo( theVideoServer, path, node, type ); CHECK_VL( info != NULL, "vlGetControlInfo failed" ); strncpy( name, info->name, VL_NAME_SIZE ); vlFreeControlInfo( theVideoServer, info ); return name; } /******** * * setIntControl * ********/ void setIntControl( VLPath path, VLNode node, VLControlType type, int newValue ) { VLControlValue val; int vlstatus; val.intVal = newValue; vlstatus = vlSetControl( theVideoServer, path, node, type, &val ); #ifdef DEBUG printf( "Setting value %s: %d\n", getControlName( path, node, type ), val.intVal ); #endif if ( vlstatus != 0 && vlGetErrno() == VLValueOutOfRange ) { vlstatus = vlGetControl( theVideoServer, path, node, type, &val ); CHECK_VL( vlstatus == 0, "Could not get control" ); printf( "Actual value for %s: %d\n", getControlName( path, node, type ), val.intVal ); } else { CHECK_VL( vlstatus == 0, "Could not set control" ); } } /******** * * setFractControl * ********/ void setFractControl( VLPath path, VLNode node, VLControlType type, int numerator, int denominator ) { VLControlValue val; int vlstatus; val.fractVal.numerator = numerator; val.fractVal.denominator = denominator; #ifdef DEBUG printf( "Setting value %s: %d/%d\n", getControlName( path, node, type ), val.fractVal.numerator, val.fractVal.denominator ); #endif vlstatus = vlSetControl( theVideoServer, path, node, type, &val ); if ( vlstatus != 0 && vlGetErrno() == VLValueOutOfRange ) { vlstatus = vlGetControl( theVideoServer, path, node, type, &val ); CHECK_VL( vlstatus == 0, "Could not get control" ); printf( "Actual value for %s: %d/%d\n", getControlName( path, node, type ), val.fractVal.numerator, val.fractVal.denominator ); } else { CHECK_VL( vlstatus == 0, "Could not set control" ); } } /******** * * setXYControl * ********/ void setXYControl( VLPath path, VLNode node, VLControlType type, int x, int y ) { VLControlValue val; int vlstatus; val.xyVal.x = x; val.xyVal.y = y; #ifdef DEBUG printf( "Setting value %s: %d %d\n", getControlName( path, node, type ), val.xyVal.x, val.xyVal.y ); #endif vlstatus = vlSetControl( theVideoServer, path, node, type, &val ); if ( vlstatus != 0 && vlGetErrno() == VLValueOutOfRange ) { vlstatus = vlGetControl( theVideoServer, path, node, type, &val ); CHECK_VL( vlstatus == 0, "Could not get control" ); printf( "Actual value for %s: %d %d\n", getControlName( path, node, type ), val.xyVal.x, val.xyVal.y ); } else { CHECK_VL( vlstatus == 0, "Could not set control" ); /* Make sure the VL was not lying: */ vlstatus = vlGetControl( theVideoServer, path, node, type, &val ); if ( val.xyVal.x != x || val.xyVal.y != y ) { printf( "XXX Actual value for %s: %d %d\n", getControlName( path, node, type ), val.xyVal.x, val.xyVal.y ); } } } /******** * * getXYControl * ********/ void getXYControl( VLPath path, VLNode node, VLControlType type, int* returnX, int* returnY ) { VLControlValue val; int vlstatus; vlstatus = vlGetControl( theVideoServer, path, node, type, &val ); CHECK_VL( vlstatus == 0, "Could not get control" ); *returnX = val.xyVal.x; *returnY = val.xyVal.y; } /******** * * theErrorFlag * ********/ static DMboolean theErrorFlag = DM_FALSE; /******** * * theOldHandler * ********/ static int (*theOldHandler) ( Display*, XErrorEvent* ) = NULL; /******** * * errorHandler * ********/ static int errorHandler( Display* display, XErrorEvent* event ) { theErrorFlag = DM_TRUE; (*theOldHandler)( display, event ); return 0; /* return value is ignored */ } /******** * * installXErrorHandler * ********/ void installXErrorHandler( void ) { /* * This is probably overly paranoid. It will cause any errors * that are in the pipe already to use the old handler. */ XSync( theDisplay, DM_FALSE ); /* * Clear the flag. It will get set if an error occurs. */ theErrorFlag = DM_FALSE; /* * Install our error handler and save the old one. */ theOldHandler = XSetErrorHandler( errorHandler ); } /******** * * removeXErrorHandler * ********/ DMstatus removeXErrorHandler( void ) { /* * Force any errors out of the pipe. */ XSync( theDisplay, DM_FALSE ); /* * Replace the old error handler. */ XSetErrorHandler( theOldHandler ); /* * Did an error happen? */ if ( theErrorFlag ) { return DM_FAILURE; } else { return DM_SUCCESS; } } /********************************************************************** * * Video Input Paths * **********************************************************************/ /******** * * setupVideoInput * ********/ void setupVideoInput( int sourceNodeNumber, int layout, int zoomWidth, int zoomHeight, int width, int height, VLPath* returnPath, VLNode* returnNode ) { VLNode source; VLNode drain; VLPath path; VLControlValue value; int vlstatus; /* * Create the two nodes. */ source = vlGetNode( theVideoServer, VL_SRC, VL_VIDEO, sourceNodeNumber ); CHECK_VL( source != -1, "Could not create video source node." ) drain = vlGetNode( theVideoServer, VL_DRN, VL_MEM, VL_ANY ); CHECK_VL( drain != -1, "Could not create video drain node." ); /* * Create the path. */ path = vlCreatePath( theVideoServer, VL_ANY, source, drain ); CHECK_VL( path != -1, "Could not create video path." ); vlstatus = vlSetupPaths( theVideoServer, &path, 1, VL_SHARE, VL_SHARE ); CHECK_VL( vlstatus == 0, "Could not set up video path." ); /* * Set the controls on the path for the correct format. The * important one for this example is the layout parameter. * VL_LAYOUT_GRAPHICS generates images in the right format so that * the buffer can be used as a render area. * VL_LAYOUT_MIPMAP generated mipmapped images for use as textures. */ setIntControl ( path, source, VL_TIMING, MY_VL_TIMING ); /* * Set the ZOOMSIZE and SIZE. These must be set before setting * MIPMAP, but after setting the timing. */ setIntControl ( path, drain, VL_PACKING, MY_VL_PACKING ); setIntControl ( path, drain, VL_CAP_TYPE, VL_CAPTURE_INTERLEAVED ); setIntControl ( path, drain, VL_FORMAT, MY_VL_FORMAT ); setXYControl ( path, drain, VL_MVP_ZOOMSIZE, zoomWidth, zoomHeight ); setXYControl ( path, drain, VL_SIZE, width, height ); setFractControl( path, drain, VL_RATE, MY_VL_RATE, 1 ); setIntControl ( path, drain, VL_LAYOUT, layout ); *returnPath = path; *returnNode = drain; } /******** * * getVideoPoolConstraints * ********/ void getVideoPoolConstraints( DMparams* poolParams, VLPath path, VLNode node ) { int vlstatus; int prevBuffCount; int videoBuffCount; /* * Because this program has multiple paths in operation at the * same time, the buffer counts for the paths need to be summed. * By default, though, vlDMPoolGetParams assumes that multiple * paths will not be used simultaneously, so it just increases the * buffer count if it is not already enough for the one path. */ prevBuffCount = dmParamsGetInt( poolParams, DM_BUFFER_COUNT ); dmParamsSetInt( poolParams, DM_BUFFER_COUNT, 0 ); vlstatus = vlDMPoolGetParams( theVideoServer, path, node, poolParams ); CHECK_VL( vlstatus == 0, "Could not get video pool parameters" ); videoBuffCount = dmParamsGetInt( poolParams, DM_BUFFER_COUNT ); dmParamsSetInt( poolParams, DM_BUFFER_COUNT, prevBuffCount + videoBuffCount ); } /******** * * startVideoInput * ********/ void startVideoInput( VLPath path, VLNode drain, DMbufferpool pool ) { int vlstatus; /* * Tell the video library where to get the memory to store the * incoming frames. */ vlstatus = vlDMPoolRegister( theVideoServer, path, drain, pool ); CHECK_VL( vlstatus == 0, "Could not register pool with input path" ); /* * Start the transfer. */ vlstatus = vlBeginTransfer( theVideoServer, path, 0, NULL ); CHECK_VL( vlstatus == 0, "Could not start video input transfer" ); } /******** * * getOneInputFrame * ********/ DMbuffer getOneInputFrame( const char* pathName, VLPath path, stamp_t* msc ) { VLEvent event; DMbuffer buffer; int vlstatus; /* * Get the next event. */ vlstatus = vlEventRecv( theVideoServer, path, &event ); while ( vlstatus == -1 && vlGetErrno() == VLAgain ) { sginap( 1 ); vlstatus = vlEventRecv( theVideoServer, path, &event ); } CHECK( vlstatus == 0, "Could not get video input event" ); CHECK( event.reason == VLTransferComplete, "Unexpected video event received" ); /* * Get the buffer associated with the event. */ vlstatus = vlEventToDMBuffer( &event, &buffer ); CHECK( vlstatus == 0, "Video event does not have a buffer" ); /* * Verify the MSC. */ { USTMSCpair pair; pair = dmBufferGetUSTMSCpair( buffer ); if ( *msc != 0 && pair.msc != *msc + 1 ) { printf( "skipped a frame: %s\n", pathName ); } *msc = pair.msc; } return buffer; } /******** * * frameTooEarly * ********/ DMboolean frameTooEarly( DMbuffer frame, DMbuffer otherFrame ) { USTMSCpair pair1 = dmBufferGetUSTMSCpair( frame ); USTMSCpair pair2 = dmBufferGetUSTMSCpair( otherFrame ); const stamp_t slop = 20000000; /* 20ms */ return pair1.ust + slop < pair2.ust; } /********************************************************************** * * Video Output Path * **********************************************************************/ /******** * * setupVideoOutput * ********/ void setupVideoOutput( VLPath* returnPath, VLNode* returnNode ) { VLNode source; VLNode drain; VLPath path; VLControlValue value; int vlstatus; /* * Create the two nodes. */ source = vlGetNode( theVideoServer, VL_SRC, VL_MEM, VL_ANY ); CHECK_VL( source != -1, "Could not create video source node." ) drain = vlGetNode( theVideoServer, VL_DRN, VL_VIDEO, VL_ANY ); CHECK_VL( drain != -1, "Could not create video drain node." ); /* * Create the path. */ path = vlCreatePath( theVideoServer, VL_ANY, source, drain ); CHECK_VL( path != -1, "Could not create video path." ); vlstatus = vlSetupPaths( theVideoServer, &path, 1, VL_SHARE, VL_SHARE ); CHECK_VL( vlstatus == 0, "Could not set up video path." ); /* * Set the controls on the path for the correct format. The * important one for this example is VL_LAYOUT_GRAPHICS, which * specifies that the frame sent for video output will be coming * from OpenGL. */ setIntControl ( path, source, VL_PACKING, MY_VL_PACKING ); setIntControl ( path, source, VL_CAP_TYPE, VL_CAPTURE_INTERLEAVED ); setFractControl( path, source, VL_RATE, MY_VL_RATE, 1 ); setIntControl ( path, source, VL_FORMAT, MY_VL_FORMAT ); setIntControl ( path, source, VL_LAYOUT, VL_LAYOUT_GRAPHICS ); setIntControl ( path, drain, VL_TIMING, MY_VL_TIMING ); setXYControl ( path, source, VL_SIZE, MY_WIDTH, MY_HEIGHT ); *returnPath = path; *returnNode = source; } /******** * * startVideoOutput * ********/ void startVideoOutput( VLPath path ) { int vlstatus; /* * Start the transfer. */ vlstatus = vlBeginTransfer( theVideoServer, path, 0, NULL ); CHECK_VL( vlstatus == 0, "Could not start video input transfer" ); } /******** * * putOneOutputFrame * ********/ void putOneOutputFrame( VLPath path, DMbuffer frame ) { int vlstatus; vlstatus = vlDMBufferSend( theVideoServer, path, frame ); CHECK( vlstatus == 0, "Could not send video frame" ); } /********************************************************************** * * Graphics * **********************************************************************/ #define ARRAY_COUNT(a) (sizeof(a)/sizeof(a[0])) static void printFBConfig( GLXFBConfigSGIX config ) { static struct { int attrib; const char* name; } attribs [] = { { GLX_BUFFER_SIZE, "GLX_BUFFER_SIZE" }, { GLX_LEVEL, "GLX_LEVEL" }, { GLX_DOUBLEBUFFER, "GLX_DOUBLEBUFFER" }, { GLX_AUX_BUFFERS, "GLX_AUX_BUFFERS" }, { GLX_RED_SIZE, "GLX_RED_SIZE" }, { GLX_GREEN_SIZE, "GLX_GREEN_SIZE" }, { GLX_BLUE_SIZE, "GLX_BLUE_SIZE" }, { GLX_ALPHA_SIZE, "GLX_ALPHA_SIZE" }, { GLX_DEPTH_SIZE, "GLX_DEPTH_SIZE" }, { GLX_DRAWABLE_TYPE_SGIX, "GLX_DRAWABLE_TYPE_SGIX" }, { GLX_RENDER_TYPE_SGIX, "GLX_RENDER_TYPE_SGIX" }, { GLX_X_RENDERABLE_SGIX, "GLX_X_RENDERABLE_SGIX" }, { GLX_MAX_PBUFFER_WIDTH_SGIX, "GLX_MAX_PBUFFER_WIDTH_SGIX" }, { GLX_MAX_PBUFFER_HEIGHT_SGIX, "GLX_MAX_PBUFFER_HEIGHT_SGIX"}, }; int i; for ( i = 0; i < ARRAY_COUNT( attribs ); i++ ) { int value; int errorCode = glXGetFBConfigAttribSGIX( theDisplay, config, attribs[i].attrib, &value ); CHECK( errorCode == 0, "glXGetFBConfigAttribSGIX failed" ); printf( " %24s = %d\n", attribs[i].name, value ); } } /******** * * getFBConfig * ********/ GLXFBConfigSGIX getFBConfig( void ) { int i; /* * Find a frame buffer configuration that suits our needs: it must * work with both the pbuffers and the window, and must have 5551 pixels, * because that is the only format that can be used to capture * mipmapped video. */ GLXFBConfigSGIX config = NULL; int params[] = { GLX_RENDER_TYPE_SGIX, GLX_RGBA_BIT_SGIX, GLX_DRAWABLE_TYPE_SGIX, GLX_PBUFFER_BIT_SGIX, GLX_ALPHA_SIZE, 1, GLX_RED_SIZE, 5, GLX_GREEN_SIZE, 5, GLX_BLUE_SIZE, 5, GLX_DEPTH_SIZE, 24, GLX_X_RENDERABLE_SGIX, False, (int)None, }; int configCount; GLXFBConfigSGIX* configs = glXChooseFBConfigSGIX( theDisplay, DefaultScreen(theDisplay), params, &configCount ); CHECK( configs != NULL || configCount != 0, "Could not get FBConfig" ); for ( i = 0; i < configCount; i++ ) { int value; glXGetFBConfigAttribSGIX(theDisplay, configs[i], GLX_ALPHA_SIZE, &value); if ( value != 1 ) continue; glXGetFBConfigAttribSGIX(theDisplay, configs[i], GLX_RED_SIZE, &value); if ( value != 5 ) continue; glXGetFBConfigAttribSGIX(theDisplay, configs[i], GLX_GREEN_SIZE, &value); if ( value != 5 ) continue; glXGetFBConfigAttribSGIX(theDisplay, configs[i], GLX_BLUE_SIZE, &value); if ( value != 5 ) continue; break; } if ( i != configCount ) { config = configs[i]; } CHECK( config != NULL, "No 5551 FBconfigs were found" ); XFree( configs ); printFBConfig( config ); return config; } /******** * * setupDrawable * ********/ void setupDrawable( int width, int height, GLXPbufferSGIX* returnDrawable ) { GLXPbufferSGIX drawable; int attrib[] = { GLX_PRESERVED_CONTENTS_SGIX, GL_TRUE, GLX_DIGITAL_MEDIA_PBUFFER_SGIX, GL_TRUE, None}; /* * Find a frame buffer configuration that suits our needs. */ GLXFBConfigSGIX config = getFBConfig(); /* * Create the GLXPbufferSGIX. */ printf( "Drawable size: %d x %d\n", width, height ); installXErrorHandler(); drawable = glXCreateGLXPbufferSGIX( theDisplay, config, width, height, attrib); CHECK( removeXErrorHandler() == DM_SUCCESS && drawable != None, "Could not create pbuffer" ); /* * All done. */ *returnDrawable = drawable; } /******** * * getGraphicsPoolConstraints * ********/ void getGraphicsPoolConstraints( DMparams* imageFormat, DMparams* poolParams ) { DMstatus status; status = dmBufferGetGLPoolParams( imageFormat, poolParams ); CHECK_DM( status == DM_SUCCESS, "Could not get graphics pool params" ); status = dmParamsSetInt( poolParams, DM_BUFFER_COUNT, dmParamsGetInt( poolParams, DM_BUFFER_COUNT ) + 2 ); CHECK_DM( status == DM_SUCCESS, "dmParamsSetInt failed" ); } /******** * * setupTexture * ********/ void setupTexture( void ) { /* * Before the mipmapped texture can be copied from video, there * must already be a texture there to copy into. */ int w = MY_TEX_WIDTH; int h = MY_TEX_HEIGHT; int level = 0; void* black = calloc( w * h, 4 ); level = 0; while ( 1 ) { glTexImage2D( GL_TEXTURE_2D, level, GL_RGB5_A1_EXT, /* number of components */ w, h, 0, /* border */ GL_RGBA, GL_UNSIGNED_BYTE, black ); printf( "%d: %d x %d\n", level, w, h ); if ( w == 1 && h == 1 ) { break; } if ( w > 1 ) w /= 2; if ( h > 1 ) h /= 2; level++; } free( black ); } /******** * * startGraphics * ********/ void startGraphics( GLXPbufferSGIX drawable, DMparams* drawableFormat, GLXPbufferSGIX readable, DMparams* readableFormat, DMbufferpool pool1, DMbufferpool pool2, GLXContext* returnContext ) { GLXFBConfigSGIX config; GLXContext context; int ok; /* * Get the FB configuration to use. */ config = getFBConfig(); /* * Create the graphics context. */ installXErrorHandler(); context = glXCreateContextWithConfigSGIX( theDisplay, config, GLX_RGBA_TYPE_SGIX, NULL, GL_TRUE ); CHECK( removeXErrorHandler() == DM_SUCCESS && context != NULL, "Could not create graphics context" ); /* * The two pbuffers must have associated DMbuffer before they can * be made current. (The will be associated with real video * frames before any rendering happens.) */ { DMbuffer buffer; CHECK( dmBufferAllocate( pool1, &buffer ) == DM_SUCCESS, "could not create dummy buffer" ); installXErrorHandler(); ok = glXAssociateDMPbufferSGIX( theDisplay, drawable, drawableFormat, buffer); CHECK( removeXErrorHandler() == DM_SUCCESS && ok, "Could not associate DMbuffer with pbuffer" ); } { DMbuffer buffer; CHECK( dmBufferAllocate( pool2, &buffer ) == DM_SUCCESS, "could not create dummy buffer" ); installXErrorHandler(); ok = glXAssociateDMPbufferSGIX( theDisplay, readable, readableFormat, buffer); CHECK( removeXErrorHandler() == DM_SUCCESS && ok, "Could not associate DMbuffer with pbuffer" ); } /* * Because of a bug in OpenGL, dmpbuffers must be made current as * a drawable before being used as a readable. */ installXErrorHandler(); ok = glXMakeCurrent( theDisplay, readable, context ); CHECK( removeXErrorHandler() == DM_SUCCESS && ok, "glXMakeCurrent failed" ); /* * Make it the current context. * DM Pbuffers must have associated DMbuffers before * they can be made current. */ installXErrorHandler(); ok = glXMakeCurrentReadSGI( theDisplay, drawable, readable, context ); CHECK( removeXErrorHandler() == DM_SUCCESS && ok, "glXMakeCurrent failed" ); /* * Set up the OpenGL transforms. */ glViewport( 0, 0, MY_WIDTH, MY_HEIGHT ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glFrustum( -MY_WIDTH / 2.0, MY_WIDTH / 2.0, -MY_HEIGHT / 2.0, MY_HEIGHT / 2.0, CAMERA_Z - MY_WIDTH / 2.0, CAMERA_Z + MY_WIDTH / 2.0 ); glMatrixMode( GL_MODELVIEW ); glTranslatef( 0.0, 0.0, -CAMERA_Z ); glEnable( GL_TEXTURE_2D ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glEnable( GL_DEPTH_TEST ); /* * Load a black texture. Video images will be loaded as * subtextures. * XXX This is only until the mipmap/dmbuffer/OpenGL stuff works. */ setupTexture(); /* * Turn on the mipmap extension so that OpenGL will use the * mipmaps created by video. */ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); *returnContext = context; } /******** * * drawFace * ********/ void drawFace( Bool borders, float z ) { if ( borders ) { glDisable( GL_TEXTURE_2D ); glBegin( GL_QUADS ); { glTexCoord2f( 0.0, 0.0 ); glVertex3i( -160, -160, z ); glTexCoord2f( 0.0, 1.0 ); glVertex3i( -160, -120, z ); glTexCoord2f( 1.0, 1.0 ); glVertex3i( 160, -120, z ); glTexCoord2f( 1.0, 0.0 ); glVertex3i( 160, -160, z ); glTexCoord2f( 0.0, 0.0 ); glVertex3i( -160, 120, z ); glTexCoord2f( 0.0, 1.0 ); glVertex3i( -160, 160, z ); glTexCoord2f( 1.0, 1.0 ); glVertex3i( 160, 160, z ); glTexCoord2f( 1.0, 0.0 ); glVertex3i( 160, 120, z ); } glEnd(); } glEnable( GL_TEXTURE_2D ); glBegin( GL_QUADS ); { glTexCoord2f( 0.0, 0.0 ); glVertex3i( -160, -120, z ); glTexCoord2f( 0.0, 1.0 ); glVertex3i( -160, 120, z ); glTexCoord2f( 1.0, 1.0 ); glVertex3i( 160, 120, z ); glTexCoord2f( 1.0, 0.0 ); glVertex3i( 160, -120, z ); } glEnd(); } /******** * * drawCube * ********/ void drawCube( void ) { glColor4f( 0.5, 0.5, 0.5, 1.0 ); /* front */ drawFace( False, 160 ); /* right */ glPushMatrix(); glRotatef( 90.0, 0.0, 1.0, 0.0 ); drawFace( False, 160 ); glPopMatrix(); /* left */ glPushMatrix(); glRotatef( -90.0, 0.0, 1.0, 0.0 ); drawFace( False, 160 ); glPopMatrix(); /* back */ glPushMatrix(); glRotatef( 180.0, 0.0, 1.0, 0.0 ); drawFace( False, 160 ); glPopMatrix(); /* top */ glPushMatrix(); glRotatef( -90.0, 1.0, 0.0, 0.0 ); drawFace( True, 120 ); glPopMatrix(); /* bottom */ glPushMatrix(); glRotatef( 90.0, 1.0, 0.0, 0.0 ); drawFace( True, 120 ); glPopMatrix(); } /******** * * renderOneFrame * ********/ void renderOneFrame( stamp_t msc, DMbuffer frame1, DMbuffer frame2 ) { static int counter = 0; /* * Load the texture. This call to "copy" the texture actually * just changes the texture object to point at this dmbuffer * because: * 1) the entire texture is copied. * 2) the format of the dmbuffer is the same as the texture. */ glCopyTexSubImage2DEXT( GL_TEXTURE_2D, 0, /* level */ 0, 0, /* x, y offset into target */ 0, 0, /* lower-left corner of copied rect */ MY_TEX_WIDTH, MY_TEX_HEIGHT /* size of copied rect */ ); /* * The depth buffer must be cleared. It is not part of the * DMbuffer, so is retained from frame to frame. */ glClear( GL_DEPTH_BUFFER_BIT ); /* * Animate the cube. */ { float count = ( (float) (counter++) ); float angleX = count / 2; float angleY = count / M_PI; float scale = ( cosf( count / 300.0 ) + 2.0 ) / 2.5; glMatrixMode( GL_MODELVIEW ); glPushMatrix(); glRotatef( angleX, 1.0, 0.0, 0.0 ); glRotatef( angleY, 0.0, 1.0, 0.0 ); glScalef( scale, scale, scale ); } /* * Draw a cube using the texture. */ drawCube(); /* * Restore the transform */ glPopMatrix(); } /********************************************************************** * * Top-Level Control * **********************************************************************/ /******** * * processOneFrame * ********/ void processOneFrame( DMparams* path1Format, VLPath path1, VLNode drain1, stamp_t* path1MSC, DMparams* path2Format, VLPath path2, VLNode drain2, stamp_t* path2MSC, GLXPbufferSGIX drawable, GLXPbufferSGIX readable, VLPath path3, VLNode source3, int bufferSize1, int bufferSize2 ) { DMbuffer frame1; DMbuffer frame2; Bool ok; /* * Get a frame from each of the input paths. */ frame1 = getOneInputFrame( "path 1", path1, path1MSC ); frame2 = getOneInputFrame( "path 2", path2, path2MSC ); /* * Make sure that they are in sync. */ while ( frameTooEarly( frame1, frame2 ) ) { printf( "path1 frame was too early.\n" ); dmBufferFree( frame1 ); frame1 = getOneInputFrame( "path 1", path1, path1MSC ); } while ( frameTooEarly( frame2, frame1 ) ) { printf( "path2 frame was too early.\n" ); dmBufferFree( frame2 ); frame2 = getOneInputFrame( "path 2", path2, path2MSC ); } #ifdef DEBUG printf( "path1 MSC = %lld path2 MSC = %lld\n", *path1MSC, *path2MSC ); #endif /* * Bind them to the drawable and readable so that we can get at * them from OpenGL. */ ok = glXAssociateDMPbufferSGIX( theDisplay, drawable, path1Format, frame1); CHECK( ok, "Could not associate path1 DMbuffer with pbuffer" ); ok = glXAssociateDMPbufferSGIX( theDisplay, readable, path2Format, frame2); CHECK( ok, "Could not associate path2 DMbuffer with pbuffer" ); /* * Do the graphics. */ renderOneFrame( *path1MSC, frame1, frame2 ); /* * Make sure the graphics are complete before we pass the output * frame to video. */ glFinish(); /* * Send the processed frame out. */ putOneOutputFrame( path3, frame1 ); /* * We're done with both of the buffers now. The video library * still has a handle to frame1 (from the output path), which will * be released when the frame goes out. glX still has handles to * both (from the drawable and readable), which will be released * the next time this function is called. */ dmBufferFree( frame1 ); dmBufferFree( frame2 ); } /******** * * main * ********/ int main( ) { DMstatus status; DMbufferpool pool1; /* For paths 1 and 3 */ DMbufferpool pool2; /* For path 2 */ int bufferSize1; int bufferSize2; VLPath path1; /* The first input path (passed through) */ VLNode drain1; VLPath path2; /* The other input path (used as texture) */ VLNode drain2; GLXPbufferSGIX drawable; /* This is where the rendering happens */ GLXPbufferSGIX readable; /* This is the source for loading textures */ GLXContext context; VLPath path3; /* The output path */ VLNode source3; DMparams* path1Format; /* this is also the format for path3 */ DMparams* path2Format; int vlstatus; /* * Set up the parameter lists that describes the image formats. */ status = dmParamsCreate( &path1Format ); CHECK_DM( status == DM_SUCCESS, "Could not create dmparams" ); status = dmSetImageDefaults( path1Format, MY_WIDTH, MY_HEIGHT, DM_IMAGE_PACKING_XRGB1555 ); CHECK_DM( status == DM_SUCCESS, "Could not set image param" ); status = dmParamsSetEnum( path1Format, DM_IMAGE_LAYOUT, DM_IMAGE_LAYOUT_GRAPHICS ); CHECK_DM( status == DM_SUCCESS, "Could not set image param" ); status = dmParamsCreate( &path2Format ); CHECK_DM( status == DM_SUCCESS, "Could not create dmparams" ); status = dmSetImageDefaults( path2Format, MY_TEX_WIDTH, MY_TEX_HEIGHT, DM_IMAGE_PACKING_XRGB1555 ); CHECK_DM( status == DM_SUCCESS, "Could not set image param" ); status = dmParamsSetEnum( path2Format, DM_IMAGE_LAYOUT, DM_IMAGE_LAYOUT_MIPMAP ); CHECK_DM( status == DM_SUCCESS, "Could not set image param" ); printf( "\n\nPath 1 format:\n" ); PrintParams( path1Format, 4 ); printf( "\n\nPath 2 format:\n" ); PrintParams( path2Format, 4 ); /* XXX What should the orientation be? */ /* * Open the connection to the X server. */ theDisplay = XOpenDisplay( ":0" ); CHECK( theDisplay != NULL, "Could not open connection to local X server" ); /* * Open the video library. */ theVideoServer = vlOpenVideo( "" ); CHECK_VL( theVideoServer != NULL, "Could not open connection to video server" ); /* * Set up the input and output video paths. */ setupVideoInput( VL_MVP_VIDEO_SOURCE_CAMERA, VL_LAYOUT_GRAPHICS, MY_WIDTH, MY_HEIGHT, /* ZOOMSIZE */ MY_WIDTH, MY_HEIGHT, /* SIZE */ &path1, &drain1 ); setupVideoInput( VL_MVP_VIDEO_SOURCE_COMPOSITE, VL_LAYOUT_MIPMAP, MY_TEX_WIDTH, MY_TEX_HEIGHT, /* ZOOMSIZE */ MY_TEX_WIDTH, MY_TEX_HEIGHT, /* SIZE */ &path2, &drain2 ); setupVideoOutput( &path3, &source3 ); /* * Set up the drawable and readable. */ setupDrawable( MY_WIDTH, MY_HEIGHT, &drawable ); setupDrawable( MY_TEX_WIDTH, MY_TEX_HEIGHT, &readable ); /* * Create the memory pools that will be used to hold the images. */ { DMparams* poolParams; status = dmParamsCreate( &poolParams ); CHECK_DM( status == DM_SUCCESS, "Could not create pool params." ); status = dmBufferSetPoolDefaults( poolParams, 0, 0, DM_FALSE, DM_FALSE ); CHECK_DM( status == DM_SUCCESS, "Could not set pool defaults." ); printf( "\n\nDefault pool params:\n" ); PrintParams( poolParams, 4 ); getVideoPoolConstraints( poolParams, path1, drain1 ); printf( "\n\nAfter path1:\n" ); PrintParams( poolParams, 4 ); getVideoPoolConstraints( poolParams, path3, source3 ); printf( "\n\nAfter path3:\n" ); PrintParams( poolParams, 4 ); getGraphicsPoolConstraints( path1Format, poolParams ); printf( "\n\nAfter path1 graphics:\n" ); PrintParams( poolParams, 4 ); printf( "\n\nFinal:\n" ); PrintParams( poolParams, 4 ); status = dmBufferCreatePool( poolParams, &pool1 ); CHECK_DM( status == DM_SUCCESS, "Could not create buffer pool" ); bufferSize1 = dmParamsGetInt( poolParams, DM_BUFFER_SIZE ); dmParamsDestroy( poolParams ); } { DMparams* poolParams; status = dmParamsCreate( &poolParams ); CHECK_DM( status == DM_SUCCESS, "Could not create pool params." ); status = dmBufferSetPoolDefaults( poolParams, 0, 0, DM_FALSE, DM_FALSE ); CHECK_DM( status == DM_SUCCESS, "Could not set pool defaults." ); printf( "\n\nDefault pool params:\n" ); PrintParams( poolParams, 4 ); getVideoPoolConstraints( poolParams, path2, drain2 ); printf( "\n\nAfter path2:\n" ); PrintParams( poolParams, 4 ); getGraphicsPoolConstraints( path2Format, poolParams ); printf( "\n\nAfter path2 graphics:\n" ); PrintParams( poolParams, 4 ); printf( "\n\nFinal:\n" ); PrintParams( poolParams, 4 ); status = dmBufferCreatePool( poolParams, &pool2 ); CHECK_DM( status == DM_SUCCESS, "Could not create buffer pool" ); bufferSize2 = dmParamsGetInt( poolParams, DM_BUFFER_SIZE ); dmParamsDestroy( poolParams ); } /* * Do one-time initialization of graphics. */ startGraphics( drawable, path1Format, readable, path2Format, pool1, pool2, &context ); /* * Start all three video paths. */ printf( "Starting path 1...\n" ); startVideoInput( path1, drain1, pool1 ); printf( "Starting path 2...\n" ); startVideoInput( path2, drain2, pool2 ); printf( "Starting path 3...\n" ); startVideoOutput( path3 ); /* * This is where the work gets done. */ { int frameNum; stamp_t path1MSC = 0; stamp_t path2MSC = 0; for ( frameNum = 0; frameNum < 10000; frameNum++ ) { processOneFrame( path1Format, path1, drain1, &path1MSC, path2Format, path2, drain2, &path2MSC, drawable, readable, path3, source3, bufferSize1, bufferSize2 ); } } /* * Clean up. */ glXMakeCurrentReadSGI(theDisplay, None, None, NULL); glXDestroyContext( theDisplay, context ); glXDestroyGLXPbufferSGIX( theDisplay, drawable ); glXDestroyGLXPbufferSGIX( theDisplay, readable ); vlstatus = vlDestroyPath( theVideoServer, path1 ); CHECK_VL( vlstatus == 0, "vlDestroyPath failed" ); vlstatus = vlDestroyPath( theVideoServer, path2 ); CHECK_VL( vlstatus == 0, "vlDestroyPath failed" ); vlstatus = vlDestroyPath( theVideoServer, path3 ); CHECK_VL( vlstatus == 0, "vlDestroyPath failed" ); vlCloseVideo( theVideoServer ); return 0; }