mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-08 18:39:54 +00:00
607d32dd1f
Original commit message from CVS: * ext/sdl/sdlvideosink.c: (gst_sdlvideosink_supported), (gst_sdlvideosink_xoverlay_set_xwindow_id), (gst_sdlvideosink_deinitsdl), (gst_sdlv_process_events), (gst_sdlvideosink_event_thread), (gst_sdlvideosink_initsdl), (gst_sdlvideosink_destroy), (gst_sdlvideosink_create), (gst_sdlvideosink_setcaps), (gst_sdlvideosink_show_frame), (gst_sdlvideosink_change_state), (gst_sdlvideosink_navigation_send_event): * ext/sdl/sdlvideosink.h: Fix output stride copying, so that it displays correctly on framebuffer devices that don't match our implict GStreamer stride arrangement. Fix locking things. Offer XOverlay only when SDL is running against X. Make non-scaled (and ugly) embedding work via X Overlay. It can't actually match the embedded window size because there's no way to figure out what size that should be from the XOverlay interface. See comment in sdlvideosink.c
1008 lines
30 KiB
C
1008 lines
30 KiB
C
/* GStreamer SDL plugin
|
|
* Copyright (C) 2001-2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/* let's not forget to mention that all this was based on aasink ;-) */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <gst/interfaces/xoverlay.h>
|
|
#include <gst/interfaces/navigation.h>
|
|
|
|
#include "sdlvideosink.h"
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (sdl_debug);
|
|
#define GST_CAT_DEFAULT sdl_debug
|
|
|
|
/* These macros are adapted from videotestsrc.c
|
|
* and/or gst-plugins/gst/games/gstvideoimage.c */
|
|
#define I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
|
|
#define I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
|
|
#define I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(I420_Y_ROWSTRIDE(width)))/2)
|
|
|
|
#define I420_Y_OFFSET(w,h) (0)
|
|
#define I420_U_OFFSET(w,h) (I420_Y_OFFSET(w,h)+(I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
|
|
#define I420_V_OFFSET(w,h) (I420_U_OFFSET(w,h)+(I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
|
|
|
|
#define I420_SIZE(w,h) (I420_V_OFFSET(w,h)+(I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
|
|
|
|
/* elementfactory information */
|
|
static GstElementDetails gst_sdlvideosink_details = {
|
|
"Video sink",
|
|
"Sink/Video",
|
|
"An SDL-based videosink",
|
|
"Ronald Bultje <rbultje@ronald.bitfreak.net>"
|
|
"Edgard Lima <edgard.lima@indt.org.br>"
|
|
"Jan Schmidt <thaytan@mad.scientist.com>"
|
|
};
|
|
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_FULLSCREEN
|
|
};
|
|
|
|
static void gst_sdlvideosink_interface_init (GstImplementsInterfaceClass *
|
|
klass);
|
|
static gboolean gst_sdlvideosink_supported (GstImplementsInterface * iface,
|
|
GType type);
|
|
|
|
static void gst_sdlvideosink_xoverlay_init (GstXOverlayClass * klass);
|
|
static void gst_sdlvideosink_xoverlay_set_xwindow_id
|
|
(GstXOverlay * overlay, unsigned long parent);
|
|
|
|
static gboolean gst_sdlvideosink_lock (GstSDLVideoSink * sdl);
|
|
static void gst_sdlvideosink_unlock (GstSDLVideoSink * sdl);
|
|
|
|
static gboolean gst_sdlvideosink_initsdl (GstSDLVideoSink * sdl);
|
|
static void gst_sdlvideosink_deinitsdl (GstSDLVideoSink * sdl);
|
|
|
|
static gboolean gst_sdlvideosink_create (GstSDLVideoSink * sdl);
|
|
static void gst_sdlvideosink_destroy (GstSDLVideoSink * sdl);
|
|
|
|
static gboolean gst_sdlvideosink_setcaps (GstBaseSink * bsink, GstCaps * caps);
|
|
|
|
static GstFlowReturn gst_sdlvideosink_show_frame (GstBaseSink * bsink,
|
|
GstBuffer * buff);
|
|
|
|
static void gst_sdlvideosink_set_property (GObject * object,
|
|
guint prop_id, const GValue * value, GParamSpec * pspec);
|
|
static void gst_sdlvideosink_get_property (GObject * object,
|
|
guint prop_id, GValue * value, GParamSpec * pspec);
|
|
static GstStateChangeReturn
|
|
gst_sdlvideosink_change_state (GstElement * element, GstStateChange transition);
|
|
|
|
static void gst_sdlvideosink_navigation_init (GstNavigationInterface * iface);
|
|
|
|
static void gst_sdlv_process_events (GstSDLVideoSink * sdlvideosink);
|
|
|
|
static GstPadTemplate *sink_template;
|
|
|
|
static void
|
|
_do_init (GType type)
|
|
{
|
|
static const GInterfaceInfo iface_info = {
|
|
(GInterfaceInitFunc) gst_sdlvideosink_interface_init,
|
|
NULL,
|
|
NULL,
|
|
};
|
|
static const GInterfaceInfo xoverlay_info = {
|
|
(GInterfaceInitFunc) gst_sdlvideosink_xoverlay_init,
|
|
NULL,
|
|
NULL,
|
|
};
|
|
static const GInterfaceInfo navigation_info = {
|
|
(GInterfaceInitFunc) gst_sdlvideosink_navigation_init,
|
|
NULL,
|
|
NULL,
|
|
};
|
|
|
|
g_type_add_interface_static (type,
|
|
GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
|
|
g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info);
|
|
g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &navigation_info);
|
|
|
|
}
|
|
|
|
GST_BOILERPLATE_FULL (GstSDLVideoSink, gst_sdlvideosink, GstVideoSink,
|
|
GST_TYPE_VIDEO_SINK, _do_init)
|
|
|
|
static void gst_sdlvideosink_base_init (gpointer g_class)
|
|
{
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
|
GstCaps *capslist;
|
|
gint i;
|
|
guint32 formats[] = {
|
|
GST_MAKE_FOURCC ('I', '4', '2', '0'),
|
|
GST_MAKE_FOURCC ('Y', 'V', '1', '2'),
|
|
GST_MAKE_FOURCC ('Y', 'U', 'Y', '2')
|
|
/*
|
|
GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U'),
|
|
GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y')
|
|
*/
|
|
};
|
|
|
|
/* make a list of all available caps */
|
|
capslist = gst_caps_new_empty ();
|
|
for (i = 0; i < G_N_ELEMENTS (formats); i++) {
|
|
gst_caps_append_structure (capslist,
|
|
gst_structure_new ("video/x-raw-yuv",
|
|
"format", GST_TYPE_FOURCC, formats[i],
|
|
"width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
|
|
"height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
|
|
"framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 100, 1, NULL));
|
|
}
|
|
|
|
sink_template = gst_pad_template_new ("sink",
|
|
GST_PAD_SINK, GST_PAD_ALWAYS, capslist);
|
|
|
|
gst_element_class_add_pad_template (element_class, sink_template);
|
|
gst_element_class_set_details (element_class, &gst_sdlvideosink_details);
|
|
|
|
}
|
|
|
|
static void
|
|
gst_sdlvideosink_finalize (GObject * obj)
|
|
{
|
|
g_mutex_free (GST_SDLVIDEOSINK (obj)->lock);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (obj);
|
|
}
|
|
|
|
static void
|
|
gst_sdlvideosink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
|
|
GstClockTime * start, GstClockTime * end)
|
|
{
|
|
GstSDLVideoSink *sdlvideosink = GST_SDLVIDEOSINK (basesink);
|
|
GstClockTime timestamp, duration;
|
|
|
|
timestamp = GST_BUFFER_TIMESTAMP (buffer);
|
|
if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
|
|
*start = timestamp;
|
|
duration = GST_BUFFER_DURATION (buffer);
|
|
if (GST_CLOCK_TIME_IS_VALID (duration)) {
|
|
*end = timestamp + duration;
|
|
} else {
|
|
if (sdlvideosink->framerate_n > 0) {
|
|
*end = timestamp +
|
|
gst_util_uint64_scale_int (GST_SECOND, sdlvideosink->framerate_d,
|
|
sdlvideosink->framerate_n);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_sdlvideosink_class_init (GstSDLVideoSinkClass * klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GstElementClass *gstelement_class;
|
|
GstBaseSinkClass *gstvs_class;
|
|
|
|
gobject_class = (GObjectClass *) klass;
|
|
gstelement_class = (GstElementClass *) klass;
|
|
gstvs_class = (GstBaseSinkClass *) klass;
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
gobject_class->set_property = gst_sdlvideosink_set_property;
|
|
gobject_class->get_property = gst_sdlvideosink_get_property;
|
|
|
|
gobject_class->finalize = gst_sdlvideosink_finalize;
|
|
|
|
gstelement_class->change_state =
|
|
GST_DEBUG_FUNCPTR (gst_sdlvideosink_change_state);
|
|
|
|
gstvs_class->set_caps = GST_DEBUG_FUNCPTR (gst_sdlvideosink_setcaps);
|
|
gstvs_class->get_times = GST_DEBUG_FUNCPTR (gst_sdlvideosink_get_times);
|
|
gstvs_class->preroll = GST_DEBUG_FUNCPTR (gst_sdlvideosink_show_frame);
|
|
gstvs_class->render = GST_DEBUG_FUNCPTR (gst_sdlvideosink_show_frame);
|
|
|
|
g_object_class_install_property (gobject_class, PROP_FULLSCREEN,
|
|
g_param_spec_boolean ("fullscreen", "Fullscreen",
|
|
"If true it will be Full screen", FALSE, G_PARAM_READWRITE));
|
|
|
|
/*gstvs_class->set_video_out = gst_sdlvideosink_set_video_out;
|
|
gstvs_class->push_ui_event = gst_sdlvideosink_push_ui_event;
|
|
gstvs_class->set_geometry = gst_sdlvideosink_set_geometry; */
|
|
}
|
|
|
|
#if 0
|
|
/* FIXME */
|
|
static GstBuffer *
|
|
gst_sdlvideosink_buffer_new (GstBufferPool * pool,
|
|
gint64 location, guint size, gpointer user_data)
|
|
{
|
|
GstSDLVideoSink *sdlvideosink = GST_SDLVIDEOSINK (user_data);
|
|
GstBuffer *buffer;
|
|
|
|
if (!sdlvideosink->overlay)
|
|
return NULL;
|
|
|
|
if (!gst_sdlvideosink_lock (sdlvideosink)) {
|
|
return NULL;
|
|
}
|
|
|
|
/* this protects the buffer from being written over multiple times */
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
|
|
buffer = gst_buffer_new ();
|
|
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_DONTFREE);
|
|
GST_BUFFER_DATA (buffer) = sdlvideosink->overlay->pixels[0];
|
|
if (sdlvideosink->format == SDL_YV12_OVERLAY ||
|
|
sdlvideosink->format == SDL_IYUV_OVERLAY) {
|
|
GST_BUFFER_SIZE (buffer) =
|
|
sdlvideosink->width * sdlvideosink->height * 3 / 2;
|
|
} else {
|
|
GST_BUFFER_SIZE (buffer) = sdlvideosink->width * sdlvideosink->height * 2;
|
|
}
|
|
GST_BUFFER_MAXSIZE (buffer) = GST_BUFFER_SIZE (buffer);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static void
|
|
gst_sdlvideosink_buffer_free (GstBufferPool * pool,
|
|
GstBuffer * buffer, gpointer user_data)
|
|
{
|
|
GstSDLVideoSink *sdlvideosink = GST_SDLVIDEOSINK (user_data);
|
|
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
gst_sdlvideosink_unlock (sdlvideosink);
|
|
|
|
gst_buffer_default_free (buffer);
|
|
}
|
|
|
|
|
|
static GstBufferPool *
|
|
gst_sdlvideosink_get_bufferpool (GstPad * pad)
|
|
{
|
|
GstSDLVideoSink *sdlvideosink = GST_SDLVIDEOSINK (gst_pad_get_parent (pad));
|
|
|
|
if (sdlvideosink->overlay)
|
|
return sdlvideosink->bufferpool;
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
gst_sdlvideosink_init (GstSDLVideoSink * sdlvideosink,
|
|
GstSDLVideoSinkClass * g_class)
|
|
{
|
|
|
|
sdlvideosink->width = -1;
|
|
sdlvideosink->height = -1;
|
|
sdlvideosink->framerate_n = 0;
|
|
sdlvideosink->framerate_d = 1;
|
|
sdlvideosink->full_screen = FALSE;
|
|
|
|
sdlvideosink->overlay = NULL;
|
|
sdlvideosink->screen = NULL;
|
|
|
|
sdlvideosink->xwindow_id = 0;
|
|
|
|
//sdlvideosink->capslist = capslist;
|
|
|
|
sdlvideosink->init = FALSE;
|
|
|
|
sdlvideosink->event_thread = NULL;
|
|
sdlvideosink->running = FALSE;
|
|
|
|
sdlvideosink->lock = g_mutex_new ();
|
|
}
|
|
|
|
static void
|
|
gst_sdlvideosink_interface_init (GstImplementsInterfaceClass * klass)
|
|
{
|
|
klass->supported = gst_sdlvideosink_supported;
|
|
}
|
|
|
|
static gboolean
|
|
gst_sdlvideosink_supported (GstImplementsInterface * interface,
|
|
GType iface_type)
|
|
{
|
|
GstSDLVideoSink *sdlvideosink = GST_SDLVIDEOSINK (interface);
|
|
gboolean result = FALSE;
|
|
|
|
/* check SDL for whether it was compiled against X, FB, etc. */
|
|
if (iface_type == GST_TYPE_X_OVERLAY) {
|
|
gchar tmp[4];
|
|
|
|
if (!sdlvideosink->init) {
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
SDL_Init (SDL_INIT_VIDEO);
|
|
|
|
/* True if the video driver is X11 */
|
|
result = (strcmp ("x11", SDL_VideoDriverName (tmp, 4)) == 0);
|
|
SDL_Quit ();
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
} else
|
|
result = sdlvideosink->is_xwindows;
|
|
} else if (iface_type == GST_TYPE_NAVIGATION)
|
|
result = TRUE;
|
|
|
|
return result;
|
|
}
|
|
|
|
/* SDL Video sink and X overlay:
|
|
*
|
|
* SDL supports creating an Xv window/overlay within an existing X window
|
|
* through the horrible mechanism of setting the WINDOWID environment
|
|
* variable.
|
|
* It will then display the x overlay within that window, but not at the
|
|
* full window size. Instead, we need to explicitly tell SDL the size.
|
|
*
|
|
* Unfortunately, the XOverlay interface in GStreamer doesn't supply
|
|
* that information. The only way to get it would be to do what X[v]imagesink
|
|
* does and retrieve it using X11 calls, and linking to Xlib. That would
|
|
* defeat the whole purpose of using the SDL abstraction and plugin entirely
|
|
* however.
|
|
*
|
|
* I have no nice solution to this problem for you, dear readers.
|
|
*/
|
|
static void
|
|
gst_sdlvideosink_xoverlay_init (GstXOverlayClass * klass)
|
|
{
|
|
klass->set_xwindow_id = gst_sdlvideosink_xoverlay_set_xwindow_id;
|
|
}
|
|
|
|
static void
|
|
gst_sdlvideosink_xoverlay_set_xwindow_id (GstXOverlay * overlay,
|
|
unsigned long parent)
|
|
{
|
|
GstSDLVideoSink *sdlvideosink = GST_SDLVIDEOSINK (overlay);
|
|
|
|
if (sdlvideosink->xwindow_id == parent)
|
|
return;
|
|
|
|
sdlvideosink->xwindow_id = parent;
|
|
|
|
/* are we running yet? */
|
|
if (sdlvideosink->init) {
|
|
gboolean negotiated;
|
|
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
|
|
negotiated = (sdlvideosink->overlay != NULL);
|
|
|
|
if (negotiated)
|
|
gst_sdlvideosink_destroy (sdlvideosink);
|
|
|
|
/* Call initsdl to set the WINDOWID env var urk */
|
|
gst_sdlvideosink_initsdl (sdlvideosink);
|
|
|
|
if (negotiated)
|
|
gst_sdlvideosink_create (sdlvideosink);
|
|
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
}
|
|
}
|
|
|
|
static guint32
|
|
gst_sdlvideosink_get_sdl_from_fourcc (GstSDLVideoSink * sdlvideosink,
|
|
guint32 code)
|
|
{
|
|
switch (code) {
|
|
/* Note: SDL_IYUV_OVERLAY does not always work for I420 */
|
|
case GST_MAKE_FOURCC ('I', '4', '2', '0'):
|
|
return SDL_YV12_OVERLAY;
|
|
case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
|
|
return SDL_YV12_OVERLAY;
|
|
case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
|
|
return SDL_YUY2_OVERLAY;
|
|
case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
|
|
return SDL_UYVY_OVERLAY;
|
|
case GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U'):
|
|
return SDL_YVYU_OVERLAY;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_sdlvideosink_lock (GstSDLVideoSink * sdlvideosink)
|
|
{
|
|
/* assure that we've got a screen */
|
|
if (!sdlvideosink->screen || !sdlvideosink->overlay)
|
|
goto no_setup;
|
|
|
|
/* Lock SDL/yuv-overlay */
|
|
if (SDL_MUSTLOCK (sdlvideosink->screen)) {
|
|
if (SDL_LockSurface (sdlvideosink->screen) < 0)
|
|
goto could_not_lock;
|
|
}
|
|
if (SDL_LockYUVOverlay (sdlvideosink->overlay) < 0)
|
|
goto lock_yuv;
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
no_setup:
|
|
{
|
|
GST_ELEMENT_ERROR (sdlvideosink, LIBRARY, TOO_LAZY, (NULL),
|
|
("Tried to lock screen without being set-up"));
|
|
return FALSE;
|
|
}
|
|
could_not_lock:
|
|
{
|
|
GST_ELEMENT_ERROR (sdlvideosink, LIBRARY, TOO_LAZY, (NULL),
|
|
("SDL: couldn't lock the SDL video window: %s", SDL_GetError ()));
|
|
return FALSE;
|
|
}
|
|
lock_yuv:
|
|
{
|
|
GST_ELEMENT_ERROR (sdlvideosink, LIBRARY, TOO_LAZY, (NULL),
|
|
("SDL: couldn\'t lock the SDL YUV overlay: %s", SDL_GetError ()));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
gst_sdlvideosink_unlock (GstSDLVideoSink * sdlvideosink)
|
|
{
|
|
/* Unlock SDL_overlay */
|
|
SDL_UnlockYUVOverlay (sdlvideosink->overlay);
|
|
if (SDL_MUSTLOCK (sdlvideosink->screen))
|
|
SDL_UnlockSurface (sdlvideosink->screen);
|
|
}
|
|
|
|
/* Must be called with ->lock held */
|
|
static void
|
|
gst_sdlvideosink_deinitsdl (GstSDLVideoSink * sdlvideosink)
|
|
{
|
|
if (sdlvideosink->init) {
|
|
sdlvideosink->running = FALSE;
|
|
if (sdlvideosink->event_thread) {
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
g_thread_join (sdlvideosink->event_thread);
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
sdlvideosink->event_thread = NULL;
|
|
}
|
|
|
|
SDL_Quit ();
|
|
sdlvideosink->init = FALSE;
|
|
|
|
}
|
|
}
|
|
|
|
/* Process pending events. Call with ->lock held */
|
|
static void
|
|
gst_sdlv_process_events (GstSDLVideoSink * sdlvideosink)
|
|
{
|
|
SDL_Event event;
|
|
int numevents;
|
|
char *keysym;
|
|
|
|
do {
|
|
SDL_PumpEvents ();
|
|
numevents = SDL_PeepEvents (&event, 1, SDL_GETEVENT,
|
|
SDL_KEYDOWNMASK | SDL_KEYUPMASK |
|
|
SDL_MOUSEMOTIONMASK | SDL_MOUSEBUTTONDOWNMASK |
|
|
SDL_MOUSEBUTTONUPMASK | SDL_QUITMASK | SDL_VIDEORESIZEMASK);
|
|
|
|
if (numevents > 0 && (event.type == SDL_KEYUP || event.type == SDL_KEYDOWN)) {
|
|
keysym = SDL_GetKeyName (event.key.keysym.sym);
|
|
}
|
|
|
|
if (numevents > 0) {
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
switch (event.type) {
|
|
case SDL_MOUSEMOTION:
|
|
gst_navigation_send_mouse_event (GST_NAVIGATION (sdlvideosink),
|
|
"mouse-move", 0, event.motion.x, event.motion.y);
|
|
break;
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
gst_navigation_send_mouse_event (GST_NAVIGATION (sdlvideosink),
|
|
"mouse-button-press",
|
|
event.button.button, event.button.x, event.button.y);
|
|
break;
|
|
case SDL_MOUSEBUTTONUP:
|
|
gst_navigation_send_mouse_event (GST_NAVIGATION (sdlvideosink),
|
|
"mouse-button-release",
|
|
event.button.button, event.button.x, event.button.y);
|
|
break;
|
|
case SDL_KEYUP:
|
|
GST_DEBUG ("key press event %s !",
|
|
SDL_GetKeyName (event.key.keysym.sym));
|
|
gst_navigation_send_key_event (GST_NAVIGATION (sdlvideosink),
|
|
"key-release", keysym);
|
|
break;
|
|
case SDL_KEYDOWN:
|
|
if (SDLK_ESCAPE != event.key.keysym.sym) {
|
|
GST_DEBUG ("key press event %s !",
|
|
SDL_GetKeyName (event.key.keysym.sym));
|
|
gst_navigation_send_key_event (GST_NAVIGATION (sdlvideosink),
|
|
"key-press", keysym);
|
|
break;
|
|
} else {
|
|
/* fall through */
|
|
}
|
|
case SDL_QUIT:
|
|
sdlvideosink->running = FALSE;
|
|
GST_ELEMENT_ERROR (sdlvideosink, RESOURCE, OPEN_WRITE,
|
|
("Video output device is gone."),
|
|
("We were running fullscreen and user "
|
|
"pressed the ESC key, stopping playback."));
|
|
break;
|
|
case SDL_VIDEORESIZE:
|
|
/* create a SDL window of the size requested by the user */
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
GST_VIDEO_SINK_WIDTH (sdlvideosink) = event.resize.w;
|
|
GST_VIDEO_SINK_HEIGHT (sdlvideosink) = event.resize.h;
|
|
gst_sdlvideosink_create (sdlvideosink);
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
break;
|
|
}
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
}
|
|
} while (numevents > 0);
|
|
}
|
|
|
|
static gpointer
|
|
gst_sdlvideosink_event_thread (GstSDLVideoSink * sdlvideosink)
|
|
{
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
while (sdlvideosink->running) {
|
|
gst_sdlv_process_events (sdlvideosink);
|
|
|
|
/* Done events, sleep for 50 ms */
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
g_usleep (50000);
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
}
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Must be called with the SDL lock held */
|
|
static gboolean
|
|
gst_sdlvideosink_initsdl (GstSDLVideoSink * sdlvideosink)
|
|
{
|
|
gst_sdlvideosink_deinitsdl (sdlvideosink);
|
|
|
|
if (sdlvideosink->is_xwindows && !sdlvideosink->xwindow_id) {
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (sdlvideosink));
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
}
|
|
|
|
if (!sdlvideosink->xwindow_id) {
|
|
unsetenv ("SDL_WINDOWID");
|
|
} else {
|
|
char SDL_hack[32];
|
|
|
|
sprintf (SDL_hack, "%lu", sdlvideosink->xwindow_id);
|
|
setenv ("SDL_WINDOWID", SDL_hack, 1);
|
|
}
|
|
|
|
/* Initialize the SDL library */
|
|
if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) < 0)
|
|
goto init_failed;
|
|
|
|
sdlvideosink->init = TRUE;
|
|
|
|
sdlvideosink->running = TRUE;
|
|
sdlvideosink->event_thread =
|
|
g_thread_create ((GThreadFunc) gst_sdlvideosink_event_thread,
|
|
sdlvideosink, TRUE, NULL);
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
init_failed:
|
|
{
|
|
GST_ELEMENT_ERROR (sdlvideosink, LIBRARY, INIT, (NULL),
|
|
("Couldn't initialize SDL: %s", SDL_GetError ()));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Must be called with the sdl lock held */
|
|
static void
|
|
gst_sdlvideosink_destroy (GstSDLVideoSink * sdlvideosink)
|
|
{
|
|
if (sdlvideosink->overlay) {
|
|
SDL_FreeYUVOverlay (sdlvideosink->overlay);
|
|
sdlvideosink->overlay = NULL;
|
|
}
|
|
|
|
if (sdlvideosink->screen) {
|
|
SDL_FreeSurface (sdlvideosink->screen);
|
|
sdlvideosink->screen = NULL;
|
|
}
|
|
sdlvideosink->xwindow_id = 0;
|
|
}
|
|
|
|
/* Must be called with the sdl lock held */
|
|
static gboolean
|
|
gst_sdlvideosink_create (GstSDLVideoSink * sdlvideosink)
|
|
{
|
|
if (GST_VIDEO_SINK_HEIGHT (sdlvideosink) <= 0)
|
|
GST_VIDEO_SINK_HEIGHT (sdlvideosink) = sdlvideosink->height;
|
|
if (GST_VIDEO_SINK_WIDTH (sdlvideosink) <= 0)
|
|
GST_VIDEO_SINK_WIDTH (sdlvideosink) = sdlvideosink->width;
|
|
|
|
gst_sdlvideosink_destroy (sdlvideosink);
|
|
|
|
if (sdlvideosink->is_xwindows && !sdlvideosink->xwindow_id) {
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (sdlvideosink));
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
}
|
|
|
|
/* create a SDL window of the size requested by the user */
|
|
if (sdlvideosink->full_screen) {
|
|
sdlvideosink->screen =
|
|
SDL_SetVideoMode (GST_VIDEO_SINK_WIDTH (sdlvideosink),
|
|
GST_VIDEO_SINK_HEIGHT (sdlvideosink), 0,
|
|
SDL_SWSURFACE | SDL_FULLSCREEN);
|
|
} else {
|
|
sdlvideosink->screen =
|
|
SDL_SetVideoMode (GST_VIDEO_SINK_WIDTH (sdlvideosink),
|
|
GST_VIDEO_SINK_HEIGHT (sdlvideosink), 0, SDL_HWSURFACE | SDL_RESIZABLE);
|
|
}
|
|
if (sdlvideosink->screen == NULL)
|
|
goto no_screen;
|
|
|
|
/* create a new YUV overlay */
|
|
sdlvideosink->overlay = SDL_CreateYUVOverlay (sdlvideosink->width,
|
|
sdlvideosink->height, sdlvideosink->format, sdlvideosink->screen);
|
|
if (sdlvideosink->overlay == NULL)
|
|
goto no_overlay;
|
|
|
|
|
|
GST_DEBUG ("Using a %dx%d %dbpp SDL screen with a %dx%d \'%"
|
|
GST_FOURCC_FORMAT "\' YUV overlay", GST_VIDEO_SINK_WIDTH (sdlvideosink),
|
|
GST_VIDEO_SINK_HEIGHT (sdlvideosink),
|
|
sdlvideosink->screen->format->BitsPerPixel, sdlvideosink->width,
|
|
sdlvideosink->height, GST_FOURCC_ARGS (sdlvideosink->format));
|
|
|
|
sdlvideosink->rect.x = 0;
|
|
sdlvideosink->rect.y = 0;
|
|
sdlvideosink->rect.w = GST_VIDEO_SINK_WIDTH (sdlvideosink);
|
|
sdlvideosink->rect.h = GST_VIDEO_SINK_HEIGHT (sdlvideosink);
|
|
|
|
/*SDL_DisplayYUVOverlay (sdlvideosink->overlay, &(sdlvideosink->rect)); */
|
|
|
|
GST_DEBUG ("sdlvideosink: setting %08x (%" GST_FOURCC_FORMAT ")",
|
|
sdlvideosink->format, GST_FOURCC_ARGS (sdlvideosink->format));
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
no_screen:
|
|
{
|
|
GST_ELEMENT_ERROR (sdlvideosink, LIBRARY, TOO_LAZY, (NULL),
|
|
("SDL: Couldn't set %dx%d: %s", GST_VIDEO_SINK_WIDTH (sdlvideosink),
|
|
GST_VIDEO_SINK_HEIGHT (sdlvideosink), SDL_GetError ()));
|
|
return FALSE;
|
|
}
|
|
no_overlay:
|
|
{
|
|
GST_ELEMENT_ERROR (sdlvideosink, LIBRARY, TOO_LAZY, (NULL),
|
|
("SDL: Couldn't create SDL YUV overlay (%dx%d \'%" GST_FOURCC_FORMAT
|
|
"\'): %s", sdlvideosink->width, sdlvideosink->height,
|
|
GST_FOURCC_ARGS (sdlvideosink->format), SDL_GetError ()));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_sdlvideosink_setcaps (GstBaseSink * bsink, GstCaps * vscapslist)
|
|
{
|
|
GstSDLVideoSink *sdlvideosink;
|
|
guint32 format;
|
|
GstStructure *structure;
|
|
gboolean res = TRUE;
|
|
|
|
sdlvideosink = GST_SDLVIDEOSINK (bsink);
|
|
|
|
structure = gst_caps_get_structure (vscapslist, 0);
|
|
gst_structure_get_fourcc (structure, "format", &sdlvideosink->fourcc);
|
|
sdlvideosink->format =
|
|
gst_sdlvideosink_get_sdl_from_fourcc (sdlvideosink, sdlvideosink->fourcc);
|
|
gst_structure_get_int (structure, "width", &sdlvideosink->width);
|
|
gst_structure_get_int (structure, "height", &sdlvideosink->height);
|
|
gst_structure_get_fraction (structure, "framerate",
|
|
&sdlvideosink->framerate_n, &sdlvideosink->framerate_d);
|
|
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
if (!sdlvideosink->format || !gst_sdlvideosink_create (sdlvideosink))
|
|
res = FALSE;
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static GstFlowReturn
|
|
gst_sdlvideosink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
|
|
{
|
|
|
|
GstSDLVideoSink *sdlvideosink;
|
|
SDL_Event sdl_event;
|
|
|
|
sdlvideosink = GST_SDLVIDEOSINK (bsink);
|
|
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
if (!sdlvideosink->init ||
|
|
!sdlvideosink->overlay || !sdlvideosink->overlay->pixels)
|
|
goto not_init;
|
|
|
|
/* if (GST_BUFFER_DATA (buf) != sdlvideosink->overlay->pixels[0]) */
|
|
if (TRUE) {
|
|
guint8 *out;
|
|
gint l;
|
|
|
|
if (!gst_sdlvideosink_lock (sdlvideosink))
|
|
goto cannot_lock;
|
|
|
|
/* buf->yuv - FIXME: bufferpool! */
|
|
if (sdlvideosink->format == SDL_YV12_OVERLAY) {
|
|
guint8 *y, *u, *v;
|
|
|
|
switch (sdlvideosink->fourcc) {
|
|
case GST_MAKE_FOURCC ('I', '4', '2', '0'):
|
|
y = GST_BUFFER_DATA (buf);
|
|
/* I420 is YV12 with switched colour planes and different offsets */
|
|
v = y + I420_U_OFFSET (sdlvideosink->width, sdlvideosink->height);
|
|
u = y + I420_V_OFFSET (sdlvideosink->width, sdlvideosink->height);
|
|
break;
|
|
case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
|
|
y = GST_BUFFER_DATA (buf);
|
|
u = y + I420_U_OFFSET (sdlvideosink->width, sdlvideosink->height);
|
|
v = y + I420_V_OFFSET (sdlvideosink->width, sdlvideosink->height);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
/* Y Plane */
|
|
out = sdlvideosink->overlay->pixels[0];
|
|
for (l = 0; l < sdlvideosink->height; l++) {
|
|
memcpy (out, y, I420_Y_ROWSTRIDE (sdlvideosink->width));
|
|
out += sdlvideosink->overlay->pitches[0];
|
|
y += I420_Y_ROWSTRIDE (sdlvideosink->width);
|
|
}
|
|
|
|
/* U plane */
|
|
out = sdlvideosink->overlay->pixels[1];
|
|
for (l = 0; l < (sdlvideosink->height / 2); l++) {
|
|
memcpy (out, u, I420_U_ROWSTRIDE (sdlvideosink->width));
|
|
out += sdlvideosink->overlay->pitches[1];
|
|
u += I420_U_ROWSTRIDE (sdlvideosink->width);
|
|
}
|
|
|
|
/* V plane */
|
|
out = sdlvideosink->overlay->pixels[2];
|
|
for (l = 0; l < (sdlvideosink->height / 2); l++) {
|
|
memcpy (out, v, I420_V_ROWSTRIDE (sdlvideosink->width));
|
|
out += sdlvideosink->overlay->pitches[2];
|
|
v += I420_V_ROWSTRIDE (sdlvideosink->width);
|
|
}
|
|
} else {
|
|
guint8 *in = GST_BUFFER_DATA (buf);
|
|
gint in_stride = sdlvideosink->width * 2;
|
|
|
|
out = sdlvideosink->overlay->pixels[0];
|
|
|
|
for (l = 0; l < sdlvideosink->height; l++) {
|
|
memcpy (out, in, in_stride);
|
|
out += sdlvideosink->overlay->pitches[0];
|
|
in += in_stride;
|
|
}
|
|
}
|
|
gst_sdlvideosink_unlock (sdlvideosink);
|
|
}
|
|
|
|
/* Show, baby, show! */
|
|
SDL_DisplayYUVOverlay (sdlvideosink->overlay, &(sdlvideosink->rect));
|
|
|
|
/* Handle any resize */
|
|
gst_sdlv_process_events (sdlvideosink);
|
|
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
/* ERRORS */
|
|
not_init:
|
|
{
|
|
GST_ELEMENT_ERROR (sdlvideosink, CORE, NEGOTIATION, (NULL),
|
|
("not negotiated."));
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
|
}
|
|
cannot_lock:
|
|
{
|
|
/* lock function posted detailed message */
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
gst_sdlvideosink_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstSDLVideoSink *sdlvideosink;
|
|
|
|
sdlvideosink = GST_SDLVIDEOSINK (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_FULLSCREEN:
|
|
sdlvideosink->full_screen = g_value_get_boolean (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
gst_sdlvideosink_get_property (GObject * object, guint prop_id, GValue * value,
|
|
GParamSpec * pspec)
|
|
{
|
|
GstSDLVideoSink *sdlvideosink;
|
|
|
|
sdlvideosink = GST_SDLVIDEOSINK (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_FULLSCREEN:
|
|
g_value_set_boolean (value, sdlvideosink->full_screen);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static GstStateChangeReturn
|
|
gst_sdlvideosink_change_state (GstElement * element, GstStateChange transition)
|
|
{
|
|
GstSDLVideoSink *sdlvideosink;
|
|
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
|
|
|
g_return_val_if_fail (GST_IS_SDLVIDEOSINK (element),
|
|
GST_STATE_CHANGE_FAILURE);
|
|
sdlvideosink = GST_SDLVIDEOSINK (element);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
sdlvideosink->is_xwindows = GST_IS_X_OVERLAY (sdlvideosink);
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
if (!gst_sdlvideosink_initsdl (sdlvideosink)) {
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
goto init_failed;
|
|
}
|
|
GST_OBJECT_FLAG_SET (sdlvideosink, GST_SDLVIDEOSINK_OPEN);
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
break;
|
|
default: /* do nothing */
|
|
break;
|
|
}
|
|
|
|
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
|
sdlvideosink->framerate_n = 0;
|
|
sdlvideosink->framerate_d = 1;
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
gst_sdlvideosink_destroy (sdlvideosink);
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
break;
|
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
g_mutex_lock (sdlvideosink->lock);
|
|
gst_sdlvideosink_deinitsdl (sdlvideosink);
|
|
GST_OBJECT_FLAG_UNSET (sdlvideosink, GST_SDLVIDEOSINK_OPEN);
|
|
g_mutex_unlock (sdlvideosink->lock);
|
|
break;
|
|
default: /* do nothing */
|
|
break;
|
|
}
|
|
return ret;
|
|
|
|
init_failed:
|
|
{
|
|
/* method posted detailed error message */
|
|
GST_DEBUG_OBJECT (sdlvideosink, "init failed");
|
|
return GST_STATE_CHANGE_FAILURE;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
gst_sdlvideosink_navigation_send_event (GstNavigation * navigation,
|
|
GstStructure * structure)
|
|
{
|
|
GstSDLVideoSink *sdlvideosink = GST_SDLVIDEOSINK (navigation);
|
|
GstEvent *event;
|
|
GstVideoRectangle src, dst, result;
|
|
gint width, height;
|
|
double x, y;
|
|
GstPad *pad = NULL;
|
|
|
|
src.w = GST_VIDEO_SINK_WIDTH (sdlvideosink);
|
|
src.h = GST_VIDEO_SINK_HEIGHT (sdlvideosink);
|
|
dst.w = sdlvideosink->width;
|
|
dst.h = sdlvideosink->height;
|
|
gst_video_sink_center_rect (src, dst, &result, FALSE);
|
|
|
|
event = gst_event_new_navigation (structure);
|
|
|
|
/* Our coordinates can be wrong here if we centered the video */
|
|
|
|
/* Converting pointer coordinates to the non scaled geometry */
|
|
if (gst_structure_get_double (structure, "pointer_x", &x)) {
|
|
double old_x = x;
|
|
|
|
if (x >= result.x && x <= (result.x + result.w)) {
|
|
x -= result.x;
|
|
x *= sdlvideosink->width;
|
|
x /= result.w;
|
|
} else {
|
|
x = 0;
|
|
}
|
|
GST_DEBUG_OBJECT (sdlvideosink, "translated navigation event x "
|
|
"coordinate from %f to %f", old_x, x);
|
|
gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
|
|
}
|
|
if (gst_structure_get_double (structure, "pointer_y", &y)) {
|
|
double old_y = y;
|
|
|
|
if (y >= result.y && y <= (result.y + result.h)) {
|
|
y -= result.y;
|
|
y *= sdlvideosink->height;
|
|
y /= result.h;
|
|
} else {
|
|
y = 0;
|
|
}
|
|
GST_DEBUG_OBJECT (sdlvideosink, "translated navigation event y "
|
|
"coordinate from %f to %f", old_y, y);
|
|
gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
|
|
}
|
|
|
|
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sdlvideosink));
|
|
|
|
if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
|
|
gst_pad_send_event (pad, event);
|
|
|
|
gst_object_unref (pad);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_sdlvideosink_navigation_init (GstNavigationInterface * iface)
|
|
{
|
|
iface->send_event = gst_sdlvideosink_navigation_send_event;
|
|
}
|