lumiera_documentation/src/gui/output/xvdisplayer.cpp

235 lines
7.8 KiB
C++

/*
XvDisplayer - XVideo display
Copyright (C) Lumiera.org
2000, Arne Schirmacher <arne@schirmacher.de>
2001-2007, Dan Dennedy <dan@dennedy.org>
2008, Joel Holdsworth <joel@airwebreathe.org.uk>
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., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "gui/gtk-lumiera.hpp"
#include "gui/output/xvdisplayer.hpp"
#include "include/logging.h"
#include <gdk/gdkx.h>
namespace gui {
namespace output {
XvDisplayer::XvDisplayer(Gtk::Widget *drawing_area,
int width, int height)
: gotPort(false)
, drawingArea(drawing_area)
, xvImage(NULL)
{
REQUIRE(drawing_area != NULL);
REQUIRE(width > 0);
REQUIRE(height > 0);
INFO(gui, "Trying XVideo at %d x %d", width, height);
imageWidth = width;
imageHeight = height;
shmInfo.shmaddr = NULL;
Glib::RefPtr<Gdk::Window> area_window = drawing_area->get_window();
window = GDK_WINDOW_XID (area_window->gobj());
display = GDK_WINDOW_XDISPLAY (area_window->gobj());
unsigned int count;
XvAdaptorInfo* adaptorInfo;
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, %lu, %lu", adaptorInfo[ n ].name,
adaptorInfo[ n ].base_id, adaptorInfo[ n ].num_ports - 1);
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 )
NOBUG_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 )
NOBUG_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( display, &shmInfo ))
{
gotPort = false;
}
XSync( display, false );
shmctl( shmInfo.shmid, IPC_RMID, 0 );
}
}
}
else
{
gotPort = false;
}
}
XvDisplayer::~XvDisplayer()
{
NOBUG_ERROR(gui, "Destroying XV Displayer");
if ( gotPort )
{
XvUngrabPort( display, grabbedPort, CurrentTime );
}
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;
}
void
XvDisplayer::put (void* const 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);
XvShmPutImage (display, grabbedPort, window, gc, xvImage,
0, 0, preferredWidth(), preferredHeight(),
video_x, video_y, video_width, video_height, false);
}
}
}} // namespace gui::output