2003-10-28 18:54:20 +00:00
/* GStreamer
* Copyright ( C ) < 1999 > Erik Walthinsen < omega @ cse . ogi . edu >
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This library 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
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 02111 - 1307 , USA .
*/
2003-10-31 16:40:20 +00:00
/*
< ds - work > your element belongs to a scheduler , which calls some functions from the same thread
< ds - work > all the other functions could be called from any random thread
< gernot > ds - work : which are the " some " function in that case ?
< gernot > It is quite costly to do glXGetCurrentContext for every function call .
< ds - work > _chain , - get , _loop
*/
2004-01-12 04:15:47 +00:00
# ifdef HAVE_CONFIG_H
2003-10-28 18:54:20 +00:00
# include <config.h>
2004-01-12 04:15:47 +00:00
# endif
2003-10-28 18:54:20 +00:00
# include <string.h>
# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include <GL/glx.h>
# include <GL/gl.h>
# include <GL/glu.h>
# include <sys/time.h>
/*#define GST_DEBUG_FORCE_DISABLE*/
# include "gstglsink.h"
/* elementfactory information */
static GstElementDetails gst_glsink_details = {
" OpenGL Sink/GLX " ,
" Sink/GLVideo " ,
" An OpenGL based video sink - uses OpenGL and GLX to draw video, utilizing different acceleration options " ,
2003-11-06 00:27:03 +00:00
" Gernot Ziegler <gz@lysator.liu.se> "
2003-10-28 18:54:20 +00:00
} ;
2003-11-06 00:27:03 +00:00
/* default template - initiated with class struct to allow gst-register to work
with X running */
GST_PAD_TEMPLATE_FACTORY ( gst_glsink_sink_template_factory ,
2004-03-14 22:34:33 +00:00
" sink " ,
GST_PAD_SINK ,
GST_PAD_ALWAYS ,
GST_CAPS_NEW ( " glsink_rgbsink " , " video/x-raw-rgb " ,
2004-03-15 19:32:27 +00:00
" framerate " , GST_PROPS_FLOAT_RANGE ( 0 , G_MAXFLOAT ) ,
" width " , GST_PROPS_INT_RANGE ( 0 , G_MAXINT ) ,
" height " , GST_PROPS_INT_RANGE ( 0 , G_MAXINT ) ) ,
2004-03-14 22:34:33 +00:00
GST_CAPS_NEW ( " glsink_yuvsink " , " video/x-raw-yuv " ,
2004-03-15 19:32:27 +00:00
" framerate " , GST_PROPS_FLOAT_RANGE ( 0 , G_MAXFLOAT ) ,
" width " , GST_PROPS_INT_RANGE ( 0 , G_MAXINT ) ,
" height " , GST_PROPS_INT_RANGE ( 0 , G_MAXINT ) )
2004-03-14 22:34:33 +00:00
)
2003-11-06 00:27:03 +00:00
2003-10-28 18:54:20 +00:00
/* glsink signals and args */
2004-03-14 22:34:33 +00:00
enum
{
LAST_SIGNAL
} ;
enum
{
ARG_0 ,
ARG_WIDTH ,
ARG_HEIGHT ,
ARG_FRAMES_DISPLAYED ,
ARG_FRAME_TIME ,
ARG_HOOK ,
ARG_MUTE ,
ARG_REPAINT ,
ARG_DEMO ,
ARG_DUMP
} ;
2003-10-28 18:54:20 +00:00
/* GLsink class */
# define GST_TYPE_GLSINK (gst_glsink_get_type())
# define GST_GLSINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_GLSINK,GstGLSink))
# define GST_GLSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_GLSINK,GstGLSink))
# define GST_IS_GLSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_GLSINK))
# define GST_IS_GLSINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_GLSINK))
2004-03-14 22:34:33 +00:00
typedef struct _GstGLSink GstGLSink ;
typedef struct _GstGLSinkClass GstGLSinkClass ;
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
struct _GstGLSink
{
GstElement element ;
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
GstPad * sinkpad ;
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
gint frames_displayed ;
guint64 frame_time ;
gint width , height ;
gboolean muted ;
2004-03-15 19:32:27 +00:00
gint demo ; // some kind of fun demo mode to let GL show its 3D capabilities
gboolean dumpvideo ; // dump the video down to .ppm:s
GstBuffer * last_image ; /* not thread safe ? */
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
GstClock * clock ;
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
GMutex * cache_lock ;
GList * cache ;
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
/* plugins */
GstImagePlugin * plugin ;
GstImageConnection * conn ;
/* allow anybody to hook in here */
GstImageInfo * hook ;
} ;
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
struct _GstGLSinkClass
{
GstElementClass parent_class ;
/* plugins */
GList * plugins ;
} ;
static GType gst_glsink_get_type ( void ) ;
static void gst_glsink_base_init ( gpointer g_class ) ;
static void gst_glsink_class_init ( GstGLSinkClass * klass ) ;
static void gst_glsink_init ( GstGLSink * sink ) ;
2003-10-28 18:54:20 +00:00
/* static void gst_glsink_dispose (GObject *object); */
2004-03-14 22:34:33 +00:00
static void gst_glsink_chain ( GstPad * pad , GstData * _data ) ;
static void gst_glsink_set_clock ( GstElement * element , GstClock * clock ) ;
static GstElementStateReturn gst_glsink_change_state ( GstElement *
element ) ;
static GstPadLinkReturn gst_glsink_sinkconnect ( GstPad * pad ,
GstCaps * caps ) ;
static GstCaps * gst_glsink_getcaps ( GstPad * pad , GstCaps * caps ) ;
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
static void gst_glsink_set_property ( GObject * object , guint prop_id ,
const GValue * value , GParamSpec * pspec ) ;
static void gst_glsink_get_property ( GObject * object , guint prop_id ,
GValue * value , GParamSpec * pspec ) ;
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
static void gst_glsink_release_conn ( GstGLSink * sink ) ;
static void gst_glsink_append_cache ( GstGLSink * sink ,
GstImageData * image ) ;
static gboolean gst_glsink_set_caps ( GstGLSink * sink , GstCaps * caps ) ;
2003-10-28 18:54:20 +00:00
/* prototypes from plugins */
2004-03-14 22:34:33 +00:00
extern GstImagePlugin * get_gl_rgbimage_plugin ( void ) ;
extern GstImagePlugin * get_gl_nvimage_plugin ( void ) ;
2003-10-28 18:54:20 +00:00
/* default output */
2004-03-14 22:34:33 +00:00
extern void gst_glxwindow_new ( GstGLSink * sink ) ;
extern void gst_glxwindow_hook_context ( GstImageInfo * info ) ;
extern void gst_glxwindow_unhook_context ( GstImageInfo * info ) ;
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
static GstPadTemplate * sink_template ;
static GstElementClass * parent_class = NULL ;
2003-10-28 18:54:20 +00:00
/* static guint gst_glsink_signals[LAST_SIGNAL] = { 0 }; */
2004-03-14 22:34:33 +00:00
static GType gst_glsink_get_type ( void )
2003-10-28 18:54:20 +00:00
{
static GType videosink_type = 0 ;
if ( ! videosink_type ) {
static const GTypeInfo videosink_info = {
2004-03-14 22:34:33 +00:00
sizeof ( GstGLSinkClass ) ,
2003-11-06 00:27:03 +00:00
gst_glsink_base_init ,
2003-10-28 18:54:20 +00:00
NULL ,
( GClassInitFunc ) gst_glsink_class_init ,
NULL ,
NULL ,
2004-03-14 22:34:33 +00:00
sizeof ( GstGLSink ) ,
2003-10-28 18:54:20 +00:00
0 ,
( GInstanceInitFunc ) gst_glsink_init ,
} ;
2004-03-15 19:32:27 +00:00
2004-03-14 22:34:33 +00:00
videosink_type =
2004-03-15 19:32:27 +00:00
g_type_register_static ( GST_TYPE_ELEMENT , " GstGLSink " , & videosink_info ,
0 ) ;
2003-10-28 18:54:20 +00:00
}
return videosink_type ;
}
2003-11-06 00:27:03 +00:00
static void
gst_glsink_base_init ( gpointer g_class )
{
GstElementClass * element_class = GST_ELEMENT_CLASS ( g_class ) ;
2004-03-14 22:34:33 +00:00
2003-11-06 00:27:03 +00:00
gst_element_class_set_details ( element_class , & gst_glsink_details ) ;
2004-03-14 22:34:33 +00:00
gst_element_class_add_pad_template ( element_class ,
GST_PAD_TEMPLATE_GET ( gst_glsink_sink_template_factory ) ) ;
2003-11-06 00:27:03 +00:00
}
2003-10-28 18:54:20 +00:00
static void
2004-03-14 22:34:33 +00:00
gst_glsink_class_init ( GstGLSinkClass * klass )
2003-10-28 18:54:20 +00:00
{
GObjectClass * gobject_class ;
GstElementClass * gstelement_class ;
gobject_class = ( GObjectClass * ) klass ;
2004-03-14 22:34:33 +00:00
gstelement_class = ( GstElementClass * ) klass ;
2003-10-28 18:54:20 +00:00
parent_class = g_type_class_ref ( GST_TYPE_ELEMENT ) ;
2004-03-15 19:32:27 +00:00
g_object_class_install_property ( gobject_class , ARG_WIDTH , g_param_spec_int ( " width " , " Width " , " The video width " , G_MININT , G_MAXINT , 0 , G_PARAM_READABLE ) ) ; /* CHECKME */
g_object_class_install_property ( gobject_class , ARG_HEIGHT , g_param_spec_int ( " height " , " Height " , " The video height " , G_MININT , G_MAXINT , 0 , G_PARAM_READABLE ) ) ; /* CHECKME */
g_object_class_install_property ( gobject_class , ARG_FRAMES_DISPLAYED , g_param_spec_int ( " frames_displayed " , " Frames Displayed " , " The number of frames displayed so far " , G_MININT , G_MAXINT , 0 , G_PARAM_READWRITE ) ) ; /* CHECKME */
g_object_class_install_property ( gobject_class , ARG_FRAME_TIME , g_param_spec_int ( " frame_time " , " Frame time " , " The interval between frames " , G_MININT , G_MAXINT , 0 , G_PARAM_READWRITE ) ) ; /* CHECKME */
2003-10-28 18:54:20 +00:00
g_object_class_install_property ( gobject_class , ARG_HOOK ,
2004-03-14 22:34:33 +00:00
g_param_spec_pointer ( " hook " , " Hook " , " The object receiving the output " ,
2004-03-15 19:32:27 +00:00
G_PARAM_WRITABLE ) ) ;
2003-10-28 18:54:20 +00:00
g_object_class_install_property ( gobject_class , ARG_MUTE ,
2004-03-14 22:34:33 +00:00
g_param_spec_boolean ( " mute " , " Mute " , " mute the output ? " , FALSE ,
2004-03-15 19:32:27 +00:00
G_PARAM_READWRITE ) ) ;
2003-10-28 18:54:20 +00:00
g_object_class_install_property ( gobject_class , ARG_REPAINT ,
2004-03-14 22:34:33 +00:00
g_param_spec_boolean ( " repaint " , " Repaint " , " repaint the current frame " ,
2004-03-15 19:32:27 +00:00
FALSE , G_PARAM_WRITABLE ) ) ;
2003-10-28 18:54:20 +00:00
g_object_class_install_property ( gobject_class , ARG_DEMO ,
2004-03-14 22:34:33 +00:00
g_param_spec_int ( " demo " , " Demo " , " demo mode (shows 3D capabilities) " , 0 ,
2004-03-15 19:32:27 +00:00
1 , 0 , G_PARAM_READWRITE ) ) ;
2003-10-28 18:54:20 +00:00
g_object_class_install_property ( gobject_class , ARG_DUMP ,
2004-03-14 22:34:33 +00:00
g_param_spec_boolean ( " dump " , " Dump " ,
2004-03-15 19:32:27 +00:00
" stores sequence of frames in .ppm files " , FALSE , G_PARAM_READWRITE ) ) ;
2003-10-28 18:54:20 +00:00
gobject_class - > set_property = gst_glsink_set_property ;
gobject_class - > get_property = gst_glsink_get_property ;
/*gobject_class->dispose = gst_glsink_dispose; */
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
gstelement_class - > change_state = gst_glsink_change_state ;
gstelement_class - > set_clock = gst_glsink_set_clock ;
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
/* plugins */
klass - > plugins = NULL ;
klass - > plugins = g_list_append ( klass - > plugins , get_gl_rgbimage_plugin ( ) ) ;
klass - > plugins = g_list_append ( klass - > plugins , get_gl_nvimage_plugin ( ) ) ;
}
/*
GLSink has its own Buffer management - this allows special plugins to create special memory areas for
buffer upload
*/
static void
2004-03-14 22:34:33 +00:00
gst_glsink_init ( GstGLSink * sink )
2003-10-28 18:54:20 +00:00
{
sink - > sinkpad = gst_pad_new_from_template ( sink_template , " sink " ) ;
gst_element_add_pad ( GST_ELEMENT ( sink ) , sink - > sinkpad ) ;
gst_pad_set_chain_function ( sink - > sinkpad , gst_glsink_chain ) ;
gst_pad_set_link_function ( sink - > sinkpad , gst_glsink_sinkconnect ) ;
gst_pad_set_getcaps_function ( sink - > sinkpad , gst_glsink_getcaps ) ;
gst_pad_set_bufferpool_function ( sink - > sinkpad , gst_glsink_get_bufferpool ) ;
sink - > last_image = NULL ;
sink - > width = 0 ;
sink - > height = 0 ;
sink - > muted = FALSE ;
2004-03-14 22:34:33 +00:00
sink - > clock = NULL ;
GST_FLAG_SET ( sink , GST_ELEMENT_THREAD_SUGGESTED ) ;
2003-10-28 18:54:20 +00:00
GST_FLAG_SET ( sink , GST_ELEMENT_EVENT_AWARE ) ;
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
/* create bufferpool and image cache */
GST_DEBUG ( " glsink: creating bufferpool " ) ;
2004-03-14 22:34:33 +00:00
sink - > cache_lock = g_mutex_new ( ) ;
sink - > cache = NULL ;
2003-10-28 18:54:20 +00:00
/* plugins */
sink - > plugin = NULL ;
sink - > conn = NULL ;
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
/* do initialization of default hook here */
2004-03-14 22:34:33 +00:00
gst_glxwindow_new ( sink ) ;
2003-10-31 16:40:20 +00:00
//printf("GLSink_init: Current context %p\n", glXGetCurrentContext());
2004-03-14 22:34:33 +00:00
gst_glxwindow_unhook_context ( sink - > hook ) ;
2003-10-28 18:54:20 +00:00
}
2003-10-31 16:40:20 +00:00
/** frees the temporary connection that tests the window system capabilities */
2003-10-28 18:54:20 +00:00
static void
2004-03-14 22:34:33 +00:00
gst_glsink_release_conn ( GstGLSink * sink )
2003-10-28 18:54:20 +00:00
{
2004-03-14 22:34:33 +00:00
if ( sink - > conn = = NULL )
return ;
2003-10-28 18:54:20 +00:00
/* free last image if any */
2004-03-14 22:34:33 +00:00
if ( sink - > last_image ! = NULL ) {
2003-10-28 18:54:20 +00:00
gst_buffer_unref ( sink - > last_image ) ;
sink - > last_image = NULL ;
}
/* free cache */
g_mutex_lock ( sink - > cache_lock ) ;
2004-03-14 22:34:33 +00:00
while ( sink - > cache ) {
2003-10-28 18:54:20 +00:00
sink - > plugin - > free_image ( ( GstImageData * ) sink - > cache - > data ) ;
sink - > cache = g_list_delete_link ( sink - > cache , sink - > cache ) ;
}
g_mutex_unlock ( sink - > cache_lock ) ;
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
/* release connection */
sink - > conn - > free_conn ( sink - > conn ) ;
sink - > conn = NULL ;
}
2004-03-14 22:34:33 +00:00
static void
gst_glsink_append_cache ( GstGLSink * sink , GstImageData * image )
2003-10-28 18:54:20 +00:00
{
g_mutex_lock ( sink - > cache_lock ) ;
sink - > cache = g_list_prepend ( sink - > cache , image ) ;
g_mutex_unlock ( sink - > cache_lock ) ;
}
2003-12-22 01:47:09 +00:00
#if 0
2003-10-28 18:54:20 +00:00
/*
Create a new buffer to hand up the chain .
This allows the plugins to make its own decoding buffers
*/
2004-03-14 22:34:33 +00:00
static GstBuffer *
gst_glsink_buffer_new ( GstBufferPool * pool , gint64 location ,
guint size , gpointer user_data )
2003-10-28 18:54:20 +00:00
{
GstGLSink * sink ;
GstBuffer * buffer ;
GstImageData * image ;
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
sink = GST_GLSINK ( user_data ) ;
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
/* If cache is non-empty, get buffer from there */
if ( sink - > cache ! = NULL ) {
g_mutex_lock ( sink - > cache_lock ) ;
image = ( GstImageData * ) sink - > cache - > data ;
sink - > cache = g_list_delete_link ( sink - > cache , sink - > cache ) ;
g_mutex_unlock ( sink - > cache_lock ) ;
} else {
/* otherwise, get one from the plugin */
image = sink - > plugin - > get_image ( sink - > hook , sink - > conn ) ;
}
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
buffer = gst_buffer_new ( ) ;
GST_BUFFER_DATA ( buffer ) = image - > data ;
GST_BUFFER_SIZE ( buffer ) = image - > size ;
GST_BUFFER_POOL_PRIVATE ( buffer ) = image ;
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
return buffer ;
}
/*
Free a buffer that the chain doesn ' t need anymore .
*/
static void
2004-03-14 22:34:33 +00:00
gst_glsink_buffer_free ( GstBufferPool * pool , GstBuffer * buffer ,
gpointer user_data )
2003-10-28 18:54:20 +00:00
{
2004-03-14 22:34:33 +00:00
GstGLSink * sink =
GST_GLSINK ( gst_buffer_pool_get_user_data ( GST_BUFFER_BUFFERPOOL
2004-03-15 19:32:27 +00:00
( buffer ) ) ) ;
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
gst_glsink_append_cache ( sink ,
( GstImageData * ) GST_BUFFER_POOL_PRIVATE ( buffer ) ) ;
2003-10-28 18:54:20 +00:00
/* set to NULL so the data is not freed */
GST_BUFFER_DATA ( buffer ) = NULL ;
gst_buffer_default_free ( buffer ) ;
}
2003-12-22 01:47:09 +00:00
# endif
2003-10-28 18:54:20 +00:00
/*
Set the caps that the application desires .
Go through the plugin list , finding the plugin that first fits the given parameters
*/
static gboolean
2004-03-14 22:34:33 +00:00
gst_glsink_set_caps ( GstGLSink * sink , GstCaps * caps )
2003-10-28 18:54:20 +00:00
{
2004-03-14 22:34:33 +00:00
g_warning ( " in glsink set caps! \n " ) ;
printf ( " Getting GLstring, context is %p \n " , glXGetCurrentContext ( ) ) ;
2003-10-28 18:54:20 +00:00
GList * list = ( ( GstGLSinkClass * ) G_OBJECT_GET_CLASS ( sink ) ) - > plugins ;
GstImageConnection * conn = NULL ;
2004-03-14 22:34:33 +00:00
while ( list ) {
printf ( " AGetting GLstring, context is %p \n " , glXGetCurrentContext ( ) ) ;
2003-10-28 18:54:20 +00:00
GstImagePlugin * plugin = ( GstImagePlugin * ) list - > data ;
2004-03-14 22:34:33 +00:00
if ( ( conn = plugin - > set_caps ( sink - > hook , caps ) ) ! = NULL ) {
2003-10-31 16:40:20 +00:00
//gst_glsink_release_conn (sink);
2004-03-14 22:34:33 +00:00
printf ( " BGetting GLstring, context is %p \n " , glXGetCurrentContext ( ) ) ;
2003-10-28 18:54:20 +00:00
sink - > conn = conn ;
2004-03-14 22:34:33 +00:00
printf ( " CGetting GLstring, context is %p \n " , glXGetCurrentContext ( ) ) ;
2003-10-28 18:54:20 +00:00
sink - > plugin = plugin ;
sink - > conn - > open_conn ( sink - > conn , sink - > hook ) ;
return TRUE ;
}
list = g_list_next ( list ) ;
}
return FALSE ;
}
/**
Link the input video sink internally .
*/
static GstPadLinkReturn
2004-03-14 22:34:33 +00:00
gst_glsink_sinkconnect ( GstPad * pad , GstCaps * caps )
2003-10-28 18:54:20 +00:00
{
2004-03-14 22:34:33 +00:00
g_warning ( " in glsink sinkconnect! \n " ) ;
2003-10-28 18:54:20 +00:00
GstGLSink * sink ;
2003-10-31 16:40:20 +00:00
guint32 fourcc , print_format , result ;
2003-10-28 18:54:20 +00:00
sink = GST_GLSINK ( gst_pad_get_parent ( pad ) ) ;
/* we are not going to act on variable caps */
if ( ! GST_CAPS_IS_FIXED ( caps ) )
return GST_PAD_LINK_DELAYED ;
2004-03-14 22:34:33 +00:00
gst_glxwindow_hook_context ( sink - > hook ) ;
2003-10-28 18:54:20 +00:00
/* try to set the caps on the output */
2003-10-31 16:40:20 +00:00
result = gst_glsink_set_caps ( sink , caps ) ;
2004-03-14 22:34:33 +00:00
gst_glxwindow_unhook_context ( sink - > hook ) ;
if ( result = = FALSE ) {
return GST_PAD_LINK_REFUSED ;
}
2003-10-31 16:40:20 +00:00
2003-10-28 18:54:20 +00:00
/* remember width & height */
gst_caps_get_int ( caps , " width " , & sink - > width ) ;
gst_caps_get_int ( caps , " height " , & sink - > height ) ;
gst_caps_get_fourcc_int ( caps , " format " , & fourcc ) ;
print_format = GULONG_FROM_LE ( fourcc ) ;
2004-03-14 22:34:33 +00:00
GST_DEBUG ( " glsink: setting %08x (%4.4s) %dx%d \n " ,
fourcc , ( gchar * ) & print_format , sink - > width , sink - > height ) ;
2003-10-28 18:54:20 +00:00
/* emit signal */
g_object_freeze_notify ( G_OBJECT ( sink ) ) ;
g_object_notify ( G_OBJECT ( sink ) , " width " ) ;
g_object_notify ( G_OBJECT ( sink ) , " height " ) ;
g_object_thaw_notify ( G_OBJECT ( sink ) ) ;
return GST_PAD_LINK_OK ;
}
static GstCaps *
2004-03-14 22:34:33 +00:00
gst_glsink_getcaps ( GstPad * pad , GstCaps * caps )
2003-10-28 18:54:20 +00:00
{
2004-03-14 22:34:33 +00:00
g_warning ( " in glsink get caps! \n " ) ;
2003-10-28 18:54:20 +00:00
/* what is the "caps" parameter good for? */
GstGLSink * sink = GST_GLSINK ( gst_pad_get_parent ( pad ) ) ;
GstCaps * ret = NULL ;
GList * list = ( ( GstGLSinkClass * ) G_OBJECT_GET_CLASS ( sink ) ) - > plugins ;
2004-03-14 22:34:33 +00:00
gst_glxwindow_hook_context ( sink - > hook ) ;
while ( list ) {
ret =
2004-03-15 19:32:27 +00:00
gst_caps_append ( ret ,
( ( GstImagePlugin * ) list - > data ) - > get_caps ( sink - > hook ) ) ;
2003-10-28 18:54:20 +00:00
list = g_list_next ( list ) ;
}
2004-03-14 22:34:33 +00:00
gst_glxwindow_unhook_context ( sink - > hook ) ;
2003-10-28 18:54:20 +00:00
return ret ;
}
static void
2004-03-14 22:34:33 +00:00
gst_glsink_set_clock ( GstElement * element , GstClock * clock )
2003-10-28 18:54:20 +00:00
{
GstGLSink * sink = GST_GLSINK ( element ) ;
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
sink - > clock = clock ;
}
static void
2004-03-14 22:34:33 +00:00
gst_glsink_chain ( GstPad * pad , GstData * _data )
2003-10-28 18:54:20 +00:00
{
//g_warning("in glsink_chain!\n");
GstBuffer * buf = GST_BUFFER ( _data ) ;
GstGLSink * sink ;
2003-10-31 16:40:20 +00:00
2003-10-28 18:54:20 +00:00
GstBuffer * buffer ;
g_return_if_fail ( pad ! = NULL ) ;
g_return_if_fail ( GST_IS_PAD ( pad ) ) ;
g_return_if_fail ( buf ! = NULL ) ;
sink = GST_GLSINK ( gst_pad_get_parent ( pad ) ) ;
if ( GST_IS_EVENT ( buf ) ) {
GstEvent * event = GST_EVENT ( buf ) ;
switch ( GST_EVENT_TYPE ( event ) ) {
default :
2004-03-15 19:32:27 +00:00
gst_pad_event_default ( pad , event ) ;
2003-10-28 18:54:20 +00:00
}
return ;
}
2004-03-14 22:34:33 +00:00
GST_DEBUG ( " glsink: clock wait: %llu %u " ,
GST_BUFFER_TIMESTAMP ( buf ) , GST_BUFFER_SIZE ( buf ) ) ;
2003-10-28 18:54:20 +00:00
2003-10-31 16:40:20 +00:00
#if 0
GstClockTime time = GST_BUFFER_TIMESTAMP ( buf ) ;
static int frame_drops = 0 ;
2003-10-28 18:54:20 +00:00
if ( sink - > clock & & time ! = - 1 ) {
2004-03-14 22:34:33 +00:00
if ( time < gst_clock_get_time ( sink - > clock ) ) {
g_warning ( " Frame drop (%d consecutive) !! " , frame_drops ) ;
/* we are going to drop late buffers */
gst_buffer_unref ( buf ) ;
frame_drops + + ;
return ;
}
2004-03-15 19:32:27 +00:00
frame_drops = 0 ; // we made it - reset time
2003-10-28 18:54:20 +00:00
GstClockReturn ret ;
2004-03-14 22:34:33 +00:00
GstClockID id =
2004-03-15 19:32:27 +00:00
gst_clock_new_single_shot_id ( sink - > clock , GST_BUFFER_TIMESTAMP ( buf ) ) ;
2003-10-28 18:54:20 +00:00
ret = gst_element_clock_wait ( GST_ELEMENT ( sink ) , id , NULL ) ;
gst_clock_id_free ( id ) ;
/* we are going to drop early buffers */
if ( ret = = GST_CLOCK_EARLY ) {
gst_buffer_unref ( buf ) ;
return ;
}
}
# endif
/* call the notify _before_ displaying so the handlers can react */
sink - > frames_displayed + + ;
g_object_notify ( G_OBJECT ( sink ) , " frames_displayed " ) ;
2004-03-14 22:34:33 +00:00
if ( ! sink - > muted ) {
if ( glXGetCurrentContext ( ) = = NULL ) {
printf ( " Rehooking window ! \n " ) ;
gst_glxwindow_hook_context ( sink - > hook ) ;
2003-10-31 16:40:20 +00:00
# if 1
2004-03-14 22:34:33 +00:00
GST_DEBUG ( " Initializing OpenGL parameters \n " ) ;
/* initialize OpenGL drawing */
glEnable ( GL_DEPTH_TEST ) ;
glEnable ( GL_TEXTURE_2D ) ;
glClearDepth ( 1.0f ) ;
glClearColor ( 0 , 0 , 0 , 0 ) ;
2004-03-15 19:32:27 +00:00
glEnable ( GL_AUTO_NORMAL ) ; // let OpenGL generate the Normals
2004-03-14 22:34:33 +00:00
glDisable ( GL_BLEND ) ;
glDisable ( GL_CULL_FACE ) ;
glPolygonMode ( GL_FRONT , GL_FILL ) ;
glPolygonMode ( GL_BACK , GL_FILL ) ;
glShadeModel ( GL_SMOOTH ) ;
glHint ( GL_PERSPECTIVE_CORRECTION_HINT , GL_NICEST ) ;
GstGLImageInfo * window = ( GstGLImageInfo * ) sink - > hook ;
int w = window - > width , h = window - > height ;
glViewport ( 0 , 0 , ( GLint ) w , ( GLint ) h ) ;
glMatrixMode ( GL_PROJECTION ) ;
glLoadIdentity ( ) ;
GLfloat aspect = ( GLfloat ) w / ( GLfloat ) h ;
glFrustum ( - aspect , aspect , - 1.0 , 1.0 , 5.0 , 500.0 ) ;
2003-10-31 16:40:20 +00:00
# endif
2004-03-14 22:34:33 +00:00
gst_glxwindow_unhook_context ( sink - > hook ) ;
gst_glxwindow_hook_context ( sink - > hook ) ;
glMatrixMode ( GL_MODELVIEW ) ;
#if 0
sink - > hook - > free_info ( sink - > hook ) ;
printf ( " Reallocating window brutally ! \n " ) ;
gst_glxwindow_new ( sink ) ;
2003-10-31 16:40:20 +00:00
# endif
2004-03-14 22:34:33 +00:00
}
2003-10-28 18:54:20 +00:00
/* free last_image, if any */
if ( sink - > last_image ! = NULL )
gst_buffer_unref ( sink - > last_image ) ;
if ( sink - > bufferpool & & GST_BUFFER_BUFFERPOOL ( buf ) = = sink - > bufferpool ) {
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
// awful hack ! But I currently have no other solution without changing the API
sink - > hook - > demo = sink - > demo ;
sink - > hook - > dumpvideo = sink - > dumpvideo ;
2004-03-14 22:34:33 +00:00
sink - > plugin - > put_image ( sink - > hook ,
2004-03-15 19:32:27 +00:00
( GstImageData * ) GST_BUFFER_POOL_PRIVATE ( buf ) ) ;
2003-10-28 18:54:20 +00:00
sink - > last_image = buf ;
} else {
2004-03-14 22:34:33 +00:00
buffer =
2004-03-15 19:32:27 +00:00
gst_buffer_new_from_pool ( gst_glsink_get_bufferpool ( sink - > sinkpad ) ,
0 , GST_BUFFER_SIZE ( buf ) ) ;
2004-03-14 22:34:33 +00:00
memcpy ( GST_BUFFER_DATA ( buffer ) , GST_BUFFER_DATA ( buf ) ,
2004-03-15 19:32:27 +00:00
GST_BUFFER_SIZE ( buf ) >
GST_BUFFER_SIZE ( buffer ) ? GST_BUFFER_SIZE ( buffer ) :
GST_BUFFER_SIZE ( buf ) ) ;
2003-10-28 18:54:20 +00:00
2004-03-14 22:34:33 +00:00
sink - > plugin - > put_image ( sink - > hook ,
2004-03-15 19:32:27 +00:00
( GstImageData * ) GST_BUFFER_POOL_PRIVATE ( buffer ) ) ;
2003-10-28 18:54:20 +00:00
sink - > last_image = buffer ;
gst_buffer_unref ( buf ) ;
}
2003-10-31 16:40:20 +00:00
//gst_glxwindow_unhook_context(sink->hook);
2003-10-28 18:54:20 +00:00
}
2004-03-14 22:34:33 +00:00
2003-10-28 18:54:20 +00:00
}
static void
2004-03-14 22:34:33 +00:00
gst_glsink_set_property ( GObject * object , guint prop_id , const GValue * value ,
GParamSpec * pspec )
2003-10-28 18:54:20 +00:00
{
//g_warning("in set_property!\n");
GstGLSink * sink ;
/* it's not null if we got it, but it might not be ours */
g_return_if_fail ( GST_IS_GLSINK ( object ) ) ;
sink = GST_GLSINK ( object ) ;
switch ( prop_id ) {
case ARG_FRAMES_DISPLAYED :
sink - > frames_displayed = g_value_get_int ( value ) ;
g_object_notify ( object , " frames_displayed " ) ;
break ;
case ARG_FRAME_TIME :
sink - > frame_time = g_value_get_int ( value ) ;
break ;
case ARG_HOOK :
2004-03-14 22:34:33 +00:00
if ( sink - > hook ) {
2004-03-15 19:32:27 +00:00
sink - > hook - > free_info ( sink - > hook ) ;
2003-10-28 18:54:20 +00:00
}
sink - > hook = g_value_get_pointer ( value ) ;
break ;
case ARG_MUTE :
sink - > muted = g_value_get_boolean ( value ) ;
g_object_notify ( object , " mute " ) ;
break ;
case ARG_DEMO :
sink - > demo = g_value_get_int ( value ) ;
g_object_notify ( object , " demo " ) ;
break ;
case ARG_DUMP :
sink - > dumpvideo = g_value_get_boolean ( value ) ;
g_object_notify ( object , " dump " ) ;
break ;
case ARG_REPAINT :
if ( sink - > last_image ! = NULL ) {
2004-03-15 19:32:27 +00:00
sink - > plugin - > put_image ( sink - > hook ,
( GstImageData * ) GST_BUFFER_POOL_PRIVATE ( sink - > last_image ) ) ;
2003-10-28 18:54:20 +00:00
}
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID ( object , prop_id , pspec ) ;
break ;
}
}
static void
2004-03-14 22:34:33 +00:00
gst_glsink_get_property ( GObject * object , guint prop_id , GValue * value ,
GParamSpec * pspec )
2003-10-28 18:54:20 +00:00
{
//g_warning("in get_property!\n");
GstGLSink * sink ;
/* it's not null if we got it, but it might not be ours */
2004-03-14 22:34:33 +00:00
sink = GST_GLSINK ( object ) ;
2003-10-28 18:54:20 +00:00
switch ( prop_id ) {
case ARG_WIDTH :
g_value_set_int ( value , sink - > width ) ;
break ;
case ARG_HEIGHT :
g_value_set_int ( value , sink - > height ) ;
break ;
case ARG_FRAMES_DISPLAYED :
g_value_set_int ( value , sink - > frames_displayed ) ;
break ;
case ARG_FRAME_TIME :
2004-03-14 22:34:33 +00:00
g_value_set_int ( value , sink - > frame_time / 1000000 ) ;
2003-10-28 18:54:20 +00:00
break ;
case ARG_MUTE :
g_value_set_boolean ( value , sink - > muted ) ;
break ;
case ARG_DEMO :
g_value_set_int ( value , sink - > demo ) ;
break ;
case ARG_DUMP :
g_value_set_boolean ( value , sink - > dumpvideo ) ;
break ;
2004-03-14 22:34:33 +00:00
default :
2003-10-28 18:54:20 +00:00
G_OBJECT_WARN_INVALID_PROPERTY_ID ( object , prop_id , pspec ) ;
break ;
}
}
static GstElementStateReturn
2004-03-14 22:34:33 +00:00
gst_glsink_change_state ( GstElement * element )
2003-10-28 18:54:20 +00:00
{
//g_warning("in change_state!\n");
GstGLSink * sink ;
sink = GST_GLSINK ( element ) ;
switch ( GST_STATE_TRANSITION ( element ) ) {
case GST_STATE_NULL_TO_READY :
break ;
case GST_STATE_READY_TO_PAUSED :
2004-03-14 22:34:33 +00:00
{
//g_warning("Going GST_STATE_READY_TO_PAUSED: %p", sink->conn);
}
2003-10-28 18:54:20 +00:00
break ;
case GST_STATE_PAUSED_TO_PLAYING :
2004-03-14 22:34:33 +00:00
{
//g_warning("Going GST_STATE_PAUSED_TO_PLAYING: %p", sink->conn);
}
2003-10-28 18:54:20 +00:00
break ;
case GST_STATE_PLAYING_TO_PAUSED :
break ;
case GST_STATE_PAUSED_TO_READY :
if ( sink - > conn )
2004-03-15 19:32:27 +00:00
sink - > conn - > close_conn ( sink - > conn , sink - > hook ) ;
2003-10-28 18:54:20 +00:00
if ( sink - > last_image ) {
2004-03-15 19:32:27 +00:00
gst_buffer_unref ( sink - > last_image ) ;
sink - > last_image = NULL ;
2003-10-28 18:54:20 +00:00
}
break ;
case GST_STATE_READY_TO_NULL :
gst_glsink_release_conn ( sink ) ;
break ;
}
parent_class - > change_state ( element ) ;
return GST_STATE_SUCCESS ;
}
static gboolean
2004-03-14 22:34:33 +00:00
plugin_init ( GstPlugin * plugin )
2003-10-28 18:54:20 +00:00
{
/* Loading the library containing GstVideoSink, our parent object */
if ( ! gst_library_load ( " gstvideo " ) )
return FALSE ;
/* this is needed later on in the _real_ init (during a gst-launch) */
2004-03-14 22:34:33 +00:00
sink_template = gst_pad_template_new ( " sink " ,
GST_PAD_SINK , GST_PAD_ALWAYS , NULL ) ;
2003-10-28 18:54:20 +00:00
2003-11-06 00:27:03 +00:00
if ( ! gst_element_register ( plugin , " glsink " , GST_RANK_NONE , GST_TYPE_GLSINK ) )
return FALSE ;
2003-10-28 18:54:20 +00:00
return TRUE ;
}
2004-03-14 22:34:33 +00:00
GST_PLUGIN_DEFINE ( GST_VERSION_MAJOR ,
GST_VERSION_MINOR ,
" glsink " ,
" An OpenGL based video sink - uses OpenGL and GLX to draw video, utilizing different acceleration options " ,
plugin_init , VERSION , GST_LICENSE , GST_PACKAGE , GST_ORIGIN ) ;