mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 18:21:04 +00:00
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:
parent
4c75dffedc
commit
c980279fa4
14 changed files with 3676 additions and 1 deletions
20
ChangeLog
20
ChangeLog
|
@ -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>
|
2008-08-24 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
|
||||||
|
|
||||||
* gst/pcapparse/gstpcapparse.c:
|
* gst/pcapparse/gstpcapparse.c:
|
||||||
|
|
|
@ -1298,6 +1298,7 @@ sys/oss4/Makefile
|
||||||
sys/qtwrapper/Makefile
|
sys/qtwrapper/Makefile
|
||||||
sys/vcd/Makefile
|
sys/vcd/Makefile
|
||||||
sys/wininet/Makefile
|
sys/wininet/Makefile
|
||||||
|
sys/winks/Makefile
|
||||||
sys/winscreencap/Makefile
|
sys/winscreencap/Makefile
|
||||||
examples/Makefile
|
examples/Makefile
|
||||||
examples/app/Makefile
|
examples/app/Makefile
|
||||||
|
|
|
@ -61,5 +61,5 @@ endif
|
||||||
SUBDIRS = $(ACM_DIR) $(DVB_DIR) $(FBDEV_DIR) $(OSS4_DIR) $(QT_DIR) $(VCD_DIR) $(WININET_DIR)
|
SUBDIRS = $(ACM_DIR) $(DVB_DIR) $(FBDEV_DIR) $(OSS4_DIR) $(QT_DIR) $(VCD_DIR) $(WININET_DIR)
|
||||||
|
|
||||||
DIST_SUBDIRS = acmenc dvb fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \
|
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
9
sys/winks/Makefile.am
Normal 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
356
sys/winks/gstksclock.c
Normal 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
69
sys/winks/gstksclock.h
Normal 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
1092
sys/winks/gstksvideodevice.c
Normal file
File diff suppressed because it is too large
Load diff
76
sys/winks/gstksvideodevice.h
Normal file
76
sys/winks/gstksvideodevice.h
Normal 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
816
sys/winks/gstksvideosrc.c
Normal 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
55
sys/winks/gstksvideosrc.h
Normal 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
471
sys/winks/kshelpers.c
Normal 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
61
sys/winks/kshelpers.h
Normal 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
585
sys/winks/ksvideohelpers.c
Normal 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;
|
||||||
|
}
|
64
sys/winks/ksvideohelpers.h
Normal file
64
sys/winks/ksvideohelpers.h
Normal 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__ */
|
Loading…
Reference in a new issue