2008-08-24 22:05:48 +00:00
|
|
|
/*
|
|
|
|
* 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
|
2012-11-03 20:38:00 +00:00
|
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
2008-08-24 22:05:48 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "gstksvideodevice.h"
|
|
|
|
|
|
|
|
#include "gstksclock.h"
|
|
|
|
#include "kshelpers.h"
|
|
|
|
#include "ksvideohelpers.h"
|
|
|
|
|
|
|
|
#define READ_TIMEOUT (10 * 1000)
|
|
|
|
#define MJPEG_MAX_PADDING 128
|
|
|
|
#define MAX_OUTSTANDING_FRAMES 128
|
2009-09-07 14:45:57 +00:00
|
|
|
|
|
|
|
#define KS_BUFFER_ALIGNMENT 4096
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
#define DEFAULT_DEVICE_PATH NULL
|
|
|
|
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
|
|
|
|
#define GST_CAT_DEFAULT gst_ks_debug
|
|
|
|
|
2009-09-07 14:45:57 +00:00
|
|
|
#define GST_DEBUG_IS_ENABLED() \
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
(gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_DEBUG)
|
2009-09-07 14:45:57 +00:00
|
|
|
#define UNREF_BUFFER(b) \
|
|
|
|
G_STMT_START { \
|
|
|
|
if (*(b) != NULL) { \
|
|
|
|
gst_buffer_unref (*(b)); \
|
|
|
|
*(b) = NULL; \
|
|
|
|
} \
|
|
|
|
} G_STMT_END
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
|
2008-08-24 22:05:48 +00:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
|
|
|
PROP_CLOCK,
|
|
|
|
PROP_DEVICE_PATH,
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
KSSTREAM_HEADER header;
|
|
|
|
KS_FRAME_INFO frame_info;
|
|
|
|
} KSSTREAM_READ_PARAMS;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
KSSTREAM_READ_PARAMS params;
|
2009-09-07 14:45:57 +00:00
|
|
|
GstBuffer *buf;
|
2008-08-24 22:05:48 +00:00
|
|
|
OVERLAPPED overlapped;
|
|
|
|
} ReadRequest;
|
|
|
|
|
2009-09-07 14:09:34 +00:00
|
|
|
struct _GstKsVideoDevicePrivate
|
2008-08-24 22:05:48 +00:00
|
|
|
{
|
|
|
|
gboolean open;
|
|
|
|
KSSTATE state;
|
|
|
|
|
|
|
|
GstKsClock *clock;
|
|
|
|
gchar *dev_path;
|
|
|
|
HANDLE filter_handle;
|
|
|
|
GList *media_types;
|
|
|
|
GstCaps *cached_caps;
|
|
|
|
HANDLE cancel_event;
|
|
|
|
|
|
|
|
KsVideoMediaType *cur_media_type;
|
|
|
|
GstCaps *cur_fixed_caps;
|
|
|
|
guint width;
|
|
|
|
guint height;
|
|
|
|
guint fps_n;
|
|
|
|
guint fps_d;
|
|
|
|
guint8 *rgb_swap_buf;
|
2015-04-29 19:37:56 +00:00
|
|
|
gboolean is_muxed;
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
HANDLE pin_handle;
|
|
|
|
|
|
|
|
gboolean requests_submitted;
|
|
|
|
gulong num_requests;
|
|
|
|
GArray *requests;
|
|
|
|
GArray *request_events;
|
2009-09-07 14:45:57 +00:00
|
|
|
GstBuffer *spare_buffers[2];
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
GstClockTime last_timestamp;
|
2009-09-07 14:45:57 +00:00
|
|
|
};
|
2008-08-24 22:05:48 +00:00
|
|
|
|
2009-09-07 14:09:34 +00:00
|
|
|
#define GST_KS_VIDEO_DEVICE_GET_PRIVATE(o) ((o)->priv)
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
static void gst_ks_video_device_dispose (GObject * object);
|
|
|
|
static void gst_ks_video_device_get_property (GObject * object, guint prop_id,
|
|
|
|
GValue * value, GParamSpec * pspec);
|
|
|
|
static void gst_ks_video_device_set_property (GObject * object, guint prop_id,
|
|
|
|
const GValue * value, GParamSpec * pspec);
|
|
|
|
|
|
|
|
static void gst_ks_video_device_reset_caps (GstKsVideoDevice * self);
|
2009-09-07 14:45:57 +00:00
|
|
|
static guint gst_ks_video_device_get_frame_size (GstKsVideoDevice * self);
|
2008-08-24 22:05:48 +00:00
|
|
|
|
2018-06-23 22:17:26 +00:00
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GstKsVideoDevice, gst_ks_video_device,
|
|
|
|
G_TYPE_OBJECT);
|
2008-08-24 22:05:48 +00:00
|
|
|
|
2014-03-14 17:23:51 +00:00
|
|
|
static GstKsVideoDeviceClass *parent_class = NULL;
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
gst_ks_video_device_class_init (GstKsVideoDeviceClass * klass)
|
|
|
|
{
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
2014-03-14 17:23:51 +00:00
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
|
2008-08-24 22:05:48 +00:00
|
|
|
gobject_class->dispose = gst_ks_video_device_dispose;
|
|
|
|
gobject_class->get_property = gst_ks_video_device_get_property;
|
|
|
|
gobject_class->set_property = gst_ks_video_device_set_property;
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_CLOCK,
|
|
|
|
g_param_spec_object ("clock", "Clock to use",
|
|
|
|
"Clock to use", GST_TYPE_KS_CLOCK,
|
sys/winks/gstksvideodevice.c (gst_ks_video_device_class_init, gst_ks_video_device_set_state):
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (gst_ks_video_device_class_init,
gst_ks_video_device_set_state):
Don't set the pin state to KSSTATE_RUN from the streaming thread.
Skip KSSTATE_ACQUIRE when changing pin state downwards.
Be nice and specify G_PARAM_STATIC_STRINGS.
Remove unused finalize method.
* sys/winks/gstksvideosrc.c (DEFAULT_ENABLE_QUIRKS, PROP_ENABLE_QUIRKS,
enable_quirks, gst_ks_video_src_class_init, gst_ks_video_src_init,
gst_ks_video_src_finalize, gst_ks_video_src_get_property,
gst_ks_video_src_set_property, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_change_state,
gst_ks_video_src_set_caps):
First driver quirk: work around Logitech's hostile driver software to
improve stability and performance. See comments for details.
Provide a property to disable driver quirks (enabled by default).
Be nice and specify G_PARAM_STATIC_STRINGS.
Remove unused dispose method.
Tweak include order.
2008-08-27 21:33:07 +00:00
|
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_DEVICE_PATH,
|
|
|
|
g_param_spec_string ("device-path", "Device Path",
|
|
|
|
"The device path", DEFAULT_DEVICE_PATH,
|
sys/winks/gstksvideodevice.c (gst_ks_video_device_class_init, gst_ks_video_device_set_state):
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (gst_ks_video_device_class_init,
gst_ks_video_device_set_state):
Don't set the pin state to KSSTATE_RUN from the streaming thread.
Skip KSSTATE_ACQUIRE when changing pin state downwards.
Be nice and specify G_PARAM_STATIC_STRINGS.
Remove unused finalize method.
* sys/winks/gstksvideosrc.c (DEFAULT_ENABLE_QUIRKS, PROP_ENABLE_QUIRKS,
enable_quirks, gst_ks_video_src_class_init, gst_ks_video_src_init,
gst_ks_video_src_finalize, gst_ks_video_src_get_property,
gst_ks_video_src_set_property, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_change_state,
gst_ks_video_src_set_caps):
First driver quirk: work around Logitech's hostile driver software to
improve stability and performance. See comments for details.
Provide a property to disable driver quirks (enabled by default).
Be nice and specify G_PARAM_STATIC_STRINGS.
Remove unused dispose method.
Tweak include order.
2008-08-27 21:33:07 +00:00
|
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
2008-08-24 22:05:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-03-14 17:23:51 +00:00
|
|
|
gst_ks_video_device_init (GstKsVideoDevice * self)
|
2008-08-24 22:05:48 +00:00
|
|
|
{
|
2009-09-07 14:09:34 +00:00
|
|
|
GstKsVideoDevicePrivate *priv;
|
|
|
|
|
2018-06-23 22:17:26 +00:00
|
|
|
self->priv = gst_ks_video_device_get_instance_private (self);
|
2008-08-24 22:05:48 +00:00
|
|
|
|
2009-09-07 14:09:34 +00:00
|
|
|
priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
2008-08-24 22:05:48 +00:00
|
|
|
priv->open = FALSE;
|
|
|
|
priv->state = KSSTATE_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_ks_video_device_dispose (GObject * object)
|
|
|
|
{
|
|
|
|
GstKsVideoDevice *self = GST_KS_VIDEO_DEVICE (object);
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
gst_ks_video_device_reset_caps (self);
|
|
|
|
gst_ks_video_device_close (self);
|
|
|
|
|
|
|
|
if (priv->clock != NULL) {
|
|
|
|
g_object_unref (priv->clock);
|
|
|
|
priv->clock = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_ks_video_device_get_property (GObject * object, guint prop_id,
|
|
|
|
GValue * value, GParamSpec * pspec)
|
|
|
|
{
|
|
|
|
GstKsVideoDevice *self = GST_KS_VIDEO_DEVICE (object);
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
switch (prop_id) {
|
|
|
|
case PROP_CLOCK:
|
|
|
|
g_value_set_object (value, priv->clock);
|
|
|
|
break;
|
|
|
|
case PROP_DEVICE_PATH:
|
|
|
|
g_value_set_string (value, priv->dev_path);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_ks_video_device_set_property (GObject * object, guint prop_id,
|
|
|
|
const GValue * value, GParamSpec * pspec)
|
|
|
|
{
|
|
|
|
GstKsVideoDevice *self = GST_KS_VIDEO_DEVICE (object);
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
switch (prop_id) {
|
|
|
|
case PROP_CLOCK:
|
|
|
|
if (priv->clock != NULL)
|
|
|
|
g_object_unref (priv->clock);
|
|
|
|
priv->clock = g_value_dup_object (value);
|
|
|
|
break;
|
|
|
|
case PROP_DEVICE_PATH:
|
|
|
|
g_free (priv->dev_path);
|
|
|
|
priv->dev_path = g_value_dup_string (value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_ks_video_device_parse_win32_error (const gchar * func_name,
|
|
|
|
DWORD error_code, gulong * ret_error_code, gchar ** ret_error_str)
|
|
|
|
{
|
|
|
|
if (ret_error_code != NULL)
|
|
|
|
*ret_error_code = error_code;
|
|
|
|
|
|
|
|
if (ret_error_str != NULL) {
|
|
|
|
GString *message;
|
|
|
|
gchar buf[1480];
|
|
|
|
DWORD result;
|
|
|
|
|
|
|
|
message = g_string_sized_new (1600);
|
|
|
|
g_string_append_printf (message, "%s returned ", func_name);
|
|
|
|
|
|
|
|
result =
|
|
|
|
FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |
|
|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error_code, 0, buf, sizeof (buf),
|
|
|
|
NULL);
|
|
|
|
if (result != 0) {
|
2012-08-07 16:22:36 +00:00
|
|
|
g_string_append_printf (message, "0x%08x: %s", (guint) error_code,
|
2008-08-24 22:05:48 +00:00
|
|
|
g_strchomp (buf));
|
|
|
|
} else {
|
|
|
|
DWORD format_error_code = GetLastError ();
|
|
|
|
|
|
|
|
g_string_append_printf (message,
|
2012-08-07 16:22:36 +00:00
|
|
|
"<0x%08x (FormatMessage error code: %s)>", (guint) error_code,
|
2008-08-24 22:05:48 +00:00
|
|
|
(format_error_code == ERROR_MR_MID_NOT_FOUND)
|
|
|
|
? "no system error message found"
|
|
|
|
: "failed to retrieve system error message");
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret_error_str = message->str;
|
|
|
|
g_string_free (message, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_ks_video_device_clear_buffers (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
if (priv->requests == NULL)
|
|
|
|
return;
|
|
|
|
|
2010-07-01 13:07:41 +00:00
|
|
|
/* Join any pending requests */
|
2008-08-24 22:05:48 +00:00
|
|
|
for (i = 0; i < priv->num_requests; i++) {
|
|
|
|
ReadRequest *req = &g_array_index (priv->requests, ReadRequest, i);
|
2010-07-01 13:07:41 +00:00
|
|
|
HANDLE ev = g_array_index (priv->request_events, HANDLE, i);
|
|
|
|
DWORD n;
|
2008-08-24 22:05:48 +00:00
|
|
|
|
2010-07-01 13:07:41 +00:00
|
|
|
if (!GetOverlappedResult (priv->pin_handle, &req->overlapped, &n, FALSE)) {
|
|
|
|
if (WaitForSingleObject (ev, 1000) == WAIT_OBJECT_0)
|
|
|
|
GetOverlappedResult (priv->pin_handle, &req->overlapped, &n, FALSE);
|
|
|
|
}
|
2008-08-24 22:05:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Clean up */
|
2009-09-07 14:45:57 +00:00
|
|
|
for (i = 0; i < G_N_ELEMENTS (priv->spare_buffers); i++) {
|
|
|
|
gst_buffer_unref (priv->spare_buffers[i]);
|
|
|
|
priv->spare_buffers[i] = NULL;
|
|
|
|
}
|
|
|
|
|
2008-08-24 22:05:48 +00:00
|
|
|
for (i = 0; i < priv->requests->len; i++) {
|
|
|
|
ReadRequest *req = &g_array_index (priv->requests, ReadRequest, i);
|
|
|
|
HANDLE ev = g_array_index (priv->request_events, HANDLE, i);
|
|
|
|
|
2009-09-07 14:45:57 +00:00
|
|
|
gst_buffer_unref (req->buf);
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
if (ev)
|
|
|
|
CloseHandle (ev);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_array_free (priv->requests, TRUE);
|
|
|
|
priv->requests = NULL;
|
|
|
|
|
|
|
|
g_array_free (priv->request_events, TRUE);
|
|
|
|
priv->request_events = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_ks_video_device_prepare_buffers (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
guint i;
|
|
|
|
guint frame_size;
|
|
|
|
|
|
|
|
g_assert (priv->cur_media_type != NULL);
|
|
|
|
|
|
|
|
gst_ks_video_device_clear_buffers (self);
|
|
|
|
|
|
|
|
priv->requests = g_array_sized_new (FALSE, TRUE, sizeof (ReadRequest),
|
|
|
|
priv->num_requests);
|
|
|
|
priv->request_events = g_array_sized_new (FALSE, TRUE, sizeof (HANDLE),
|
|
|
|
priv->num_requests + 1);
|
|
|
|
|
|
|
|
frame_size = gst_ks_video_device_get_frame_size (self);
|
|
|
|
|
2009-09-07 14:45:57 +00:00
|
|
|
for (i = 0; i < G_N_ELEMENTS (priv->spare_buffers); i++) {
|
|
|
|
priv->spare_buffers[i] = self->allocfunc (frame_size, KS_BUFFER_ALIGNMENT,
|
|
|
|
self->allocfunc_data);
|
|
|
|
}
|
|
|
|
|
2008-08-24 22:05:48 +00:00
|
|
|
for (i = 0; i < priv->num_requests; i++) {
|
2014-03-14 17:23:51 +00:00
|
|
|
ReadRequest req;
|
|
|
|
memset (&req, '0', sizeof (ReadRequest));
|
2008-08-24 22:05:48 +00:00
|
|
|
|
2009-09-07 14:45:57 +00:00
|
|
|
req.buf = self->allocfunc (frame_size, KS_BUFFER_ALIGNMENT,
|
|
|
|
self->allocfunc_data);
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
req.overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
|
|
|
|
|
|
|
|
g_array_append_val (priv->requests, req);
|
|
|
|
g_array_append_val (priv->request_events, req.overlapped.hEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_array_append_val (priv->request_events, priv->cancel_event);
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* REVISIT: Could probably remove this later, for now it's here to help
|
|
|
|
* track down the case where we capture old frames. This has been
|
|
|
|
* observed with UVC cameras, presumably with some system load.
|
|
|
|
*/
|
|
|
|
priv->last_timestamp = GST_CLOCK_TIME_NONE;
|
2008-08-24 22:05:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_ks_video_device_dump_supported_property_sets (GstKsVideoDevice * self,
|
|
|
|
const gchar * obj_name, const GUID * propsets, gulong propsets_len)
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
|
2012-08-07 16:22:36 +00:00
|
|
|
GST_DEBUG ("%s supports %lu property set%s", obj_name, propsets_len,
|
2008-08-24 22:05:48 +00:00
|
|
|
(propsets_len != 1) ? "s" : "");
|
|
|
|
|
|
|
|
for (i = 0; i < propsets_len; i++) {
|
|
|
|
gchar *propset_name = ks_property_set_to_string (&propsets[i]);
|
|
|
|
GST_DEBUG ("[%d] %s", i, propset_name);
|
|
|
|
g_free (propset_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-07 14:45:57 +00:00
|
|
|
GstKsVideoDevice *
|
|
|
|
gst_ks_video_device_new (const gchar * device_path, GstKsClock * clock,
|
|
|
|
GstKsAllocFunction allocfunc, gpointer allocfunc_data)
|
|
|
|
{
|
|
|
|
GstKsVideoDevice *device;
|
|
|
|
|
|
|
|
device = g_object_new (GST_TYPE_KS_VIDEO_DEVICE,
|
|
|
|
"device-path", device_path, "clock", clock, NULL);
|
|
|
|
device->allocfunc = allocfunc;
|
|
|
|
device->allocfunc_data = allocfunc_data;
|
|
|
|
|
|
|
|
return device;
|
|
|
|
}
|
|
|
|
|
2008-08-24 22:05:48 +00:00
|
|
|
gboolean
|
|
|
|
gst_ks_video_device_open (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
GUID *propsets = NULL;
|
|
|
|
gulong propsets_len;
|
|
|
|
GList *cur;
|
|
|
|
|
|
|
|
g_assert (!priv->open);
|
|
|
|
g_assert (priv->dev_path != NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open the filter.
|
|
|
|
*/
|
|
|
|
priv->filter_handle = CreateFile (priv->dev_path,
|
|
|
|
GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
|
|
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
|
|
|
if (!ks_is_valid_handle (priv->filter_handle))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Query the filter for supported property sets.
|
|
|
|
*/
|
|
|
|
if (ks_object_get_supported_property_sets (priv->filter_handle, &propsets,
|
|
|
|
&propsets_len)) {
|
|
|
|
gst_ks_video_device_dump_supported_property_sets (self, "filter",
|
|
|
|
propsets, propsets_len);
|
|
|
|
g_free (propsets);
|
|
|
|
} else {
|
|
|
|
GST_DEBUG ("failed to query filter for supported property sets");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Probe for supported media types.
|
|
|
|
*/
|
|
|
|
priv->media_types = ks_video_probe_filter_for_caps (priv->filter_handle);
|
|
|
|
priv->cached_caps = gst_caps_new_empty ();
|
|
|
|
|
|
|
|
for (cur = priv->media_types; cur != NULL; cur = cur->next) {
|
|
|
|
KsVideoMediaType *media_type = cur->data;
|
|
|
|
|
|
|
|
gst_caps_append (priv->cached_caps,
|
|
|
|
gst_caps_copy (media_type->translated_caps));
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
{
|
|
|
|
gchar *str;
|
|
|
|
str = gst_caps_to_string (media_type->translated_caps);
|
|
|
|
GST_DEBUG ("pin[%d]: found media type: %s", media_type->pin_id, str);
|
|
|
|
g_free (str);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
priv->cancel_event = CreateEvent (NULL, TRUE, FALSE, NULL);
|
|
|
|
|
|
|
|
priv->open = TRUE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
error:
|
|
|
|
g_free (priv->dev_path);
|
|
|
|
priv->dev_path = NULL;
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gst_ks_video_device_close (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
GList *cur;
|
|
|
|
|
|
|
|
gst_ks_video_device_reset_caps (self);
|
|
|
|
|
|
|
|
g_free (priv->dev_path);
|
|
|
|
priv->dev_path = NULL;
|
|
|
|
|
|
|
|
if (ks_is_valid_handle (priv->filter_handle)) {
|
|
|
|
CloseHandle (priv->filter_handle);
|
|
|
|
priv->filter_handle = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (cur = priv->media_types; cur != NULL; cur = cur->next) {
|
|
|
|
KsVideoMediaType *mt = cur->data;
|
|
|
|
ks_video_media_type_free (mt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (priv->media_types != NULL) {
|
|
|
|
g_list_free (priv->media_types);
|
|
|
|
priv->media_types = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (priv->cached_caps != NULL) {
|
|
|
|
gst_caps_unref (priv->cached_caps);
|
|
|
|
priv->cached_caps = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ks_is_valid_handle (priv->cancel_event))
|
|
|
|
CloseHandle (priv->cancel_event);
|
|
|
|
priv->cancel_event = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
|
|
priv->open = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
GstCaps *
|
|
|
|
gst_ks_video_device_get_available_caps (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
g_assert (priv->open);
|
|
|
|
|
|
|
|
return gst_caps_ref (priv->cached_caps);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
gst_ks_video_device_has_caps (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
return (priv->cur_media_type != NULL) ? TRUE : FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HANDLE
|
|
|
|
gst_ks_video_device_create_pin (GstKsVideoDevice * self,
|
|
|
|
KsVideoMediaType * media_type, gulong * num_outstanding)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
HANDLE pin_handle = INVALID_HANDLE_VALUE;
|
|
|
|
KSPIN_CONNECT *pin_conn = NULL;
|
|
|
|
DWORD ret;
|
2010-04-20 09:59:23 +00:00
|
|
|
guint retry_count;
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
GUID *propsets = NULL;
|
|
|
|
gulong propsets_len;
|
|
|
|
gboolean supports_mem_transport = FALSE;
|
|
|
|
|
|
|
|
KSALLOCATOR_FRAMING *framing = NULL;
|
|
|
|
gulong framing_size = sizeof (KSALLOCATOR_FRAMING);
|
|
|
|
KSALLOCATOR_FRAMING_EX *framing_ex = NULL;
|
|
|
|
gulong alignment;
|
|
|
|
|
|
|
|
DWORD mem_transport;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Instantiate the pin.
|
|
|
|
*/
|
|
|
|
pin_conn = ks_video_create_pin_conn_from_media_type (media_type);
|
|
|
|
|
2010-04-20 09:59:23 +00:00
|
|
|
for (retry_count = 0; retry_count != 5; retry_count++) {
|
|
|
|
|
|
|
|
GST_DEBUG ("calling KsCreatePin with pin_id = %d", media_type->pin_id);
|
|
|
|
|
|
|
|
ret = KsCreatePin (priv->filter_handle, pin_conn, GENERIC_READ,
|
|
|
|
&pin_handle);
|
|
|
|
if (ret != ERROR_NOT_READY)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* wait and retry, like the reference implementation does */
|
|
|
|
if (WaitForSingleObject (priv->cancel_event, 1000) == WAIT_OBJECT_0)
|
|
|
|
goto cancelled;
|
|
|
|
}
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
if (ret != ERROR_SUCCESS)
|
|
|
|
goto error_create_pin;
|
|
|
|
|
|
|
|
GST_DEBUG ("KsCreatePin succeeded, pin %p created", pin_handle);
|
|
|
|
|
|
|
|
g_free (pin_conn);
|
|
|
|
pin_conn = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Query the pin for supported property sets.
|
|
|
|
*/
|
|
|
|
if (ks_object_get_supported_property_sets (pin_handle, &propsets,
|
|
|
|
&propsets_len)) {
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
gst_ks_video_device_dump_supported_property_sets (self, "pin", propsets,
|
|
|
|
propsets_len);
|
|
|
|
|
|
|
|
for (i = 0; i < propsets_len; i++) {
|
|
|
|
if (IsEqualGUID (&propsets[i], &KSPROPSETID_MemoryTransport))
|
|
|
|
supports_mem_transport = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (propsets);
|
|
|
|
} else {
|
|
|
|
GST_DEBUG ("failed to query pin for supported property sets");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-09-02 19:08:44 +00:00
|
|
|
* Figure out how many simultaneous requests it prefers.
|
2008-08-24 22:05:48 +00:00
|
|
|
*
|
|
|
|
* This is really important as it depends on the driver and the device.
|
|
|
|
* Doing too few will result in poor capture performance, whilst doing too
|
|
|
|
* many will make some drivers crash really horribly and leave you with a
|
|
|
|
* BSOD. I've experienced the latter with older Logitech drivers.
|
|
|
|
*/
|
|
|
|
*num_outstanding = 0;
|
|
|
|
alignment = 0;
|
|
|
|
|
|
|
|
if (ks_object_get_property (pin_handle, KSPROPSETID_Connection,
|
2014-03-14 17:23:51 +00:00
|
|
|
KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX, (void *) &framing_ex, NULL,
|
|
|
|
NULL)) {
|
2008-08-24 22:05:48 +00:00
|
|
|
if (framing_ex->CountItems >= 1) {
|
|
|
|
*num_outstanding = framing_ex->FramingItem[0].Frames;
|
|
|
|
alignment = framing_ex->FramingItem[0].FileAlignment;
|
|
|
|
} else {
|
|
|
|
GST_DEBUG ("ignoring empty ALLOCATORFRAMING_EX");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
GST_DEBUG ("query for ALLOCATORFRAMING_EX failed, trying "
|
|
|
|
"ALLOCATORFRAMING");
|
|
|
|
|
|
|
|
if (ks_object_get_property (pin_handle, KSPROPSETID_Connection,
|
2014-03-14 17:23:51 +00:00
|
|
|
KSPROPERTY_CONNECTION_ALLOCATORFRAMING, (void *) &framing,
|
|
|
|
&framing_size, NULL)) {
|
2008-08-24 22:05:48 +00:00
|
|
|
*num_outstanding = framing->Frames;
|
|
|
|
alignment = framing->FileAlignment;
|
|
|
|
} else {
|
|
|
|
GST_DEBUG ("query for ALLOCATORFRAMING failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-07 16:22:36 +00:00
|
|
|
GST_DEBUG ("num_outstanding: %lu alignment: 0x%08x", *num_outstanding,
|
2014-03-14 17:23:51 +00:00
|
|
|
(guint) alignment);
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
if (*num_outstanding == 0 || *num_outstanding > MAX_OUTSTANDING_FRAMES) {
|
|
|
|
GST_DEBUG ("setting number of allowable outstanding frames to 1");
|
|
|
|
*num_outstanding = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (framing);
|
2010-04-20 09:59:23 +00:00
|
|
|
framing = NULL;
|
2008-08-24 22:05:48 +00:00
|
|
|
g_free (framing_ex);
|
2010-04-20 09:59:23 +00:00
|
|
|
framing_ex = NULL;
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
/*
|
2009-09-07 14:45:57 +00:00
|
|
|
* TODO: We also need to respect alignment, but for now we just assume
|
|
|
|
* that allocfunc provides the appropriate alignment...
|
2008-08-24 22:05:48 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* Set the memory transport to use. */
|
|
|
|
if (supports_mem_transport) {
|
|
|
|
mem_transport = 0; /* REVISIT: use the constant here */
|
|
|
|
if (!ks_object_set_property (pin_handle, KSPROPSETID_MemoryTransport,
|
|
|
|
KSPROPERTY_MEMORY_TRANSPORT, &mem_transport,
|
2009-10-12 15:26:15 +00:00
|
|
|
sizeof (mem_transport), NULL)) {
|
2008-08-24 22:05:48 +00:00
|
|
|
GST_DEBUG ("failed to set memory transport, sticking with the default");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
* Override the clock if we have one and the pin doesn't have any either.
|
2008-08-24 22:05:48 +00:00
|
|
|
*/
|
|
|
|
if (priv->clock != NULL) {
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
HANDLE *cur_clock_handle = NULL;
|
|
|
|
gulong cur_clock_handle_size = sizeof (HANDLE);
|
|
|
|
|
|
|
|
if (ks_object_get_property (pin_handle, KSPROPSETID_Stream,
|
|
|
|
KSPROPERTY_STREAM_MASTERCLOCK, (gpointer *) & cur_clock_handle,
|
2009-10-12 15:26:15 +00:00
|
|
|
&cur_clock_handle_size, NULL)) {
|
2014-11-24 13:25:55 +00:00
|
|
|
GST_DEBUG ("current master clock handle: %p", *cur_clock_handle);
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
CloseHandle (*cur_clock_handle);
|
|
|
|
g_free (cur_clock_handle);
|
2008-08-24 22:05:48 +00:00
|
|
|
} else {
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
HANDLE new_clock_handle = gst_ks_clock_get_handle (priv->clock);
|
|
|
|
|
|
|
|
if (ks_object_set_property (pin_handle, KSPROPSETID_Stream,
|
|
|
|
KSPROPERTY_STREAM_MASTERCLOCK, &new_clock_handle,
|
2009-10-12 15:26:15 +00:00
|
|
|
sizeof (new_clock_handle), NULL)) {
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
gst_ks_clock_prepare (priv->clock);
|
|
|
|
} else {
|
|
|
|
GST_WARNING ("failed to set pin's master clock");
|
|
|
|
}
|
2008-08-24 22:05:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pin_handle;
|
|
|
|
|
|
|
|
/* ERRORS */
|
|
|
|
error_create_pin:
|
|
|
|
{
|
|
|
|
gchar *str;
|
|
|
|
|
|
|
|
gst_ks_video_device_parse_win32_error ("KsCreatePin", ret, NULL, &str);
|
|
|
|
GST_ERROR ("%s", str);
|
|
|
|
g_free (str);
|
|
|
|
|
|
|
|
goto beach;
|
|
|
|
}
|
2010-04-20 09:59:23 +00:00
|
|
|
cancelled:
|
2008-08-24 22:05:48 +00:00
|
|
|
beach:
|
|
|
|
{
|
|
|
|
g_free (framing);
|
2010-04-20 09:59:23 +00:00
|
|
|
g_free (framing_ex);
|
2008-08-24 22:05:48 +00:00
|
|
|
if (ks_is_valid_handle (pin_handle))
|
|
|
|
CloseHandle (pin_handle);
|
|
|
|
g_free (pin_conn);
|
|
|
|
|
|
|
|
return INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_ks_video_device_close_current_pin (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
if (!ks_is_valid_handle (priv->pin_handle))
|
|
|
|
return;
|
|
|
|
|
2009-10-12 15:26:15 +00:00
|
|
|
gst_ks_video_device_set_state (self, KSSTATE_STOP, NULL);
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
CloseHandle (priv->pin_handle);
|
|
|
|
priv->pin_handle = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_ks_video_device_reset_caps (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
gst_ks_video_device_close_current_pin (self);
|
|
|
|
|
|
|
|
ks_video_media_type_free (priv->cur_media_type);
|
|
|
|
priv->cur_media_type = NULL;
|
|
|
|
|
|
|
|
priv->width = priv->height = priv->fps_n = priv->fps_d = 0;
|
|
|
|
|
|
|
|
g_free (priv->rgb_swap_buf);
|
|
|
|
priv->rgb_swap_buf = NULL;
|
|
|
|
|
|
|
|
if (priv->cur_fixed_caps != NULL) {
|
|
|
|
gst_caps_unref (priv->cur_fixed_caps);
|
|
|
|
priv->cur_fixed_caps = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
gst_ks_video_device_set_caps (GstKsVideoDevice * self, GstCaps * caps)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
GList *cur;
|
|
|
|
GstStructure *s;
|
|
|
|
|
|
|
|
/* State to be committed on success */
|
|
|
|
KsVideoMediaType *media_type = NULL;
|
2012-08-07 16:23:05 +00:00
|
|
|
gint width, height, fps_n, fps_d;
|
2008-08-24 22:05:48 +00:00
|
|
|
HANDLE pin_handle = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
|
|
/* Reset? */
|
|
|
|
if (caps == NULL) {
|
|
|
|
gst_ks_video_device_reset_caps (self);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Validate the caps */
|
|
|
|
if (!gst_caps_is_subset (caps, priv->cached_caps)) {
|
|
|
|
gchar *string_caps = gst_caps_to_string (caps);
|
|
|
|
gchar *string_c_caps = gst_caps_to_string (priv->cached_caps);
|
|
|
|
|
|
|
|
GST_ERROR ("caps (%s) is not a subset of device caps (%s)",
|
|
|
|
string_caps, string_c_caps);
|
|
|
|
|
|
|
|
g_free (string_caps);
|
|
|
|
g_free (string_c_caps);
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (cur = priv->media_types; cur != NULL; cur = cur->next) {
|
|
|
|
KsVideoMediaType *mt = cur->data;
|
|
|
|
|
|
|
|
if (gst_caps_is_subset (caps, mt->translated_caps)) {
|
|
|
|
media_type = ks_video_media_type_dup (mt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (media_type == NULL)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
s = gst_caps_get_structure (caps, 0);
|
|
|
|
if (!gst_structure_get_int (s, "width", &width) ||
|
|
|
|
!gst_structure_get_int (s, "height", &height) ||
|
|
|
|
!gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) {
|
2015-04-29 19:37:56 +00:00
|
|
|
gst_structure_get_boolean (s, "systemstream", &priv->is_muxed);
|
|
|
|
if (!priv->is_muxed) {
|
|
|
|
GST_ERROR ("Failed to get width/height/fps");
|
|
|
|
goto error;
|
|
|
|
}
|
2015-04-08 16:51:50 +00:00
|
|
|
} else {
|
|
|
|
if (!ks_video_fixate_media_type (media_type->range,
|
|
|
|
media_type->format, width, height, fps_n, fps_d))
|
|
|
|
goto error;
|
2008-08-24 22:05:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (priv->cur_media_type != NULL) {
|
|
|
|
if (media_type->format_size == priv->cur_media_type->format_size &&
|
|
|
|
memcmp (media_type->format, priv->cur_media_type->format,
|
|
|
|
priv->cur_media_type->format_size) == 0) {
|
|
|
|
GST_DEBUG ("%s: re-using existing pin", G_STRFUNC);
|
|
|
|
goto same_caps;
|
|
|
|
} else {
|
|
|
|
GST_DEBUG ("%s: re-creating pin", G_STRFUNC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_ks_video_device_close_current_pin (self);
|
|
|
|
|
|
|
|
pin_handle = gst_ks_video_device_create_pin (self, media_type,
|
|
|
|
&priv->num_requests);
|
|
|
|
if (!ks_is_valid_handle (pin_handle)) {
|
|
|
|
/* Re-create the old pin */
|
|
|
|
if (priv->cur_media_type != NULL)
|
|
|
|
priv->pin_handle = gst_ks_video_device_create_pin (self,
|
|
|
|
priv->cur_media_type, &priv->num_requests);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Commit state: no turning back past this */
|
|
|
|
gst_ks_video_device_reset_caps (self);
|
|
|
|
|
|
|
|
priv->cur_media_type = media_type;
|
|
|
|
priv->width = width;
|
|
|
|
priv->height = height;
|
|
|
|
priv->fps_n = fps_n;
|
|
|
|
priv->fps_d = fps_d;
|
|
|
|
|
2016-08-15 06:37:44 +00:00
|
|
|
if (media_type->is_rgb)
|
2008-08-24 22:05:48 +00:00
|
|
|
priv->rgb_swap_buf = g_malloc (media_type->sample_size / priv->height);
|
|
|
|
else
|
|
|
|
priv->rgb_swap_buf = NULL;
|
|
|
|
|
|
|
|
priv->pin_handle = pin_handle;
|
|
|
|
|
|
|
|
priv->cur_fixed_caps = gst_caps_copy (caps);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
error:
|
|
|
|
{
|
|
|
|
ks_video_media_type_free (media_type);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
same_caps:
|
|
|
|
{
|
|
|
|
ks_video_media_type_free (media_type);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
2009-10-12 15:26:15 +00:00
|
|
|
gst_ks_video_device_set_state (GstKsVideoDevice * self, KSSTATE state,
|
|
|
|
gulong * error_code)
|
2008-08-24 22:05:48 +00:00
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
KSSTATE initial_state;
|
|
|
|
gint addend;
|
|
|
|
|
|
|
|
g_assert (priv->cur_media_type != NULL);
|
|
|
|
|
|
|
|
if (state == priv->state)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
initial_state = priv->state;
|
|
|
|
addend = (state > priv->state) ? 1 : -1;
|
|
|
|
|
|
|
|
GST_DEBUG ("Initiating pin 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;
|
|
|
|
|
sys/winks/gstksvideodevice.c (gst_ks_video_device_class_init, gst_ks_video_device_set_state):
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (gst_ks_video_device_class_init,
gst_ks_video_device_set_state):
Don't set the pin state to KSSTATE_RUN from the streaming thread.
Skip KSSTATE_ACQUIRE when changing pin state downwards.
Be nice and specify G_PARAM_STATIC_STRINGS.
Remove unused finalize method.
* sys/winks/gstksvideosrc.c (DEFAULT_ENABLE_QUIRKS, PROP_ENABLE_QUIRKS,
enable_quirks, gst_ks_video_src_class_init, gst_ks_video_src_init,
gst_ks_video_src_finalize, gst_ks_video_src_get_property,
gst_ks_video_src_set_property, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_change_state,
gst_ks_video_src_set_caps):
First driver quirk: work around Logitech's hostile driver software to
improve stability and performance. See comments for details.
Provide a property to disable driver quirks (enabled by default).
Be nice and specify G_PARAM_STATIC_STRINGS.
Remove unused dispose method.
Tweak include order.
2008-08-27 21:33:07 +00:00
|
|
|
/* Skip the ACQUIRE step on the way down like DirectShow does */
|
|
|
|
if (addend < 0 && next_state == KSSTATE_ACQUIRE)
|
|
|
|
next_state = KSSTATE_STOP;
|
|
|
|
|
2008-08-24 22:05:48 +00:00
|
|
|
GST_DEBUG ("Changing pin state from %s to %s",
|
|
|
|
ks_state_to_string (priv->state), ks_state_to_string (next_state));
|
|
|
|
|
2009-10-12 15:26:15 +00:00
|
|
|
if (ks_object_set_connection_state (priv->pin_handle, next_state,
|
|
|
|
error_code)) {
|
2008-08-24 22:05:48 +00:00
|
|
|
priv->state = next_state;
|
|
|
|
|
|
|
|
GST_DEBUG ("Changed pin state to %s", ks_state_to_string (priv->state));
|
|
|
|
|
|
|
|
if (priv->state == KSSTATE_PAUSE && addend > 0)
|
|
|
|
gst_ks_video_device_prepare_buffers (self);
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
else if (priv->state == KSSTATE_STOP && addend < 0)
|
2008-08-24 22:05:48 +00:00
|
|
|
gst_ks_video_device_clear_buffers (self);
|
|
|
|
} else {
|
|
|
|
GST_WARNING ("Failed to change pin state to %s",
|
|
|
|
ks_state_to_string (next_state));
|
2009-10-12 15:26:15 +00:00
|
|
|
|
2008-08-24 22:05:48 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GST_DEBUG ("Finished pin state change from %s to %s",
|
|
|
|
ks_state_to_string (initial_state), ks_state_to_string (state));
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2009-09-07 14:45:57 +00:00
|
|
|
static guint
|
2008-08-24 22:05:48 +00:00
|
|
|
gst_ks_video_device_get_frame_size (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
g_assert (priv->cur_media_type != NULL);
|
|
|
|
|
|
|
|
return priv->cur_media_type->sample_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
GstClockTime
|
|
|
|
gst_ks_video_device_get_duration (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
g_assert (priv->cur_media_type != NULL);
|
|
|
|
|
|
|
|
return gst_util_uint64_scale_int (GST_SECOND, priv->fps_d, priv->fps_n);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
gst_ks_video_device_get_latency (GstKsVideoDevice * self,
|
|
|
|
GstClockTime * min_latency, GstClockTime * max_latency)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
if (priv->cur_media_type == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
*min_latency =
|
|
|
|
gst_util_uint64_scale_int (GST_SECOND, priv->fps_d, priv->fps_n);
|
|
|
|
*max_latency = *min_latency;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2009-09-07 14:45:57 +00:00
|
|
|
static gboolean
|
|
|
|
gst_ks_read_request_pick_buffer (GstKsVideoDevice * self, ReadRequest * req)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
gboolean buffer_found = FALSE;
|
|
|
|
guint i;
|
|
|
|
|
2015-02-03 22:44:34 +00:00
|
|
|
buffer_found = gst_buffer_is_writable (req->buf)
|
|
|
|
&& gst_buffer_is_all_memory_writable (req->buf);
|
2009-09-07 14:45:57 +00:00
|
|
|
|
|
|
|
for (i = 0; !buffer_found && i < G_N_ELEMENTS (priv->spare_buffers); i++) {
|
2015-02-03 22:44:34 +00:00
|
|
|
if (gst_buffer_is_writable (priv->spare_buffers[i])
|
|
|
|
&& gst_buffer_is_all_memory_writable (priv->spare_buffers[i])) {
|
2009-09-07 14:45:57 +00:00
|
|
|
GstBuffer *hold;
|
|
|
|
|
|
|
|
hold = req->buf;
|
|
|
|
req->buf = priv->spare_buffers[i];
|
|
|
|
priv->spare_buffers[i] = hold;
|
|
|
|
|
|
|
|
buffer_found = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!buffer_found) {
|
|
|
|
gst_buffer_unref (req->buf);
|
|
|
|
req->buf = self->allocfunc (gst_ks_video_device_get_frame_size (self),
|
|
|
|
KS_BUFFER_ALIGNMENT, self->allocfunc_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req->buf != NULL) {
|
|
|
|
GST_BUFFER_FLAGS (req->buf) = 0;
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-24 22:05:48 +00:00
|
|
|
static gboolean
|
|
|
|
gst_ks_video_device_request_frame (GstKsVideoDevice * self, ReadRequest * req,
|
|
|
|
gulong * error_code, gchar ** error_str)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
HANDLE event;
|
|
|
|
KSSTREAM_READ_PARAMS *params;
|
|
|
|
BOOL success;
|
|
|
|
DWORD bytes_returned = 0;
|
2014-03-14 17:23:51 +00:00
|
|
|
GstMapInfo info;
|
2008-08-24 22:05:48 +00:00
|
|
|
|
2009-09-07 14:45:57 +00:00
|
|
|
if (!gst_ks_read_request_pick_buffer (self, req))
|
|
|
|
goto error_pick_buffer;
|
|
|
|
|
2008-08-24 22:05:48 +00:00
|
|
|
/* Reset the OVERLAPPED structure */
|
|
|
|
event = req->overlapped.hEvent;
|
|
|
|
memset (&req->overlapped, 0, sizeof (OVERLAPPED));
|
|
|
|
req->overlapped.hEvent = event;
|
|
|
|
|
|
|
|
/* Fill out KSSTREAM_HEADER and KS_FRAME_INFO */
|
|
|
|
params = &req->params;
|
|
|
|
memset (params, 0, sizeof (KSSTREAM_READ_PARAMS));
|
|
|
|
|
2015-02-03 22:44:34 +00:00
|
|
|
if (!gst_buffer_map (req->buf, &info, GST_MAP_WRITE))
|
|
|
|
goto map_failed;
|
|
|
|
|
2015-04-29 19:37:56 +00:00
|
|
|
params->header.Size = sizeof (KSSTREAM_HEADER);
|
|
|
|
if (!priv->is_muxed) {
|
|
|
|
params->header.Size += sizeof (KS_FRAME_INFO);
|
|
|
|
}
|
2008-08-24 22:05:48 +00:00
|
|
|
params->header.PresentationTime.Numerator = 1;
|
|
|
|
params->header.PresentationTime.Denominator = 1;
|
|
|
|
params->header.FrameExtent = gst_ks_video_device_get_frame_size (self);
|
2014-03-14 17:23:51 +00:00
|
|
|
params->header.Data = info.data;
|
2008-08-24 22:05:48 +00:00
|
|
|
params->frame_info.ExtendedHeaderSize = sizeof (KS_FRAME_INFO);
|
|
|
|
|
|
|
|
success = DeviceIoControl (priv->pin_handle, IOCTL_KS_READ_STREAM, NULL, 0,
|
|
|
|
params, params->header.Size, &bytes_returned, &req->overlapped);
|
2014-03-14 17:23:51 +00:00
|
|
|
gst_buffer_unmap (req->buf, &info);
|
2008-08-24 22:05:48 +00:00
|
|
|
if (!success && GetLastError () != ERROR_IO_PENDING)
|
|
|
|
goto error_ioctl;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
/* ERRORS */
|
2009-09-07 14:45:57 +00:00
|
|
|
error_pick_buffer:
|
|
|
|
{
|
|
|
|
if (error_code != NULL)
|
|
|
|
*error_code = 0;
|
|
|
|
if (error_str != NULL)
|
|
|
|
*error_str = NULL;
|
|
|
|
return FALSE;
|
|
|
|
}
|
2008-08-24 22:05:48 +00:00
|
|
|
error_ioctl:
|
|
|
|
{
|
|
|
|
gst_ks_video_device_parse_win32_error ("DeviceIoControl", GetLastError (),
|
|
|
|
error_code, error_str);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2015-02-03 22:44:34 +00:00
|
|
|
map_failed:
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
2008-08-24 22:05:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GstFlowReturn
|
2009-09-07 14:45:57 +00:00
|
|
|
gst_ks_video_device_read_frame (GstKsVideoDevice * self, GstBuffer ** buf,
|
|
|
|
GstClockTime * presentation_time, gulong * error_code, gchar ** error_str)
|
2008-08-24 22:05:48 +00:00
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
guint req_idx;
|
|
|
|
DWORD wait_ret;
|
|
|
|
BOOL success;
|
|
|
|
DWORD bytes_returned;
|
|
|
|
|
|
|
|
g_assert (priv->cur_media_type != NULL);
|
|
|
|
|
|
|
|
/* First time we're called, submit the requests. */
|
|
|
|
if (G_UNLIKELY (!priv->requests_submitted)) {
|
|
|
|
priv->requests_submitted = TRUE;
|
|
|
|
|
|
|
|
for (req_idx = 0; req_idx < priv->num_requests; req_idx++) {
|
|
|
|
ReadRequest *req = &g_array_index (priv->requests, ReadRequest, req_idx);
|
|
|
|
|
|
|
|
if (!gst_ks_video_device_request_frame (self, req, error_code, error_str))
|
|
|
|
goto error_request_failed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-07 14:45:57 +00:00
|
|
|
*buf = NULL;
|
|
|
|
|
2008-08-24 22:05:48 +00:00
|
|
|
do {
|
|
|
|
/* Wait for either a request to complete, a cancel or a timeout */
|
|
|
|
wait_ret = WaitForMultipleObjects (priv->request_events->len,
|
|
|
|
(HANDLE *) priv->request_events->data, FALSE, READ_TIMEOUT);
|
|
|
|
if (wait_ret == WAIT_TIMEOUT)
|
|
|
|
goto error_timeout;
|
|
|
|
else if (wait_ret == WAIT_FAILED)
|
|
|
|
goto error_wait;
|
|
|
|
|
|
|
|
/* Stopped? */
|
|
|
|
if (WaitForSingleObject (priv->cancel_event, 0) == WAIT_OBJECT_0)
|
|
|
|
goto error_cancel;
|
|
|
|
|
|
|
|
/* Find the last ReadRequest that finished and get the result, immediately
|
|
|
|
* re-issuing each request that has completed. */
|
|
|
|
for (req_idx = wait_ret - WAIT_OBJECT_0;
|
|
|
|
req_idx < priv->num_requests; req_idx++) {
|
|
|
|
ReadRequest *req = &g_array_index (priv->requests, ReadRequest, req_idx);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Completed? WaitForMultipleObjects() returns the lowest index if
|
|
|
|
* multiple objects are in the signaled state, and we know that requests
|
|
|
|
* are processed one by one so there's no point in looking further once
|
|
|
|
* we've found the first that's non-signaled.
|
|
|
|
*/
|
|
|
|
if (WaitForSingleObject (req->overlapped.hEvent, 0) != WAIT_OBJECT_0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
success = GetOverlappedResult (priv->pin_handle, &req->overlapped,
|
|
|
|
&bytes_returned, TRUE);
|
|
|
|
|
|
|
|
ResetEvent (req->overlapped.hEvent);
|
|
|
|
|
|
|
|
if (success) {
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
KSSTREAM_HEADER *hdr = &req->params.header;
|
|
|
|
KS_FRAME_INFO *frame_info = &req->params.frame_info;
|
|
|
|
GstClockTime timestamp = GST_CLOCK_TIME_NONE;
|
|
|
|
GstClockTime duration = GST_CLOCK_TIME_NONE;
|
|
|
|
|
|
|
|
if (hdr->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID)
|
|
|
|
timestamp = hdr->PresentationTime.Time * 100;
|
|
|
|
|
|
|
|
if (hdr->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DURATIONVALID)
|
|
|
|
duration = hdr->Duration * 100;
|
|
|
|
|
2009-09-07 14:45:57 +00:00
|
|
|
UNREF_BUFFER (buf);
|
|
|
|
|
|
|
|
if (G_LIKELY (hdr->DataUsed != 0)) {
|
|
|
|
/* Assume it's a good frame */
|
2014-03-14 17:23:51 +00:00
|
|
|
gst_buffer_set_size (req->buf, hdr->DataUsed);
|
2009-09-07 14:45:57 +00:00
|
|
|
*buf = gst_buffer_ref (req->buf);
|
|
|
|
}
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
|
|
|
|
if (G_LIKELY (presentation_time != NULL))
|
|
|
|
*presentation_time = timestamp;
|
|
|
|
|
|
|
|
if (G_UNLIKELY (GST_DEBUG_IS_ENABLED ())) {
|
|
|
|
gchar *options_flags_str =
|
|
|
|
ks_options_flags_to_string (hdr->OptionsFlags);
|
|
|
|
|
|
|
|
GST_DEBUG ("PictureNumber=%" G_GUINT64_FORMAT ", DropCount=%"
|
|
|
|
G_GUINT64_FORMAT ", PresentationTime=%" GST_TIME_FORMAT
|
2012-08-07 16:22:36 +00:00
|
|
|
", Duration=%" GST_TIME_FORMAT ", OptionsFlags=%s: %lu bytes",
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
frame_info->PictureNumber, frame_info->DropCount,
|
|
|
|
GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration),
|
|
|
|
options_flags_str, hdr->DataUsed);
|
|
|
|
|
|
|
|
g_free (options_flags_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Protect against old frames. This should never happen, see previous
|
|
|
|
* comment on last_timestamp. */
|
|
|
|
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (timestamp))) {
|
|
|
|
if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->last_timestamp) &&
|
|
|
|
timestamp < priv->last_timestamp)) {
|
2010-10-28 14:14:34 +00:00
|
|
|
GST_INFO ("got an old frame (last_timestamp=%" GST_TIME_FORMAT
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
", timestamp=%" GST_TIME_FORMAT ")",
|
|
|
|
GST_TIME_ARGS (priv->last_timestamp),
|
|
|
|
GST_TIME_ARGS (timestamp));
|
2009-09-07 14:45:57 +00:00
|
|
|
UNREF_BUFFER (buf);
|
sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp, gst_ks_video_device_prepare_buffers, gst_ks_video...
Original commit message from CVS:
* sys/winks/gstksvideodevice.c (GST_DEBUG_IS_ENABLED, last_timestamp,
gst_ks_video_device_prepare_buffers, gst_ks_video_device_create_pin,
gst_ks_video_device_set_state, gst_ks_video_device_request_frame,
gst_ks_video_device_read_frame):
Guard against capturing old frames by keeping track of the last
timestamp and also zero-fill the buffers before each capture.
Only assign a master clock if the pin hasn't already got one.
Actually free buffers on the way down to avoid a huge memory leak,
as this was previously done when changing state to ACQUIRE downwards
and we now skip that state on the way down.
Add some debug.
* sys/winks/gstksvideosrc.c (DEFAULT_DEVICE_PATH, DEFAULT_DEVICE_NAME,
DEFAULT_DEVICE_INDEX, KS_WORKER_LOCK, KS_WORKER_UNLOCK,
KS_WORKER_WAIT, KS_WORKER_NOTIFY, KS_WORKER_WAIT_FOR_RESULT,
KS_WORKER_NOTIFY_RESULT, KS_WORKER_STATE_STARTING,
KS_WORKER_STATE_READY, KS_WORKER_STATE_STOPPING,
KS_WORKER_STATE_ERROR, KsWorkerState, device_path, device_name,
device_index, running, worker_thread, worker_lock,
worker_notify_cond, worker_result_cond, worker_state,
worker_pending_caps, worker_setcaps_result, worker_pending_run,
worker_run_result, gst_ks_video_src_reset,
gst_ks_video_src_apply_driver_quirks, gst_ks_video_src_open_device,
gst_ks_video_src_close_device, gst_ks_video_src_worker_func,
gst_ks_video_src_start_worker, gst_ks_video_src_stop_worker,
gst_ks_video_src_change_state, gst_ks_video_src_set_clock,
gst_ks_video_src_set_caps, gst_ks_video_src_timestamp_buffer,
gst_ks_video_src_create):
Remove ENABLE_CLOCK_DEBUG define, it's GST_LEVEL_DEBUG after all.
Get rid of PROP_ENSLAVE_KSCLOCK and always slave the ks clock to the
GStreamer clock, it doesn't seem to hurt and matches DirectShow's
behavior. As an added bonus we usually get PresentationTime set for
each frame, so we can expand on this later for smarter latency
reporting (by looking at the diff between the timestamp from the
driver and the time according to the GStreamer clock).
Use an internal worker thread for opening the device, setting caps,
changing its state and closing it. This way we're a lot more
compatible with drivers that rely on hacks to do video-effects
between the low-level NT API and the application. Ick.
Start the ks clock and set the pin to KSSTATE_RUN on the first
create() so that we'll hopefully get hold of the GStreamer clock
from the very beginning. This way there's no chance that the
timestamps will make a sudden jump in the beginning of the stream
when we're running with a clock.
* sys/winks/kshelpers.c (CHECK_OPTIONS_FLAG,
ks_options_flags_to_string):
Reorder the flags to match the headerfile order, and make the string
a bit more compact.
* sys/winks/ksvideohelpers.c (ks_video_probe_filter_for_caps):
Avoid leaking KSPROPERTY_PIN_DATARANGES.
2008-09-09 23:58:02 +00:00
|
|
|
} else {
|
|
|
|
priv->last_timestamp = timestamp;
|
|
|
|
}
|
|
|
|
}
|
2009-09-07 14:45:57 +00:00
|
|
|
} else if (GetLastError () != ERROR_OPERATION_ABORTED) {
|
2008-08-24 22:05:48 +00:00
|
|
|
goto error_get_result;
|
2009-09-07 14:45:57 +00:00
|
|
|
}
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
/* Submit a new request immediately */
|
|
|
|
if (!gst_ks_video_device_request_frame (self, req, error_code, error_str))
|
|
|
|
goto error_request_failed;
|
|
|
|
}
|
2009-09-07 14:45:57 +00:00
|
|
|
} while (*buf == NULL);
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
|
|
|
|
/* ERRORS */
|
|
|
|
error_request_failed:
|
|
|
|
{
|
2009-09-07 14:45:57 +00:00
|
|
|
UNREF_BUFFER (buf);
|
|
|
|
|
2008-08-24 22:05:48 +00:00
|
|
|
return GST_FLOW_ERROR;
|
|
|
|
}
|
|
|
|
error_timeout:
|
|
|
|
{
|
|
|
|
GST_DEBUG ("IOCTL_KS_READ_STREAM timed out");
|
|
|
|
|
|
|
|
if (error_code != NULL)
|
|
|
|
*error_code = 0;
|
|
|
|
if (error_str != NULL)
|
|
|
|
*error_str = NULL;
|
|
|
|
|
2014-03-14 17:23:51 +00:00
|
|
|
return GST_FLOW_CUSTOM_ERROR;
|
2008-08-24 22:05:48 +00:00
|
|
|
}
|
|
|
|
error_wait:
|
|
|
|
{
|
|
|
|
gst_ks_video_device_parse_win32_error ("WaitForMultipleObjects",
|
|
|
|
GetLastError (), error_code, error_str);
|
|
|
|
|
|
|
|
return GST_FLOW_ERROR;
|
|
|
|
}
|
|
|
|
error_cancel:
|
|
|
|
{
|
|
|
|
if (error_code != NULL)
|
|
|
|
*error_code = 0;
|
|
|
|
if (error_str != NULL)
|
|
|
|
*error_str = NULL;
|
|
|
|
|
2012-02-08 15:37:13 +00:00
|
|
|
return GST_FLOW_FLUSHING;
|
2008-08-24 22:05:48 +00:00
|
|
|
}
|
|
|
|
error_get_result:
|
|
|
|
{
|
|
|
|
gst_ks_video_device_parse_win32_error ("GetOverlappedResult",
|
|
|
|
GetLastError (), error_code, error_str);
|
|
|
|
|
|
|
|
return GST_FLOW_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-03 22:44:34 +00:00
|
|
|
gboolean
|
2018-04-22 17:27:37 +00:00
|
|
|
gst_ks_video_device_postprocess_frame (GstKsVideoDevice * self,
|
|
|
|
GstBuffer ** bufptr)
|
2008-08-24 22:05:48 +00:00
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
2016-08-15 06:37:44 +00:00
|
|
|
GstBuffer *buf = *bufptr;
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
/* If it's RGB we need to flip the image */
|
|
|
|
if (priv->rgb_swap_buf != NULL) {
|
2015-02-03 22:44:34 +00:00
|
|
|
GstMapInfo info;
|
2008-08-24 22:05:48 +00:00
|
|
|
gint stride, line;
|
|
|
|
guint8 *dst, *src;
|
|
|
|
|
2016-08-15 06:37:44 +00:00
|
|
|
/* Need to make the buffer writable because
|
|
|
|
* the pseudo-bufferpool of requests keeps a ref */
|
|
|
|
buf = gst_buffer_make_writable (buf);
|
|
|
|
|
2015-02-03 22:44:34 +00:00
|
|
|
if (!gst_buffer_map (buf, &info, GST_MAP_READWRITE))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
stride = info.size / priv->height;
|
|
|
|
dst = info.data;
|
|
|
|
src = info.data + info.size - stride;
|
2008-08-24 22:05:48 +00:00
|
|
|
|
|
|
|
for (line = 0; line < priv->height / 2; line++) {
|
|
|
|
memcpy (priv->rgb_swap_buf, dst, stride);
|
|
|
|
|
|
|
|
memcpy (dst, src, stride);
|
|
|
|
memcpy (src, priv->rgb_swap_buf, stride);
|
|
|
|
|
|
|
|
dst += stride;
|
|
|
|
src -= stride;
|
|
|
|
}
|
2015-02-03 22:44:34 +00:00
|
|
|
|
|
|
|
gst_buffer_unmap (buf, &info);
|
2008-08-24 22:05:48 +00:00
|
|
|
}
|
2016-08-15 06:37:44 +00:00
|
|
|
*bufptr = buf;
|
2015-02-03 22:44:34 +00:00
|
|
|
|
|
|
|
return TRUE;
|
2008-08-24 22:05:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gst_ks_video_device_cancel (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
SetEvent (priv->cancel_event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gst_ks_video_device_cancel_stop (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
ResetEvent (priv->cancel_event);
|
|
|
|
}
|
2015-04-29 07:57:52 +00:00
|
|
|
|
|
|
|
gboolean
|
|
|
|
gst_ks_video_device_stream_is_muxed (GstKsVideoDevice * self)
|
|
|
|
{
|
|
|
|
GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
return priv->is_muxed;
|
|
|
|
}
|