Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
gst-plugins-good
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jakub Adam
gst-plugins-good
Commits
538d3a95
Commit
538d3a95
authored
Oct 02, 2010
by
Rob Clark
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
v4l2sink: add navigation support
parent
9e1d419d
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
231 additions
and
14 deletions
+231
-14
sys/v4l2/gstv4l2sink.c
sys/v4l2/gstv4l2sink.c
+87
-3
sys/v4l2/gstv4l2sink.h
sys/v4l2/gstv4l2sink.h
+2
-0
sys/v4l2/gstv4l2xoverlay.c
sys/v4l2/gstv4l2xoverlay.c
+138
-11
sys/v4l2/gstv4l2xoverlay.h
sys/v4l2/gstv4l2xoverlay.h
+4
-0
No files found.
sys/v4l2/gstv4l2sink.c
View file @
538d3a95
...
...
@@ -33,6 +33,18 @@
* |[
* gst-launch videotestsrc ! v4l2sink device=/dev/video1
* ]| This pipeline displays a test pattern on /dev/video1
* |[
* gst-launch -v videotestsrc ! navigationtest ! v4l2sink
* ]| A pipeline to test navigation events.
* While moving the mouse pointer over the test signal you will see a black box
* following the mouse pointer. If you press the mouse button somewhere on the
* video and release it somewhere else a green box will appear where you pressed
* the button and a red one where you released it. (The navigationtest element
* is part of gst-plugins-good.) You can observe here that even if the images
* are scaled through hardware the pointer coordinates are converted back to the
* original video frame geometry so that the box can be drawn to the correct
* position. This also handles borders correctly, limiting coordinates to the
* image area
* </refsect2>
*/
...
...
@@ -94,6 +106,7 @@ gst_v4l2sink_iface_supported (GstImplementsInterface * iface, GType iface_type)
g_assert
(
iface_type
==
GST_TYPE_TUNER
||
#ifdef HAVE_XVIDEO
iface_type
==
GST_TYPE_X_OVERLAY
||
iface_type
==
GST_TYPE_NAVIGATION
||
#endif
iface_type
==
GST_TYPE_COLOR_BALANCE
||
iface_type
==
GST_TYPE_VIDEO_ORIENTATION
);
...
...
@@ -102,8 +115,10 @@ gst_v4l2sink_iface_supported (GstImplementsInterface * iface, GType iface_type)
return
FALSE
;
#ifdef HAVE_XVIDEO
if
(
iface_type
==
GST_TYPE_X_OVERLAY
&&
!
GST_V4L2_IS_OVERLAY
(
v4l2object
))
return
FALSE
;
if
(
!
GST_V4L2_IS_OVERLAY
(
v4l2object
))
{
if
(
iface_type
==
GST_TYPE_X_OVERLAY
||
iface_type
==
GST_TYPE_NAVIGATION
)
return
FALSE
;
}
#endif
return
TRUE
;
...
...
@@ -118,6 +133,16 @@ gst_v4l2sink_interface_init (GstImplementsInterfaceClass * klass)
klass
->
supported
=
gst_v4l2sink_iface_supported
;
}
#ifdef HAVE_XVIDEO
static
void
gst_v4l2sink_navigation_send_event
(
GstNavigation
*
navigation
,
GstStructure
*
structure
);
static
void
gst_v4l2sink_navigation_init
(
GstNavigationInterface
*
iface
)
{
iface
->
send_event
=
gst_v4l2sink_navigation_send_event
;
}
#endif
static
void
gst_v4l2sink_init_interfaces
(
GType
type
)
{
...
...
@@ -137,6 +162,11 @@ gst_v4l2sink_init_interfaces (GType type)
NULL
,
NULL
,
};
static
const
GInterfaceInfo
v4l2_navigation_info
=
{
(
GInterfaceInitFunc
)
gst_v4l2sink_navigation_init
,
NULL
,
NULL
,
};
#endif
static
const
GInterfaceInfo
v4l2_colorbalance_info
=
{
(
GInterfaceInitFunc
)
gst_v4l2sink_color_balance_interface_init
,
...
...
@@ -159,6 +189,8 @@ gst_v4l2sink_init_interfaces (GType type)
g_type_add_interface_static
(
type
,
GST_TYPE_TUNER
,
&
v4l2_tuner_info
);
#ifdef HAVE_XVIDEO
g_type_add_interface_static
(
type
,
GST_TYPE_X_OVERLAY
,
&
v4l2_xoverlay_info
);
g_type_add_interface_static
(
type
,
GST_TYPE_NAVIGATION
,
&
v4l2_navigation_info
);
#endif
g_type_add_interface_static
(
type
,
GST_TYPE_COLOR_BALANCE
,
&
v4l2_colorbalance_info
);
...
...
@@ -195,7 +227,6 @@ static GstFlowReturn gst_v4l2sink_buffer_alloc (GstBaseSink * bsink,
static
GstFlowReturn
gst_v4l2sink_show_frame
(
GstBaseSink
*
bsink
,
GstBuffer
*
buf
);
static
void
gst_v4l2sink_base_init
(
gpointer
g_class
)
{
...
...
@@ -709,6 +740,15 @@ gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
return
FALSE
;
}
v4l2sink
->
video_width
=
w
;
v4l2sink
->
video_height
=
h
;
/* TODO: videosink width/height should be scaled according to
* pixel-aspect-ratio
*/
GST_VIDEO_SINK_WIDTH
(
v4l2sink
)
=
w
;
GST_VIDEO_SINK_HEIGHT
(
v4l2sink
)
=
h
;
v4l2sink
->
current_caps
=
gst_caps_ref
(
caps
);
return
TRUE
;
...
...
@@ -866,3 +906,47 @@ gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
return
GST_FLOW_OK
;
}
#ifdef HAVE_XVIDEO
static
void
gst_v4l2sink_navigation_send_event
(
GstNavigation
*
navigation
,
GstStructure
*
structure
)
{
GstV4l2Sink
*
v4l2sink
=
GST_V4L2SINK
(
navigation
);
GstV4l2Xv
*
xv
=
v4l2sink
->
v4l2object
->
xv
;
GstPad
*
peer
;
if
(
!
xv
)
return
;
if
((
peer
=
gst_pad_get_peer
(
GST_VIDEO_SINK_PAD
(
v4l2sink
))))
{
GstVideoRectangle
rect
;
gdouble
x
,
y
,
xscale
=
1
.
0
,
yscale
=
1
.
0
;
gst_v4l2_xoverlay_get_render_rect
(
v4l2sink
->
v4l2object
,
&
rect
);
/* We calculate scaling using the original video frames geometry to
* include pixel aspect ratio scaling.
*/
xscale
=
(
gdouble
)
v4l2sink
->
video_width
/
rect
.
w
;
yscale
=
(
gdouble
)
v4l2sink
->
video_height
/
rect
.
h
;
/* Converting pointer coordinates to the non scaled geometry */
if
(
gst_structure_get_double
(
structure
,
"pointer_x"
,
&
x
))
{
x
=
MIN
(
x
,
rect
.
x
+
rect
.
w
);
x
=
MAX
(
x
-
rect
.
x
,
0
);
gst_structure_set
(
structure
,
"pointer_x"
,
G_TYPE_DOUBLE
,
(
gdouble
)
x
*
xscale
,
NULL
);
}
if
(
gst_structure_get_double
(
structure
,
"pointer_y"
,
&
y
))
{
y
=
MIN
(
y
,
rect
.
y
+
rect
.
h
);
y
=
MAX
(
y
-
rect
.
y
,
0
);
gst_structure_set
(
structure
,
"pointer_y"
,
G_TYPE_DOUBLE
,
(
gdouble
)
y
*
yscale
,
NULL
);
}
gst_pad_send_event
(
peer
,
gst_event_new_navigation
(
structure
));
gst_object_unref
(
peer
);
}
}
#endif
sys/v4l2/gstv4l2sink.h
View file @
538d3a95
...
...
@@ -60,6 +60,8 @@ struct _GstV4l2Sink {
guint32
num_buffers
;
guint32
min_queued_bufs
;
gint
video_width
,
video_height
;
/* original (unscaled) video w/h */
/*
* field to store requested overlay and crop top/left/width/height props:
* note, could maybe be combined with 'vwin' field in GstV4l2Object?
...
...
sys/v4l2/gstv4l2xoverlay.c
View file @
538d3a95
...
...
@@ -33,6 +33,8 @@
#include <X11/extensions/Xvlib.h>
#include <sys/stat.h>
#include <gst/interfaces/navigation.h>
#include "gstv4l2xoverlay.h"
#include "gstv4l2object.h"
#include "v4l2_calls.h"
...
...
@@ -43,7 +45,7 @@ struct _GstV4l2Xv
{
Display
*
dpy
;
gint
port
,
idle_id
,
event_id
;
GMutex
*
mutex
;
GMutex
*
mutex
;
/* to serialize calls to X11 */
};
GST_DEBUG_CATEGORY_STATIC
(
v4l2xv_debug
);
...
...
@@ -175,15 +177,53 @@ gst_v4l2_xoverlay_stop (GstV4l2Object * v4l2object)
gst_v4l2_xoverlay_close
(
v4l2object
);
}
/* should be called with mutex held */
static
gboolean
get_render_rect
(
GstV4l2Object
*
v4l2object
,
GstVideoRectangle
*
rect
)
{
GstV4l2Xv
*
v4l2xv
=
v4l2object
->
xv
;
if
(
v4l2xv
&&
v4l2xv
->
dpy
&&
v4l2object
->
xwindow_id
)
{
XWindowAttributes
attr
;
XGetWindowAttributes
(
v4l2xv
->
dpy
,
v4l2object
->
xwindow_id
,
&
attr
);
/* this is where we'd add support to maintain aspect ratio */
rect
->
x
=
0
;
rect
->
y
=
0
;
rect
->
w
=
attr
.
width
;
rect
->
h
=
attr
.
height
;
return
TRUE
;
}
else
{
return
FALSE
;
}
}
gboolean
gst_v4l2_xoverlay_get_render_rect
(
GstV4l2Object
*
v4l2object
,
GstVideoRectangle
*
rect
)
{
GstV4l2Xv
*
v4l2xv
=
v4l2object
->
xv
;
gboolean
ret
=
FALSE
;
if
(
v4l2xv
)
{
g_mutex_lock
(
v4l2xv
->
mutex
);
ret
=
get_render_rect
(
v4l2object
,
rect
);
g_mutex_unlock
(
v4l2xv
->
mutex
);
}
return
ret
;
}
static
void
update_geometry
(
GstV4l2Object
*
v4l2object
)
{
GstV4l2Xv
*
v4l2xv
=
v4l2object
->
xv
;
XWindowAttributes
attr
;
XGetWindowAttributes
(
v4l2xv
->
dpy
,
v4l2object
->
xwindow_id
,
&
attr
);
GstVideoRectangle
rect
;
if
(
!
get_render_rect
(
v4l2object
,
&
rect
))
return
;
/* note: we don't pass in valid video x/y/w/h.. currently the xserver
* doesn't need to know these, as they come from v4l2 by setting the
* crop..
*/
XvPutVideo
(
v4l2xv
->
dpy
,
v4l2xv
->
port
,
v4l2object
->
xwindow_id
,
DefaultGC
(
v4l2xv
->
dpy
,
DefaultScreen
(
v4l2xv
->
dpy
)),
0
,
0
,
attr
.
width
,
attr
.
height
,
0
,
0
,
attr
.
width
,
attr
.
height
);
0
,
0
,
rect
.
w
,
rect
.
h
,
rect
.
x
,
rect
.
y
,
rect
.
w
,
rect
.
h
);
}
static
gboolean
...
...
@@ -221,6 +261,92 @@ event_refresh (gpointer data)
g_mutex_lock
(
v4l2xv
->
mutex
);
/* If the element supports navigation, collect the relavent input
* events and push them upstream as navigation events
*/
if
(
GST_IS_NAVIGATION
(
v4l2object
->
element
))
{
guint
pointer_x
=
0
,
pointer_y
=
0
;
gboolean
pointer_moved
=
FALSE
;
/* We get all pointer motion events, only the last position is
* interesting.
*/
while
(
XCheckWindowEvent
(
v4l2xv
->
dpy
,
v4l2object
->
xwindow_id
,
PointerMotionMask
,
&
e
))
{
switch
(
e
.
type
)
{
case
MotionNotify
:
pointer_x
=
e
.
xmotion
.
x
;
pointer_y
=
e
.
xmotion
.
y
;
pointer_moved
=
TRUE
;
break
;
default:
break
;
}
}
if
(
pointer_moved
)
{
GST_DEBUG_OBJECT
(
v4l2object
->
element
,
"pointer moved over window at %d,%d"
,
pointer_x
,
pointer_y
);
g_mutex_unlock
(
v4l2xv
->
mutex
);
gst_navigation_send_mouse_event
(
GST_NAVIGATION
(
v4l2object
->
element
),
"mouse-move"
,
0
,
e
.
xbutton
.
x
,
e
.
xbutton
.
y
);
g_mutex_lock
(
v4l2xv
->
mutex
);
}
/* We get all events on our window to throw them upstream
*/
while
(
XCheckWindowEvent
(
v4l2xv
->
dpy
,
v4l2object
->
xwindow_id
,
KeyPressMask
|
KeyReleaseMask
|
ButtonPressMask
|
ButtonReleaseMask
,
&
e
))
{
KeySym
keysym
;
const
char
*
key_str
=
NULL
;
g_mutex_unlock
(
v4l2xv
->
mutex
);
switch
(
e
.
type
)
{
case
ButtonPress
:
GST_DEBUG_OBJECT
(
v4l2object
->
element
,
"button %d pressed over window at %d,%d"
,
e
.
xbutton
.
button
,
e
.
xbutton
.
x
,
e
.
xbutton
.
y
);
gst_navigation_send_mouse_event
(
GST_NAVIGATION
(
v4l2object
->
element
),
"mouse-button-press"
,
e
.
xbutton
.
button
,
e
.
xbutton
.
x
,
e
.
xbutton
.
y
);
break
;
case
ButtonRelease
:
GST_DEBUG_OBJECT
(
v4l2object
->
element
,
"button %d released over window at %d,%d"
,
e
.
xbutton
.
button
,
e
.
xbutton
.
x
,
e
.
xbutton
.
y
);
gst_navigation_send_mouse_event
(
GST_NAVIGATION
(
v4l2object
->
element
),
"mouse-button-release"
,
e
.
xbutton
.
button
,
e
.
xbutton
.
x
,
e
.
xbutton
.
y
);
break
;
case
KeyPress
:
case
KeyRelease
:
g_mutex_lock
(
v4l2xv
->
mutex
);
keysym
=
XKeycodeToKeysym
(
v4l2xv
->
dpy
,
e
.
xkey
.
keycode
,
0
);
if
(
keysym
!=
NoSymbol
)
{
key_str
=
XKeysymToString
(
keysym
);
}
else
{
key_str
=
"unknown"
;
}
g_mutex_unlock
(
v4l2xv
->
mutex
);
GST_DEBUG_OBJECT
(
v4l2object
->
element
,
"key %d pressed over window at %d,%d (%s)"
,
e
.
xkey
.
keycode
,
e
.
xkey
.
x
,
e
.
xkey
.
y
,
key_str
);
gst_navigation_send_key_event
(
GST_NAVIGATION
(
v4l2object
->
element
),
e
.
type
==
KeyPress
?
"key-press"
:
"key-release"
,
key_str
);
break
;
default:
GST_DEBUG_OBJECT
(
v4l2object
->
element
,
"unhandled X event (%d)"
,
e
.
type
);
}
g_mutex_lock
(
v4l2xv
->
mutex
);
}
}
/* Handle ConfigureNotify */
while
(
XCheckWindowEvent
(
v4l2xv
->
dpy
,
v4l2object
->
xwindow_id
,
StructureNotifyMask
,
&
e
))
{
...
...
@@ -312,6 +438,7 @@ gst_v4l2_xoverlay_prepare_xwindow_id (GstV4l2Object * v4l2object,
GstV4l2Xv
*
v4l2xv
;
Window
win
;
int
width
,
height
;
long
event_mask
;
if
(
!
v4l2object
->
xv
&&
GST_V4L2_IS_OPEN
(
v4l2object
))
gst_v4l2_xoverlay_open
(
v4l2object
);
...
...
@@ -338,13 +465,13 @@ gst_v4l2_xoverlay_prepare_xwindow_id (GstV4l2Object * v4l2object,
GST_DEBUG_OBJECT
(
v4l2object
->
element
,
"win=%lu"
,
win
);
/* @todo add mouse events for all windows, and button events for self
* created windows, and hook up to navigation interface.. note that at
* least some of the events we want to handle regardless of whether it
* is a self created window or not.. such as mouse/button events in
* order to implement navigation interface?
*/
XSelectInput
(
v4l2xv
->
dpy
,
win
,
ExposureMask
|
StructureNotifyM
ask
);
event_mask
=
ExposureMask
|
StructureNotifyMask
;
if
(
GST_IS_NAVIGATION
(
v4l2object
->
element
))
{
event_mask
|=
PointerMotionMask
|
KeyPressMask
|
KeyReleaseMask
|
ButtonPressMask
|
ButtonReleaseMask
;
}
XSelectInput
(
v4l2xv
->
dpy
,
win
,
event_m
ask
);
v4l2xv
->
event_id
=
g_timeout_add
(
45
,
event_refresh
,
v4l2object
);
XMapRaised
(
v4l2xv
->
dpy
,
win
);
...
...
sys/v4l2/gstv4l2xoverlay.h
View file @
538d3a95
...
...
@@ -28,6 +28,8 @@
#include <gst/gst.h>
#include <gst/interfaces/xoverlay.h>
#include <gst/interfaces/navigation.h>
#include <gst/video/gstvideosink.h>
/* for GstVideoRectange */
#include "gstv4l2object.h"
...
...
@@ -35,6 +37,8 @@ G_BEGIN_DECLS
void
gst_v4l2_xoverlay_start
(
GstV4l2Object
*
v4l2object
);
void
gst_v4l2_xoverlay_stop
(
GstV4l2Object
*
v4l2object
);
gboolean
gst_v4l2_xoverlay_get_render_rect
(
GstV4l2Object
*
v4l2object
,
GstVideoRectangle
*
rect
);
void
gst_v4l2_xoverlay_interface_init
(
GstXOverlayClass
*
klass
);
void
gst_v4l2_xoverlay_set_window_handle
(
GstV4l2Object
*
v4l2object
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment