Move videocrop and osxvideo to -good.

Original commit message from CVS:
* configure.ac:
* docs/plugins/Makefile.am:
* docs/plugins/gst-plugins-bad-plugins-docs.sgml:
* docs/plugins/gst-plugins-bad-plugins-sections.txt:
* docs/plugins/inspect/plugin-osxvideo.xml:
* docs/plugins/inspect/plugin-videocrop.xml:
* gst-plugins-bad.spec.in:
* gst/videocrop/Makefile.am:
* gst/videocrop/gstvideocrop.c:
* gst/videocrop/gstvideocrop.h:
* gst/videocrop/videocrop.vcproj:
* sys/Makefile.am:
* sys/osxvideo/Makefile.am:
* sys/osxvideo/cocoawindow.h:
* sys/osxvideo/cocoawindow.m:
* sys/osxvideo/osxvideosink.h:
* sys/osxvideo/osxvideosink.m:
* tests/check/Makefile.am:
* tests/check/elements/videocrop.c:
* tests/icles/Makefile.am:
* tests/icles/videocrop-test.c:
Move videocrop and osxvideo to -good.
This commit is contained in:
Jan Schmidt 2007-06-12 20:15:14 +00:00
parent 823e63b705
commit 4c98dfa57c
22 changed files with 29 additions and 3505 deletions

View file

@ -1,3 +1,28 @@
2007-06-12 Jan Schmidt <thaytan@mad.scientist.com>
* configure.ac:
* docs/plugins/Makefile.am:
* docs/plugins/gst-plugins-bad-plugins-docs.sgml:
* docs/plugins/gst-plugins-bad-plugins-sections.txt:
* docs/plugins/inspect/plugin-osxvideo.xml:
* docs/plugins/inspect/plugin-videocrop.xml:
* gst-plugins-bad.spec.in:
* gst/videocrop/Makefile.am:
* gst/videocrop/gstvideocrop.c:
* gst/videocrop/gstvideocrop.h:
* gst/videocrop/videocrop.vcproj:
* sys/Makefile.am:
* sys/osxvideo/Makefile.am:
* sys/osxvideo/cocoawindow.h:
* sys/osxvideo/cocoawindow.m:
* sys/osxvideo/osxvideosink.h:
* sys/osxvideo/osxvideosink.m:
* tests/check/Makefile.am:
* tests/check/elements/videocrop.c:
* tests/icles/Makefile.am:
* tests/icles/videocrop-test.c:
Move videocrop and osxvideo to -good.
2007-06-12 Jan Schmidt <thaytan@mad.scientist.com>
* configure.ac:

View file

@ -102,7 +102,6 @@ GST_PLUGINS_ALL="\
speed \
switch \
tta \
videocrop \
videoparse \
videosignal \
vmnc \
@ -317,23 +316,6 @@ AG_GST_CHECK_FEATURE(X, [X libraries and plugins],
CPPFLAGS="$ac_cppflags_save"
])
dnl *** OS X video ***
translit(dnm, m, l) AM_CONDITIONAL(USE_OSX_VIDEO, true)
HAVE_OSX_VIDEO="no"
AG_GST_CHECK_FEATURE(OSX_VIDEO, [OSX video], osxvideosink, [
AC_CHECK_HEADER(OpenGL/gl.h, HAVE_OSX_VIDEO="yes", HAVE_OSX_VIDEO="no")
])
dnl in case header OpenGL/gl.h is found on other platforms
case "$host" in
*-*darwin*)
dnl do nothing
;;
*)
HAVE_OSX_VIDEO="no"
;;
esac
dnl *** ext plug-ins ***
dnl keep this list sorted alphabetically !
@ -911,7 +893,6 @@ AM_CONDITIONAL(USE_MUSICBRAINZ, false)
AM_CONDITIONAL(USE_MYTHTV, false)
AM_CONDITIONAL(USE_NAS, false)
AM_CONDITIONAL(USE_NEON, false)
AM_CONDITIONAL(USE_OSX_VIDEO, false)
AM_CONDITIONAL(USE_SDL, false)
AM_CONDITIONAL(USE_SNDFILE, false)
AM_CONDITIONAL(USE_SOUNDTOUCH, false)
@ -999,7 +980,6 @@ gst/spectrum/Makefile
gst/speed/Makefile
gst/switch/Makefile
gst/tta/Makefile
gst/videocrop/Makefile
gst/videoparse/Makefile
gst/videosignal/Makefile
gst/vmnc/Makefile
@ -1012,7 +992,6 @@ gst-libs/gst/app/Makefile
sys/Makefile
sys/glsink/Makefile
sys/dvb/Makefile
sys/osxvideo/Makefile
examples/Makefile
examples/app/Makefile
examples/directfb/Makefile

View file

@ -61,7 +61,7 @@ FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/gobject \
# Used for dependencies.
HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.h
CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.c $(DOC_SOURCE_DIR)/*/*/*.cc $(DOC_SOURCE_DIR)/*/*/*.m
CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*/*.c $(DOC_SOURCE_DIR)/*/*/*.cc
# this is a wingo addition
# thomasvs: another nice wingo addition would be an explanation on why
@ -89,7 +89,6 @@ EXAMPLE_CFILES = \
$(top_srcdir)/ext/directfb/dfb-example.c
EXTRA_HFILES = \
$(top_srcdir)/sys/osxvideo/osxvideosink.h \
$(top_srcdir)/ext/directfb/dfbvideosink.h \
$(top_srcdir)/ext/jack/gstjackaudiosink.h \
$(top_srcdir)/ext/musicbrainz/gsttrm.h \
@ -104,7 +103,6 @@ EXTRA_HFILES = \
$(top_srcdir)/gst/rtpmanager/gstrtpptdemux.h \
$(top_srcdir)/gst/rtpmanager/gstrtpsession.h \
$(top_srcdir)/gst/rtpmanager/gstrtpssrcdemux.h \
$(top_srcdir)/gst/videocrop/gstvideocrop.h \
$(top_srcdir)/gst/videosignal/gstvideoanalyse.h \
$(top_srcdir)/gst/videosignal/gstvideodetect.h \
$(top_srcdir)/gst/videosignal/gstvideomark.h

View file

@ -14,7 +14,6 @@
<title>gst-plugins-bad Elements</title>
<xi:include href="xml/element-dfbvideosink.xml" />
<xi:include href="xml/element-jackaudiosink.xml" />
<xi:include href="xml/element-osxvideosink.xml" />
<xi:include href="xml/element-rganalysis.xml" />
<xi:include href="xml/element-rglimiter.xml" />
<xi:include href="xml/element-rgvolume.xml" />
@ -26,7 +25,6 @@
<xi:include href="xml/element-sdlaudiosink.xml" />
<xi:include href="xml/element-sdlvideosink.xml" />
<xi:include href="xml/element-trm.xml" />
<xi:include href="xml/element-videocrop.xml" />
<xi:include href="xml/element-videoanalyse.xml" />
<xi:include href="xml/element-videodetect.xml" />
<xi:include href="xml/element-videomark.xml" />
@ -48,14 +46,12 @@
<xi:include href="xml/plugin-musepack.xml" />
<xi:include href="xml/plugin-musicbrainz.xml" />
<xi:include href="xml/plugin-neon.xml" />
<xi:include href="xml/plugin-osxvideo.xml" />
<xi:include href="xml/plugin-replaygain.xml" />
<xi:include href="xml/plugin-gstrtpmanager.xml" />
<xi:include href="xml/plugin-sdl.xml" />
<xi:include href="xml/plugin-spectrum.xml" />
<xi:include href="xml/plugin-speed.xml" />
<xi:include href="xml/plugin-tta.xml" />
<xi:include href="xml/plugin-videocrop.xml" />
<xi:include href="xml/plugin-videosignal.xml" />
<xi:include href="xml/plugin-xingheader.xml" />

View file

@ -15,22 +15,6 @@ GstJackConnect
GstJackAudioSinkClass
</SECTION>
<SECTION>
<FILE>element-osxvideosink</FILE>
GstOSXVideoSink
<TITLE>osxvideosink</TITLE>
<SUBSECTION Standard>
GstOSXVideoSinkClass
<SUBSECTION Private>
GST_IS_OSX_VIDEO_SINK
GST_IS_OSX_VIDEO_SINK_CLASS
GST_OSX_VIDEO_SINK
GST_OSX_VIDEO_SINK_CLASS
GST_TYPE_OSXVIDEOBUFFER
GST_TYPE_OSX_VIDEO_SINK
GstOSXWindow
</SECTION>
<SECTION>
<FILE>element-rganalysis</FILE>
GstRgAnalysis
@ -169,14 +153,6 @@ GstTRM
GstTRMClass
</SECTION>
<SECTION>
<FILE>element-videocrop</FILE>
GstVideoCrop
<TITLE>videocrop</TITLE>
<SUBSECTION Standard>
GstVideoCropClass
</SECTION>
<SECTION>
<FILE>element-videoanalyse</FILE>
<TITLE>videoanalyse</TITLE>

View file

@ -1,20 +0,0 @@
<plugin>
<name>osxvideo</name>
<description>OSX native video output plugin</description>
<filename>../../ext/osxvideo/.libs/libgstosxvideo.so</filename>
<basename>libgstosxvideo.so</basename>
<version>0.10.4</version>
<license>LGPL</license>
<source>gst-plugins-bad</source>
<package>Gstreamer</package>
<origin>http://gstreamer.freedesktop.org</origin>
<elements>
<element>
<name>osxvideosink</name>
<longname>OSX Video sink</longname>
<class>Sink/Video</class>
<description>OSX native videosink</description>
<author>Zaheer Abbas Merali &lt;zaheerabas at merali dot org&gt;</author>
</element>
</elements>
</plugin>

View file

@ -1,20 +0,0 @@
<plugin>
<name>videocrop</name>
<description>Crops video into a user-defined region</description>
<filename>../../gst/videocrop/.libs/libgstvideocrop.so</filename>
<basename>libgstvideocrop.so</basename>
<version>0.10.4.1</version>
<license>LGPL</license>
<source>gst-plugins-bad</source>
<package>GStreamer Bad Plug-ins CVS/prerelease</package>
<origin>Unknown package origin</origin>
<elements>
<element>
<name>videocrop</name>
<longname>Crop</longname>
<class>Filter/Effect/Video</class>
<description>Crops video into a user-defined region</description>
<author>Tim-Philipp Müller &lt;tim centricular net&gt;</author>
</element>
</elements>
</plugin>

View file

@ -91,7 +91,6 @@ rm -rf $RPM_BUILD_ROOT
%{_libdir}/gstreamer-%{majorminor}/libgstspectrum.so
%{_libdir}/gstreamer-%{majorminor}/libgstfilter.so
%{_libdir}/gstreamer-%{majorminor}/libgstnsf.so
%{_libdir}/gstreamer-%{majorminor}/libgstvideocrop.so
%{_libdir}/gstreamer-%{majorminor}/libgstdvbsrc.so
%{_libdir}/gstreamer-%{majorminor}/libgstreplaygain.so
%{_libdir}/gstreamer-%{majorminor}/libgstdeinterlace.so

View file

@ -1,12 +0,0 @@
plugin_LTLIBRARIES = libgstvideocrop.la
# Note: we only use defines from gst/video/video.h, but none
# of the functions, so we don't need to link to libgstvideo
libgstvideocrop_la_SOURCES = gstvideocrop.c
libgstvideocrop_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) \
$(GST_PLUGINS_BASE_CFLAGS)
libgstvideocrop_la_LIBADD = $(GST_BASE_LIBS)
libgstvideocrop_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_HEADERS = gstvideocrop.h

View file

@ -1,725 +0,0 @@
/* GStreamer video frame cropping
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular 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.
*/
/**
* SECTION:element-videocrop
* @see_also: GstVideoBox
*
* <refsect2>
* <para>
* This element crops video frames, meaning it can remove parts of the
* picture on the left, right, top or bottom of the picture and output
* a smaller picture than the input picture, with the unwanted parts at the
* border removed.
* </para>
* <para>
* The videocrop element is similar to the videobox element, but its main
* goal is to support a multitude of formats as efficiently as possible.
* Unlike videbox, it cannot add borders to the picture and unlike videbox
* it will always output images in exactly the same format as the input image.
* </para>
* <para>
* If there is nothing to crop, the element will operate in pass-through mode.
* </para>
* <para>
* Note that no special efforts are made to handle chroma-subsampled formats
* in the case of odd-valued cropping and compensate for sub-unit chroma plane
* shifts for such formats in the case where the "left" or "top" property is
* set to an odd number. This doesn't matter for most use cases, but it might
* matter for yours.
* </para>
* <title>Example launch line</title>
* <para>
* <programlisting>
* gst-launch -v videotestsrc ! videocrop top=42 left=1 right=4 bottom=0 ! ximagesink
* </programlisting>
* </para>
* </refsect2>
*/
/* TODO:
* - for packed formats, we could avoid memcpy() in case crop_left
* and crop_right are 0 and just create a sub-buffer of the input
* buffer
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstvideocrop.h"
#include <string.h>
GST_DEBUG_CATEGORY_STATIC (videocrop_debug);
#define GST_CAT_DEFAULT videocrop_debug
static const GstElementDetails video_crop_details = GST_ELEMENT_DETAILS ("Crop",
"Filter/Effect/Video",
"Crops video into a user-defined region",
"Tim-Philipp Müller <tim centricular net>");
enum
{
ARG_0,
ARG_LEFT,
ARG_RIGHT,
ARG_TOP,
ARG_BOTTOM
};
/* the formats we support */
#define VIDEO_CROP_CAPS \
GST_VIDEO_CAPS_RGBx ";" \
GST_VIDEO_CAPS_xRGB ";" \
GST_VIDEO_CAPS_BGRx ";" \
GST_VIDEO_CAPS_xBGR ";" \
GST_VIDEO_CAPS_RGBA ";" \
GST_VIDEO_CAPS_ARGB ";" \
GST_VIDEO_CAPS_BGRA ";" \
GST_VIDEO_CAPS_ABGR ";" \
GST_VIDEO_CAPS_RGB ";" \
GST_VIDEO_CAPS_BGR ";" \
GST_VIDEO_CAPS_YUV ("AYUV") ";" \
GST_VIDEO_CAPS_YUV ("YUY2") ";" \
GST_VIDEO_CAPS_YUV ("YVYU") ";" \
GST_VIDEO_CAPS_YUV ("UYVY") ";" \
GST_VIDEO_CAPS_YUV ("Y800") ";" \
GST_VIDEO_CAPS_YUV ("I420") ";" \
GST_VIDEO_CAPS_YUV ("YV12") ";" \
GST_VIDEO_CAPS_RGB_16 ";" \
GST_VIDEO_CAPS_RGB_15
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (VIDEO_CROP_CAPS)
);
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (VIDEO_CROP_CAPS)
);
GST_BOILERPLATE (GstVideoCrop, gst_video_crop, GstBaseTransform,
GST_TYPE_BASE_TRANSFORM);
static void gst_video_crop_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_video_crop_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static GstCaps *gst_video_crop_transform_caps (GstBaseTransform * trans,
GstPadDirection direction, GstCaps * caps);
static GstFlowReturn gst_video_crop_transform (GstBaseTransform * trans,
GstBuffer * inbuf, GstBuffer * outbuf);
static gboolean gst_video_crop_get_unit_size (GstBaseTransform * trans,
GstCaps * caps, guint * size);
static gboolean gst_video_crop_set_caps (GstBaseTransform * trans,
GstCaps * in_caps, GstCaps * outcaps);
static void
gst_video_crop_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_set_details (element_class, &video_crop_details);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_template));
}
static void
gst_video_crop_class_init (GstVideoCropClass * klass)
{
GObjectClass *gobject_class;
GstBaseTransformClass *basetransform_class;
gobject_class = (GObjectClass *) klass;
basetransform_class = (GstBaseTransformClass *) klass;
gobject_class->set_property = gst_video_crop_set_property;
gobject_class->get_property = gst_video_crop_get_property;
g_object_class_install_property (gobject_class, ARG_LEFT,
g_param_spec_int ("left", "Left", "Pixels to crop at left",
0, G_MAXINT, 0, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, ARG_RIGHT,
g_param_spec_int ("right", "Right", "Pixels to crop at right",
0, G_MAXINT, 0, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, ARG_TOP,
g_param_spec_int ("top", "Top", "Pixels to crop at top",
0, G_MAXINT, 0, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, ARG_BOTTOM,
g_param_spec_int ("bottom", "Bottom", "Pixels to crop at bottom",
0, G_MAXINT, 0, G_PARAM_READWRITE));
basetransform_class->transform = GST_DEBUG_FUNCPTR (gst_video_crop_transform);
basetransform_class->transform_caps =
GST_DEBUG_FUNCPTR (gst_video_crop_transform_caps);
basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_crop_set_caps);
basetransform_class->get_unit_size =
GST_DEBUG_FUNCPTR (gst_video_crop_get_unit_size);
basetransform_class->passthrough_on_same_caps = FALSE;
}
static void
gst_video_crop_init (GstVideoCrop * vcrop, GstVideoCropClass * klass)
{
vcrop->crop_right = 0;
vcrop->crop_left = 0;
vcrop->crop_top = 0;
vcrop->crop_bottom = 0;
vcrop->noop = TRUE;
GST_BASE_TRANSFORM (vcrop)->passthrough = vcrop->noop;
}
static gboolean
gst_video_crop_get_image_details_from_caps (GstVideoCrop * vcrop,
GstVideoCropImageDetails * details, GstCaps * caps)
{
GstStructure *structure;
gint width, height;
structure = gst_caps_get_structure (caps, 0);
if (!gst_structure_get_int (structure, "width", &width) ||
!gst_structure_get_int (structure, "height", &height)) {
goto incomplete_format;
}
details->width = width;
details->height = height;
if (gst_structure_has_name (structure, "video/x-raw-rgb")) {
gint bpp = 0;
if (!gst_structure_get_int (structure, "bpp", &bpp) || (bpp & 0x07) != 0)
goto incomplete_format;
details->packing = VIDEO_CROP_PIXEL_FORMAT_PACKED_SIMPLE;
details->bytes_per_pixel = bpp / 8;
details->stride = GST_ROUND_UP_4 (width * details->bytes_per_pixel);
details->size = details->stride * height;
} else if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
guint32 format = 0;
if (!gst_structure_get_fourcc (structure, "format", &format))
goto incomplete_format;
switch (format) {
case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
details->packing = VIDEO_CROP_PIXEL_FORMAT_PACKED_SIMPLE;
details->bytes_per_pixel = 4;
details->stride = GST_ROUND_UP_4 (width * 4);
details->size = details->stride * height;
break;
case GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U'):
case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
details->packing = VIDEO_CROP_PIXEL_FORMAT_PACKED_COMPLEX;
details->bytes_per_pixel = 2;
details->stride = GST_ROUND_UP_4 (width * 2);
details->size = details->stride * height;
if (format == GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y')) {
/* UYVY = 4:2:2 - [U0 Y0 V0 Y1] [U2 Y2 V2 Y3] [U4 Y4 V4 Y5] */
details->macro_y_off = 1;
} else {
/* YUYV = 4:2:2 - [Y0 U0 Y1 V0] [Y2 U2 Y3 V2] [Y4 U4 Y5 V4] = YUY2 */
details->macro_y_off = 0;
}
break;
case GST_MAKE_FOURCC ('Y', '8', '0', '0'):
details->packing = VIDEO_CROP_PIXEL_FORMAT_PACKED_SIMPLE;
details->bytes_per_pixel = 1;
details->stride = GST_ROUND_UP_4 (width);
details->size = details->stride * height;
break;
case GST_MAKE_FOURCC ('I', '4', '2', '0'):
case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):{
details->packing = VIDEO_CROP_PIXEL_FORMAT_PLANAR;
details->y_stride = GST_ROUND_UP_4 (width);
details->u_stride = GST_ROUND_UP_8 (width) / 2;
details->v_stride = GST_ROUND_UP_8 (width) / 2;
/* I420 and YV12 have U/V planes swapped, but doesn't matter for us */
details->y_off = 0;
details->u_off = 0 + details->y_stride * GST_ROUND_UP_2 (height);
details->v_off = details->u_off +
details->u_stride * (GST_ROUND_UP_2 (height) / 2);
details->size = details->v_off +
details->v_stride * (GST_ROUND_UP_2 (height) / 2);
break;
}
default:
goto unknown_format;
}
} else {
goto unknown_format;
}
return TRUE;
/* ERRORS */
unknown_format:
{
GST_ELEMENT_ERROR (vcrop, STREAM, NOT_IMPLEMENTED, (NULL),
("Unsupported format"));
return FALSE;
}
incomplete_format:
{
GST_ELEMENT_ERROR (vcrop, CORE, NEGOTIATION, (NULL),
("Incomplete caps, some required field is missing"));
return FALSE;
}
}
static gboolean
gst_video_crop_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
guint * size)
{
GstVideoCropImageDetails img_details = { 0, };
GstVideoCrop *vcrop = GST_VIDEO_CROP (trans);
if (!gst_video_crop_get_image_details_from_caps (vcrop, &img_details, caps))
return FALSE;
*size = img_details.size;
return TRUE;
}
#define ROUND_DOWN_2(n) ((n)&(~1))
static void
gst_video_crop_transform_packed_complex (GstVideoCrop * vcrop,
GstBuffer * inbuf, GstBuffer * outbuf)
{
guint8 *in_data, *out_data;
guint i, dx;
in_data = GST_BUFFER_DATA (inbuf);
out_data = GST_BUFFER_DATA (outbuf);
in_data += vcrop->crop_top * vcrop->in.stride;
/* rounding down here so we end up at the start of a macro-pixel and not
* in the middle of one */
in_data += ROUND_DOWN_2 (vcrop->crop_left) * vcrop->in.bytes_per_pixel;
dx = vcrop->out.width * vcrop->out.bytes_per_pixel;
/* UYVY = 4:2:2 - [U0 Y0 V0 Y1] [U2 Y2 V2 Y3] [U4 Y4 V4 Y5]
* YUYV = 4:2:2 - [Y0 U0 Y1 V0] [Y2 U2 Y3 V2] [Y4 U4 Y5 V4] = YUY2 */
if ((vcrop->crop_left % 2) != 0) {
for (i = 0; i < vcrop->out.height; ++i) {
gint j;
memcpy (out_data, in_data, dx);
/* move just the Y samples one pixel to the left, don't worry about
* chroma shift */
for (j = vcrop->in.macro_y_off; j < vcrop->out.stride - 2; j += 2)
out_data[j] = in_data[j + 2];
in_data += vcrop->in.stride;
out_data += vcrop->out.stride;
}
} else {
for (i = 0; i < vcrop->out.height; ++i) {
memcpy (out_data, in_data, dx);
in_data += vcrop->in.stride;
out_data += vcrop->out.stride;
}
}
}
static void
gst_video_crop_transform_packed_simple (GstVideoCrop * vcrop,
GstBuffer * inbuf, GstBuffer * outbuf)
{
guint8 *in_data, *out_data;
guint i, dx;
in_data = GST_BUFFER_DATA (inbuf);
out_data = GST_BUFFER_DATA (outbuf);
in_data += vcrop->crop_top * vcrop->in.stride;
in_data += vcrop->crop_left * vcrop->in.bytes_per_pixel;
dx = vcrop->out.width * vcrop->out.bytes_per_pixel;
for (i = 0; i < vcrop->out.height; ++i) {
memcpy (out_data, in_data, dx);
in_data += vcrop->in.stride;
out_data += vcrop->out.stride;
}
}
static void
gst_video_crop_transform_planar (GstVideoCrop * vcrop, GstBuffer * inbuf,
GstBuffer * outbuf)
{
guint8 *y_out, *u_out, *v_out;
guint8 *y_in, *u_in, *v_in;
guint i, dx;
/* Y plane */
y_in = GST_BUFFER_DATA (inbuf);
y_out = GST_BUFFER_DATA (outbuf);
y_in += (vcrop->crop_top * vcrop->in.y_stride) + vcrop->crop_left;
dx = vcrop->out.width * 1;
for (i = 0; i < vcrop->out.height; ++i) {
memcpy (y_out, y_in, dx);
y_in += vcrop->in.y_stride;
y_out += vcrop->out.y_stride;
}
/* U + V planes */
u_in = GST_BUFFER_DATA (inbuf) + vcrop->in.u_off;
u_out = GST_BUFFER_DATA (outbuf) + vcrop->out.u_off;
u_in += (vcrop->crop_top / 2) * vcrop->in.u_stride;
u_in += vcrop->crop_left / 2;
v_in = GST_BUFFER_DATA (inbuf) + vcrop->in.v_off;
v_out = GST_BUFFER_DATA (outbuf) + vcrop->out.v_off;
v_in += (vcrop->crop_top / 2) * vcrop->in.v_stride;
v_in += vcrop->crop_left / 2;
dx = GST_ROUND_UP_2 (vcrop->out.width) / 2;
for (i = 0; i < GST_ROUND_UP_2 (vcrop->out.height) / 2; ++i) {
memcpy (u_out, u_in, dx);
memcpy (v_out, v_in, dx);
u_in += vcrop->in.u_stride;
u_out += vcrop->out.u_stride;
v_in += vcrop->in.v_stride;
v_out += vcrop->out.v_stride;
}
}
static GstFlowReturn
gst_video_crop_transform (GstBaseTransform * trans, GstBuffer * inbuf,
GstBuffer * outbuf)
{
GstVideoCrop *vcrop = GST_VIDEO_CROP (trans);
/* we should be operating in passthrough mode if there's nothing to do */
g_assert (vcrop->noop == FALSE);
GST_OBJECT_LOCK (vcrop);
if (G_UNLIKELY ((vcrop->crop_left + vcrop->crop_right) >= vcrop->in.width ||
(vcrop->crop_top + vcrop->crop_bottom) >= vcrop->in.height)) {
GST_OBJECT_UNLOCK (vcrop);
goto cropping_too_much;
}
switch (vcrop->in.packing) {
case VIDEO_CROP_PIXEL_FORMAT_PACKED_SIMPLE:
gst_video_crop_transform_packed_simple (vcrop, inbuf, outbuf);
break;
case VIDEO_CROP_PIXEL_FORMAT_PACKED_COMPLEX:
gst_video_crop_transform_packed_complex (vcrop, inbuf, outbuf);
break;
case VIDEO_CROP_PIXEL_FORMAT_PLANAR:
gst_video_crop_transform_planar (vcrop, inbuf, outbuf);
break;
default:
g_assert_not_reached ();
}
GST_OBJECT_UNLOCK (vcrop);
return GST_FLOW_OK;
cropping_too_much:
{
/* is there a better error code for this? */
GST_ELEMENT_ERROR (vcrop, LIBRARY, SETTINGS, (NULL),
("Can't crop more pixels than there are"));
return GST_FLOW_ERROR;
}
}
static gint
gst_video_crop_transform_dimension (gint val, gint delta)
{
gint64 new_val = (gint64) val + (gint64) delta;
new_val = CLAMP (new_val, 1, G_MAXINT);
return (gint) new_val;
}
static gboolean
gst_video_crop_transform_dimension_value (const GValue * src_val,
gint delta, GValue * dest_val)
{
gboolean ret = TRUE;
g_value_init (dest_val, G_VALUE_TYPE (src_val));
if (G_VALUE_HOLDS_INT (src_val)) {
gint ival = g_value_get_int (src_val);
ival = gst_video_crop_transform_dimension (ival, delta);
g_value_set_int (dest_val, ival);
} else if (GST_VALUE_HOLDS_INT_RANGE (src_val)) {
gint min = gst_value_get_int_range_min (src_val);
gint max = gst_value_get_int_range_max (src_val);
min = gst_video_crop_transform_dimension (min, delta);
max = gst_video_crop_transform_dimension (max, delta);
gst_value_set_int_range (dest_val, min, max);
} else if (GST_VALUE_HOLDS_LIST (src_val)) {
gint i;
for (i = 0; i < gst_value_list_get_size (src_val); ++i) {
const GValue *list_val;
GValue newval = { 0, };
list_val = gst_value_list_get_value (src_val, i);
if (gst_video_crop_transform_dimension_value (list_val, delta, &newval))
gst_value_list_append_value (dest_val, &newval);
g_value_unset (&newval);
}
if (gst_value_list_get_size (dest_val) == 0) {
g_value_unset (dest_val);
ret = FALSE;
}
} else {
g_value_unset (dest_val);
ret = FALSE;
}
return ret;
}
static GstCaps *
gst_video_crop_transform_caps (GstBaseTransform * trans,
GstPadDirection direction, GstCaps * caps)
{
GstVideoCrop *vcrop;
GstCaps *other_caps;
gint dy, dx, i;
vcrop = GST_VIDEO_CROP (trans);
GST_OBJECT_LOCK (vcrop);
GST_LOG_OBJECT (vcrop, "l=%d,r=%d,b=%d,t=%d noop=%d",
vcrop->crop_left, vcrop->crop_right, vcrop->crop_bottom,
vcrop->crop_top, vcrop->noop);
if (vcrop->noop) {
GST_OBJECT_UNLOCK (vcrop);
return gst_caps_ref (caps);
}
if (direction == GST_PAD_SRC) {
dx = vcrop->crop_left + vcrop->crop_right;
dy = vcrop->crop_top + vcrop->crop_bottom;
} else {
dx = 0 - (vcrop->crop_left + vcrop->crop_right);
dy = 0 - (vcrop->crop_top + vcrop->crop_bottom);
}
GST_OBJECT_UNLOCK (vcrop);
GST_LOG_OBJECT (vcrop, "transforming caps %" GST_PTR_FORMAT, caps);
other_caps = gst_caps_new_empty ();
for (i = 0; i < gst_caps_get_size (caps); ++i) {
const GValue *v;
GstStructure *structure, *new_structure;
GValue w_val = { 0, }, h_val = {
0,};
structure = gst_caps_get_structure (caps, i);
v = gst_structure_get_value (structure, "width");
if (!gst_video_crop_transform_dimension_value (v, dx, &w_val)) {
GST_WARNING_OBJECT (vcrop, "could not tranform width value with dx=%d"
", caps structure=%" GST_PTR_FORMAT, dx, structure);
continue;
}
v = gst_structure_get_value (structure, "height");
if (!gst_video_crop_transform_dimension_value (v, dy, &h_val)) {
g_value_unset (&w_val);
GST_WARNING_OBJECT (vcrop, "could not tranform height value with dy=%d"
", caps structure=%" GST_PTR_FORMAT, dy, structure);
continue;
}
new_structure = gst_structure_copy (structure);
gst_structure_set_value (new_structure, "width", &w_val);
gst_structure_set_value (new_structure, "height", &h_val);
g_value_unset (&w_val);
g_value_unset (&h_val);
GST_LOG_OBJECT (vcrop, "transformed structure %2d: %" GST_PTR_FORMAT
" => %" GST_PTR_FORMAT, i, structure, new_structure);
gst_caps_append_structure (other_caps, new_structure);
}
if (gst_caps_is_empty (other_caps)) {
gst_caps_unref (other_caps);
other_caps = NULL;
}
return other_caps;
}
static gboolean
gst_video_crop_set_caps (GstBaseTransform * trans, GstCaps * incaps,
GstCaps * outcaps)
{
GstVideoCrop *crop = GST_VIDEO_CROP (trans);
if (!gst_video_crop_get_image_details_from_caps (crop, &crop->in, incaps)) {
GST_DEBUG_OBJECT (crop, "failed to parse input caps %" GST_PTR_FORMAT,
incaps);
return FALSE;
}
if (!gst_video_crop_get_image_details_from_caps (crop, &crop->out, outcaps)) {
GST_DEBUG_OBJECT (crop, "failed to parse output caps %" GST_PTR_FORMAT,
outcaps);
return FALSE;
}
GST_LOG_OBJECT (crop, "incaps = %" GST_PTR_FORMAT ", outcaps = %"
GST_PTR_FORMAT, incaps, outcaps);
return TRUE;
}
/* This is extremely hackish, but the only way to force basetransform to
* renegotiated at the moment. There should really be a basetransform
* function for this */
static void
gst_videocrop_clear_negotiated_caps_locked (GstVideoCrop * crop)
{
GST_LOG_OBJECT (crop, "clearing negotiated caps");
GST_BASE_TRANSFORM (crop)->negotiated = FALSE;
gst_caps_replace (&GST_PAD_CAPS (GST_BASE_TRANSFORM (crop)->srcpad), NULL);
gst_caps_replace (&GST_PAD_CAPS (GST_BASE_TRANSFORM (crop)->sinkpad), NULL);
gst_caps_replace (&GST_BASE_TRANSFORM (crop)->cache_caps1, NULL);
GST_BASE_TRANSFORM (crop)->cache_caps1_size = 0;
gst_caps_replace (&GST_BASE_TRANSFORM (crop)->cache_caps2, NULL);
GST_BASE_TRANSFORM (crop)->cache_caps2_size = 0;
GST_LOG_OBJECT (crop, "clearing caps done");
}
static void
gst_video_crop_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstVideoCrop *video_crop;
video_crop = GST_VIDEO_CROP (object);
GST_OBJECT_LOCK (video_crop);
switch (prop_id) {
case ARG_LEFT:
video_crop->crop_left = g_value_get_int (value);
break;
case ARG_RIGHT:
video_crop->crop_right = g_value_get_int (value);
break;
case ARG_TOP:
video_crop->crop_top = g_value_get_int (value);
break;
case ARG_BOTTOM:
video_crop->crop_bottom = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
video_crop->noop = ((video_crop->crop_left | video_crop->crop_right |
video_crop->crop_top | video_crop->crop_bottom) == 0);
GST_LOG_OBJECT (video_crop, "l=%d,r=%d,b=%d,t=%d noop=%d",
video_crop->crop_left, video_crop->crop_right, video_crop->crop_bottom,
video_crop->crop_top, video_crop->noop);
GST_BASE_TRANSFORM (video_crop)->passthrough = video_crop->noop;
gst_videocrop_clear_negotiated_caps_locked (video_crop);
GST_OBJECT_UNLOCK (video_crop);
}
static void
gst_video_crop_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstVideoCrop *video_crop;
video_crop = GST_VIDEO_CROP (object);
GST_OBJECT_LOCK (video_crop);
switch (prop_id) {
case ARG_LEFT:
g_value_set_int (value, video_crop->crop_left);
break;
case ARG_RIGHT:
g_value_set_int (value, video_crop->crop_right);
break;
case ARG_TOP:
g_value_set_int (value, video_crop->crop_top);
break;
case ARG_BOTTOM:
g_value_set_int (value, video_crop->crop_bottom);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (video_crop);
}
static gboolean
plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (videocrop_debug, "videocrop", 0, "videocrop");
return gst_element_register (plugin, "videocrop", GST_RANK_NONE,
GST_TYPE_VIDEO_CROP);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"videocrop",
"Crops video into a user-defined region",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

View file

@ -1,92 +0,0 @@
/* GStreamer video frame cropping
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular 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.
*/
#ifndef __GST_VIDEO_CROP_H__
#define __GST_VIDEO_CROP_H__
#include <gst/base/gstbasetransform.h>
G_BEGIN_DECLS
#define GST_TYPE_VIDEO_CROP \
(gst_video_crop_get_type())
#define GST_VIDEO_CROP(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_CROP,GstVideoCrop))
#define GST_VIDEO_CROP_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_CROP,GstVideoCropClass))
#define GST_IS_VIDEO_CROP(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_CROP))
#define GST_IS_VIDEO_CROP_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_CROP))
typedef enum {
VIDEO_CROP_PIXEL_FORMAT_PACKED_SIMPLE = 0, /* RGBx, AYUV */
VIDEO_CROP_PIXEL_FORMAT_PACKED_COMPLEX, /* UYVY, YVYU */
VIDEO_CROP_PIXEL_FORMAT_PLANAR /* I420, YV12 */
} VideoCropPixelFormat;
typedef struct _GstVideoCropImageDetails GstVideoCropImageDetails;
struct _GstVideoCropImageDetails
{
/*< private >*/
VideoCropPixelFormat packing;
guint width;
guint height;
guint size;
/* for packed RGB and YUV */
guint stride;
guint bytes_per_pixel;
guint8 macro_y_off; /* for YUY2, YVYU, UYVY, Y offset within macropixel in bytes */
/* for planar YUV */
guint y_stride, y_off;
guint u_stride, u_off;
guint v_stride, v_off;
};
typedef struct _GstVideoCrop GstVideoCrop;
typedef struct _GstVideoCropClass GstVideoCropClass;
struct _GstVideoCrop
{
GstBaseTransform basetransform;
/*< private >*/
gboolean noop; /* TRUE if crop_left,_right,_top and _bottom are all 0 */
gint crop_left;
gint crop_right;
gint crop_top;
gint crop_bottom;
GstVideoCropImageDetails in; /* details of input image */
GstVideoCropImageDetails out; /* details of output image */
};
struct _GstVideoCropClass
{
GstBaseTransformClass basetransform_class;
};
G_END_DECLS
#endif /* __GST_VIDEO_CROP_H__ */

View file

@ -1,145 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="videocrop"
ProjectGUID="{979C216F-0ACF-4956-AE00-055A42D678D5}"
Keyword="Win32Proj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="../../win32/Debug"
IntermediateDirectory="../../win32/Debug"
ConfigurationType="2"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;&quot;../../gst-libs&quot;;../../../popt/include;../../../libxml2/include/libxml2"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;videocrop_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="4"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib"
OutputFile="$(OutDir)/gstvideocrop.dll"
LinkIncremental="2"
AdditionalLibraryDirectories="../../../gstreamer/win32/Debug;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib"
ModuleDefinitionFile=""
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/videocrop.pdb"
SubSystem="2"
OptimizeReferences="2"
ImportLibrary="$(OutDir)/gstvideocrop.lib"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="../../win32/Release"
IntermediateDirectory="../../win32/Release"
ConfigurationType="2"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="../../../gstreamer/win32;../../../gstreamer;../../../gstreamer/libs;../../../glib;../../../glib/glib;../../../glib/gmodule;&quot;../../gst-libs&quot;;../../../popt/include;../../../libxml2/include/libxml2"
PreprocessorDefinitions="WIN32;NDEBUG;GST_DISABLE_GST_DEBUG;_WINDOWS;_USRDLL;videocrop_EXPORTS;HAVE_CONFIG_H;_USE_MATH_DEFINES"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="glib-2.0.lib gmodule-2.0.lib gthread-2.0.lib gobject-2.0.lib libgstreamer.lib gstbytestream.lib iconv.lib intl.lib"
OutputFile="$(OutDir)/gstvideocrop.dll"
LinkIncremental="1"
AdditionalLibraryDirectories="../../../gstreamer/win32/Release;../../../glib/glib;../../../glib/gmodule;../../../glib/gthread;../../../glib/gobject;../../../gettext/lib;../../../libiconv/lib"
ModuleDefinitionFile=""
GenerateDebugInformation="TRUE"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
ImportLibrary="$(OutDir)/gstvideocrop.lib"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy /Y $(TargetPath) c:\gstreamer\plugins"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
<File
RelativePath=".\gstvideocrop.c">
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View file

@ -34,12 +34,6 @@ else
DVB_DIR=
endif
if USE_OSX_VIDEO
OSX_VIDEO_DIR=osxvideo
else
OSX_VIDEO_DIR=
endif
SUBDIRS = $(GL_DIR) $(DVB_DIR)
SUBDIRS = $(GL_DIR) $(DVB_DIR) $(OSX_VIDEO_DIR)
DIST_SUBDIRS = glsink dvb osxvideo
DIST_SUBDIRS = glsink dvb

View file

@ -1,17 +0,0 @@
# FIXME: clean up this crap
OBJC=gcc
plugin_LTLIBRARIES = libgstosxvideosink.la
libgstosxvideosink_la_SOURCES = osxvideosink.m cocoawindow.m
libgstosxvideosink_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) \
$(GST_PLUGINS_BASE_CFLAGS)
libgstosxvideosink_la_LIBADD = \
$(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR) \
-lgstinterfaces-$(GST_MAJORMINOR)
libgstosxvideosink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -Wl,-framework -Wl,Cocoa -Wl,-framework -Wl,QuickTime -Wl,-framework -Wl,OpenGL
AM_OBJCFLAGS=$(CFLAGS) $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS)
noinst_HEADERS = osxvideosink.h cocoawindow.h

View file

@ -1,70 +0,0 @@
/* GStreamer
* Copyright (C) 2004 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
*
* 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.
*
* The development of this code was made possible due to the involvement of Pioneers
* of the Inevitable, the creators of the Songbird Music player
*
*/
/* inspiration gained from looking at source of osx video out of xine and vlc
* and is reflected in the code
*/
#import <Cocoa/Cocoa.h>
#import <QuickTime/QuickTime.h>
#import <glib.h>
struct _GstOSXImage;
@interface GstGLView : NSOpenGLView
{
int i_effect;
unsigned long pi_texture;
float f_x;
float f_y;
int initDone;
char* data;
int width, height;
BOOL fullscreen;
NSOpenGLContext* fullScreenContext;
NSOpenGLContext* actualContext;
}
- (void) drawQuad;
- (void) drawRect: (NSRect) rect;
- (id) initWithFrame: (NSRect) frame;
- (void) initTextures;
- (void) reloadTexture;
- (void) cleanUp;
- (void) displayTexture;
- (char*) getTextureBuffer;
- (void) setFullScreen: (BOOL) flag;
- (void) reshape;
- (void) setVideoSize: (int) w: (int) h;
@end
@interface GstOSXVideoSinkWindow: NSWindow {
int width, height;
GstGLView *gstview;
}
- (void) setContentSize: (NSSize) size;
- (GstGLView *) gstView;
- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag screen:(NSScreen *)aScreen;
@end

View file

@ -1,400 +0,0 @@
/* GStreamer
* Copyright (C) 2004 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
*
* 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.
*
* The development of this code was made possible due to the involvement of Pioneers
* of the Inevitable, the creators of the Songbird Music player
*
*/
/* inspiration gained from looking at source of osx video out of xine and vlc
* and is reflected in the code
*/
#include <Cocoa/Cocoa.h>
#include <gst/gst.h>
#import "cocoawindow.h"
#import "osxvideosink.h"
#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
/* Debugging category */
#include <gst/gstinfo.h>
@ implementation GstOSXVideoSinkWindow
/* The object has to be released */
- (id) initWithContentRect: (NSRect) rect
styleMask: (unsigned int) styleMask
backing: (NSBackingStoreType) bufferingType
defer: (BOOL) flag
screen:(NSScreen *) aScreen
{
self = [super initWithContentRect: rect
styleMask: styleMask
backing: bufferingType
defer: flag
screen:aScreen];
GST_DEBUG ("Initializing GstOSXvideoSinkWindow");
gstview = [[GstGLView alloc] initWithFrame:rect];
if (gstview)
[self setContentView:gstview];
[self setTitle:@"GStreamer Video Output"];
return self;
}
- (void) setContentSize:(NSSize) size {
width = size.width;
height = size.height;
[gstview setVideoSize: (int) width:(int) height];
[super setContentSize:size];
}
- (GstGLView *) gstView {
return gstview;
}
- (void) awakeFromNib {
[self setAcceptsMouseMovedEvents:YES];
}
- (void) sendEvent:(NSEvent *) event {
BOOL taken = NO;
GST_DEBUG ("event %p type:%d", event,[event type]);
if ([event type] == NSKeyDown) {
}
/*taken = [gstview keyDown:event]; */
if (!taken) {
[super sendEvent:event];
}
}
@end
//
// OpenGL implementation
//
@ implementation GstGLView
- (id) initWithFrame:(NSRect) frame {
NSOpenGLPixelFormat *fmt;
NSOpenGLPixelFormatAttribute attribs[] = {
NSOpenGLPFAAccelerated,
NSOpenGLPFANoRecovery,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAWindow,
0
};
fmt = [[NSOpenGLPixelFormat alloc]
initWithAttributes:attribs];
if (!fmt) {
GST_WARNING ("Cannot create NSOpenGLPixelFormat");
return nil;
}
self = [super initWithFrame: frame pixelFormat:fmt];
actualContext = [self openGLContext];
[actualContext makeCurrentContext];
[actualContext update];
/* Black background */
glClearColor (0.0, 0.0, 0.0, 0.0);
pi_texture = 0;
data = nil;
width = frame.size.width;
height = frame.size.height;
GST_LOG ("Width: %d Height: %d", width, height);
[self initTextures];
return self;
}
- (void) reshape {
NSRect bounds;
GST_LOG ("reshaping");
if (!initDone) {
return;
}
[actualContext makeCurrentContext];
bounds = [self bounds];
glViewport (0, 0, (GLint) bounds.size.width, (GLint) bounds.size.height);
}
- (void) initTextures {
[actualContext makeCurrentContext];
/* Free previous texture if any */
if (pi_texture) {
glDeleteTextures (1, &pi_texture);
}
if (data) {
data = g_realloc (data, width * height * sizeof(short)); // short or 3byte?
} else {
data = g_malloc0(width * height * sizeof(short));
}
/* Create textures */
glGenTextures (1, &pi_texture);
glEnable (GL_TEXTURE_RECTANGLE_EXT);
glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE);
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
/* Use VRAM texturing */
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
/* Tell the driver not to make a copy of the texture but to use
our buffer */
glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
/* Linear interpolation */
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/* I have no idea what this exactly does, but it seems to be
necessary for scaling */
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); WHY ??
glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
width, height, 0,
GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
initDone = 1;
}
- (void) reloadTexture {
if (!initDone) {
return;
}
GST_LOG ("Reloading Texture");
[actualContext makeCurrentContext];
glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
/* glTexSubImage2D is faster than glTexImage2D
http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
TextureRange/MainOpenGLView.m.htm */
glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
width, height,
GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); //FIXME
}
- (void) cleanUp {
initDone = 0;
}
- (void) drawQuad {
f_x = 1.0;
f_y = 1.0;
glBegin (GL_QUADS);
/* Top left */
glTexCoord2f (0.0, 0.0);
glVertex2f (-f_x, f_y);
/* Bottom left */
glTexCoord2f (0.0, (float) height);
glVertex2f (-f_x, -f_y);
/* Bottom right */
glTexCoord2f ((float) width, (float) height);
glVertex2f (f_x, -f_y);
/* Top right */
glTexCoord2f ((float) width, 0.0);
glVertex2f (f_x, f_y);
glEnd ();
}
- (void) drawRect:(NSRect) rect {
long params[] = { 1 };
[actualContext makeCurrentContext];
CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params);
/* Black background */
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (!initDone) {
[actualContext flushBuffer];
return;
}
/* Draw */
glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME
[self drawQuad];
/* Draw */
[actualContext flushBuffer];
}
- (void) displayTexture {
if ([self lockFocusIfCanDraw]) {
[self drawRect:[self bounds]];
[self reloadTexture];
[self unlockFocus];
}
}
- (char *) getTextureBuffer {
return data;
}
- (void) setFullScreen:(BOOL) flag {
if (!fullscreen && flag) {
// go to full screen
/* Create the new pixel format */
NSOpenGLPixelFormat *fmt;
NSOpenGLPixelFormatAttribute attribs[] = {
NSOpenGLPFAAccelerated,
NSOpenGLPFANoRecovery,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAFullScreen,
NSOpenGLPFAScreenMask,
CGDisplayIDToOpenGLDisplayMask (kCGDirectMainDisplay),
0
};
fmt = [[NSOpenGLPixelFormat alloc]
initWithAttributes:attribs];
if (!fmt) {
GST_WARNING ("Cannot create NSOpenGLPixelFormat");
return;
}
/* Create the new OpenGL context */
fullScreenContext = [[NSOpenGLContext alloc]
initWithFormat: fmt shareContext:nil];
if (!fullScreenContext) {
GST_WARNING ("Failed to create new NSOpenGLContext");
return;
}
actualContext = fullScreenContext;
/* Capture display, switch to fullscreen */
if (CGCaptureAllDisplays () != CGDisplayNoErr) {
GST_WARNING ("CGCaptureAllDisplays() failed");
return;
}
[fullScreenContext setFullScreen];
[fullScreenContext makeCurrentContext];
fullscreen = YES;
[self initTextures];
[self setNeedsDisplay:YES];
} else if (fullscreen && !flag) {
// fullscreen now and needs to go back to normal
initDone = NO;
actualContext = [self openGLContext];
[NSOpenGLContext clearCurrentContext];
[fullScreenContext clearDrawable];
[fullScreenContext release];
fullScreenContext = nil;
CGReleaseAllDisplays ();
[self reshape];
[self initTextures];
[self setNeedsDisplay:YES];
fullscreen = NO;
initDone = YES;
}
}
- (void) setVideoSize: (int) w:(int) h {
GST_LOG ("width:%d, height:%d", w, h);
width = w;
height = h;
// if (data) g_free(data);
// data = g_malloc0 (2 * w * h);
[self initTextures];
}
- (void) dealloc {
GST_LOG ("dealloc called");
if (data) g_free(data);
if (fullScreenContext) {
[NSOpenGLContext clearCurrentContext];
[fullScreenContext clearDrawable];
[fullScreenContext release];
if (actualContext == fullScreenContext) actualContext = nil;
fullScreenContext = nil;
}
[super dealloc];
}
@end

View file

@ -1,107 +0,0 @@
/* GStreamer
* Copyright (C) 2004-6 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
*
*
* 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.
*
*
* The development of this code was made possible due to the involvement of Pioneers
* of the Inevitable, the creators of the Songbird Music player
*
*/
#ifndef __GST_OSX_VIDEO_SINK_H__
#define __GST_OSX_VIDEO_SINK_H__
#include <gst/video/gstvideosink.h>
#include <string.h>
#include <math.h>
#include <Cocoa/Cocoa.h>
#include <QuickTime/QuickTime.h>
#import "cocoawindow.h"
GST_DEBUG_CATEGORY_EXTERN (gst_debug_osx_video_sink);
#define GST_CAT_DEFAULT gst_debug_osx_video_sink
G_BEGIN_DECLS
#define GST_TYPE_OSX_VIDEO_SINK \
(gst_osx_video_sink_get_type())
#define GST_OSX_VIDEO_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_OSX_VIDEO_SINK, GstOSXVideoSink))
#define GST_OSX_VIDEO_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_OSX_VIDEO_SINK, GstOSXVideoSinkClass))
#define GST_IS_OSX_VIDEO_SINK(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OSX_VIDEO_SINK))
#define GST_IS_OSX_VIDEO_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_OSX_VIDEO_SINK))
typedef struct _GstOSXWindow GstOSXWindow;
typedef struct _GstOSXVideoSink GstOSXVideoSink;
typedef struct _GstOSXVideoSinkClass GstOSXVideoSinkClass;
#define GST_TYPE_OSXVIDEOBUFFER (gst_osxvideobuffer_get_type())
/* OSXWindow stuff */
struct _GstOSXWindow {
gint width, height;
gboolean internal;
GstOSXVideoSinkWindow* win;
GstGLView* gstview;
NSAutoreleasePool *pool;
};
struct _GstOSXVideoSink {
/* Our element stuff */
GstVideoSink videosink;
GstOSXWindow *osxwindow;
gint fps_n;
gint fps_d;
/* Unused */
gint pixel_width, pixel_height;
GstClockTime time;
gboolean embed;
gboolean fullscreen;
gboolean sw_scaling_failed;
};
struct _GstOSXVideoSinkClass {
GstVideoSinkClass parent_class;
};
GType gst_osx_video_sink_get_type(void);
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
@interface NSApplication(AppleMenu)
- (void)setAppleMenu:(NSMenu *)menu;
@end
#endif
@interface GstAppDelegate : NSObject
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
@end
G_END_DECLS
#endif /* __GST_OSX_VIDEO_SINK_H__ */

View file

@ -1,665 +0,0 @@
/* GStreamer
* OSX video sink
* Copyright (C) 2004-6 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
*
* 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.
*
* The development of this code was made possible due to the involvement of
* Pioneers of the Inevitable, the creators of the Songbird Music player.
*
*/
/**
* SECTION:element-osxvideosink
*
* <refsect2>
* <para>
* The OSXVideoSink renders video frames to a MacOSX window. The video output
* can be directed to a window embedded in an existing NSApp. This can be done
* by setting the "embed" property to #TRUE. When the NSView to be embedded is
* created an element #GstMessage with a name of 'have-ns-view' will be created
* and posted on the bus. The pointer to the NSView to embed will be in the
* 'nsview' field of that message. If no embedding is requested, the plugin will
* create a standalone window.
* </para>
* <title>Examples</title>
* <para>
* Simple timeline to test the sink :
* <programlisting>
* gst-launch-0.10 -v videotestsrc ! osxvideosink
* </programlisting>
* </para>
* </refsect2>
*/
#include "config.h"
/* Object header */
#include "osxvideosink.h"
#include <unistd.h>
#import "cocoawindow.h"
/* Debugging category */
GST_DEBUG_CATEGORY (gst_debug_osx_video_sink);
#define GST_CAT_DEFAULT gst_debug_osx_video_sink
/* ElementFactory information */
static const GstElementDetails gst_osx_video_sink_details =
GST_ELEMENT_DETAILS ("OSX Video sink",
"Sink/Video",
"OSX native videosink",
"Zaheer Abbas Merali <zaheerabbas at merali dot org>");
/* Default template - initiated with class struct to allow gst-register to work
without X running */
static GstStaticPadTemplate gst_osx_video_sink_sink_template_factory =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-raw-yuv, "
"framerate = (fraction) [ 0, MAX ], "
"width = (int) [ 1, MAX ], "
"height = (int) [ 1, MAX ], "
#if G_BYTE_ORDER == G_BIG_ENDIAN
"format = (fourcc) YUY2")
#else
"format = (fourcc) UYVY")
#endif
);
// much of the following cocoa NSApp code comes from libsdl and libcaca
@implementation NSApplication(Gst)
- (void)setRunning
{
_running = 1;
}
@end
@implementation GstAppDelegate : NSObject
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
// destroy stuff here!
GST_DEBUG("Kill me please!");
return NSTerminateNow;
}
@end
enum
{
ARG_0,
ARG_EMBED,
ARG_FULLSCREEN
/* FILL ME */
};
static GstVideoSinkClass *parent_class = NULL;
/* cocoa event loop - needed if not run in own app */
gint
cocoa_event_loop (GstOSXVideoSink * vsink)
{
NSAutoreleasePool *pool;
gboolean ret = TRUE;
GST_DEBUG_OBJECT (vsink, "Entering event loop");
pool = [[NSAutoreleasePool alloc] init];
if ([NSApp isRunning]) {
NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode dequeue:YES ];
if ( event != nil ) {
switch ([event type]) {
default: //XXX Feed me please
[NSApp sendEvent:event];
break;
}
}
}
[pool release];
GST_DEBUG_OBJECT (vsink, "Leaving event loop with ret : %d", ret);
return ret;
}
static NSString *
GetApplicationName(void)
{
NSDictionary *dict;
NSString *appName = 0;
/* Determine the application name */
dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
if (dict)
appName = [dict objectForKey: @"CFBundleName"];
if (![appName length])
appName = [[NSProcessInfo processInfo] processName];
return appName;
}
static void
CreateApplicationMenus(void)
{
NSString *appName;
NSString *title;
NSMenu *appleMenu;
NSMenu *windowMenu;
NSMenuItem *menuItem;
/* Create the main menu bar */
[NSApp setMainMenu:[[NSMenu alloc] init]];
/* Create the application menu */
appName = GetApplicationName();
appleMenu = [[NSMenu alloc] initWithTitle:@""];
/* Add menu items */
title = [@"About " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
[appleMenu addItem:[NSMenuItem separatorItem]];
title = [@"Hide " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@/*"h"*/""];
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@/*"h"*/""];
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
[appleMenu addItem:[NSMenuItem separatorItem]];
title = [@"Quit " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@/*"q"*/""];
/* Put menu into the menubar */
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
[menuItem setSubmenu:appleMenu];
[[NSApp mainMenu] addItem:menuItem];
[menuItem release];
/* Tell the application object that this is now the application menu */
[NSApp setAppleMenu:appleMenu];
[appleMenu release];
/* Create the window menu */
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
/* "Minimize" item */
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@/*"m"*/""];
[windowMenu addItem:menuItem];
[menuItem release];
/* Put menu into the menubar */
menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
[menuItem setSubmenu:windowMenu];
[[NSApp mainMenu] addItem:menuItem];
[menuItem release];
/* Tell the application object that this is now the window menu */
[NSApp setWindowsMenu:windowMenu];
[windowMenu release];
}
/* This function handles osx window creation */
static GstOSXWindow *
gst_osx_video_sink_osxwindow_new (GstOSXVideoSink * osxvideosink, gint width,
gint height)
{
NSRect rect;
GstOSXWindow *osxwindow = NULL;
g_return_val_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink), NULL);
osxwindow = g_new0 (GstOSXWindow, 1);
osxwindow->width = width;
osxwindow->height = height;
osxwindow->internal = TRUE;
osxwindow->pool = [[NSAutoreleasePool alloc] init];
if (osxvideosink->embed == FALSE) {
ProcessSerialNumber psn;
unsigned int mask = NSTitledWindowMask |
NSClosableWindowMask |
NSResizableWindowMask |
NSTexturedBackgroundWindowMask |
NSMiniaturizableWindowMask;
rect.origin.x = 100.0;
rect.origin.y = 100.0;
rect.size.width = (float) osxwindow->width;
rect.size.height = (float) osxwindow->height;
if (!GetCurrentProcess(&psn)) {
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
SetFrontProcess(&psn);
}
[NSApplication sharedApplication];
osxwindow->win =[[GstOSXVideoSinkWindow alloc]
initWithContentRect: rect
styleMask: mask
backing: NSBackingStoreBuffered
defer: NO
screen: nil];
[osxwindow->win autorelease];
[NSApplication sharedApplication];
[osxwindow->win makeKeyAndOrderFront:NSApp];
osxwindow->gstview =[osxwindow->win gstView];
[osxwindow->gstview autorelease];
if (osxvideosink->fullscreen)
[osxwindow->gstview setFullScreen:YES];
CreateApplicationMenus();
[NSApp finishLaunching];
[NSApp setDelegate:[[GstAppDelegate alloc] init]];
[NSApp setRunning];
// insert event dispatch in the glib main loop
g_idle_add ((GSourceFunc) cocoa_event_loop, osxvideosink);
} else {
GstStructure *s;
GstMessage *msg;
gchar * tmp;
/* Needs to be embedded */
rect.origin.x = 0.0;
rect.origin.y = 0.0;
rect.size.width = (float) osxwindow->width;
rect.size.height = (float) osxwindow->height;
osxwindow->gstview =[[GstGLView alloc] initWithFrame:rect];
[osxwindow->gstview autorelease];
s = gst_structure_new ("have-ns-view",
"nsview", G_TYPE_POINTER, osxwindow->gstview,
nil);
tmp = gst_structure_to_string (s);
GST_DEBUG_OBJECT (osxvideosink, "Sending message %s",
tmp);
g_free (tmp);
msg = gst_message_new_element (GST_OBJECT (osxvideosink), s);
gst_element_post_message (GST_ELEMENT (osxvideosink), msg);
GST_LOG_OBJECT (osxvideosink, "'have-ns-view' message sent");
}
return osxwindow;
}
/* This function destroys a GstXWindow */
static void
gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink,
GstOSXWindow * osxwindow)
{
g_return_if_fail (osxwindow != NULL);
g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
[osxwindow->pool release];
g_free (osxwindow);
}
/* This function resizes a GstXWindow */
static void
gst_osx_video_sink_osxwindow_resize (GstOSXVideoSink * osxvideosink,
GstOSXWindow * osxwindow, guint width, guint height)
{
NSSize size;
NSAutoreleasePool *subPool = [[NSAutoreleasePool alloc] init];
g_return_if_fail (osxwindow != NULL);
g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
osxwindow->width = width;
osxwindow->height = height;
size.width = width;
size.height = height;
/* Call relevant cocoa function to resize window */
[osxwindow->win setContentSize:size];
[subPool release];
}
static void
gst_osx_video_sink_osxwindow_clear (GstOSXVideoSink * osxvideosink,
GstOSXWindow * osxwindow)
{
g_return_if_fail (osxwindow != NULL);
g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
}
/* Element stuff */
static gboolean
gst_osx_video_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
{
GstOSXVideoSink *osxvideosink;
GstStructure *structure;
gboolean res, result = FALSE;
gint video_width, video_height;
const GValue *framerate;
osxvideosink = GST_OSX_VIDEO_SINK (bsink);
GST_DEBUG_OBJECT (osxvideosink, "caps: %" GST_PTR_FORMAT, caps);
structure = gst_caps_get_structure (caps, 0);
res = gst_structure_get_int (structure, "width", &video_width);
res &= gst_structure_get_int (structure, "height", &video_height);
framerate = gst_structure_get_value (structure, "framerate");
res &= (framerate != NULL);
if (!res) {
goto beach;
}
osxvideosink->fps_n = gst_value_get_fraction_numerator (framerate);
osxvideosink->fps_d = gst_value_get_fraction_denominator (framerate);
GST_DEBUG_OBJECT (osxvideosink, "our format is: %dx%d video at %d/%d fps",
video_width, video_height, osxvideosink->fps_n, osxvideosink->fps_d);
GST_VIDEO_SINK_WIDTH (osxvideosink) = video_width;
GST_VIDEO_SINK_HEIGHT (osxvideosink) = video_height;
gst_osx_video_sink_osxwindow_resize (osxvideosink, osxvideosink->osxwindow,
video_width, video_height);
result = TRUE;
beach:
return result;
}
static GstStateChangeReturn
gst_osx_video_sink_change_state (GstElement * element,
GstStateChange transition)
{
GstOSXVideoSink *osxvideosink;
osxvideosink = GST_OSX_VIDEO_SINK (element);
GST_DEBUG_OBJECT (osxvideosink, "%s => %s",
gst_element_state_get_name(GST_STATE_TRANSITION_CURRENT (transition)),
gst_element_state_get_name(GST_STATE_TRANSITION_NEXT (transition)));
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
/* Creating our window and our image */
if (!osxvideosink->osxwindow) {
GST_VIDEO_SINK_WIDTH (osxvideosink) = 320;
GST_VIDEO_SINK_HEIGHT (osxvideosink) = 240;
osxvideosink->osxwindow =
gst_osx_video_sink_osxwindow_new (osxvideosink,
GST_VIDEO_SINK_WIDTH (osxvideosink),
GST_VIDEO_SINK_HEIGHT (osxvideosink));
gst_osx_video_sink_osxwindow_clear (osxvideosink,
osxvideosink->osxwindow);
} else {
if (osxvideosink->osxwindow->internal)
gst_osx_video_sink_osxwindow_resize (osxvideosink,
osxvideosink->osxwindow, GST_VIDEO_SINK_WIDTH (osxvideosink),
GST_VIDEO_SINK_HEIGHT (osxvideosink));
}
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
GST_DEBUG ("ready to paused");
if (osxvideosink->osxwindow)
gst_osx_video_sink_osxwindow_clear (osxvideosink,
osxvideosink->osxwindow);
osxvideosink->time = 0;
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
osxvideosink->fps_n = 0;
osxvideosink->fps_d = 0;
osxvideosink->sw_scaling_failed = FALSE;
GST_VIDEO_SINK_WIDTH (osxvideosink) = 0;
GST_VIDEO_SINK_HEIGHT (osxvideosink) = 0;
break;
case GST_STATE_CHANGE_READY_TO_NULL:
if (osxvideosink->osxwindow) {
gst_osx_video_sink_osxwindow_destroy (osxvideosink,
osxvideosink->osxwindow);
osxvideosink->osxwindow = NULL;
}
break;
}
return (GST_ELEMENT_CLASS (parent_class))->change_state (element, transition);
}
static GstFlowReturn
gst_osx_video_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
{
GstOSXVideoSink *osxvideosink;
osxvideosink = GST_OSX_VIDEO_SINK (bsink);
char *viewdata =[osxvideosink->osxwindow->gstview getTextureBuffer];
GST_DEBUG ("show_frame");
memcpy (viewdata, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
[osxvideosink->osxwindow->gstview displayTexture];
return GST_FLOW_OK;
}
/* Buffer management */
/* =========================================== */
/* */
/* Init & Class init */
/* */
/* =========================================== */
static void
gst_osx_video_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstOSXVideoSink *osxvideosink;
g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object));
osxvideosink = GST_OSX_VIDEO_SINK (object);
switch (prop_id) {
case ARG_EMBED:
osxvideosink->embed = g_value_get_boolean (value);
break;
case ARG_FULLSCREEN:
osxvideosink->fullscreen = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_osx_video_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstOSXVideoSink *osxvideosink;
g_return_if_fail (GST_IS_OSX_VIDEO_SINK (object));
osxvideosink = GST_OSX_VIDEO_SINK (object);
switch (prop_id) {
case ARG_EMBED:
g_value_set_boolean (value, osxvideosink->embed);
break;
case ARG_FULLSCREEN:
g_value_set_boolean (value, osxvideosink->fullscreen);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_osx_video_sink_init (GstOSXVideoSink * osxvideosink)
{
osxvideosink->osxwindow = NULL;
osxvideosink->fps_n = 0;
osxvideosink->fps_d = 0;
osxvideosink->pixel_width = osxvideosink->pixel_height = 1;
osxvideosink->sw_scaling_failed = FALSE;
osxvideosink->embed = FALSE;
osxvideosink->fullscreen = FALSE;
}
static void
gst_osx_video_sink_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_set_details (element_class, &gst_osx_video_sink_details);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_osx_video_sink_sink_template_factory));
}
static void
gst_osx_video_sink_class_init (GstOSXVideoSinkClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseSinkClass *gstbasesink_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbasesink_class = (GstBaseSinkClass *) klass;
parent_class = g_type_class_ref (GST_TYPE_VIDEO_SINK);
gobject_class->set_property = gst_osx_video_sink_set_property;
gobject_class->get_property = gst_osx_video_sink_get_property;
gstbasesink_class->set_caps = gst_osx_video_sink_setcaps;
gstbasesink_class->preroll = gst_osx_video_sink_show_frame;
gstbasesink_class->render = gst_osx_video_sink_show_frame;
gstelement_class->change_state = gst_osx_video_sink_change_state;
/**
* GstOSXVideoSink:embed
*
* Set to #TRUE if you are embedding the video window in an application.
*
**/
g_object_class_install_property (gobject_class, ARG_EMBED,
g_param_spec_boolean ("embed", "embed", "When enabled, it "
"can be embedded", FALSE, G_PARAM_READWRITE));
/**
* GstOSXVideoSink:fullscreen
*
* Set to #TRUE to have the video displayed in fullscreen.
**/
g_object_class_install_property (gobject_class, ARG_FULLSCREEN,
g_param_spec_boolean ("fullscreen", "fullscreen",
"When enabled, the view " "is fullscreen", FALSE,
G_PARAM_READWRITE));
}
/* ============================================================= */
/* */
/* Public Methods */
/* */
/* ============================================================= */
/* =========================================== */
/* */
/* Object typing & Creation */
/* */
/* =========================================== */
GType
gst_osx_video_sink_get_type (void)
{
static GType osxvideosink_type = 0;
if (!osxvideosink_type) {
static const GTypeInfo osxvideosink_info = {
sizeof (GstOSXVideoSinkClass),
gst_osx_video_sink_base_init,
NULL,
(GClassInitFunc) gst_osx_video_sink_class_init,
NULL,
NULL,
sizeof (GstOSXVideoSink),
0,
(GInstanceInitFunc) gst_osx_video_sink_init,
};
osxvideosink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
"GstOSXVideoSink", &osxvideosink_info, 0);
}
return osxvideosink_type;
}
static gboolean
plugin_init (GstPlugin * plugin)
{
if (!gst_element_register (plugin, "osxvideosink",
GST_RANK_PRIMARY, GST_TYPE_OSX_VIDEO_SINK))
return FALSE;
GST_DEBUG_CATEGORY_INIT (gst_debug_osx_video_sink, "osxvideosink", 0,
"osxvideosink element");
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"osxvideo",
"OSX native video output plugin",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

View file

@ -42,9 +42,7 @@ VALGRIND_TO_FIX = \
elements/mpeg2enc
# valgrind testing
# videocrop disabled since it takes way too long in valgrind
VALGRIND_TESTS_DISABLE = \
elements/videocrop \
$(VALGRIND_TO_FIX)
check_PROGRAMS = \
@ -53,7 +51,6 @@ check_PROGRAMS = \
elements/rganalysis \
elements/rglimiter \
elements/rgvolume \
elements/videocrop \
elements/y4menc
TESTS = $(check_PROGRAMS)
@ -61,7 +58,3 @@ TESTS = $(check_PROGRAMS)
AM_CFLAGS = $(GST_OBJ_CFLAGS) $(GST_CHECK_CFLAGS) $(CHECK_CFLAGS)
LDADD = $(GST_OBJ_LIBS) $(GST_CHECK_LIBS) $(CHECK_LIBS)
elements_videocrop_LDADD = $(LDADD) $(GST_BASE_LIBS)
elements_videocrop_CFLAGS = $(CFLAGS) $(AM_CFLAGS) $(GST_BASE_CFLAGS)

View file

@ -1,802 +0,0 @@
/* GStreamer unit test for the videocrop element
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular 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.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_VALGRIND
# include <valgrind/valgrind.h>
#endif
#include <unistd.h>
#include <gst/check/gstcheck.h>
#include <gst/base/gstbasetransform.h>
/* return a list of caps where we only need to set
* width and height to get fixed caps */
static GList *
video_crop_get_test_caps (GstElement * videocrop)
{
const GstCaps *allowed_caps;
GstPad *srcpad;
GList *list = NULL;
guint i;
srcpad = gst_element_get_pad (videocrop, "src");
fail_unless (srcpad != NULL);
allowed_caps = gst_pad_get_pad_template_caps (srcpad);
fail_unless (allowed_caps != NULL);
for (i = 0; i < gst_caps_get_size (allowed_caps); ++i) {
GstStructure *new_structure;
GstCaps *single_caps;
single_caps = gst_caps_new_empty ();
new_structure =
gst_structure_copy (gst_caps_get_structure (allowed_caps, i));
gst_structure_set (new_structure, "framerate", GST_TYPE_FRACTION,
1, 1, NULL);
gst_structure_remove_field (new_structure, "width");
gst_structure_remove_field (new_structure, "height");
gst_caps_append_structure (single_caps, new_structure);
/* should be fixed without width/height */
fail_unless (gst_caps_is_fixed (single_caps));
list = g_list_prepend (list, single_caps);
}
gst_object_unref (srcpad);
return list;
}
GST_START_TEST (test_unit_sizes)
{
GstBaseTransformClass *csp_klass, *vcrop_klass;
GstElement *videocrop, *csp;
GList *caps_list, *l;
gint i;
videocrop = gst_element_factory_make ("videocrop", "videocrop");
fail_unless (videocrop != NULL, "Failed to create videocrop element");
vcrop_klass = GST_BASE_TRANSFORM_GET_CLASS (videocrop);
csp = gst_element_factory_make ("ffmpegcolorspace", "csp");
fail_unless (csp != NULL, "Failed to create ffmpegcolorspace element");
csp_klass = GST_BASE_TRANSFORM_GET_CLASS (csp);
caps_list = video_crop_get_test_caps (videocrop);
for (l = caps_list; l != NULL; l = l->next) {
const struct
{
gint width, height;
} sizes_to_try[] = {
{
160, 120}, {
161, 120}, {
160, 121}, {
161, 121}, {
159, 120}, {
160, 119}, {
159, 119}, {
159, 121}
};
GstStructure *s;
GstCaps *caps;
gint i;
caps = gst_caps_copy (GST_CAPS (l->data));
s = gst_caps_get_structure (caps, 0);
fail_unless (s != NULL);
for (i = 0; i < G_N_ELEMENTS (sizes_to_try); ++i) {
gchar *caps_str;
guint32 format = 0;
guint csp_size = 0;
guint vc_size = 0;
gst_structure_set (s, "width", G_TYPE_INT, sizes_to_try[i].width,
"height", G_TYPE_INT, sizes_to_try[i].height, NULL);
caps_str = gst_caps_to_string (caps);
GST_INFO ("Testing unit size for %s", caps_str);
/* skip if ffmpegcolorspace doesn't support these caps
* (only works with gst-plugins-base 0.10.9.1 or later) */
if (!csp_klass->get_unit_size ((GstBaseTransform *) csp, caps, &csp_size)) {
GST_INFO ("ffmpegcolorspace does not support format %s", caps_str);
g_free (caps_str);
continue;
}
fail_unless (vcrop_klass->get_unit_size ((GstBaseTransform *) videocrop,
caps, &vc_size));
fail_unless (vc_size == csp_size,
"videocrop and ffmpegcolorspace return different unit sizes for "
"caps %s: vc_size=%d, csp_size=%d", caps_str, vc_size, csp_size);
g_free (caps_str);
}
gst_caps_unref (caps);
}
g_list_foreach (caps_list, (GFunc) gst_caps_unref, NULL);
g_list_free (caps_list);
gst_object_unref (csp);
gst_object_unref (videocrop);
}
GST_END_TEST;
typedef struct
{
GstElement *pipeline;
GstElement *src;
GstElement *filter;
GstElement *crop;
GstElement *sink;
GstBuffer *last_buf;
} GstVideoCropTestContext;
static void
handoff_cb (GstElement * sink, GstBuffer * buf, GstPad * pad,
GstBuffer ** p_buf)
{
gst_buffer_replace (p_buf, buf);
}
static void
videocrop_test_cropping_init_context (GstVideoCropTestContext * ctx)
{
fail_unless (ctx != NULL);
ctx->pipeline = gst_pipeline_new ("pipeline");
fail_unless (ctx->pipeline != NULL);
ctx->src = gst_element_factory_make ("videotestsrc", "src");
fail_unless (ctx->src != NULL, "Failed to create videotestsrc element");
ctx->filter = gst_element_factory_make ("capsfilter", "filter");
fail_unless (ctx->filter != NULL, "Failed to create capsfilter element");
ctx->crop = gst_element_factory_make ("videocrop", "crop");
fail_unless (ctx->crop != NULL, "Failed to create videocrop element");
ctx->sink = gst_element_factory_make ("fakesink", "sink");
fail_unless (ctx->sink != NULL, "Failed to create fakesink element");
gst_bin_add_many (GST_BIN (ctx->pipeline), ctx->src, ctx->filter,
ctx->crop, ctx->sink, NULL);
gst_element_link_many (ctx->src, ctx->filter, ctx->crop, ctx->sink, NULL);
/* set pattern to 'red' - for our purposes it doesn't matter anyway */
g_object_set (ctx->src, "pattern", 4, NULL);
g_object_set (ctx->sink, "signal-handoffs", TRUE, NULL);
g_signal_connect (ctx->sink, "preroll-handoff", G_CALLBACK (handoff_cb),
&ctx->last_buf);
ctx->last_buf = NULL;
GST_LOG ("context inited");
}
static void
videocrop_test_cropping_deinit_context (GstVideoCropTestContext * ctx)
{
GST_LOG ("deiniting context");
gst_element_set_state (ctx->pipeline, GST_STATE_NULL);
gst_object_unref (ctx->pipeline);
gst_buffer_replace (&ctx->last_buf, NULL);
memset (ctx, 0x00, sizeof (GstVideoCropTestContext));
}
typedef void (*GstVideoCropTestBufferFunc) (GstBuffer * buffer);
static void
videocrop_test_cropping (GstVideoCropTestContext * ctx, GstCaps * in_caps,
gint left, gint right, gint top, gint bottom,
GstVideoCropTestBufferFunc func)
{
GST_LOG ("lrtb = %03u %03u %03u %03u, caps = %" GST_PTR_FORMAT, left, right,
top, bottom, in_caps);
g_object_set (ctx->filter, "caps", in_caps, NULL);
g_object_set (ctx->crop, "left", left, "right", right, "top", top,
"bottom", bottom, NULL);
/* this will fail if videotestsrc doesn't support our format; we need
* videotestsrc from -base CVS 0.10.9.1 with RGBA and AYUV support */
fail_unless (gst_element_set_state (ctx->pipeline,
GST_STATE_PAUSED) != GST_STATE_CHANGE_FAILURE);
fail_unless (gst_element_get_state (ctx->pipeline, NULL, NULL,
-1) == GST_STATE_CHANGE_SUCCESS);
if (func != NULL) {
func (ctx->last_buf);
}
gst_element_set_state (ctx->pipeline, GST_STATE_NULL);
}
static void
check_1x1_buffer (GstBuffer * buf)
{
GstStructure *s;
fail_unless (buf != NULL);
fail_unless (GST_BUFFER_CAPS (buf) != NULL);
s = gst_caps_get_structure (GST_BUFFER_CAPS (buf), 0);
if (gst_structure_has_name (s, "video/x-raw-yuv")) {
guint32 format = 0;
fail_unless (gst_structure_get_fourcc (s, "format", &format));
/* the exact values we check for come from videotestsrc */
switch (format) {
case GST_MAKE_FOURCC ('I', '4', '2', '0'):
fail_unless_equals_int (GST_BUFFER_DATA (buf)[0], 76);
fail_unless_equals_int (GST_BUFFER_DATA (buf)[8], 85);
fail_unless_equals_int (GST_BUFFER_DATA (buf)[12], 255);
break;
case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
fail_unless_equals_int (GST_BUFFER_DATA (buf)[0], 76);
fail_unless_equals_int (GST_BUFFER_DATA (buf)[8], 255);
fail_unless_equals_int (GST_BUFFER_DATA (buf)[12], 85);
break;
case GST_MAKE_FOURCC ('Y', '8', '0', '0'):
fail_unless_equals_int (GST_BUFFER_DATA (buf)[0], 76);
/* no chroma planes */
break;
case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
fail_unless_equals_int (GST_BUFFER_DATA (buf)[1], 76);
fail_unless_equals_int (GST_BUFFER_DATA (buf)[2], 85);
fail_unless_equals_int (GST_BUFFER_DATA (buf)[3], 255);
/* no chroma planes */
break;
default:
GST_LOG ("not checking %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (format));
break;
}
} else if (gst_structure_has_name (s, "video/x-raw-rgb")) {
guint32 pixel;
gint rmask = 0, bmask = 0, gmask = 0, endianness = 0, bpp = 0;
gint rshift, gshift, bshift;
fail_unless (gst_structure_get_int (s, "red_mask", &rmask));
fail_unless (gst_structure_get_int (s, "blue_mask", &bmask));
fail_unless (gst_structure_get_int (s, "green_mask", &gmask));
fail_unless (gst_structure_get_int (s, "bpp", &bpp));
fail_unless (gst_structure_get_int (s, "endianness", &endianness));
fail_unless (rmask != 0);
fail_unless (gmask != 0);
fail_unless (bmask != 0);
fail_unless (bpp != 0);
fail_unless (endianness != 0);
rshift = g_bit_nth_lsf (rmask, -1);
gshift = g_bit_nth_lsf (gmask, -1);
bshift = g_bit_nth_lsf (bmask, -1);
switch (bpp) {
case 32:{
if (endianness == G_LITTLE_ENDIAN)
pixel = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
else
pixel = GST_READ_UINT32_BE (GST_BUFFER_DATA (buf));
break;
}
case 24:{
if (endianness == G_BIG_ENDIAN) {
pixel = (GST_READ_UINT8 (GST_BUFFER_DATA (buf)) << 16) |
(GST_READ_UINT8 (GST_BUFFER_DATA (buf) + 1) << 8) |
(GST_READ_UINT8 (GST_BUFFER_DATA (buf) + 2) << 0);
} else {
pixel = (GST_READ_UINT8 (GST_BUFFER_DATA (buf) + 2) << 16) |
(GST_READ_UINT8 (GST_BUFFER_DATA (buf) + 1) << 8) |
(GST_READ_UINT8 (GST_BUFFER_DATA (buf) + 0) << 0);
}
break;
}
default:{
GST_LOG ("not checking RGB-format buffer with %ubpp", bpp);
return;
}
}
fail_unless_equals_int ((pixel & rmask) >> rshift, 0xff);
fail_unless_equals_int ((pixel & gmask) >> gshift, 0x00);
fail_unless_equals_int ((pixel & bmask) >> bshift, 0x00);
}
}
GST_START_TEST (test_crop_to_1x1)
{
GstVideoCropTestContext ctx;
GList *caps_list, *node;
videocrop_test_cropping_init_context (&ctx);
caps_list = video_crop_get_test_caps (ctx.crop);
for (node = caps_list; node != NULL; node = node->next) {
GstStructure *s;
GstCaps *caps;
caps = gst_caps_copy (GST_CAPS (node->data));
s = gst_caps_get_structure (caps, 0);
fail_unless (s != NULL);
GST_INFO ("testing format: %" GST_PTR_FORMAT, caps);
gst_structure_set (s, "width", G_TYPE_INT, 160,
"height", G_TYPE_INT, 160, NULL);
videocrop_test_cropping (&ctx, caps, 159, 0, 159, 0, check_1x1_buffer);
/* commented out because they don't really add anything useful check-wise:
videocrop_test_cropping (&ctx, caps, 0, 159, 0, 159, check_1x1_buffer);
videocrop_test_cropping (&ctx, caps, 159, 0, 0, 159, check_1x1_buffer);
videocrop_test_cropping (&ctx, caps, 0, 159, 159, 0, check_1x1_buffer);
*/
gst_caps_unref (caps);
}
g_list_foreach (caps_list, (GFunc) gst_caps_unref, NULL);
g_list_free (caps_list);
videocrop_test_cropping_deinit_context (&ctx);
}
GST_END_TEST;
GST_START_TEST (test_cropping)
{
GstVideoCropTestContext ctx;
struct
{
gint width, height;
} sizes_to_try[] = {
{
160, 160}, {
161, 160}, {
160, 161}, {
161, 161}, {
159, 160}, {
160, 159}, {
159, 159}, {
159, 161}
};
GList *caps_list, *node;
gint i;
videocrop_test_cropping_init_context (&ctx);
caps_list = video_crop_get_test_caps (ctx.crop);
for (node = caps_list; node != NULL; node = node->next) {
GstStructure *s;
GstCaps *caps;
caps = gst_caps_copy (GST_CAPS (node->data));
s = gst_caps_get_structure (caps, 0);
fail_unless (s != NULL);
GST_INFO ("testing format: %" GST_PTR_FORMAT, caps);
for (i = 0; i < G_N_ELEMENTS (sizes_to_try); ++i) {
gst_structure_set (s, "width", G_TYPE_INT, sizes_to_try[i].width,
"height", G_TYPE_INT, sizes_to_try[i].height, NULL);
GST_INFO (" - %d x %d", sizes_to_try[i].width, sizes_to_try[i].height);
videocrop_test_cropping (&ctx, caps, 0, 0, 0, 0, NULL);
videocrop_test_cropping (&ctx, caps, 1, 0, 0, 0, NULL);
videocrop_test_cropping (&ctx, caps, 0, 1, 0, 0, NULL);
videocrop_test_cropping (&ctx, caps, 0, 0, 1, 0, NULL);
videocrop_test_cropping (&ctx, caps, 0, 0, 0, 1, NULL);
videocrop_test_cropping (&ctx, caps, 63, 0, 0, 0, NULL);
videocrop_test_cropping (&ctx, caps, 0, 63, 0, 0, NULL);
videocrop_test_cropping (&ctx, caps, 0, 0, 63, 0, NULL);
videocrop_test_cropping (&ctx, caps, 0, 0, 0, 63, NULL);
videocrop_test_cropping (&ctx, caps, 63, 0, 0, 1, NULL);
videocrop_test_cropping (&ctx, caps, 0, 63, 1, 0, NULL);
videocrop_test_cropping (&ctx, caps, 0, 1, 63, 0, NULL);
videocrop_test_cropping (&ctx, caps, 1, 0, 0, 63, NULL);
videocrop_test_cropping (&ctx, caps, 0, 0, 0, 0, NULL);
videocrop_test_cropping (&ctx, caps, 32, 0, 0, 128, NULL);
videocrop_test_cropping (&ctx, caps, 0, 32, 128, 0, NULL);
videocrop_test_cropping (&ctx, caps, 0, 128, 32, 0, NULL);
videocrop_test_cropping (&ctx, caps, 128, 0, 0, 32, NULL);
videocrop_test_cropping (&ctx, caps, 1, 1, 1, 1, NULL);
videocrop_test_cropping (&ctx, caps, 63, 63, 63, 63, NULL);
videocrop_test_cropping (&ctx, caps, 64, 64, 64, 64, NULL);
}
gst_caps_unref (caps);
}
g_list_foreach (caps_list, (GFunc) gst_caps_unref, NULL);
g_list_free (caps_list);
videocrop_test_cropping_deinit_context (&ctx);
}
GST_END_TEST;
static gboolean
buffer_probe_cb (GstPad * pad, GstBuffer * buf, GstBuffer ** p_buf)
{
gst_buffer_replace (p_buf, buf);
return TRUE; /* keep data */
}
GST_START_TEST (test_passthrough)
{
GstStateChangeReturn state_ret;
GstVideoCropTestContext ctx;
GstPad *srcpad;
GstBuffer *gen_buf = NULL; /* buffer generated by videotestsrc */
videocrop_test_cropping_init_context (&ctx);
g_object_set (ctx.src, "num-buffers", 1, NULL);
srcpad = gst_element_get_pad (ctx.src, "src");
fail_unless (srcpad != NULL);
gst_pad_add_buffer_probe (srcpad, G_CALLBACK (buffer_probe_cb), &gen_buf);
gst_object_unref (srcpad);
g_object_set (ctx.crop, "left", 0, "right", 0, "top", 0, "bottom", 0, NULL);
state_ret = gst_element_set_state (ctx.pipeline, GST_STATE_PAUSED);
fail_unless (state_ret != GST_STATE_CHANGE_FAILURE,
"couldn't set pipeline to PAUSED state");
state_ret = gst_element_get_state (ctx.pipeline, NULL, NULL, -1);
fail_unless (state_ret == GST_STATE_CHANGE_SUCCESS,
"pipeline failed to go to PAUSED state");
fail_unless (gen_buf != NULL);
fail_unless (ctx.last_buf != NULL);
/* pass through should do nothing */
fail_unless (gen_buf == ctx.last_buf);
videocrop_test_cropping_deinit_context (&ctx);
fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (gen_buf), 1);
gst_buffer_unref (gen_buf);
}
GST_END_TEST;
static gint
notgst_value_list_get_nth_int (const GValue * list_val, guint n)
{
const GValue *v;
fail_unless (GST_VALUE_HOLDS_LIST (list_val));
fail_unless (n < gst_value_list_get_size (list_val));
v = gst_value_list_get_value (list_val, n);
fail_unless (G_VALUE_HOLDS_INT (v));
return g_value_get_int (v);
}
GST_START_TEST (test_caps_transform)
{
GstVideoCropTestContext ctx;
GstBaseTransformClass *klass;
GstBaseTransform *crop;
const GValue *w_val;
const GValue *h_val;
GstCaps *caps, *adj_caps;
videocrop_test_cropping_init_context (&ctx);
crop = GST_BASE_TRANSFORM (ctx.crop);
klass = GST_BASE_TRANSFORM_GET_CLASS (ctx.crop);
fail_unless (klass != NULL);
caps = gst_caps_new_simple ("video/x-raw-yuv",
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
"framerate", GST_TYPE_FRACTION, 1, 1,
"width", G_TYPE_INT, 200, "height", G_TYPE_INT, 100, NULL);
/* by default, it should be no cropping and hence passthrough */
adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_is_equal (adj_caps, caps));
gst_caps_unref (adj_caps);
adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_is_equal (adj_caps, caps));
gst_caps_unref (adj_caps);
/* make sure that's still true after changing properties back and forth */
g_object_set (ctx.crop, "left", 1, "right", 3, "top", 5, "bottom", 7, NULL);
g_object_set (ctx.crop, "left", 0, "right", 0, "top", 0, "bottom", 0, NULL);
adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_is_equal (adj_caps, caps));
gst_caps_unref (adj_caps);
adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_is_equal (adj_caps, caps));
gst_caps_unref (adj_caps);
/* now check adjustments made ... */
g_object_set (ctx.crop, "left", 1, "right", 3, "top", 5, "bottom", 7, NULL);
/* ========= (1) fixed value ============================================= */
/* sink => source, source must be bigger if we crop stuff off */
adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_get_size (adj_caps) == 1);
w_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
fail_unless (w_val != NULL);
fail_unless (G_VALUE_HOLDS_INT (w_val));
fail_unless_equals_int (g_value_get_int (w_val), 200 + (1 + 3));
h_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
fail_unless (h_val != NULL);
fail_unless (G_VALUE_HOLDS_INT (h_val));
fail_unless_equals_int (g_value_get_int (h_val), 100 + (5 + 7));
gst_caps_unref (adj_caps);
/* source => sink becomes smaller */
adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_get_size (adj_caps) == 1);
w_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
fail_unless (w_val != NULL);
fail_unless (G_VALUE_HOLDS_INT (w_val));
fail_unless_equals_int (g_value_get_int (w_val), 200 - (1 + 3));
h_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
fail_unless (h_val != NULL);
fail_unless (G_VALUE_HOLDS_INT (h_val));
fail_unless_equals_int (g_value_get_int (h_val), 100 - (5 + 7));
gst_caps_unref (adj_caps);
/* ========= (2) range (simple adjustment) =============================== */
gst_structure_set (gst_caps_get_structure (caps, 0),
"width", GST_TYPE_INT_RANGE, 1000, 2000,
"height", GST_TYPE_INT_RANGE, 3000, 4000, NULL);
/* sink => source, source must be bigger if we crop stuff off */
adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_get_size (adj_caps) == 1);
w_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
fail_unless (w_val != NULL);
fail_unless (GST_VALUE_HOLDS_INT_RANGE (w_val));
fail_unless_equals_int (gst_value_get_int_range_min (w_val), 1000 + (1 + 3));
fail_unless_equals_int (gst_value_get_int_range_max (w_val), 2000 + (1 + 3));
h_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
fail_unless (h_val != NULL);
fail_unless (GST_VALUE_HOLDS_INT_RANGE (h_val));
fail_unless_equals_int (gst_value_get_int_range_min (h_val), 3000 + (5 + 7));
fail_unless_equals_int (gst_value_get_int_range_max (h_val), 4000 + (5 + 7));
gst_caps_unref (adj_caps);
/* source => sink becomes smaller */
adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_get_size (adj_caps) == 1);
w_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
fail_unless (w_val != NULL);
fail_unless (GST_VALUE_HOLDS_INT_RANGE (w_val));
fail_unless_equals_int (gst_value_get_int_range_min (w_val), 1000 - (1 + 3));
fail_unless_equals_int (gst_value_get_int_range_max (w_val), 2000 - (1 + 3));
h_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
fail_unless (h_val != NULL);
fail_unless (GST_VALUE_HOLDS_INT_RANGE (h_val));
fail_unless_equals_int (gst_value_get_int_range_min (h_val), 3000 - (5 + 7));
fail_unless_equals_int (gst_value_get_int_range_max (h_val), 4000 - (5 + 7));
gst_caps_unref (adj_caps);
/* ========= (3) range (adjustment at boundary) ========================== */
gst_structure_set (gst_caps_get_structure (caps, 0),
"width", GST_TYPE_INT_RANGE, 2, G_MAXINT,
"height", GST_TYPE_INT_RANGE, 2, G_MAXINT, NULL);
/* sink => source, source must be bigger if we crop stuff off */
adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_get_size (adj_caps) == 1);
w_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
fail_unless (w_val != NULL);
fail_unless (GST_VALUE_HOLDS_INT_RANGE (w_val));
fail_unless_equals_int (gst_value_get_int_range_min (w_val), 2 + (1 + 3));
fail_unless_equals_int (gst_value_get_int_range_max (w_val), G_MAXINT);
h_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
fail_unless (h_val != NULL);
fail_unless (GST_VALUE_HOLDS_INT_RANGE (h_val));
fail_unless_equals_int (gst_value_get_int_range_min (h_val), 2 + (5 + 7));
fail_unless_equals_int (gst_value_get_int_range_max (h_val), G_MAXINT);
gst_caps_unref (adj_caps);
/* source => sink becomes smaller */
adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_get_size (adj_caps) == 1);
w_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
fail_unless (w_val != NULL);
fail_unless (GST_VALUE_HOLDS_INT_RANGE (w_val));
fail_unless_equals_int (gst_value_get_int_range_min (w_val), 1);
fail_unless_equals_int (gst_value_get_int_range_max (w_val),
G_MAXINT - (1 + 3));
h_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
fail_unless (h_val != NULL);
fail_unless (GST_VALUE_HOLDS_INT_RANGE (h_val));
fail_unless_equals_int (gst_value_get_int_range_min (h_val), 1);
fail_unless_equals_int (gst_value_get_int_range_max (h_val),
G_MAXINT - (5 + 7));
gst_caps_unref (adj_caps);
/* ========= (4) list of values ========================================== */
{
GValue list = { 0, };
GValue ival = { 0, };
g_value_init (&ival, G_TYPE_INT);
g_value_init (&list, GST_TYPE_LIST);
g_value_set_int (&ival, 2);
gst_value_list_append_value (&list, &ival);
g_value_set_int (&ival, G_MAXINT);
gst_value_list_append_value (&list, &ival);
gst_structure_set_value (gst_caps_get_structure (caps, 0), "width", &list);
g_value_unset (&list);
g_value_unset (&ival);
g_value_init (&ival, G_TYPE_INT);
g_value_init (&list, GST_TYPE_LIST);
g_value_set_int (&ival, 5);
gst_value_list_append_value (&list, &ival);
g_value_set_int (&ival, 1000);
gst_value_list_append_value (&list, &ival);
gst_structure_set_value (gst_caps_get_structure (caps, 0), "height", &list);
g_value_unset (&list);
g_value_unset (&ival);
}
/* sink => source, source must be bigger if we crop stuff off */
adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_get_size (adj_caps) == 1);
w_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
fail_unless (w_val != NULL);
fail_unless (GST_VALUE_HOLDS_LIST (w_val));
fail_unless_equals_int (notgst_value_list_get_nth_int (w_val, 0),
2 + (1 + 3));
fail_unless_equals_int (notgst_value_list_get_nth_int (w_val, 1), G_MAXINT);
h_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
fail_unless (h_val != NULL);
fail_unless (GST_VALUE_HOLDS_LIST (h_val));
fail_unless_equals_int (notgst_value_list_get_nth_int (h_val, 0),
5 + (5 + 7));
fail_unless_equals_int (notgst_value_list_get_nth_int (h_val, 1),
1000 + (5 + 7));
gst_caps_unref (adj_caps);
/* source => sink becomes smaller */
adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps);
fail_unless (adj_caps != NULL);
fail_unless (gst_caps_get_size (adj_caps) == 1);
w_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
fail_unless (w_val != NULL);
fail_unless (GST_VALUE_HOLDS_LIST (w_val));
fail_unless_equals_int (notgst_value_list_get_nth_int (w_val, 0), 1);
fail_unless_equals_int (notgst_value_list_get_nth_int (w_val, 1),
G_MAXINT - (1 + 3));
h_val =
gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
fail_unless (h_val != NULL);
fail_unless (GST_VALUE_HOLDS_LIST (h_val));
fail_unless_equals_int (notgst_value_list_get_nth_int (h_val, 0), 1);
fail_unless_equals_int (notgst_value_list_get_nth_int (h_val, 1),
1000 - (5 + 7));
gst_caps_unref (adj_caps);
gst_caps_unref (caps);
videocrop_test_cropping_deinit_context (&ctx);
}
GST_END_TEST;
static Suite *
videocrop_suite (void)
{
Suite *s = suite_create ("videocrop");
TCase *tc_chain = tcase_create ("general");
#ifdef HAVE_VALGRIND
if (RUNNING_ON_VALGRIND) {
/* our tests take quite a long time, so increase
* timeout (~25 minutes on my 1.6GHz AMD K7) */
tcase_set_timeout (tc_chain, 30 * 60);
} else
#endif
{
/* increase timeout, these tests take a long time (60 secs here) */
tcase_set_timeout (tc_chain, 2 * 60);
}
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_crop_to_1x1);
tcase_add_test (tc_chain, test_caps_transform);
tcase_add_test (tc_chain, test_passthrough);
tcase_add_test (tc_chain, test_unit_sizes);
tcase_add_test (tc_chain, test_cropping);
return s;
}
int
main (int argc, char **argv)
{
int nf;
Suite *s = videocrop_suite ();
SRunner *sr = srunner_create (s);
#ifdef HAVE_VALGRIND
if (RUNNING_ON_VALGRIND) {
/* otherwise valgrind errors out when liboil probes CPU extensions
* in oil_init() during which it causes SIGILLs etc. to be fired */
g_setenv ("OIL_CPU_FLAGS", "0", 0);
}
#endif
gst_check_init (&argc, &argv);
srunner_run_all (sr, CK_NORMAL);
nf = srunner_ntests_failed (sr);
srunner_free (sr);
return nf;
}

View file

@ -11,14 +11,9 @@ else
GST_SOUNDTOUCH_TESTS =
endif
videocrop_test_SOURCES = videocrop-test.c
videocrop_test_CFLAGS = $(GST_CFLAGS)
videocrop_test_LDADD = $(GST_LIBS)
videocrop_test_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
equalizer_test_SOURCES = equalizer-test.c
equalizer_test_CFLAGS = $(GST_CFLAGS)
equalizer_test_LDADD = $(GST_LIBS)
equalizer_test_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_PROGRAMS = $(GST_SOUNDTOUCH_TESTS) equalizer-test videocrop-test
noinst_PROGRAMS = $(GST_SOUNDTOUCH_TESTS) equalizer-test

View file

@ -1,356 +0,0 @@
/* GStreamer interactive test for the videocrop element
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular 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.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <gst/gst.h>
#include <stdlib.h>
#include <math.h>
GST_DEBUG_CATEGORY_STATIC (videocrop_test_debug);
#define GST_CAT_DEFAULT videocrop_test_debug
#define OUT_WIDTH 640
#define OUT_HEIGHT 480
#define TIME_PER_TEST 10 /* seconds each format is tested */
#define FRAMERATE 15 /* frames per second */
#define DEFAULT_VIDEOSINK "xvimagesink"
static gboolean
check_bus_for_errors (GstBus * bus, GstClockTime max_wait_time)
{
GstMessage *msg;
msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, max_wait_time);
if (msg) {
GError *err = NULL;
gchar *debug = NULL;
g_assert (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR);
gst_message_parse_error (msg, &err, &debug);
GST_ERROR ("ERROR: %s [%s]", err->message, debug);
g_print ("\n===========> ERROR: %s\n%s\n\n", err->message, debug);
g_error_free (err);
g_free (debug);
gst_message_unref (msg);
}
return (msg != NULL);
}
static void
test_with_caps (GstElement * src, GstElement * videocrop, GstCaps * caps)
{
GstClockTime time_run;
GstElement *pipeline;
GTimer *timer;
GstBus *bus;
GstPad *pad;
guint hcrop;
guint vcrop;
/* caps must be writable, we can't check that here though */
g_assert (GST_CAPS_REFCOUNT_VALUE (caps) == 1);
timer = g_timer_new ();
vcrop = 0;
hcrop = 0;
pipeline = GST_ELEMENT (gst_element_get_parent (videocrop));
g_assert (GST_IS_PIPELINE (pipeline));
/* at this point the pipeline is in PLAYING state; we only want to capture
* errors resulting from our on-the-fly changing of the filtercaps */
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
/* pad to block */
pad = gst_element_get_pad (src, "src");
time_run = 0;
do {
GstClockTime wait_time, waited_for_block;
if (check_bus_for_errors (bus, 0))
break;
wait_time = GST_SECOND / FRAMERATE;
GST_LOG ("hcrop = %3d, vcrop = %3d", vcrop, hcrop);
g_timer_reset (timer);
/* need to block the streaming thread while changing these properties,
* otherwise we might get random not-negotiated errors (when caps are
* changed in between upstream calling pad_alloc_buffer() and pushing
* the processed buffer?) */
gst_pad_set_blocked (pad, TRUE);
g_object_set (videocrop, "left", hcrop, "top", vcrop, NULL);
gst_pad_set_blocked (pad, FALSE);
waited_for_block = g_timer_elapsed (timer, NULL) * (double) GST_SECOND;
/* GST_LOG ("waited: %" GST_TIME_FORMAT ", frame len: %" GST_TIME_FORMAT,
GST_TIME_ARGS (waited_for_block), GST_TIME_ARGS (wait_time)); */
++vcrop;
++hcrop;
if (wait_time > waited_for_block) {
g_usleep ((wait_time - waited_for_block) / GST_MSECOND);
}
time_run += wait_time;
}
while (time_run < (TIME_PER_TEST * GST_SECOND));
g_timer_destroy (timer);
gst_object_unref (bus);
gst_object_unref (pad);
}
/* return a list of caps where we only need to set
* width and height to get fixed caps */
static GList *
video_crop_get_test_caps (GstElement * videocrop)
{
const GstCaps *allowed_caps;
GstPad *srcpad;
GList *list = NULL;
guint i;
srcpad = gst_element_get_pad (videocrop, "src");
g_assert (srcpad != NULL);
allowed_caps = gst_pad_get_pad_template_caps (srcpad);
g_assert (allowed_caps != NULL);
for (i = 0; i < gst_caps_get_size (allowed_caps); ++i) {
GstStructure *new_structure;
GstCaps *single_caps;
single_caps = gst_caps_new_empty ();
new_structure =
gst_structure_copy (gst_caps_get_structure (allowed_caps, i));
gst_structure_set (new_structure, "framerate", GST_TYPE_FRACTION,
FRAMERATE, 1, NULL);
gst_structure_remove_field (new_structure, "width");
gst_structure_remove_field (new_structure, "height");
gst_caps_append_structure (single_caps, new_structure);
/* should be fixed without width/height */
g_assert (gst_caps_is_fixed (single_caps));
list = g_list_prepend (list, single_caps);
}
gst_object_unref (srcpad);
return list;
}
static gchar *opt_videosink_str; /* NULL */
static gchar *opt_filtercaps_str; /* NULL */
static gboolean opt_with_ffmpegcolorspace; /* FALSE */
int
main (int argc, char **argv)
{
static const GOptionEntry test_goptions[] = {
{"videosink", '\0', 0, G_OPTION_ARG_STRING, &opt_videosink_str,
"videosink to use (default: " DEFAULT_VIDEOSINK ")", NULL},
{"caps", '\0', 0, G_OPTION_ARG_STRING, &opt_filtercaps_str,
"filter caps to narrow down formats to test", NULL},
{"with-ffmpegcolorspace", '\0', 0, G_OPTION_ARG_NONE,
&opt_with_ffmpegcolorspace,
"whether to add an ffmpegcolorspace element in front of the sink",
NULL},
{NULL, '\0', 0, 0, NULL, NULL, NULL}
};
GOptionContext *ctx;
GError *opt_err = NULL;
GstElement *pipeline, *src, *filter1, *crop, *scale, *filter2, *csp, *sink;
GMainLoop *loop;
GstCaps *filter_caps = NULL;
GList *caps_list, *l;
if (!g_thread_supported ())
g_thread_init (NULL);
/* command line option parsing */
ctx = g_option_context_new ("");
g_option_context_add_group (ctx, gst_init_get_option_group ());
g_option_context_add_main_entries (ctx, test_goptions, NULL);
if (!g_option_context_parse (ctx, &argc, &argv, &opt_err)) {
g_error ("Error parsing command line options: %s", opt_err->message);
return -1;
}
GST_DEBUG_CATEGORY_INIT (videocrop_test_debug, "videocroptest", 0, "vctest");
loop = g_main_loop_new (NULL, FALSE);
pipeline = gst_pipeline_new ("pipeline");
src = gst_element_factory_make ("videotestsrc", "videotestsrc");
g_assert (src != NULL);
filter1 = gst_element_factory_make ("capsfilter", "capsfilter1");
g_assert (filter1 != NULL);
crop = gst_element_factory_make ("videocrop", "videocrop");
g_assert (crop != NULL);
scale = gst_element_factory_make ("videoscale", "videoscale");
g_assert (scale != NULL);
filter2 = gst_element_factory_make ("capsfilter", "capsfilter2");
g_assert (filter2 != NULL);
if (opt_with_ffmpegcolorspace) {
g_print ("Adding ffmpegcolorspace\n");
csp = gst_element_factory_make ("ffmpegcolorspace", "colorspace");
} else {
csp = gst_element_factory_make ("identity", "colorspace");
}
g_assert (csp != NULL);
if (opt_filtercaps_str) {
filter_caps = gst_caps_from_string (opt_filtercaps_str);
if (filter_caps == NULL) {
g_error ("Invalid filter caps string '%s'", opt_filtercaps_str);
} else {
g_print ("Using filter caps '%s'\n", opt_filtercaps_str);
}
}
if (opt_videosink_str) {
g_print ("Trying videosink '%s' ...", opt_videosink_str);
sink = gst_element_factory_make (opt_videosink_str, "sink");
g_print ("%s\n", (sink) ? "ok" : "element couldn't be created");
} else {
sink = NULL;
}
if (sink == NULL) {
g_print ("Trying videosink '%s' ...", DEFAULT_VIDEOSINK);
sink = gst_element_factory_make (DEFAULT_VIDEOSINK, "sink");
g_print ("%s\n", (sink) ? "ok" : "element couldn't be created");
}
if (sink == NULL) {
g_print ("Trying videosink '%s' ...", "xvimagesink");
sink = gst_element_factory_make ("xvimagesink", "sink");
g_print ("%s\n", (sink) ? "ok" : "element couldn't be created");
}
if (sink == NULL) {
g_print ("Trying videosink '%s' ...", "ximagesink");
sink = gst_element_factory_make ("ximagesink", "sink");
g_print ("%s\n", (sink) ? "ok" : "element couldn't be created");
}
g_assert (sink != NULL);
gst_bin_add_many (GST_BIN (pipeline), src, filter1, crop, scale, filter2,
csp, sink, NULL);
if (!gst_element_link (src, filter1))
g_error ("Failed to link videotestsrc to capsfilter1");
if (!gst_element_link (filter1, crop))
g_error ("Failed to link capsfilter1 to videocrop");
if (!gst_element_link (crop, scale))
g_error ("Failed to link videocrop to videoscale");
if (!gst_element_link (scale, filter2))
g_error ("Failed to link videoscale to capsfilter2");
if (!gst_element_link (filter2, csp))
g_error ("Failed to link capsfilter2 to ffmpegcolorspace");
if (!gst_element_link (csp, sink))
g_error ("Failed to link ffmpegcolorspace to video sink");
caps_list = video_crop_get_test_caps (crop);
for (l = caps_list; l != NULL; l = l->next) {
GstStateChangeReturn ret;
GstCaps *caps, *out_caps;
gboolean skip = FALSE;
gchar *s;
if (filter_caps) {
GstCaps *icaps;
icaps = gst_caps_intersect (filter_caps, GST_CAPS (l->data));
skip = gst_caps_is_empty (icaps);
gst_caps_unref (icaps);
}
/* this is the size of our window (stays fixed) */
out_caps = gst_caps_copy (GST_CAPS (l->data));
gst_structure_set (gst_caps_get_structure (out_caps, 0), "width",
G_TYPE_INT, OUT_WIDTH, "height", G_TYPE_INT, OUT_HEIGHT, NULL);
g_object_set (filter2, "caps", out_caps, NULL);
/* filter1 gets these too to prevent videotestsrc from renegotiating */
g_object_set (filter1, "caps", out_caps, NULL);
gst_caps_unref (out_caps);
caps = gst_caps_copy (GST_CAPS (l->data));
GST_INFO ("testing format: %" GST_PTR_FORMAT, caps);
s = gst_caps_to_string (caps);
if (skip) {
g_print ("Skipping format: %s\n", s);
g_free (s);
continue;
}
g_print ("Format: %s\n", s);
caps = gst_caps_make_writable (caps);
/* FIXME: check return values */
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
if (ret != GST_STATE_CHANGE_FAILURE) {
ret = gst_element_get_state (pipeline, NULL, NULL, -1);
if (ret != GST_STATE_CHANGE_FAILURE) {
test_with_caps (src, crop, caps);
} else {
g_print ("Format: %s not supported (failed to go to PLAYING)\n", s);
}
} else {
g_print ("Format: %s not supported\n", s);
}
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_caps_unref (caps);
g_free (s);
}
g_list_foreach (caps_list, (GFunc) gst_caps_unref, NULL);
g_list_free (caps_list);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
return 0;
}