Tidied up the displayer code and added some documentation

This commit is contained in:
Joel Holdsworth 2008-12-30 13:06:08 +00:00
parent 8cb48a7298
commit cdf26d3fe6
5 changed files with 405 additions and 316 deletions

View file

@ -33,99 +33,97 @@
namespace gui {
namespace output {
#define MAX_WIDTH 720
#define MAX_HEIGHT 576
/**
* Supported Displayer formats
**/
typedef enum {
DISPLAY_NONE,
DISPLAY_YUV,
DISPLAY_RGB,
DISPLAY_BGR,
DISPLAY_BGR0,
DISPLAY_RGB16
}
DisplayerInput;
/** Supported Displayer formats
/**
* A Displayer is a class which is responsible for rendering an image
* in some way (ie: Xvideo, GDK, OpenGL etc).
*
* @remarks All Displayer classes must extend the Displayer class and
* minimally rewrite:
*
* + usable() - to indicate if the object can be used,
* + format() - to indicate what type of input the put method expects
* + put( void * ) - deal with an image of the expected type and size
*
* By default, all images will be delivered to the put method in a
* resolution of IMG_WIDTH * IMG_HEIGHT. If another size is required,
* then the rewrite the methods:
*
* + preferredWidth
* + preferredHeight
*
* If the widget being written to doesn't need a fixed size, then
* rewrite the two other put methods as required.
*/
class Displayer
{
public:
/**
* Indicates if this object can be used to render images on the
* running system.
*/
typedef enum {
DISPLAY_NONE,
DISPLAY_YUV,
DISPLAY_RGB,
DISPLAY_BGR,
DISPLAY_BGR0,
DISPLAY_RGB16
}
DisplayerInput;
virtual bool usable();
/**
* Indicates the format required by the abstract put method.
*/
virtual DisplayerInput format();
/**
* Expected width of input to put.
*/
virtual int preferredWidth();
/**
* Expected height of input to put.
*/
virtual int preferredHeight();
/**
* A Displayer is a class which is responsible for rendering an image
* in some way (ie: Xvideo, GDK, OpenGL etc).
*
* @remarks All Displayer classes must extend the Displayer class and
* minimally rewrite:
*
* + usable() - to indicate if the object can be used,
* + format() - to indicate what type of input the put method expects
* + put( void * ) - deal with an image of the expected type and size
*
* By default, all images will be delivered to the put method in a
* resolution of IMG_WIDTH * IMG_HEIGHT. If another size is required,
* then the rewrite the methods:
*
* + preferredWidth
* + preferredHeight
*
* If the widget being written to doesn't need a fixed size, then
* rewrite the two other put methods as required.
* Put an image of a given width and height with the expected input
* format (as indicated by the format method).
*/
class Displayer
{
public:
virtual void put( const void* ) = 0;
/**
* Indicates if an object can be used to render images on the
* running system.
*/
virtual bool usable();
/**
* Indicates the format required by the abstract put method.
*/
virtual DisplayerInput format();
/**
* Expected width of input to put.
*/
virtual int preferredWidth();
/**
* Expected height of input to put.
*/
virtual int preferredHeight();
/**
* Put an image of a given width and height with the expected input
* format (as indicated by the format method).
*/
virtual void put( void * ) = 0;
protected:
/**
* Calculates the coordinates for placing a video image inside a
* widget
*
* @param[in] widget_width The width of the display widget.
* @param[in] widget_height The height of the display widget.
* @param[in] image_width The width of the video image.
* @param[in] image_height The height of the video image.
* @param[out] video_x The x-coordinate of the top left
* corner of the scaled video image.
* @param[out] video_y The y-coordinate of the top left
* corner of the scaled video image.
* @param[out] video_width The width of the scale video image.
* @param[out] video_height The height of the scale video image.
*/
static void calculateVideoLayout(
int widget_width, int widget_height,
int image_width, int image_height,
int &video_x, int &video_y, int &video_width, int &video_height );
protected:
protected:
int imageWidth;
int imageHeight;
};
/**
* Calculates the coordinates for placing a video image inside a
* widget
*
* @param[in] widget_width The width of the display widget.
* @param[in] widget_height The height of the display widget.
* @param[in] image_width The width of the video image.
* @param[in] image_height The height of the video image.
* @param[out] video_x The x-coordinate of the top left
* corner of the scaled video image.
* @param[out] video_y The y-coordinate of the top left
* corner of the scaled video image.
* @param[out] video_width The width of the scale video image.
* @param[out] video_height The height of the scale video image.
*/
static void calculateVideoLayout(
int widget_width, int widget_height,
int image_width, int image_height,
int &video_x, int &video_y, int &video_width, int &video_height );
protected:
int imageWidth;
int imageHeight;
};
} // namespace output
} // namespace gui

View file

@ -36,49 +36,49 @@ namespace output {
GdkDisplayer::GdkDisplayer( Gtk::Widget *drawing_area, int width, int height ) :
drawingArea( drawing_area )
{
REQUIRE(drawing_area != NULL);
REQUIRE(width > 0);
REQUIRE(height > 0);
imageWidth = width, imageHeight = height;
}
{
REQUIRE(drawing_area != NULL);
REQUIRE(width > 0);
REQUIRE(height > 0);
imageWidth = width, imageHeight = height;
}
bool
GdkDisplayer::usable()
{
return true;
}
{
return true;
}
void
GdkDisplayer::put( void *image )
{
int video_x = 0, video_y = 0, video_width = 0, video_height = 0;
calculateVideoLayout(
drawingArea->get_width(),
drawingArea->get_height(),
preferredWidth(), preferredHeight(),
video_x, video_y, video_width, video_height );
GdkDisplayer::put( const void* image )
{
int video_x = 0, video_y = 0, video_width = 0, video_height = 0;
calculateVideoLayout(
drawingArea->get_width(),
drawingArea->get_height(),
preferredWidth(), preferredHeight(),
video_x, video_y, video_width, video_height );
GdkWindow *window = drawingArea->get_window()->gobj();
REQUIRE(window != NULL);
GdkGC *gc = gdk_gc_new( window );
REQUIRE(gc != NULL);
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data( (const guchar*)image, GDK_COLORSPACE_RGB, FALSE, 8,
preferredWidth(), preferredHeight(), preferredWidth() * 3, NULL, NULL );
REQUIRE(pixbuf != NULL);
GdkPixbuf *scaled_image = gdk_pixbuf_scale_simple( pixbuf, video_width, video_height, GDK_INTERP_NEAREST );
REQUIRE(scaled_image != NULL);
gdk_draw_pixbuf( window, gc, scaled_image, 0, 0, video_x, video_y, -1, -1, GDK_RGB_DITHER_NORMAL, 0, 0 );
GdkWindow *window = drawingArea->get_window()->gobj();
REQUIRE(window != NULL);
g_object_unref( scaled_image );
g_object_unref( pixbuf );
g_object_unref( gc );
}
GdkGC *gc = gdk_gc_new( window );
REQUIRE(gc != NULL);
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data( (const guchar*)image, GDK_COLORSPACE_RGB, FALSE, 8,
preferredWidth(), preferredHeight(), preferredWidth() * 3, NULL, NULL );
REQUIRE(pixbuf != NULL);
GdkPixbuf *scaled_image = gdk_pixbuf_scale_simple( pixbuf, video_width, video_height, GDK_INTERP_NEAREST );
REQUIRE(scaled_image != NULL);
gdk_draw_pixbuf( window, gc, scaled_image, 0, 0, video_x, video_y, -1, -1, GDK_RGB_DITHER_NORMAL, 0, 0 );
g_object_unref( scaled_image );
g_object_unref( pixbuf );
g_object_unref( gc );
}
} // namespace output
} // namespace gui

View file

@ -40,19 +40,48 @@ namespace Gtk {
namespace gui {
namespace output {
/**
* GdkDisplayer is a class which is responsible for rendering a video
* image via GDK.
**/
class GdkDisplayer : public Displayer
{
public:
GdkDisplayer( Gtk::Widget *drawing_area, int width, int height );
{
public:
void put( void *image );
protected:
bool usable();
/**
* Constructor
* @param[in] drawing_area The widget into which the video image will
* be drawn. This value must not be NULL.
* @param[in] width The width of the video image in pixels. This value
* must be greater than zero.
* @param[in] height The height of the video image in pixels. This
* value must be greater than zero.
**/
GdkDisplayer( Gtk::Widget *drawing_area, int width, int height );
private:
Gtk::Widget *drawingArea;
};
/**
* Put an image of a given width and height with the expected input
* format (as indicated by the format method).
* @param[in] image The video image array to draw.
*/
void put( const void* image );
protected:
/**
* Indicates if this object can be used to render images on the
* running system.
*/
bool usable();
private:
/**
* The widget that video will be drawn into.
* @remarks This value must be a valid pointer.
**/
Gtk::Widget *drawingArea;
};
} // namespace output
} // namespace gui

View file

@ -35,190 +35,192 @@ XvDisplayer::XvDisplayer( Gtk::Widget *drawing_area, int width, int height ) :
gotPort( false ),
drawingArea( drawing_area ),
xvImage( NULL )
{
INFO(gui, "Trying XVideo at %d x %d", width, height);
{
REQUIRE(drawing_area != NULL);
REQUIRE(width > 0);
REQUIRE(height > 0);
INFO(gui, "Trying XVideo at %d x %d", width, height);
imageWidth = width, imageHeight = height;
imageWidth = width, imageHeight = height;
shmInfo.shmaddr = NULL;
shmInfo.shmaddr = NULL;
Glib::RefPtr<Gdk::Window> area_window = drawing_area->get_window();
Glib::RefPtr<Gdk::Window> area_window = drawing_area->get_window();
window = gdk_x11_drawable_get_xid( area_window->gobj() );
display = gdk_x11_drawable_get_xdisplay( area_window->gobj() );
window = gdk_x11_drawable_get_xid( area_window->gobj() );
display = gdk_x11_drawable_get_xdisplay( area_window->gobj() );
unsigned int count;
XvAdaptorInfo *adaptorInfo;
unsigned int count;
XvAdaptorInfo *adaptorInfo;
if ( XvQueryAdaptors( display, window, &count, &adaptorInfo ) == Success )
{
if ( XvQueryAdaptors( display, window, &count, &adaptorInfo ) == Success )
{
INFO(gui, "XvQueryAdaptors count: %d", count);
for ( unsigned int n = 0; gotPort == false && n < count; ++n )
{
// Diagnostics
INFO(gui, "%s, %d, %d, %d", adaptorInfo[ n ].name,
adaptorInfo[ n ].base_id, adaptorInfo[ n ].num_ports - 1);
INFO(gui, "XvQueryAdaptors count: %d", count);
for ( unsigned int n = 0; gotPort == false && n < count; ++n )
{
// Diagnostics
INFO(gui, "%s, %d, %d, %d", adaptorInfo[ n ].name,
adaptorInfo[ n ].base_id, adaptorInfo[ n ].num_ports - 1);
for ( port = adaptorInfo[ n ].base_id;
port < adaptorInfo[ n ].base_id + adaptorInfo[ n ].num_ports;
port ++ )
for ( unsigned int port = adaptorInfo[ n ].base_id;
port < adaptorInfo[ n ].base_id + adaptorInfo[ n ].num_ports;
port ++ )
{
if ( XvGrabPort( display, port, CurrentTime ) == Success )
{
int formats;
XvImageFormatValues *list;
list = XvListImageFormats( display, port, &formats );
INFO(gui, "formats supported: %d", formats);
for ( int i = 0; i < formats; i ++ )
{
INFO(gui, "0x%x (%c%c%c%c) %s",
list[ i ].id,
( list[ i ].id ) & 0xff,
( list[ i ].id >> 8 ) & 0xff,
( list[ i ].id >> 16 ) & 0xff,
( list[ i ].id >> 24 ) & 0xff,
( list[ i ].format == XvPacked ) ? "packed" : "planar" );
if ( list[ i ].id == 0x32595559 && !gotPort )
gotPort = true;
}
if ( !gotPort )
{
XvUngrabPort( display, port, CurrentTime );
}
else
{
grabbedPort = port;
break;
}
}
}
}
if ( gotPort )
{
int num;
unsigned int unum;
XvEncodingInfo *enc;
XvQueryEncodings( display, grabbedPort, &unum, &enc );
for ( unsigned int index = 0; index < unum; index ++ )
{
INFO(gui, "%d: %s, %ldx%ld rate = %d/%d", index, enc->name,
enc->width, enc->height, enc->rate.numerator,
enc->rate.denominator );
}
XvAttribute *xvattr = XvQueryPortAttributes( display, grabbedPort, &num );
for ( int k = 0; k < num; k++ )
{
if ( xvattr[k].flags & XvSettable )
{
if ( strcmp( xvattr[k].name, "XV_AUTOPAINT_COLORKEY") == 0 )
{
Atom val_atom = XInternAtom( display, xvattr[k].name, False );
if ( XvSetPortAttribute( display, grabbedPort, val_atom, 1 ) != Success )
ERROR(gui, "Couldn't set Xv attribute %s\n", xvattr[k].name);
}
else if ( strcmp( xvattr[k].name, "XV_COLORKEY") == 0 )
{
Atom val_atom = XInternAtom( display, xvattr[k].name, False );
if ( XvSetPortAttribute( display, grabbedPort, val_atom, 0x010102 ) != Success )
ERROR(gui, "Couldn't set Xv attribute %s\n", xvattr[k].name);
}
}
}
}
if ( gotPort )
{
XGCValues values;
memset(&values, 0, sizeof(XGCValues));
gc = XCreateGC( display, window, 0, NULL );
xvImage = ( XvImage * ) XvShmCreateImage( display, grabbedPort, 0x32595559, 0, width, height, &shmInfo );
shmInfo.shmid = shmget( IPC_PRIVATE, xvImage->data_size, IPC_CREAT | 0777 );
if (shmInfo.shmid < 0) {
perror("shmget");
gotPort = false;
}
else
{
shmInfo.shmaddr = ( char * ) shmat( shmInfo.shmid, 0, 0 );
xvImage->data = shmInfo.shmaddr;
shmInfo.readOnly = 0;
if ( !XShmAttach( gdk_display, &shmInfo ) )
{
if ( XvGrabPort( display, port, CurrentTime ) == Success )
{
int formats;
XvImageFormatValues *list;
list = XvListImageFormats( display, port, &formats );
INFO(gui, "formats supported: %d", formats);
for ( int i = 0; i < formats; i ++ )
{
INFO(gui, "0x%x (%c%c%c%c) %s",
list[ i ].id,
( list[ i ].id ) & 0xff,
( list[ i ].id >> 8 ) & 0xff,
( list[ i ].id >> 16 ) & 0xff,
( list[ i ].id >> 24 ) & 0xff,
( list[ i ].format == XvPacked ) ? "packed" : "planar" );
if ( list[ i ].id == 0x32595559 && !gotPort )
gotPort = true;
}
if ( !gotPort )
{
XvUngrabPort( display, port, CurrentTime );
}
else
{
grabbedPort = port;
break;
}
}
}
}
if ( gotPort )
{
int num;
unsigned int unum;
XvEncodingInfo *enc;
XvQueryEncodings( display, grabbedPort, &unum, &enc );
for ( unsigned int index = 0; index < unum; index ++ )
{
INFO(gui, "%d: %s, %ldx%ld rate = %d/%d", index, enc->name,
enc->width, enc->height, enc->rate.numerator,
enc->rate.denominator );
}
XvAttribute *xvattr = XvQueryPortAttributes( display, port, &num );
for ( int k = 0; k < num; k++ )
{
if ( xvattr[k].flags & XvSettable )
{
if ( strcmp( xvattr[k].name, "XV_AUTOPAINT_COLORKEY") == 0 )
{
Atom val_atom = XInternAtom( display, xvattr[k].name, False );
if ( XvSetPortAttribute( display, port, val_atom, 1 ) != Success )
ERROR(gui, "Couldn't set Xv attribute %s\n", xvattr[k].name);
}
else if ( strcmp( xvattr[k].name, "XV_COLORKEY") == 0 )
{
Atom val_atom = XInternAtom( display, xvattr[k].name, False );
if ( XvSetPortAttribute( display, port, val_atom, 0x010102 ) != Success )
ERROR(gui, "Couldn't set Xv attribute %s\n", xvattr[k].name);
}
}
}
}
if ( gotPort )
{
gc = XCreateGC( display, window, 0, &values );
xvImage = ( XvImage * ) XvShmCreateImage( display, port, 0x32595559, 0, width, height, &shmInfo );
shmInfo.shmid = shmget( IPC_PRIVATE, xvImage->data_size, IPC_CREAT | 0777 );
if (shmInfo.shmid < 0) {
perror("shmget");
gotPort = false;
}
else
{
shmInfo.shmaddr = ( char * ) shmat( shmInfo.shmid, 0, 0 );
xvImage->data = shmInfo.shmaddr;
shmInfo.readOnly = 0;
if ( !XShmAttach( gdk_display, &shmInfo ) )
{
gotPort = false;
}
XSync( display, false );
shmctl( shmInfo.shmid, IPC_RMID, 0 );
#if 0
xvImage = ( XvImage * ) XvCreateImage( display, port, 0x32595559, pix, width , height );
#endif
}
}
}
else
{
gotPort = false;
}
}
XSync( display, false );
shmctl( shmInfo.shmid, IPC_RMID, 0 );
}
}
}
else
{
gotPort = false;
}
}
XvDisplayer::~XvDisplayer()
{
ERROR(gui, "Destroying XV Displayer");
{
ERROR(gui, "Destroying XV Displayer");
if ( gotPort )
{
XvUngrabPort( display, grabbedPort, CurrentTime );
}
if ( gotPort )
{
XvUngrabPort( display, grabbedPort, CurrentTime );
}
//if ( xvImage != NULL )
// XvStopVideo( display, port, window );
if ( shmInfo.shmaddr != NULL )
{
XShmDetach( display, &shmInfo );
shmctl( shmInfo.shmid, IPC_RMID, 0 );
shmdt( shmInfo.shmaddr );
}
if ( xvImage != NULL )
XFree( xvImage );
}
if ( shmInfo.shmaddr != NULL )
{
XShmDetach( display, &shmInfo );
shmctl( shmInfo.shmid, IPC_RMID, 0 );
shmdt( shmInfo.shmaddr );
}
if ( xvImage != NULL )
XFree( xvImage );
}
bool
XvDisplayer::usable()
{
return gotPort;
}
{
return gotPort;
}
void
XvDisplayer::put( void *image )
{
REQUIRE(image != NULL);
REQUIRE(drawingArea != NULL);
if(xvImage != NULL)
{
int video_x = 0, video_y = 0, video_width = 0, video_height = 0;
calculateVideoLayout(
drawingArea->get_width(),
drawingArea->get_height(),
preferredWidth(), preferredHeight(),
video_x, video_y, video_width, video_height );
XvDisplayer::put( const void* image )
{
REQUIRE(image != NULL);
REQUIRE(drawingArea != NULL);
if(xvImage != NULL)
{
REQUIRE(display != NULL);
int video_x = 0, video_y = 0, video_width = 0, video_height = 0;
calculateVideoLayout(
drawingArea->get_width(),
drawingArea->get_height(),
preferredWidth(), preferredHeight(),
video_x, video_y, video_width, video_height );
memcpy( xvImage->data, image, xvImage->data_size );
memcpy( xvImage->data, image, xvImage->data_size );
XvShmPutImage( display, port, window, gc, xvImage,
0, 0, preferredWidth(), preferredHeight(),
video_x, video_y, video_width, video_height, false );
}
}
XvShmPutImage( display, grabbedPort, window, gc, xvImage,
0, 0, preferredWidth(), preferredHeight(),
video_x, video_y, video_width, video_height, false );
}
}
} // namespace output
} // namespace gui

View file

@ -46,30 +46,90 @@ namespace Gtk {
namespace gui {
namespace output {
class XvDisplayer : public Displayer
{
public:
XvDisplayer( Gtk::Widget *drawing_area, int width, int height );
~XvDisplayer();
void put( void *image );
/**
* XvDisplayer is a class which is responsible for rendering a video
* image via XVideo.
**/
class XvDisplayer : public Displayer
{
public:
/**
* Constructor
* @param drawing_area The widget into which the video image will be
* drawn. This value must not be NULL.
* @param width The width of the video image in pixels. This value
* must be greater than zero.
* @param height The height of the video image in pixels. This value
* must be greater than zero.
**/
XvDisplayer( Gtk::Widget *drawing_area, int width, int height );
protected:
bool usable();
/**
* Destructor
**/
~XvDisplayer();
private:
bool gotPort;
int grabbedPort;
Gtk::Widget *drawingArea;
Display *display;
Window window;
GC gc;
XGCValues values;
XvImage *xvImage;
unsigned int port;
XShmSegmentInfo shmInfo;
char pix[ MAX_WIDTH * MAX_HEIGHT * 4 ];
};
/**
* Put an image of a given width and height with the expected input
* format (as indicated by the format method).
* @param[in] image The video image array to draw.
*/
void put( const void* image );
/**
* Indicates if this object can be used to render images on the
* running system.
*/
bool usable();
private:
/**
* Specifies whether the object is currently attached to an XVideo
* port.
* @remarks This value is false until the constructor has finished
* successfully.
**/
bool gotPort;
/**
* The current port being used.
* @remarks This value is meaninless unless gotPort is true.
**/
unsigned int grabbedPort;
/**
* The widget that video will be drawn into.
* @remarks This value must be a valid pointer.
**/
Gtk::Widget *drawingArea;
/**
* The display that video will be drawn into.
**/
Display *display;
/**
* The X11 window that video will be drawn into.
**/
Window window;
/**
* The graphics context which will be used when rednering video.
**/
GC gc;
/**
* The shared memory image object which video will be written into.
**/
XvImage *xvImage;
/**
* Info about the shared memory segment.
* @remarks shmInfo.shmaddr is set to NULL, when the SHM is detached.
**/
XShmSegmentInfo shmInfo;
};
} // namespace output
} // namespace gui