New plugin for low-latency video capture on Windows (#519935).

Original commit message from CVS:
* configure.ac:
* sys/Makefile.am:
* sys/winks/Makefile.am:
* sys/winks/gstksclock.c:
* sys/winks/gstksclock.h:
* sys/winks/gstksvideodevice.c:
* sys/winks/gstksvideodevice.h:
* sys/winks/gstksvideosrc.c:
* sys/winks/gstksvideosrc.h:
* sys/winks/kshelpers.c:
* sys/winks/kshelpers.h:
* sys/winks/ksvideohelpers.c:
* sys/winks/ksvideohelpers.h:
New plugin for low-latency video capture on Windows (#519935).
Uses Kernel Streaming, the lowest level API for doing video capture
on Windows (more or less just raw ioctls).
This commit is contained in:
Ole André Vadla Ravnås 2008-08-24 22:05:48 +00:00
parent 4c75dffedc
commit c980279fa4
14 changed files with 3676 additions and 1 deletions

View file

@ -1,3 +1,23 @@
2008-08-24 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
* configure.ac:
* sys/Makefile.am:
* sys/winks/Makefile.am:
* sys/winks/gstksclock.c:
* sys/winks/gstksclock.h:
* sys/winks/gstksvideodevice.c:
* sys/winks/gstksvideodevice.h:
* sys/winks/gstksvideosrc.c:
* sys/winks/gstksvideosrc.h:
* sys/winks/kshelpers.c:
* sys/winks/kshelpers.h:
* sys/winks/ksvideohelpers.c:
* sys/winks/ksvideohelpers.h:
New plugin for low-latency video capture on Windows (#519935).
Uses Kernel Streaming, the lowest level API for doing video capture
on Windows (more or less just raw ioctls).
2008-08-24 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
* gst/pcapparse/gstpcapparse.c:

View file

@ -1298,6 +1298,7 @@ sys/oss4/Makefile
sys/qtwrapper/Makefile
sys/vcd/Makefile
sys/wininet/Makefile
sys/winks/Makefile
sys/winscreencap/Makefile
examples/Makefile
examples/app/Makefile

View file

@ -61,5 +61,5 @@ endif
SUBDIRS = $(ACM_DIR) $(DVB_DIR) $(FBDEV_DIR) $(OSS4_DIR) $(QT_DIR) $(VCD_DIR) $(WININET_DIR)
DIST_SUBDIRS = acmenc dvb fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \
oss4 qtwrapper vcd wininet winscreencap
oss4 qtwrapper vcd wininet winks winscreencap

9
sys/winks/Makefile.am Normal file
View file

@ -0,0 +1,9 @@
# This plugin isn't buildable with autotools at this point in time, so just
# ensure everything's listed in EXTRA_DIST
EXTRA_DIST = \
gstksclock.c gstksclock.h \
gstksvideodevice.c gstksvideodevice.h \
gstksvideosrc.c gstksvideosrc.h \
kshelpers.c kshelpers.h \
ksvideohelpers.c ksvideohelpers.h

356
sys/winks/gstksclock.c Normal file
View file

@ -0,0 +1,356 @@
/*
* Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.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.
*/
#include "gstksclock.h"
#include "kshelpers.h"
GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
#define GST_CAT_DEFAULT gst_ks_debug
typedef struct
{
GMutex *mutex;
GCond *client_cond;
GCond *worker_cond;
HANDLE clock_handle;
gboolean open;
gboolean closing;
KSSTATE state;
GThread *worker_thread;
gboolean worker_running;
gboolean worker_initialized;
GstClock *master_clock;
} GstKsClockPrivate;
#define GST_KS_CLOCK_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_KS_CLOCK, \
GstKsClockPrivate))
#define GST_KS_CLOCK_LOCK() g_mutex_lock (priv->mutex)
#define GST_KS_CLOCK_UNLOCK() g_mutex_unlock (priv->mutex)
static void gst_ks_clock_dispose (GObject * object);
static void gst_ks_clock_finalize (GObject * object);
GST_BOILERPLATE (GstKsClock, gst_ks_clock, GObject, G_TYPE_OBJECT);
static void
gst_ks_clock_base_init (gpointer gclass)
{
}
static void
gst_ks_clock_class_init (GstKsClockClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (GstKsClockPrivate));
gobject_class->dispose = gst_ks_clock_dispose;
gobject_class->finalize = gst_ks_clock_finalize;
}
static void
gst_ks_clock_init (GstKsClock * self, GstKsClockClass * gclass)
{
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
priv->mutex = g_mutex_new ();
priv->client_cond = g_cond_new ();
priv->worker_cond = g_cond_new ();
priv->clock_handle = INVALID_HANDLE_VALUE;
priv->open = FALSE;
priv->closing = FALSE;
priv->state = KSSTATE_STOP;
priv->worker_thread = NULL;
priv->worker_running = FALSE;
priv->worker_initialized = FALSE;
priv->master_clock = NULL;
}
static void
gst_ks_clock_dispose (GObject * object)
{
GstKsClock *self = GST_KS_CLOCK (object);
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
g_assert (!priv->open);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_ks_clock_finalize (GObject * object)
{
GstKsClock *self = GST_KS_CLOCK (object);
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
g_cond_free (priv->worker_cond);
g_cond_free (priv->client_cond);
g_mutex_free (priv->mutex);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void gst_ks_clock_close_unlocked (GstKsClock * self);
gboolean
gst_ks_clock_open (GstKsClock * self)
{
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
gboolean ret = FALSE;
GList *devices;
KsDeviceEntry *device;
KSSTATE state;
GST_KS_CLOCK_LOCK ();
g_assert (!priv->open);
priv->state = KSSTATE_STOP;
devices = ks_enumerate_devices (&KSCATEGORY_CLOCK);
if (devices == NULL)
goto error;
device = devices->data;
priv->clock_handle = CreateFile (device->path, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (!ks_is_valid_handle (priv->clock_handle))
goto error;
state = KSSTATE_STOP;
if (!ks_object_set_property (priv->clock_handle, KSPROPSETID_Clock,
KSPROPERTY_CLOCK_STATE, &state, sizeof (state)))
goto error;
ks_device_list_free (devices);
priv->open = TRUE;
GST_KS_CLOCK_UNLOCK ();
return TRUE;
error:
ks_device_list_free (devices);
gst_ks_clock_close_unlocked (self);
GST_KS_CLOCK_UNLOCK ();
return FALSE;
}
static gboolean
gst_ks_clock_set_state_unlocked (GstKsClock * self, KSSTATE state)
{
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
KSSTATE initial_state;
gint addend;
g_assert (priv->open);
if (state == priv->state)
return TRUE;
initial_state = priv->state;
addend = (state > priv->state) ? 1 : -1;
GST_DEBUG ("Initiating clock state change from %s to %s",
ks_state_to_string (priv->state), ks_state_to_string (state));
while (priv->state != state) {
KSSTATE next_state = priv->state + addend;
GST_DEBUG ("Changing clock state from %s to %s",
ks_state_to_string (priv->state), ks_state_to_string (next_state));
if (ks_object_set_property (priv->clock_handle, KSPROPSETID_Clock,
KSPROPERTY_CLOCK_STATE, &next_state, sizeof (next_state))) {
priv->state = next_state;
GST_DEBUG ("Changed clock state to %s", ks_state_to_string (priv->state));
} else {
GST_WARNING ("Failed to change clock state to %s",
ks_state_to_string (next_state));
return FALSE;
}
}
GST_DEBUG ("Finished clock state change from %s to %s",
ks_state_to_string (initial_state), ks_state_to_string (state));
return TRUE;
}
static void
gst_ks_clock_close_unlocked (GstKsClock * self)
{
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
if (priv->closing)
return;
priv->closing = TRUE;
if (priv->worker_thread != NULL) {
priv->worker_running = FALSE;
g_cond_signal (priv->worker_cond);
GST_KS_CLOCK_UNLOCK ();
g_thread_join (priv->worker_thread);
priv->worker_thread = NULL;
GST_KS_CLOCK_LOCK ();
}
gst_ks_clock_set_state_unlocked (self, KSSTATE_STOP);
if (ks_is_valid_handle (priv->clock_handle)) {
CloseHandle (priv->clock_handle);
priv->clock_handle = INVALID_HANDLE_VALUE;
}
if (priv->master_clock != NULL) {
gst_object_unref (priv->master_clock);
priv->master_clock = NULL;
}
priv->open = FALSE;
priv->closing = FALSE;
}
void
gst_ks_clock_close (GstKsClock * self)
{
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
GST_KS_CLOCK_LOCK ();
gst_ks_clock_close_unlocked (self);
GST_KS_CLOCK_UNLOCK ();
}
HANDLE
gst_ks_clock_get_handle (GstKsClock * self)
{
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
HANDLE handle;
GST_KS_CLOCK_LOCK ();
g_assert (priv->open);
handle = priv->clock_handle;
GST_KS_CLOCK_UNLOCK ();
return handle;
}
void
gst_ks_clock_prepare (GstKsClock * self)
{
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
GST_KS_CLOCK_LOCK ();
if (priv->state < KSSTATE_PAUSE)
gst_ks_clock_set_state_unlocked (self, KSSTATE_PAUSE);
GST_KS_CLOCK_UNLOCK ();
}
static gpointer
gst_ks_clock_worker_thread_func (gpointer data)
{
GstKsClock *self = GST_KS_CLOCK (data);
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
GST_KS_CLOCK_LOCK ();
gst_ks_clock_set_state_unlocked (self, KSSTATE_RUN);
while (priv->worker_running) {
GTimeVal next_wakeup;
if (priv->master_clock != NULL) {
GstClockTime now = gst_clock_get_time (priv->master_clock);
now /= 100;
if (!ks_object_set_property (priv->clock_handle, KSPROPSETID_Clock,
KSPROPERTY_CLOCK_TIME, &now, sizeof (now))) {
GST_WARNING ("Failed to sync clock");
}
}
if (!priv->worker_initialized) {
priv->worker_initialized = TRUE;
g_cond_signal (priv->client_cond);
}
g_get_current_time (&next_wakeup);
next_wakeup.tv_sec += 1;
g_cond_timed_wait (priv->worker_cond, priv->mutex, &next_wakeup);
}
priv->worker_initialized = FALSE;
GST_KS_CLOCK_UNLOCK ();
return NULL;
}
void
gst_ks_clock_start (GstKsClock * self)
{
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
GST_KS_CLOCK_LOCK ();
if (priv->worker_thread == NULL) {
priv->worker_running = TRUE;
priv->worker_initialized = FALSE;
priv->worker_thread =
g_thread_create_full (gst_ks_clock_worker_thread_func, self, 0, TRUE,
TRUE, G_THREAD_PRIORITY_HIGH, NULL);
}
while (!priv->worker_initialized)
g_cond_wait (priv->client_cond, priv->mutex);
GST_KS_CLOCK_UNLOCK ();
}
void
gst_ks_clock_provide_master_clock (GstKsClock * self, GstClock * master_clock)
{
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
GST_KS_CLOCK_LOCK ();
gst_object_ref (master_clock);
if (priv->master_clock != NULL)
gst_object_unref (priv->master_clock);
priv->master_clock = master_clock;
g_cond_signal (priv->worker_cond);
GST_KS_CLOCK_UNLOCK ();
}

69
sys/winks/gstksclock.h Normal file
View file

@ -0,0 +1,69 @@
/*
* Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.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.
*/
#ifndef __GST_KS_CLOCK_H__
#define __GST_KS_CLOCK_H__
#include <gst/gst.h>
#include <windows.h>
#include "ksvideohelpers.h"
G_BEGIN_DECLS
#define GST_TYPE_KS_CLOCK \
(gst_ks_clock_get_type ())
#define GST_KS_CLOCK(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_KS_CLOCK, GstKsClock))
#define GST_KS_CLOCK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_KS_CLOCK, GstKsClockClass))
#define GST_IS_KS_CLOCK(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_KS_CLOCK))
#define GST_IS_KS_CLOCK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_KS_CLOCK))
typedef struct _GstKsClock GstKsClock;
typedef struct _GstKsClockClass GstKsClockClass;
struct _GstKsClock
{
GObject parent;
};
struct _GstKsClockClass
{
GObjectClass parent_class;
};
GType gst_ks_clock_get_type (void);
gboolean gst_ks_clock_open (GstKsClock * self);
void gst_ks_clock_close (GstKsClock * self);
HANDLE gst_ks_clock_get_handle (GstKsClock * self);
void gst_ks_clock_prepare (GstKsClock * self);
void gst_ks_clock_start (GstKsClock * self);
void gst_ks_clock_provide_master_clock (GstKsClock * self,
GstClock * master_clock);
G_END_DECLS
#endif /* __GST_KS_CLOCK_H__ */

1092
sys/winks/gstksvideodevice.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.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.
*/
#ifndef __GST_KS_VIDEO_DEVICE_H__
#define __GST_KS_VIDEO_DEVICE_H__
#include <gst/gst.h>
#include <windows.h>
#include <ks.h>
G_BEGIN_DECLS
#define GST_TYPE_KS_VIDEO_DEVICE \
(gst_ks_video_device_get_type ())
#define GST_KS_VIDEO_DEVICE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_KS_VIDEO_DEVICE, GstKsVideoDevice))
#define GST_KS_VIDEO_DEVICE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_KS_VIDEO_DEVICE, GstKsVideoDeviceClass))
#define GST_IS_KS_VIDEO_DEVICE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_KS_VIDEO_DEVICE))
#define GST_IS_KS_VIDEO_DEVICE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_KS_VIDEO_DEVICE))
typedef struct _GstKsVideoDevice GstKsVideoDevice;
typedef struct _GstKsVideoDeviceClass GstKsVideoDeviceClass;
struct _GstKsVideoDevice
{
GObject parent;
};
struct _GstKsVideoDeviceClass
{
GObjectClass parent_class;
};
GType gst_ks_video_device_get_type (void);
gboolean gst_ks_video_device_open (GstKsVideoDevice * self);
void gst_ks_video_device_close (GstKsVideoDevice * self);
GstCaps * gst_ks_video_device_get_available_caps (GstKsVideoDevice * self);
gboolean gst_ks_video_device_has_caps (GstKsVideoDevice * self);
gboolean gst_ks_video_device_set_caps (GstKsVideoDevice * self, GstCaps * caps);
gboolean gst_ks_video_device_set_state (GstKsVideoDevice * self, KSSTATE state);
guint gst_ks_video_device_get_frame_size (GstKsVideoDevice * self);
GstClockTime gst_ks_video_device_get_duration (GstKsVideoDevice * self);
gboolean gst_ks_video_device_get_latency (GstKsVideoDevice * self, GstClockTime * min_latency, GstClockTime * max_latency);
GstFlowReturn gst_ks_video_device_read_frame (GstKsVideoDevice * self, guint8 * buf, gulong buf_size, gulong * bytes_read, GstClockTime * presentation_time, gulong * error_code, gchar ** error_str);
void gst_ks_video_device_postprocess_frame (GstKsVideoDevice * self, guint8 * buf, guint buf_size);
void gst_ks_video_device_cancel (GstKsVideoDevice * self);
void gst_ks_video_device_cancel_stop (GstKsVideoDevice * self);
G_END_DECLS
#endif /* __GST_KS_VIDEO_DEVICE_H__ */

816
sys/winks/gstksvideosrc.c Normal file
View file

@ -0,0 +1,816 @@
/*
* Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.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.
*/
/**
* SECTION:element-ksvideosrc
*
* Provides low-latency video capture from WDM cameras on Windows.
*
* <refsect2>
* <title>Example pipelines</title>
* |[
* gst-launch -v ksvideosrc do-stats=TRUE ! ffmpegcolorspace ! dshowvideosink
* ]| Capture from a camera and render using dshowvideosink.
* |[
* gst-launch -v ksvideosrc do-stats=TRUE ! image/jpeg, width=640, height=480
* ! jpegdec ! ffmpegcolorspace ! dshowvideosink
* ]| Capture from an MJPEG camera and render using dshowvideosink.
* </refsect2>
*/
#include "gstksvideosrc.h"
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "gstksclock.h"
#include "gstksvideodevice.h"
#include "kshelpers.h"
#include "ksvideohelpers.h"
#define ENABLE_CLOCK_DEBUG 0
#define DEFAULT_DEVICE_PATH NULL
#define DEFAULT_DEVICE_NAME NULL
#define DEFAULT_DEVICE_INDEX -1
#define DEFAULT_ENSLAVE_KSCLOCK FALSE
#define DEFAULT_DO_STATS FALSE
enum
{
PROP_0,
PROP_DEVICE_PATH,
PROP_DEVICE_NAME,
PROP_DEVICE_INDEX,
PROP_ENSLAVE_KSCLOCK,
PROP_DO_STATS,
PROP_FPS,
};
GST_DEBUG_CATEGORY (gst_ks_debug);
#define GST_CAT_DEFAULT gst_ks_debug
typedef struct
{
/* Properties */
gchar *device_path;
gchar *device_name;
gint device_index;
gboolean enslave_ksclock;
gboolean do_stats;
/* State */
GstKsClock *ksclock;
GstKsVideoDevice *device;
guint64 offset;
GstClockTime prev_ts;
/* Statistics */
GstClockTime last_sampling;
guint count;
guint fps;
} GstKsVideoSrcPrivate;
#define GST_KS_VIDEO_SRC_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_KS_VIDEO_SRC, \
GstKsVideoSrcPrivate))
static void gst_ks_video_src_dispose (GObject * object);
static void gst_ks_video_src_finalize (GObject * object);
static void gst_ks_video_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_ks_video_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_ks_video_src_reset (GstKsVideoSrc * self);
static GstStateChangeReturn gst_ks_video_src_change_state (GstElement * element,
GstStateChange transition);
static gboolean gst_ks_video_src_set_clock (GstElement * element,
GstClock * clock);
static GstCaps *gst_ks_video_src_get_caps (GstBaseSrc * basesrc);
static gboolean gst_ks_video_src_set_caps (GstBaseSrc * basesrc,
GstCaps * caps);
static void gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps);
static gboolean gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query);
static gboolean gst_ks_video_src_unlock (GstBaseSrc * basesrc);
static gboolean gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc);
static GstFlowReturn gst_ks_video_src_create (GstPushSrc * pushsrc,
GstBuffer ** buffer);
GST_BOILERPLATE (GstKsVideoSrc, gst_ks_video_src, GstPushSrc,
GST_TYPE_PUSH_SRC);
static void
gst_ks_video_src_base_init (gpointer gclass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
static GstElementDetails element_details = {
"KsVideoSrc",
"Source/Video",
"Stream data from a video capture device through Windows kernel streaming",
"Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>\n"
"Haakon Sporsheim <hakon.sporsheim@tandberg.com>"
};
gst_element_class_set_details (element_class, &element_details);
gst_element_class_add_pad_template (element_class,
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
ks_video_get_all_caps ()));
}
static void
gst_ks_video_src_class_init (GstKsVideoSrcClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
g_type_class_add_private (klass, sizeof (GstKsVideoSrcPrivate));
gobject_class->dispose = gst_ks_video_src_dispose;
gobject_class->finalize = gst_ks_video_src_finalize;
gobject_class->get_property = gst_ks_video_src_get_property;
gobject_class->set_property = gst_ks_video_src_set_property;
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_ks_video_src_change_state);
gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_ks_video_src_set_clock);
gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_ks_video_src_get_caps);
gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_ks_video_src_set_caps);
gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_ks_video_src_fixate);
gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_ks_video_src_query);
gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_ks_video_src_unlock);
gstbasesrc_class->unlock_stop =
GST_DEBUG_FUNCPTR (gst_ks_video_src_unlock_stop);
gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_ks_video_src_create);
g_object_class_install_property (gobject_class, PROP_DEVICE_PATH,
g_param_spec_string ("device-path", "Device Path",
"The device path", DEFAULT_DEVICE_PATH, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
g_param_spec_string ("device-name", "Device Name",
"The human-readable device name", DEFAULT_DEVICE_NAME,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX,
g_param_spec_int ("device-index", "Device Index",
"The zero-based device index", -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_ENSLAVE_KSCLOCK,
g_param_spec_boolean ("enslave-ksclock", "Enslave the clock used by KS",
"Enslave the clocked used by Kernel Streaming",
DEFAULT_ENSLAVE_KSCLOCK, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_DO_STATS,
g_param_spec_boolean ("do-stats", "Enable statistics",
"Enable logging of statistics", DEFAULT_DO_STATS, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_FPS,
g_param_spec_int ("fps", "Frames per second",
"Last measured framerate, if statistics are enabled",
-1, G_MAXINT, -1, G_PARAM_READABLE));
GST_DEBUG_CATEGORY_INIT (gst_ks_debug, "ksvideosrc",
0, "Kernel streaming video source");
}
static void
gst_ks_video_src_init (GstKsVideoSrc * self, GstKsVideoSrcClass * gclass)
{
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
GstBaseSrc *basesrc = GST_BASE_SRC (self);
gst_base_src_set_live (basesrc, TRUE);
gst_base_src_set_format (basesrc, GST_FORMAT_TIME);
gst_ks_video_src_reset (self);
priv->device_path = DEFAULT_DEVICE_PATH;
priv->device_name = DEFAULT_DEVICE_NAME;
priv->device_index = DEFAULT_DEVICE_INDEX;
priv->enslave_ksclock = DEFAULT_ENSLAVE_KSCLOCK;
priv->do_stats = DEFAULT_DO_STATS;
}
static void
gst_ks_video_src_dispose (GObject * object)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_ks_video_src_finalize (GObject * object)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_ks_video_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
switch (prop_id) {
case PROP_DEVICE_PATH:
g_value_set_string (value, priv->device_path);
break;
case PROP_DEVICE_NAME:
g_value_set_string (value, priv->device_name);
break;
case PROP_DEVICE_INDEX:
g_value_set_int (value, priv->device_index);
break;
case PROP_ENSLAVE_KSCLOCK:
g_value_set_boolean (value, priv->enslave_ksclock);
break;
case PROP_DO_STATS:
GST_OBJECT_LOCK (object);
g_value_set_boolean (value, priv->do_stats);
GST_OBJECT_UNLOCK (object);
break;
case PROP_FPS:
GST_OBJECT_LOCK (object);
g_value_set_int (value, priv->fps);
GST_OBJECT_UNLOCK (object);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_ks_video_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (object);
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
switch (prop_id) {
case PROP_DEVICE_PATH:
g_free (priv->device_path);
priv->device_path = g_value_dup_string (value);
break;
case PROP_DEVICE_NAME:
g_free (priv->device_name);
priv->device_name = g_value_dup_string (value);
break;
case PROP_DEVICE_INDEX:
priv->device_index = g_value_get_int (value);
break;
case PROP_ENSLAVE_KSCLOCK:
GST_OBJECT_LOCK (object);
if (priv->device == NULL)
priv->enslave_ksclock = g_value_get_boolean (value);
else
g_warning ("enslave-ksclock may only be changed while in NULL state");
GST_OBJECT_UNLOCK (object);
break;
case PROP_DO_STATS:
GST_OBJECT_LOCK (object);
priv->do_stats = g_value_get_boolean (value);
GST_OBJECT_UNLOCK (object);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_ks_video_src_reset (GstKsVideoSrc * self)
{
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
/* Reset statistics */
priv->last_sampling = GST_CLOCK_TIME_NONE;
priv->count = 0;
priv->fps = -1;
/* Reset timestamping state */
priv->offset = 0;
priv->prev_ts = GST_CLOCK_TIME_NONE;
}
static gboolean
gst_ks_video_src_open_device (GstKsVideoSrc * self)
{
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
GstKsVideoDevice *device = NULL;
GList *devices, *cur;
g_assert (priv->device == NULL);
devices = ks_enumerate_devices (&KSCATEGORY_VIDEO);
if (devices == NULL)
goto error_no_devices;
for (cur = devices; cur != NULL; cur = cur->next) {
KsDeviceEntry *entry = cur->data;
GST_DEBUG_OBJECT (self, "device %d: name='%s' path='%s'",
entry->index, entry->name, entry->path);
}
for (cur = devices; cur != NULL && device == NULL; cur = cur->next) {
KsDeviceEntry *entry = cur->data;
gboolean match;
if (priv->device_path != NULL) {
match = g_strcasecmp (entry->path, priv->device_path) == 0;
} else if (priv->device_name != NULL) {
match = g_strcasecmp (entry->name, priv->device_name) == 0;
} else if (priv->device_index >= 0) {
match = entry->index == priv->device_index;
} else {
match = TRUE; /* pick the first entry */
}
if (match) {
priv->ksclock = NULL;
if (priv->enslave_ksclock) {
priv->ksclock = g_object_new (GST_TYPE_KS_CLOCK, NULL);
if (priv->ksclock != NULL && !gst_ks_clock_open (priv->ksclock)) {
g_object_unref (priv->ksclock);
priv->ksclock = NULL;
}
if (priv->ksclock == NULL)
GST_WARNING_OBJECT (self, "Failed to create/open KsClock");
}
device = g_object_new (GST_TYPE_KS_VIDEO_DEVICE,
"clock", priv->ksclock, "device-path", entry->path, NULL);
}
ks_device_entry_free (entry);
}
g_list_free (devices);
if (device == NULL)
goto error_no_match;
if (!gst_ks_video_device_open (device))
goto error_open;
priv->device = device;
return TRUE;
/* ERRORS */
error_no_devices:
{
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
("No video capture devices found"), (NULL));
return FALSE;
}
error_no_match:
{
if (priv->device_path != NULL) {
GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
("Specified video capture device with path '%s' not found",
priv->device_path), (NULL));
} else if (priv->device_name != NULL) {
GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
("Specified video capture device with name '%s' not found",
priv->device_name), (NULL));
} else {
GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
("Specified video capture device with index %d not found",
priv->device_index), (NULL));
}
return FALSE;
}
error_open:
{
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
("Failed to open device"), (NULL));
g_object_unref (device);
return FALSE;
}
}
static void
gst_ks_video_src_close_device (GstKsVideoSrc * self)
{
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
g_assert (priv->device != NULL);
gst_ks_video_device_close (priv->device);
g_object_unref (priv->device);
priv->device = NULL;
if (priv->ksclock != NULL) {
gst_ks_clock_close (priv->ksclock);
g_object_unref (priv->ksclock);
priv->ksclock = NULL;
}
gst_ks_video_src_reset (self);
}
static GstStateChangeReturn
gst_ks_video_src_change_state (GstElement * element, GstStateChange transition)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
GstStateChangeReturn ret;
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
if (!gst_ks_video_src_open_device (self))
goto open_failed;
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_NULL:
gst_ks_video_src_close_device (self);
break;
}
return ret;
/* ERRORS */
open_failed:
{
return GST_STATE_CHANGE_FAILURE;
}
}
static gboolean
gst_ks_video_src_set_clock (GstElement * element, GstClock * clock)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (element);
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
GST_OBJECT_LOCK (element);
if (priv->ksclock != NULL)
gst_ks_clock_provide_master_clock (priv->ksclock, clock);
GST_OBJECT_UNLOCK (element);
return TRUE;
}
static GstCaps *
gst_ks_video_src_get_caps (GstBaseSrc * basesrc)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
if (priv->device != NULL)
return gst_ks_video_device_get_available_caps (priv->device);
else
return NULL; /* BaseSrc will return template caps */
}
static gboolean
gst_ks_video_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
if (priv->device == NULL)
return FALSE;
if (!gst_ks_video_device_set_caps (priv->device, caps))
return FALSE;
if (!gst_ks_video_device_set_state (priv->device, KSSTATE_PAUSE))
return FALSE;
return TRUE;
}
static void
gst_ks_video_src_fixate (GstBaseSrc * basesrc, GstCaps * caps)
{
GstStructure *structure = gst_caps_get_structure (caps, 0);
gst_structure_fixate_field_nearest_int (structure, "width", G_MAXINT);
gst_structure_fixate_field_nearest_int (structure, "height", G_MAXINT);
gst_structure_fixate_field_nearest_fraction (structure, "framerate",
G_MAXINT, 1);
}
static gboolean
gst_ks_video_src_query (GstBaseSrc * basesrc, GstQuery * query)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
gboolean result = FALSE;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_LATENCY:{
GstClockTime min_latency, max_latency;
if (priv->device == NULL)
goto beach;
result = gst_ks_video_device_get_latency (priv->device, &min_latency,
&max_latency);
if (!result)
goto beach;
GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT
" max %" GST_TIME_FORMAT,
GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
gst_query_set_latency (query, TRUE, min_latency, max_latency);
break;
}
default:
result = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
break;
}
beach:
return result;
}
static gboolean
gst_ks_video_src_unlock (GstBaseSrc * basesrc)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
gst_ks_video_device_cancel (priv->device);
return TRUE;
}
static gboolean
gst_ks_video_src_unlock_stop (GstBaseSrc * basesrc)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (basesrc);
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
GST_DEBUG_OBJECT (self, "%s", G_STRFUNC);
gst_ks_video_device_cancel_stop (priv->device);
return TRUE;
}
static gboolean
gst_ks_video_src_timestamp_buffer (GstKsVideoSrc * self, GstBuffer * buf,
GstClockTime presentation_time)
{
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
GstClockTime duration;
GstClock *clock;
GstClockTime timestamp;
duration = gst_ks_video_device_get_duration (priv->device);
GST_OBJECT_LOCK (self);
clock = GST_ELEMENT_CLOCK (self);
if (clock != NULL) {
gst_object_ref (clock);
timestamp = GST_ELEMENT (self)->base_time;
if (GST_CLOCK_TIME_IS_VALID (presentation_time)) {
if (presentation_time > GST_ELEMENT (self)->base_time)
presentation_time -= GST_ELEMENT (self)->base_time;
else
presentation_time = 0;
}
} else {
timestamp = GST_CLOCK_TIME_NONE;
}
GST_OBJECT_UNLOCK (self);
if (clock != NULL) {
/* The time according to the current clock */
timestamp = gst_clock_get_time (clock) - timestamp;
if (timestamp > duration)
timestamp -= duration;
else
timestamp = 0;
if (GST_CLOCK_TIME_IS_VALID (presentation_time)) {
GstClockTimeDiff diff = GST_CLOCK_DIFF (timestamp, presentation_time);
GST_DEBUG_OBJECT (self, "Diff between our and the driver's timestamp: %"
G_GINT64_FORMAT, diff);
}
gst_object_unref (clock);
clock = NULL;
/* Unless it's the first frame, align the current timestamp on a multiple
* of duration since the previous */
if (GST_CLOCK_TIME_IS_VALID (priv->prev_ts)) {
GstClockTime delta;
guint delta_remainder, delta_offset;
/* REVISIT: I've seen this happen with the GstSystemClock on Windows,
* scary... */
if (timestamp < priv->prev_ts) {
GST_WARNING_OBJECT (self, "clock is ticking backwards");
return FALSE;
}
/* Round to a duration boundary */
delta = timestamp - priv->prev_ts;
delta_remainder = delta % duration;
if (delta_remainder < duration / 3)
timestamp -= delta_remainder;
else
timestamp += duration - delta_remainder;
/* How many frames are we off then? */
delta = timestamp - priv->prev_ts;
delta_offset = delta / duration;
if (delta_offset == 1) /* perfect */
GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
else if (delta_offset > 1) {
guint lost = delta_offset - 1;
#if ENABLE_CLOCK_DEBUG
GST_INFO_OBJECT (self, "lost %d frame%s, setting discont flag",
lost, (lost > 1) ? "s" : "");
#endif
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
} else if (delta_offset == 0) { /* overproduction, skip this frame */
#if ENABLE_CLOCK_DEBUG
GST_INFO_OBJECT (self, "skipping frame");
#endif
return FALSE;
}
priv->offset += delta_offset;
}
priv->prev_ts = timestamp;
}
GST_BUFFER_OFFSET (buf) = priv->offset;
GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET (buf) + 1;
GST_BUFFER_TIMESTAMP (buf) = timestamp;
GST_BUFFER_DURATION (buf) = duration;
return TRUE;
}
static void
gst_ks_video_src_update_statistics (GstKsVideoSrc * self)
{
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
GstClock *clock;
GST_OBJECT_LOCK (self);
clock = GST_ELEMENT_CLOCK (self);
if (clock != NULL)
gst_object_ref (clock);
GST_OBJECT_UNLOCK (self);
if (clock != NULL) {
GstClockTime now = gst_clock_get_time (clock);
gst_object_unref (clock);
priv->count++;
if (GST_CLOCK_TIME_IS_VALID (priv->last_sampling)) {
if (now - priv->last_sampling >= GST_SECOND) {
GST_OBJECT_LOCK (self);
priv->fps = priv->count;
GST_OBJECT_UNLOCK (self);
g_object_notify (G_OBJECT (self), "fps");
priv->last_sampling = now;
priv->count = 0;
}
} else {
priv->last_sampling = now;
}
}
}
static GstFlowReturn
gst_ks_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer)
{
GstKsVideoSrc *self = GST_KS_VIDEO_SRC (pushsrc);
GstKsVideoSrcPrivate *priv = GST_KS_VIDEO_SRC_GET_PRIVATE (self);
guint buf_size;
GstCaps *caps;
GstBuffer *buf = NULL;
GstFlowReturn result;
GstClockTime presentation_time;
gulong error_code;
gchar *error_str;
g_assert (priv->device != NULL);
if (!gst_ks_video_device_has_caps (priv->device))
goto error_no_caps;
buf_size = gst_ks_video_device_get_frame_size (priv->device);
g_assert (buf_size);
caps = gst_pad_get_negotiated_caps (GST_BASE_SRC_PAD (self));
if (caps == NULL)
goto error_no_caps;
result = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (self), priv->offset,
buf_size, caps, &buf);
gst_caps_unref (caps);
if (G_UNLIKELY (result != GST_FLOW_OK))
goto error_alloc_buffer;
do {
gulong bytes_read;
result = gst_ks_video_device_read_frame (priv->device,
GST_BUFFER_DATA (buf), buf_size, &bytes_read, &presentation_time,
&error_code, &error_str);
if (G_UNLIKELY (result != GST_FLOW_OK))
goto error_read_frame;
GST_BUFFER_SIZE (buf) = bytes_read;
}
while (!gst_ks_video_src_timestamp_buffer (self, buf, presentation_time));
if (G_UNLIKELY (priv->do_stats))
gst_ks_video_src_update_statistics (self);
gst_ks_video_device_postprocess_frame (priv->device,
GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
*buffer = buf;
return GST_FLOW_OK;
/* ERRORS */
error_no_caps:
{
GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
("not negotiated"), ("maybe setcaps failed?"));
return GST_FLOW_ERROR;
}
error_alloc_buffer:
{
GST_ELEMENT_ERROR (self, CORE, PAD, ("alloc_buffer failed"), (NULL));
return result;
}
error_read_frame:
{
if (result != GST_FLOW_WRONG_STATE && result != GST_FLOW_UNEXPECTED) {
GST_ELEMENT_ERROR (self, RESOURCE, READ,
("read failed: %s [0x%08x]", error_str, error_code),
("gst_ks_video_device_read_frame failed"));
}
g_free (error_str);
gst_buffer_unref (buf);
return result;
}
}
static gboolean
plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "ksvideosrc",
GST_RANK_NONE, GST_TYPE_KS_VIDEO_SRC);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"winks",
"Windows kernel streaming plugin",
plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")

55
sys/winks/gstksvideosrc.h Normal file
View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.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.
*/
#ifndef __GST_KS_VIDEO_SRC_H__
#define __GST_KS_VIDEO_SRC_H__
#include <gst/base/gstpushsrc.h>
G_BEGIN_DECLS
#define GST_TYPE_KS_VIDEO_SRC \
(gst_ks_video_src_get_type ())
#define GST_KS_VIDEO_SRC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_KS_VIDEO_SRC, GstKsVideoSrc))
#define GST_KS_VIDEO_SRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_KS_VIDEO_SRC, GstKsVideoSrcClass))
#define GST_IS_KS_VIDEO_SRC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_KS_VIDEO_SRC))
#define GST_IS_KS_VIDEO_SRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_KS_VIDEO_SRC))
typedef struct _GstKsVideoSrc GstKsVideoSrc;
typedef struct _GstKsVideoSrcClass GstKsVideoSrcClass;
struct _GstKsVideoSrc
{
GstPushSrc push_src;
};
struct _GstKsVideoSrcClass
{
GstPushSrcClass parent_class;
};
GType gst_ks_video_src_get_type (void);
G_END_DECLS
#endif /* __GST_KS_VIDEO_SRC_H__ */

471
sys/winks/kshelpers.c Normal file
View file

@ -0,0 +1,471 @@
/*
* Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.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.
*/
#include "kshelpers.h"
#include <ksmedia.h>
#include <setupapi.h>
GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
#define GST_CAT_DEFAULT gst_ks_debug
gboolean
ks_is_valid_handle (HANDLE h)
{
return (h != INVALID_HANDLE_VALUE && h != NULL);
}
GList *
ks_enumerate_devices (const GUID * category)
{
GList *result = NULL;
HDEVINFO devinfo;
gint i;
devinfo = SetupDiGetClassDevsW (category, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (!ks_is_valid_handle (devinfo))
return NULL; /* no devices */
for (i = 0;; i++) {
BOOL success;
SP_DEVICE_INTERFACE_DATA if_data = { 0, };
SP_DEVICE_INTERFACE_DETAIL_DATA_W *if_detail_data;
DWORD if_detail_data_size;
SP_DEVINFO_DATA devinfo_data = { 0, };
DWORD req_size;
if_data.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
success = SetupDiEnumDeviceInterfaces (devinfo, NULL, category, i,
&if_data);
if (!success) /* all devices enumerated? */
break;
if_detail_data_size = (MAX_PATH - 1) * sizeof (gunichar2);
if_detail_data = g_malloc0 (if_detail_data_size);
if_detail_data->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA_W);
devinfo_data.cbSize = sizeof (SP_DEVINFO_DATA);
success = SetupDiGetDeviceInterfaceDetailW (devinfo, &if_data,
if_detail_data, if_detail_data_size, &req_size, &devinfo_data);
if (success) {
KsDeviceEntry *entry;
WCHAR buf[512];
entry = g_new0 (KsDeviceEntry, 1);
entry->index = i;
entry->path =
g_utf16_to_utf8 (if_detail_data->DevicePath, -1, NULL, NULL, NULL);
if (SetupDiGetDeviceRegistryPropertyW (devinfo, &devinfo_data,
SPDRP_FRIENDLYNAME, NULL, (BYTE *) buf, sizeof (buf), NULL)) {
entry->name = g_utf16_to_utf8 (buf, -1, NULL, NULL, NULL);
}
if (entry->name == NULL) {
if (SetupDiGetDeviceRegistryPropertyW (devinfo, &devinfo_data,
SPDRP_DEVICEDESC, NULL, (BYTE *) buf, sizeof (buf), NULL)) {
entry->name = g_utf16_to_utf8 (buf, -1, NULL, NULL, NULL);
}
}
if (entry->name != NULL)
result = g_list_prepend (result, entry);
else
ks_device_entry_free (entry);
}
g_free (if_detail_data);
}
SetupDiDestroyDeviceInfoList (devinfo);
return g_list_reverse (result);
}
void
ks_device_entry_free (KsDeviceEntry * entry)
{
if (entry == NULL)
return;
g_free (entry->path);
g_free (entry->name);
g_free (entry);
}
void
ks_device_list_free (GList * devices)
{
GList *cur;
for (cur = devices; cur != NULL; cur = cur->next)
ks_device_entry_free (cur->data);
g_list_free (devices);
}
static gboolean
ks_sync_device_io_control (HANDLE device, gulong io_control_code,
gpointer in_buffer, gulong in_buffer_size, gpointer out_buffer,
gulong out_buffer_size, gulong * bytes_returned)
{
OVERLAPPED overlapped = { 0, };
BOOL success;
overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
success = DeviceIoControl (device, io_control_code, in_buffer,
in_buffer_size, out_buffer, out_buffer_size, bytes_returned, &overlapped);
if (!success && GetLastError () == ERROR_IO_PENDING)
success = GetOverlappedResult (device, &overlapped, bytes_returned, TRUE);
CloseHandle (overlapped.hEvent);
return success ? TRUE : FALSE;
}
gboolean
ks_filter_get_pin_property (HANDLE filter_handle, gulong pin_id,
GUID prop_set, gulong prop_id, gpointer value, gulong value_size)
{
KSP_PIN prop = { 0, };
DWORD bytes_returned = 0;
prop.PinId = pin_id;
prop.Property.Set = prop_set;
prop.Property.Id = prop_id;
prop.Property.Flags = KSPROPERTY_TYPE_GET;
return ks_sync_device_io_control (filter_handle, IOCTL_KS_PROPERTY, &prop,
sizeof (prop), value, value_size, &bytes_returned);
}
gboolean
ks_filter_get_pin_property_multi (HANDLE filter_handle, gulong pin_id,
GUID prop_set, gulong prop_id, KSMULTIPLE_ITEM ** items)
{
KSP_PIN prop = { 0, };
DWORD items_size = 0, bytes_written = 0;
gboolean ret;
*items = NULL;
prop.PinId = pin_id;
prop.Property.Set = prop_set;
prop.Property.Id = prop_id;
prop.Property.Flags = KSPROPERTY_TYPE_GET;
ret = ks_sync_device_io_control (filter_handle, IOCTL_KS_PROPERTY,
&prop.Property, sizeof (prop), NULL, 0, &items_size);
if (!ret) {
DWORD err = GetLastError ();
if (err != ERROR_INSUFFICIENT_BUFFER && err != ERROR_MORE_DATA)
goto error;
}
*items = g_malloc0 (items_size);
ret = ks_sync_device_io_control (filter_handle, IOCTL_KS_PROPERTY, &prop,
sizeof (prop), *items, items_size, &bytes_written);
if (!ret)
goto error;
return ret;
error:
g_free (*items);
*items = NULL;
return FALSE;
}
gboolean
ks_object_query_property (HANDLE handle, GUID prop_set, gulong prop_id,
gulong prop_flags, gpointer * value, gulong * value_size)
{
KSPROPERTY prop = { 0, };
DWORD req_value_size = 0, bytes_written = 0;
gboolean ret;
*value = NULL;
prop.Set = prop_set;
prop.Id = prop_id;
prop.Flags = prop_flags;
if (value_size == NULL || *value_size == 0) {
ret = ks_sync_device_io_control (handle, IOCTL_KS_PROPERTY,
&prop, sizeof (prop), NULL, 0, &req_value_size);
if (!ret) {
DWORD err = GetLastError ();
if (err != ERROR_INSUFFICIENT_BUFFER && err != ERROR_MORE_DATA)
goto error;
}
} else {
req_value_size = *value_size;
}
*value = g_malloc0 (req_value_size);
ret = ks_sync_device_io_control (handle, IOCTL_KS_PROPERTY, &prop,
sizeof (prop), *value, req_value_size, &bytes_written);
if (!ret)
goto error;
if (value_size != NULL)
*value_size = bytes_written;
return ret;
error:
g_free (*value);
*value = NULL;
if (value_size != NULL)
*value_size = 0;
return FALSE;
}
gboolean
ks_object_get_property (HANDLE handle, GUID prop_set, gulong prop_id,
gpointer * value, gulong * value_size)
{
return ks_object_query_property (handle, prop_set, prop_id,
KSPROPERTY_TYPE_GET, value, value_size);
}
gboolean
ks_object_set_property (HANDLE handle, GUID prop_set, gulong prop_id,
gpointer value, gulong value_size)
{
KSPROPERTY prop = { 0, };
DWORD bytes_returned;
prop.Set = prop_set;
prop.Id = prop_id;
prop.Flags = KSPROPERTY_TYPE_SET;
return ks_sync_device_io_control (handle, IOCTL_KS_PROPERTY, &prop,
sizeof (prop), value, value_size, &bytes_returned);
}
gboolean
ks_object_get_supported_property_sets (HANDLE handle, GUID ** propsets,
gulong * len)
{
gulong size = 0;
*propsets = NULL;
*len = 0;
if (ks_object_query_property (handle, GUID_NULL, 0,
KSPROPERTY_TYPE_SETSUPPORT, propsets, &size)) {
if (size % sizeof (GUID) == 0) {
*len = size / sizeof (GUID);
return TRUE;
}
}
g_free (*propsets);
*propsets = NULL;
*len = 0;
return FALSE;
}
gboolean
ks_object_set_connection_state (HANDLE handle, KSSTATE state)
{
return ks_object_set_property (handle, KSPROPSETID_Connection,
KSPROPERTY_CONNECTION_STATE, &state, sizeof (state));
}
const gchar *
ks_state_to_string (KSSTATE state)
{
switch (state) {
case KSSTATE_STOP:
return "KSSTATE_STOP";
case KSSTATE_ACQUIRE:
return "KSSTATE_ACQUIRE";
case KSSTATE_PAUSE:
return "KSSTATE_PAUSE";
case KSSTATE_RUN:
return "KSSTATE_RUN";
default:
g_assert_not_reached ();
}
return "UNKNOWN";
}
#define CHECK_OPTIONS_FLAG(flag) \
if (flags & KSSTREAM_HEADER_OPTIONSF_##flag)\
{\
if (str->len > 0)\
g_string_append (str, " | ");\
g_string_append (str, G_STRINGIFY (flag));\
flags &= ~KSSTREAM_HEADER_OPTIONSF_##flag;\
}
gchar *
ks_options_flags_to_string (gulong flags)
{
gchar *ret;
GString *str;
str = g_string_sized_new (128);
CHECK_OPTIONS_FLAG (DATADISCONTINUITY);
CHECK_OPTIONS_FLAG (DURATIONVALID);
CHECK_OPTIONS_FLAG (ENDOFSTREAM);
CHECK_OPTIONS_FLAG (FLUSHONPAUSE);
CHECK_OPTIONS_FLAG (LOOPEDDATA);
CHECK_OPTIONS_FLAG (PREROLL);
CHECK_OPTIONS_FLAG (SPLICEPOINT);
CHECK_OPTIONS_FLAG (TIMEDISCONTINUITY);
CHECK_OPTIONS_FLAG (TIMEVALID);
CHECK_OPTIONS_FLAG (TYPECHANGED);
CHECK_OPTIONS_FLAG (VRAM_DATA_TRANSFER);
CHECK_OPTIONS_FLAG (BUFFEREDTRANSFER);
if (flags != 0)
g_string_append_printf (str, " | 0x%08x", flags);
ret = str->str;
g_string_free (str, FALSE);
return ret;
}
typedef struct
{
const GUID guid;
const gchar *name;
} KsPropertySetMapping;
#ifndef STATIC_KSPROPSETID_GM
#define STATIC_KSPROPSETID_GM \
0xAF627536, 0xE719, 0x11D2, 0x8A, 0x1D, 0x00, 0x60, 0x97, 0xD2, 0xDF, 0x5D
#endif
#ifndef STATIC_KSPROPSETID_Jack
#define STATIC_KSPROPSETID_Jack \
0x4509F757, 0x2D46, 0x4637, 0x8E, 0x62, 0xCE, 0x7D, 0xB9, 0x44, 0xF5, 0x7B
#endif
#ifndef STATIC_PROPSETID_VIDCAP_SELECTOR
#define STATIC_PROPSETID_VIDCAP_SELECTOR \
0x1ABDAECA, 0x68B6, 0x4F83, 0x93, 0x71, 0xB4, 0x13, 0x90, 0x7C, 0x7B, 0x9F
#endif
#ifndef STATIC_PROPSETID_EXT_DEVICE
#define STATIC_PROPSETID_EXT_DEVICE \
0xB5730A90, 0x1A2C, 0x11cf, 0x8c, 0x23, 0x00, 0xAA, 0x00, 0x6B, 0x68, 0x14
#endif
#ifndef STATIC_PROPSETID_EXT_TRANSPORT
#define STATIC_PROPSETID_EXT_TRANSPORT \
0xA03CD5F0, 0x3045, 0x11cf, 0x8c, 0x44, 0x00, 0xAA, 0x00, 0x6B, 0x68, 0x14
#endif
#ifndef STATIC_PROPSETID_TIMECODE_READER
#define STATIC_PROPSETID_TIMECODE_READER \
0x9B496CE1, 0x811B, 0x11cf, 0x8C, 0x77, 0x00, 0xAA, 0x00, 0x6B, 0x68, 0x14
#endif
static const KsPropertySetMapping known_property_sets[] = {
{{STATIC_KSPROPSETID_General}, "General"},
{{STATIC_KSPROPSETID_MediaSeeking}, "MediaSeeking"},
{{STATIC_KSPROPSETID_Topology}, "Topology"},
{{STATIC_KSPROPSETID_GM}, "GM"},
{{STATIC_KSPROPSETID_Pin}, "Pin"},
{{STATIC_KSPROPSETID_Quality}, "Quality"},
{{STATIC_KSPROPSETID_Connection}, "Connection"},
{{STATIC_KSPROPSETID_MemoryTransport}, "MemoryTransport"},
{{STATIC_KSPROPSETID_StreamAllocator}, "StreamAllocator"},
{{STATIC_KSPROPSETID_StreamInterface}, "StreamInterface"},
{{STATIC_KSPROPSETID_Stream}, "Stream"},
{{STATIC_KSPROPSETID_Clock}, "Clock"},
{{STATIC_KSPROPSETID_DirectSound3DListener}, "DirectSound3DListener"},
{{STATIC_KSPROPSETID_DirectSound3DBuffer}, "DirectSound3DBuffer"},
{{STATIC_KSPROPSETID_Hrtf3d}, "Hrtf3d"},
{{STATIC_KSPROPSETID_Itd3d}, "Itd3d"},
{{STATIC_KSPROPSETID_Bibliographic}, "Bibliographic"},
{{STATIC_KSPROPSETID_TopologyNode}, "TopologyNode"},
{{STATIC_KSPROPSETID_RtAudio}, "RtAudio"},
{{STATIC_KSPROPSETID_DrmAudioStream}, "DrmAudioStream"},
{{STATIC_KSPROPSETID_Audio}, "Audio"},
{{STATIC_KSPROPSETID_Acoustic_Echo_Cancel}, "Acoustic_Echo_Cancel"},
{{STATIC_KSPROPSETID_Wave_Queued}, "Wave_Queued"},
{{STATIC_KSPROPSETID_Wave}, "Wave"},
{{STATIC_KSPROPSETID_WaveTable}, "WaveTable"},
{{STATIC_KSPROPSETID_Cyclic}, "Cyclic"},
{{STATIC_KSPROPSETID_Sysaudio}, "Sysaudio"},
{{STATIC_KSPROPSETID_Sysaudio_Pin}, "Sysaudio_Pin"},
{{STATIC_KSPROPSETID_AudioGfx}, "AudioGfx"},
{{STATIC_KSPROPSETID_Linear}, "Linear"},
{{STATIC_KSPROPSETID_Mpeg2Vid}, "Mpeg2Vid"},
{{STATIC_KSPROPSETID_AC3}, "AC3"},
{{STATIC_KSPROPSETID_AudioDecoderOut}, "AudioDecoderOut"},
{{STATIC_KSPROPSETID_DvdSubPic}, "DvdSubPic"},
{{STATIC_KSPROPSETID_CopyProt}, "CopyProt"},
{{STATIC_KSPROPSETID_VBICAP_PROPERTIES}, "VBICAP_PROPERTIES"},
{{STATIC_KSPROPSETID_VBICodecFiltering}, "VBICodecFiltering"},
{{STATIC_KSPROPSETID_VramCapture}, "VramCapture"},
{{STATIC_KSPROPSETID_OverlayUpdate}, "OverlayUpdate"},
{{STATIC_KSPROPSETID_VPConfig}, "VPConfig"},
{{STATIC_KSPROPSETID_VPVBIConfig}, "VPVBIConfig"},
{{STATIC_KSPROPSETID_TSRateChange}, "TSRateChange"},
{{STATIC_KSPROPSETID_Jack}, "Jack"},
{{STATIC_PROPSETID_ALLOCATOR_CONTROL}, "ALLOCATOR_CONTROL"},
{{STATIC_PROPSETID_VIDCAP_VIDEOPROCAMP}, "VIDCAP_VIDEOPROCAMP"},
{{STATIC_PROPSETID_VIDCAP_SELECTOR}, "VIDCAP_SELECTOR"},
{{STATIC_PROPSETID_TUNER}, "TUNER"},
{{STATIC_PROPSETID_VIDCAP_VIDEOENCODER}, "VIDCAP_VIDEOENCODER"},
{{STATIC_PROPSETID_VIDCAP_VIDEODECODER}, "VIDCAP_VIDEODECODER"},
{{STATIC_PROPSETID_VIDCAP_CAMERACONTROL}, "VIDCAP_CAMERACONTROL"},
{{STATIC_PROPSETID_EXT_DEVICE}, "EXT_DEVICE"},
{{STATIC_PROPSETID_EXT_TRANSPORT}, "EXT_TRANSPORT"},
{{STATIC_PROPSETID_TIMECODE_READER}, "TIMECODE_READER"},
{{STATIC_PROPSETID_VIDCAP_CROSSBAR}, "VIDCAP_CROSSBAR"},
{{STATIC_PROPSETID_VIDCAP_TVAUDIO}, "VIDCAP_TVAUDIO"},
{{STATIC_PROPSETID_VIDCAP_VIDEOCOMPRESSION}, "VIDCAP_VIDEOCOMPRESSION"},
{{STATIC_PROPSETID_VIDCAP_VIDEOCONTROL}, "VIDCAP_VIDEOCONTROL"},
{{STATIC_PROPSETID_VIDCAP_DROPPEDFRAMES}, "VIDCAP_DROPPEDFRAMES"},
};
gchar *
ks_property_set_to_string (const GUID * guid)
{
guint i;
for (i = 0;
i < sizeof (known_property_sets) / sizeof (known_property_sets[0]); i++) {
if (IsEqualGUID (guid, &known_property_sets[i].guid))
return g_strdup_printf ("KSPROPSETID_%s", known_property_sets[i].name);
}
return g_strdup_printf ("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1],
guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5],
guid->Data4[6], guid->Data4[7]);
}

61
sys/winks/kshelpers.h Normal file
View file

@ -0,0 +1,61 @@
/*
* Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.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.
*/
#ifndef __KSHELPERS_H__
#define __KSHELPERS_H__
#include <glib.h>
#include <windows.h>
#include <ks.h>
G_BEGIN_DECLS
typedef struct _KsDeviceEntry KsDeviceEntry;
struct _KsDeviceEntry
{
guint index;
gchar * name;
gchar * path;
};
gboolean ks_is_valid_handle (HANDLE h);
GList * ks_enumerate_devices (const GUID * category);
void ks_device_entry_free (KsDeviceEntry * entry);
void ks_device_list_free (GList * devices);
gboolean ks_filter_get_pin_property (HANDLE filter_handle, gulong pin_id, GUID prop_set, gulong prop_id, gpointer value, gulong value_size);
gboolean ks_filter_get_pin_property_multi (HANDLE filter_handle, gulong pin_id, GUID prop_set, gulong prop_id, KSMULTIPLE_ITEM ** items);
gboolean ks_object_query_property (HANDLE handle, GUID prop_set, gulong prop_id, gulong prop_flags, gpointer * value, gulong * value_size);
gboolean ks_object_get_property (HANDLE handle, GUID prop_set, gulong prop_id, gpointer * value, gulong * value_size);
gboolean ks_object_set_property (HANDLE handle, GUID prop_set, gulong prop_id, gpointer value, gulong value_size);
gboolean ks_object_get_supported_property_sets (HANDLE handle, GUID ** propsets, gulong * len);
gboolean ks_object_set_connection_state (HANDLE handle, KSSTATE state);
const gchar * ks_state_to_string (KSSTATE state);
gchar * ks_options_flags_to_string (gulong flags);
gchar * ks_property_set_to_string (const GUID * guid);
G_END_DECLS
#endif /* __KSHELPERS_H__ */

585
sys/winks/ksvideohelpers.c Normal file
View file

@ -0,0 +1,585 @@
/*
* Copyright (C) 2007 Haakon Sporsheim <hakon.sporsheim@tandberg.com>
* 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.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.
*/
#include "ksvideohelpers.h"
#include <uuids.h>
#include "kshelpers.h"
GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
#define GST_CAT_DEFAULT gst_ks_debug
static const GUID MEDIASUBTYPE_FOURCC =
{ 0x0 /* FourCC here */ , 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xAA, 0x00,
0x38, 0x9B, 0x71} };
extern const GUID MEDIASUBTYPE_I420 =
{ 0x30323449, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B,
0x71} };
static GstStructure *
ks_video_format_to_structure (GUID subtype_guid, GUID format_guid)
{
GstStructure *structure = NULL;
if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_MJPG) || IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_TVMJ) || /* FIXME: NOT tested */
IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_WAKE) || /* FIXME: NOT tested */
IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_CFCC) || /* FIXME: NOT tested */
IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_IJPG)) { /* FIXME: NOT tested */
structure = gst_structure_new ("image/jpeg", NULL);
} else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB555) || /* FIXME: NOT tested */
IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB565) || /* FIXME: NOT tested */
IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB24) || IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB32) || /* FIXME: NOT tested */
IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB1555) || /* FIXME: NOT tested */
IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB32) || /* FIXME: NOT tested */
IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB4444)) { /* FIXME: NOT tested */
guint depth = 0, bpp = 0;
gint endianness = 0;
guint32 r_mask = 0, b_mask = 0, g_mask = 0;
if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB555)) {
bpp = 16;
depth = 15;
endianness = G_BIG_ENDIAN;
r_mask = 0x7c00;
g_mask = 0x03e0;
b_mask = 0x001f;
} else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB565)) {
bpp = depth = 16;
endianness = G_BIG_ENDIAN;
r_mask = 0xf800;
g_mask = 0x07e0;
b_mask = 0x001f;
} else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB24)) {
bpp = depth = 24;
endianness = G_BIG_ENDIAN;
r_mask = 0x0000ff;
g_mask = 0x00ff00;
b_mask = 0xff0000;
} else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB32)) {
bpp = 32;
depth = 24;
endianness = G_BIG_ENDIAN;
r_mask = 0x000000ff;
g_mask = 0x0000ff00;
b_mask = 0x00ff0000;
/* FIXME: check
*r_mask = 0xff000000;
*g_mask = 0x00ff0000;
*b_mask = 0x0000ff00;
*/
} else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB1555)) {
bpp = 16;
depth = 15;
endianness = G_BIG_ENDIAN;
r_mask = 0x7c00;
g_mask = 0x03e0;
b_mask = 0x001f;
} else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB32)) {
bpp = depth = 32;
endianness = G_BIG_ENDIAN;
r_mask = 0x000000ff;
g_mask = 0x0000ff00;
b_mask = 0x00ff0000;
/* FIXME: check
*r_mask = 0xff000000;
*g_mask = 0x00ff0000;
*b_mask = 0x0000ff00;
*/
} else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB4444)) {
bpp = 16;
depth = 12;
endianness = G_BIG_ENDIAN;
r_mask = 0x0f00;
g_mask = 0x00f0;
b_mask = 0x000f;
//r_mask = 0x000f;
//g_mask = 0x00f0;
//b_mask = 0x0f00;
} else {
g_assert_not_reached ();
}
structure = gst_structure_new ("video/x-raw-rgb",
"bpp", G_TYPE_INT, bpp,
"depth", G_TYPE_INT, depth,
"red_mask", G_TYPE_INT, r_mask,
"green_mask", G_TYPE_INT, g_mask,
"blue_mask", G_TYPE_INT, b_mask,
"endianness", G_TYPE_INT, endianness, NULL);
} else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_dvsd)) {
if (IsEqualGUID (&format_guid, &FORMAT_DvInfo)) {
structure = gst_structure_new ("video/x-dv",
"systemstream", G_TYPE_BOOLEAN, TRUE, NULL);
} else if (IsEqualGUID (&format_guid, &FORMAT_VideoInfo)) {
structure = gst_structure_new ("video/x-dv",
"systemstream", G_TYPE_BOOLEAN, FALSE,
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('d', 'v', 's', 'd'),
NULL);
}
} else if (memcmp (&subtype_guid.Data2, &MEDIASUBTYPE_FOURCC.Data2,
sizeof (subtype_guid) - sizeof (subtype_guid.Data1)) == 0) {
guint8 *p = (guint8 *) & subtype_guid.Data1;
structure = gst_structure_new ("video/x-raw-yuv",
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC (p[0], p[1], p[2], p[3]),
NULL);
}
if (!structure) {
GST_DEBUG ("Unknown DirectShow Video GUID %08x-%04x-%04x-%04x-%08x%04x",
subtype_guid.Data1, subtype_guid.Data2, subtype_guid.Data3,
*(WORD *) subtype_guid.Data4, *(DWORD *) & subtype_guid.Data4[2],
*(WORD *) & subtype_guid.Data4[6]);
}
return structure;
}
static gboolean
ks_video_append_video_stream_cfg_fields (GstStructure * structure,
const KS_VIDEO_STREAM_CONFIG_CAPS * vscc)
{
g_return_val_if_fail (structure, FALSE);
g_return_val_if_fail (vscc, FALSE);
/* width */
if (vscc->MinOutputSize.cx == vscc->MaxOutputSize.cx) {
gst_structure_set (structure,
"width", G_TYPE_INT, vscc->MaxOutputSize.cx, NULL);
} else {
gst_structure_set (structure,
"width", GST_TYPE_INT_RANGE,
vscc->MinOutputSize.cx, vscc->MaxOutputSize.cx, NULL);
}
/* height */
if (vscc->MinOutputSize.cy == vscc->MaxOutputSize.cy) {
gst_structure_set (structure,
"height", G_TYPE_INT, vscc->MaxOutputSize.cy, NULL);
} else {
gst_structure_set (structure,
"height", GST_TYPE_INT_RANGE,
vscc->MinOutputSize.cy, vscc->MaxOutputSize.cy, NULL);
}
/* framerate */
if (vscc->MinFrameInterval == vscc->MaxFrameInterval) {
gst_structure_set (structure,
"framerate", GST_TYPE_FRACTION,
(gint) (10000000 / vscc->MaxFrameInterval), 1, NULL);
} else {
gst_structure_set (structure,
"framerate", GST_TYPE_FRACTION_RANGE,
(gint) (10000000 / vscc->MaxFrameInterval), 1,
(gint) (10000000 / vscc->MinFrameInterval), 1, NULL);
}
return TRUE;
}
KsVideoMediaType *
ks_video_media_type_dup (KsVideoMediaType * media_type)
{
KsVideoMediaType *result = g_new (KsVideoMediaType, 1);
memcpy (result, media_type, sizeof (KsVideoMediaType));
result->range = g_malloc (media_type->range->FormatSize);
memcpy ((gpointer) result->range, media_type->range,
media_type->range->FormatSize);
result->format = g_malloc (media_type->format_size);
memcpy (result->format, media_type->format, media_type->format_size);
result->translated_caps = gst_caps_ref (media_type->translated_caps);
return result;
}
void
ks_video_media_type_free (KsVideoMediaType * media_type)
{
if (media_type == NULL)
return;
g_free (media_type->format);
if (media_type->translated_caps != NULL)
gst_caps_unref (media_type->translated_caps);
g_free (media_type);
}
static GList *
ks_video_media_type_list_remove_duplicates (GList * media_types)
{
GList *master, *duplicates;
do {
GList *entry;
master = duplicates = NULL;
/* Find the first set of duplicates and their master */
for (entry = media_types; entry != NULL && duplicates == NULL;
entry = entry->next) {
KsVideoMediaType *mt = entry->data;
GList *other_entry;
for (other_entry = media_types; other_entry != NULL;
other_entry = other_entry->next) {
KsVideoMediaType *other_mt = other_entry->data;
if (other_mt == mt)
continue;
if (gst_caps_is_equal (mt->translated_caps, other_mt->translated_caps))
duplicates = g_list_prepend (duplicates, other_mt);
}
if (duplicates != NULL)
master = entry;
}
if (duplicates != NULL) {
KsVideoMediaType *selected_mt = master->data;
/*
* Pick a FORMAT_VideoInfo2 if present, if not we just stay with the
* first entry
*/
for (entry = duplicates; entry != NULL; entry = entry->next) {
KsVideoMediaType *mt = entry->data;
if (IsEqualGUID (&mt->range->Specifier, &FORMAT_VideoInfo2)) {
ks_video_media_type_free (selected_mt);
selected_mt = mt;
} else {
ks_video_media_type_free (mt);
}
/* Remove the dupe from the main list */
media_types = g_list_remove (media_types, mt);
}
/* Update master node with the selected MediaType */
master->data = selected_mt;
g_list_free (duplicates);
}
}
while (master != NULL);
return media_types;
}
GList *
ks_video_probe_filter_for_caps (HANDLE filter_handle)
{
GList *ret = NULL;
gulong pin_count;
guint pin_id;
if (!ks_filter_get_pin_property (filter_handle, 0, KSPROPSETID_Pin,
KSPROPERTY_PIN_CTYPES, &pin_count, sizeof (pin_count)))
goto beach;
GST_DEBUG ("pin_count = %d", pin_count);
for (pin_id = 0; pin_id < pin_count; pin_id++) {
KSPIN_COMMUNICATION pin_comm;
KSPIN_DATAFLOW pin_flow;
GUID pin_cat;
if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin,
KSPROPERTY_PIN_COMMUNICATION, &pin_comm, sizeof (pin_comm)))
continue;
if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin,
KSPROPERTY_PIN_DATAFLOW, &pin_flow, sizeof (pin_flow)))
continue;
if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin,
KSPROPERTY_PIN_CATEGORY, &pin_cat, sizeof (pin_cat)))
continue;
GST_DEBUG ("pin[%d]: pin_comm=%d, pin_flow=%d", pin_id, pin_comm, pin_flow);
if (pin_flow == KSPIN_DATAFLOW_OUT &&
memcmp (&pin_cat, &PINNAME_CAPTURE, sizeof (GUID)) == 0) {
KSMULTIPLE_ITEM *items;
if (ks_filter_get_pin_property_multi (filter_handle, pin_id,
KSPROPSETID_Pin, KSPROPERTY_PIN_DATARANGES, &items)) {
KSDATARANGE *range = (KSDATARANGE *) (items + 1);
guint i;
for (i = 0; i < items->Count; i++) {
if (IsEqualGUID (&range->MajorFormat, &KSDATAFORMAT_TYPE_VIDEO)) {
KsVideoMediaType *entry;
gpointer src_vscc, src_format;
GstStructure *media_structure;
entry = g_new0 (KsVideoMediaType, 1);
entry->pin_id = pin_id;
entry->range = g_malloc (range->FormatSize);
memcpy ((gpointer) entry->range, range, range->FormatSize);
if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo)) {
KS_DATARANGE_VIDEO *vr = (KS_DATARANGE_VIDEO *) entry->range;
src_vscc = &vr->ConfigCaps;
src_format = &vr->VideoInfoHeader;
entry->format_size = sizeof (vr->VideoInfoHeader);
entry->sample_size = vr->VideoInfoHeader.bmiHeader.biSizeImage;
} else if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo2)) {
KS_DATARANGE_VIDEO2 *vr = (KS_DATARANGE_VIDEO2 *) entry->range;
src_vscc = &vr->ConfigCaps;
src_format = &vr->VideoInfoHeader;
entry->format_size = sizeof (vr->VideoInfoHeader);
entry->sample_size = vr->VideoInfoHeader.bmiHeader.biSizeImage;
} else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEGVideo)) {
/* Untested and probably wrong... */
KS_DATARANGE_MPEG1_VIDEO *vr =
(KS_DATARANGE_MPEG1_VIDEO *) entry->range;
src_vscc = &vr->ConfigCaps;
src_format = &vr->VideoInfoHeader;
entry->format_size = sizeof (vr->VideoInfoHeader);
entry->sample_size =
vr->VideoInfoHeader.hdr.bmiHeader.biSizeImage;
} else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEG2Video)) {
/* Untested and probably wrong... */
KS_DATARANGE_MPEG2_VIDEO *vr =
(KS_DATARANGE_MPEG2_VIDEO *) entry->range;
src_vscc = &vr->ConfigCaps;
src_format = &vr->VideoInfoHeader;
entry->format_size = sizeof (vr->VideoInfoHeader);
entry->sample_size =
vr->VideoInfoHeader.hdr.bmiHeader.biSizeImage;
} else
g_assert_not_reached ();
g_assert (entry->sample_size != 0);
memcpy ((gpointer) & entry->vscc, src_vscc, sizeof (entry->vscc));
entry->format = g_malloc (entry->format_size);
memcpy (entry->format, src_format, entry->format_size);
media_structure =
ks_video_format_to_structure (range->SubFormat,
range->MajorFormat);
if (media_structure == NULL) {
g_warning ("ks_video_format_to_structure returned NULL");
ks_video_media_type_free (entry);
entry = NULL;
} else if (ks_video_append_video_stream_cfg_fields (media_structure,
&entry->vscc)) {
entry->translated_caps = gst_caps_new_empty ();
gst_caps_append_structure (entry->translated_caps,
media_structure);
} else {
gst_structure_free (media_structure);
ks_video_media_type_free (entry);
entry = NULL;
}
if (entry != NULL)
ret = g_list_prepend (ret, entry);
}
/* REVISIT: Each KSDATARANGE should start on a 64-bit boundary */
range = (KSDATARANGE *) (((guchar *) range) + range->FormatSize);
}
}
}
}
if (ret != NULL) {
ret = g_list_reverse (ret);
ret = ks_video_media_type_list_remove_duplicates (ret);
}
beach:
return ret;
}
KSPIN_CONNECT *
ks_video_create_pin_conn_from_media_type (KsVideoMediaType * media_type)
{
KSPIN_CONNECT *conn = NULL;
KSDATAFORMAT *format = NULL;
guint8 *vih;
conn = g_malloc0 (sizeof (KSPIN_CONNECT) + sizeof (KSDATAFORMAT) +
media_type->format_size);
conn->Interface.Set = KSINTERFACESETID_Standard;
conn->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
conn->Interface.Flags = 0;
conn->Medium.Set = KSMEDIUMSETID_Standard;
conn->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
conn->Medium.Flags = 0;
conn->PinId = media_type->pin_id;
conn->PinToHandle = NULL;
conn->Priority.PriorityClass = KSPRIORITY_NORMAL;
conn->Priority.PrioritySubClass = 1;
format = (KSDATAFORMAT *) (conn + 1);
memcpy (format, media_type->range, sizeof (KSDATAFORMAT));
format->FormatSize = sizeof (KSDATAFORMAT) + media_type->format_size;
vih = (guint8 *) (format + 1);
memcpy (vih, media_type->format, media_type->format_size);
return conn;
}
gboolean
ks_video_fixate_media_type (const KSDATARANGE * range,
guint8 * format, gint width, gint height, gint fps_n, gint fps_d)
{
DWORD dwRate = (width * height * fps_n) / fps_d;
g_return_val_if_fail (format != NULL, FALSE);
if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo)) {
KS_VIDEOINFOHEADER *vih = (KS_VIDEOINFOHEADER *) format;
vih->AvgTimePerFrame = gst_util_uint64_scale_int (10000000, fps_d, fps_n);
vih->dwBitRate = dwRate * vih->bmiHeader.biBitCount;
g_assert (vih->bmiHeader.biWidth == width);
g_assert (vih->bmiHeader.biHeight == height);
} else if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo2)) {
KS_VIDEOINFOHEADER2 *vih = (KS_VIDEOINFOHEADER2 *) format;
vih->AvgTimePerFrame = gst_util_uint64_scale_int (10000000, fps_d, fps_n);
vih->dwBitRate = dwRate * vih->bmiHeader.biBitCount;
g_assert (vih->bmiHeader.biWidth == width);
g_assert (vih->bmiHeader.biHeight == height);
} else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEGVideo)) {
KS_MPEG1VIDEOINFO *vih = (KS_MPEG1VIDEOINFO *) format;
vih->hdr.AvgTimePerFrame =
gst_util_uint64_scale_int (10000000, fps_d, fps_n);
vih->hdr.dwBitRate = dwRate * vih->hdr.bmiHeader.biBitCount;
/* FIXME: set height and width? */
g_assert (vih->hdr.bmiHeader.biWidth == width);
g_assert (vih->hdr.bmiHeader.biHeight == height);
} else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEG2Video)) {
KS_MPEGVIDEOINFO2 *vih = (KS_MPEGVIDEOINFO2 *) format;
vih->hdr.AvgTimePerFrame =
gst_util_uint64_scale_int (10000000, fps_d, fps_n);
vih->hdr.dwBitRate = dwRate * vih->hdr.bmiHeader.biBitCount;
/* FIXME: set height and width? */
g_assert (vih->hdr.bmiHeader.biWidth == width);
g_assert (vih->hdr.bmiHeader.biHeight == height);
} else {
return FALSE;
}
return TRUE;
}
static GstStructure *
ks_video_append_var_video_fields (GstStructure * structure)
{
if (structure) {
gst_structure_set (structure,
"width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
"height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
"framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
}
return structure;
}
GstCaps *
ks_video_get_all_caps (void)
{
static GstCaps *caps = NULL;
if (caps == NULL) {
GstStructure *structure;
caps = gst_caps_new_empty ();
/* from Windows SDK 6.0 uuids.h */
/* RGB formats */
structure =
ks_video_append_var_video_fields (ks_video_format_to_structure
(MEDIASUBTYPE_RGB555, FORMAT_VideoInfo));
gst_caps_append_structure (caps, structure);
structure =
ks_video_append_var_video_fields (ks_video_format_to_structure
(MEDIASUBTYPE_RGB565, FORMAT_VideoInfo));
gst_caps_append_structure (caps, structure);
structure =
ks_video_append_var_video_fields (ks_video_format_to_structure
(MEDIASUBTYPE_RGB24, FORMAT_VideoInfo));
gst_caps_append_structure (caps, structure);
structure =
ks_video_append_var_video_fields (ks_video_format_to_structure
(MEDIASUBTYPE_RGB32, FORMAT_VideoInfo));
gst_caps_append_structure (caps, structure);
/* YUV formats */
structure =
ks_video_append_var_video_fields (gst_structure_new ("video/x-raw-yuv",
NULL));
gst_caps_append_structure (caps, structure);
/* Other formats */
structure =
ks_video_append_var_video_fields (ks_video_format_to_structure
(MEDIASUBTYPE_MJPG, FORMAT_VideoInfo));
gst_caps_append_structure (caps, structure);
structure =
ks_video_append_var_video_fields (ks_video_format_to_structure
(MEDIASUBTYPE_dvsd, FORMAT_VideoInfo));
gst_caps_append_structure (caps, structure);
structure = /* no variable video fields (width, height, framerate) */
ks_video_format_to_structure (MEDIASUBTYPE_dvsd, FORMAT_DvInfo);
gst_caps_append_structure (caps, structure);
}
return caps;
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (C) 2007 Haakon Sporsheim <hakon.sporsheim@tandberg.com>
* 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.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.
*/
#ifndef __KSVIDEOHELPERS_H__
#define __KSVIDEOHELPERS_H__
#include <gst/gst.h>
#include <windows.h>
#include <ks.h>
#include <ksmedia.h>
G_BEGIN_DECLS
DEFINE_GUID(MEDIASUBTYPE_I420, 0x30323449, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
typedef struct _KsVideoMediaType KsVideoMediaType;
/**
* A structure that contain metadata about capabilities
* for both KS and GStreamer for video only.
*/
struct _KsVideoMediaType
{
guint pin_id;
const KSDATARANGE * range;
const KS_VIDEO_STREAM_CONFIG_CAPS vscc;
guint8 * format;
guint format_size;
guint sample_size;
GstCaps * translated_caps;
};
KsVideoMediaType * ks_video_media_type_dup (KsVideoMediaType * media_type);
void ks_video_media_type_free (KsVideoMediaType * media_type);
GList * ks_video_probe_filter_for_caps (HANDLE filter_handle);
KSPIN_CONNECT * ks_video_create_pin_conn_from_media_type (KsVideoMediaType * media_type);
gboolean ks_video_fixate_media_type (const KSDATARANGE * range, guint8 * format, gint width, gint height, gint fps_n, gint fps_d);
GstCaps * ks_video_get_all_caps (void);
G_END_DECLS
#endif /* __KSVIDEOHELPERS_H__ */