2 * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 #include "gstksvideodevice.h"
22 #include "gstksclock.h"
23 #include "kshelpers.h"
24 #include "ksvideohelpers.h"
26 #define READ_TIMEOUT (10 * 1000)
27 #define MJPEG_MAX_PADDING 128
28 #define MAX_OUTSTANDING_FRAMES 128
30 #define KS_BUFFER_ALIGNMENT 4096
32 #define DEFAULT_DEVICE_PATH NULL
34 GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
35 #define GST_CAT_DEFAULT gst_ks_debug
37 #define GST_DEBUG_IS_ENABLED() \
38 (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_DEBUG)
39 #define UNREF_BUFFER(b) \
42 gst_buffer_unref (*(b)); \
56 KSSTREAM_HEADER header;
57 KS_FRAME_INFO frame_info;
58 } KSSTREAM_READ_PARAMS;
62 KSSTREAM_READ_PARAMS params;
64 OVERLAPPED overlapped;
67 struct _GstKsVideoDevicePrivate
79 KsVideoMediaType *cur_media_type;
80 GstCaps *cur_fixed_caps;
90 gboolean requests_submitted;
93 GArray *request_events;
94 GstBuffer *spare_buffers[2];
95 GstClockTime last_timestamp;
98 #define GST_KS_VIDEO_DEVICE_GET_PRIVATE(o) ((o)->priv)
100 static void gst_ks_video_device_dispose (GObject * object);
101 static void gst_ks_video_device_get_property (GObject * object, guint prop_id,
102 GValue * value, GParamSpec * pspec);
103 static void gst_ks_video_device_set_property (GObject * object, guint prop_id,
104 const GValue * value, GParamSpec * pspec);
106 static void gst_ks_video_device_reset_caps (GstKsVideoDevice * self);
107 static guint gst_ks_video_device_get_frame_size (GstKsVideoDevice * self);
109 G_DEFINE_TYPE_WITH_PRIVATE (GstKsVideoDevice, gst_ks_video_device,
112 static GstKsVideoDeviceClass *parent_class = NULL;
115 gst_ks_video_device_class_init (GstKsVideoDeviceClass * klass)
117 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
119 parent_class = g_type_class_peek_parent (klass);
121 gobject_class->dispose = gst_ks_video_device_dispose;
122 gobject_class->get_property = gst_ks_video_device_get_property;
123 gobject_class->set_property = gst_ks_video_device_set_property;
125 g_object_class_install_property (gobject_class, PROP_CLOCK,
126 g_param_spec_object ("clock", "Clock to use",
127 "Clock to use", GST_TYPE_KS_CLOCK,
128 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
130 g_object_class_install_property (gobject_class, PROP_DEVICE_PATH,
131 g_param_spec_string ("device-path", "Device Path",
132 "The device path", DEFAULT_DEVICE_PATH,
133 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
137 gst_ks_video_device_init (GstKsVideoDevice * self)
139 GstKsVideoDevicePrivate *priv;
141 self->priv = gst_ks_video_device_get_instance_private (self);
143 priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
145 priv->state = KSSTATE_STOP;
149 gst_ks_video_device_dispose (GObject * object)
151 GstKsVideoDevice *self = GST_KS_VIDEO_DEVICE (object);
152 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
154 gst_ks_video_device_reset_caps (self);
155 gst_ks_video_device_close (self);
157 if (priv->clock != NULL) {
158 g_object_unref (priv->clock);
162 G_OBJECT_CLASS (parent_class)->dispose (object);
166 gst_ks_video_device_get_property (GObject * object, guint prop_id,
167 GValue * value, GParamSpec * pspec)
169 GstKsVideoDevice *self = GST_KS_VIDEO_DEVICE (object);
170 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
174 g_value_set_object (value, priv->clock);
176 case PROP_DEVICE_PATH:
177 g_value_set_string (value, priv->dev_path);
180 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
186 gst_ks_video_device_set_property (GObject * object, guint prop_id,
187 const GValue * value, GParamSpec * pspec)
189 GstKsVideoDevice *self = GST_KS_VIDEO_DEVICE (object);
190 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
194 if (priv->clock != NULL)
195 g_object_unref (priv->clock);
196 priv->clock = g_value_dup_object (value);
198 case PROP_DEVICE_PATH:
199 g_free (priv->dev_path);
200 priv->dev_path = g_value_dup_string (value);
203 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
209 gst_ks_video_device_parse_win32_error (const gchar * func_name,
210 DWORD error_code, gulong * ret_error_code, gchar ** ret_error_str)
212 if (ret_error_code != NULL)
213 *ret_error_code = error_code;
215 if (ret_error_str != NULL) {
220 message = g_string_sized_new (1600);
221 g_string_append_printf (message, "%s returned ", func_name);
224 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |
225 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error_code, 0, buf, sizeof (buf),
228 g_string_append_printf (message, "0x%08x: %s", (guint) error_code,
231 DWORD format_error_code = GetLastError ();
233 g_string_append_printf (message,
234 "<0x%08x (FormatMessage error code: %s)>", (guint) error_code,
235 (format_error_code == ERROR_MR_MID_NOT_FOUND)
236 ? "no system error message found"
237 : "failed to retrieve system error message");
240 *ret_error_str = g_string_free (message, FALSE);
245 gst_ks_video_device_clear_buffers (GstKsVideoDevice * self)
247 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
250 if (priv->requests == NULL)
253 /* Join any pending requests */
254 for (i = 0; i < priv->num_requests; i++) {
255 ReadRequest *req = &g_array_index (priv->requests, ReadRequest, i);
256 HANDLE ev = g_array_index (priv->request_events, HANDLE, i);
259 if (!GetOverlappedResult (priv->pin_handle, &req->overlapped, &n, FALSE)) {
260 if (WaitForSingleObject (ev, 1000) == WAIT_OBJECT_0)
261 GetOverlappedResult (priv->pin_handle, &req->overlapped, &n, FALSE);
266 for (i = 0; i < G_N_ELEMENTS (priv->spare_buffers); i++) {
267 gst_buffer_unref (priv->spare_buffers[i]);
268 priv->spare_buffers[i] = NULL;
271 for (i = 0; i < priv->requests->len; i++) {
272 ReadRequest *req = &g_array_index (priv->requests, ReadRequest, i);
273 HANDLE ev = g_array_index (priv->request_events, HANDLE, i);
275 gst_buffer_unref (req->buf);
281 g_array_free (priv->requests, TRUE);
282 priv->requests = NULL;
284 g_array_free (priv->request_events, TRUE);
285 priv->request_events = NULL;
289 gst_ks_video_device_prepare_buffers (GstKsVideoDevice * self)
291 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
295 g_assert (priv->cur_media_type != NULL);
297 gst_ks_video_device_clear_buffers (self);
299 priv->requests = g_array_sized_new (FALSE, TRUE, sizeof (ReadRequest),
301 priv->request_events = g_array_sized_new (FALSE, TRUE, sizeof (HANDLE),
302 priv->num_requests + 1);
304 frame_size = gst_ks_video_device_get_frame_size (self);
306 for (i = 0; i < G_N_ELEMENTS (priv->spare_buffers); i++) {
307 priv->spare_buffers[i] = self->allocfunc (frame_size, KS_BUFFER_ALIGNMENT,
308 self->allocfunc_data);
311 for (i = 0; i < priv->num_requests; i++) {
313 memset (&req, '0', sizeof (ReadRequest));
315 req.buf = self->allocfunc (frame_size, KS_BUFFER_ALIGNMENT,
316 self->allocfunc_data);
318 req.overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
320 g_array_append_val (priv->requests, req);
321 g_array_append_val (priv->request_events, req.overlapped.hEvent);
324 g_array_append_val (priv->request_events, priv->cancel_event);
327 * REVISIT: Could probably remove this later, for now it's here to help
328 * track down the case where we capture old frames. This has been
329 * observed with UVC cameras, presumably with some system load.
331 priv->last_timestamp = GST_CLOCK_TIME_NONE;
335 gst_ks_video_device_dump_supported_property_sets (GstKsVideoDevice * self,
336 const gchar * obj_name, const GUID * propsets, gulong propsets_len)
340 GST_DEBUG ("%s supports %lu property set%s", obj_name, propsets_len,
341 (propsets_len != 1) ? "s" : "");
343 for (i = 0; i < propsets_len; i++) {
344 gchar *propset_name = ks_property_set_to_string (&propsets[i]);
345 GST_DEBUG ("[%d] %s", i, propset_name);
346 g_free (propset_name);
351 gst_ks_video_device_new (const gchar * device_path, GstKsClock * clock,
352 GstKsAllocFunction allocfunc, gpointer allocfunc_data)
354 GstKsVideoDevice *device;
356 device = g_object_new (GST_TYPE_KS_VIDEO_DEVICE,
357 "device-path", device_path, "clock", clock, NULL);
358 device->allocfunc = allocfunc;
359 device->allocfunc_data = allocfunc_data;
365 gst_ks_video_device_open (GstKsVideoDevice * self)
367 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
368 GUID *propsets = NULL;
372 g_assert (!priv->open);
373 g_assert (priv->dev_path != NULL);
378 priv->filter_handle = CreateFile (priv->dev_path,
379 GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
380 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
381 if (!ks_is_valid_handle (priv->filter_handle))
385 * Query the filter for supported property sets.
387 if (ks_object_get_supported_property_sets (priv->filter_handle, &propsets,
389 gst_ks_video_device_dump_supported_property_sets (self, "filter",
390 propsets, propsets_len);
393 GST_DEBUG ("failed to query filter for supported property sets");
397 * Probe for supported media types.
399 priv->media_types = ks_video_probe_filter_for_caps (priv->filter_handle);
400 priv->cached_caps = gst_caps_new_empty ();
402 for (cur = priv->media_types; cur != NULL; cur = cur->next) {
403 KsVideoMediaType *media_type = cur->data;
405 gst_caps_append (priv->cached_caps,
406 gst_caps_copy (media_type->translated_caps));
411 str = gst_caps_to_string (media_type->translated_caps);
412 GST_DEBUG ("pin[%d]: found media type: %s", media_type->pin_id, str);
418 priv->cancel_event = CreateEvent (NULL, TRUE, FALSE, NULL);
425 g_free (priv->dev_path);
426 priv->dev_path = NULL;
432 gst_ks_video_device_close (GstKsVideoDevice * self)
434 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
437 gst_ks_video_device_reset_caps (self);
439 g_free (priv->dev_path);
440 priv->dev_path = NULL;
442 if (ks_is_valid_handle (priv->filter_handle)) {
443 CloseHandle (priv->filter_handle);
444 priv->filter_handle = INVALID_HANDLE_VALUE;
447 for (cur = priv->media_types; cur != NULL; cur = cur->next) {
448 KsVideoMediaType *mt = cur->data;
449 ks_video_media_type_free (mt);
452 if (priv->media_types != NULL) {
453 g_list_free (priv->media_types);
454 priv->media_types = NULL;
457 if (priv->cached_caps != NULL) {
458 gst_caps_unref (priv->cached_caps);
459 priv->cached_caps = NULL;
462 if (ks_is_valid_handle (priv->cancel_event))
463 CloseHandle (priv->cancel_event);
464 priv->cancel_event = INVALID_HANDLE_VALUE;
470 gst_ks_video_device_get_available_caps (GstKsVideoDevice * self)
472 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
474 g_assert (priv->open);
476 return gst_caps_ref (priv->cached_caps);
480 gst_ks_video_device_has_caps (GstKsVideoDevice * self)
482 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
484 return (priv->cur_media_type != NULL) ? TRUE : FALSE;
488 gst_ks_video_device_create_pin (GstKsVideoDevice * self,
489 KsVideoMediaType * media_type, gulong * num_outstanding)
491 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
493 HANDLE pin_handle = INVALID_HANDLE_VALUE;
494 KSPIN_CONNECT *pin_conn = NULL;
498 GUID *propsets = NULL;
500 gboolean supports_mem_transport = FALSE;
502 KSALLOCATOR_FRAMING *framing = NULL;
503 gulong framing_size = sizeof (KSALLOCATOR_FRAMING);
504 KSALLOCATOR_FRAMING_EX *framing_ex = NULL;
510 * Instantiate the pin.
512 pin_conn = ks_video_create_pin_conn_from_media_type (media_type);
514 for (retry_count = 0; retry_count != 5; retry_count++) {
516 GST_DEBUG ("calling KsCreatePin with pin_id = %d", media_type->pin_id);
518 ret = KsCreatePin (priv->filter_handle, pin_conn, GENERIC_READ,
520 if (ret != ERROR_NOT_READY)
523 /* wait and retry, like the reference implementation does */
524 if (WaitForSingleObject (priv->cancel_event, 1000) == WAIT_OBJECT_0)
528 if (ret != ERROR_SUCCESS)
529 goto error_create_pin;
531 GST_DEBUG ("KsCreatePin succeeded, pin %p created", pin_handle);
537 * Query the pin for supported property sets.
539 if (ks_object_get_supported_property_sets (pin_handle, &propsets,
543 gst_ks_video_device_dump_supported_property_sets (self, "pin", propsets,
546 for (i = 0; i < propsets_len; i++) {
547 if (IsEqualGUID (&propsets[i], &KSPROPSETID_MemoryTransport))
548 supports_mem_transport = TRUE;
553 GST_DEBUG ("failed to query pin for supported property sets");
557 * Figure out how many simultaneous requests it prefers.
559 * This is really important as it depends on the driver and the device.
560 * Doing too few will result in poor capture performance, whilst doing too
561 * many will make some drivers crash really horribly and leave you with a
562 * BSOD. I've experienced the latter with older Logitech drivers.
564 *num_outstanding = 0;
567 if (ks_object_get_property (pin_handle, KSPROPSETID_Connection,
568 KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX, (void *) &framing_ex, NULL,
570 if (framing_ex->CountItems >= 1) {
571 *num_outstanding = framing_ex->FramingItem[0].Frames;
572 alignment = framing_ex->FramingItem[0].FileAlignment;
574 GST_DEBUG ("ignoring empty ALLOCATORFRAMING_EX");
577 GST_DEBUG ("query for ALLOCATORFRAMING_EX failed, trying "
580 if (ks_object_get_property (pin_handle, KSPROPSETID_Connection,
581 KSPROPERTY_CONNECTION_ALLOCATORFRAMING, (void *) &framing,
582 &framing_size, NULL)) {
583 *num_outstanding = framing->Frames;
584 alignment = framing->FileAlignment;
586 GST_DEBUG ("query for ALLOCATORFRAMING failed");
590 GST_DEBUG ("num_outstanding: %lu alignment: 0x%08x", *num_outstanding,
593 if (*num_outstanding == 0 || *num_outstanding > MAX_OUTSTANDING_FRAMES) {
594 GST_DEBUG ("setting number of allowable outstanding frames to 1");
595 *num_outstanding = 1;
604 * TODO: We also need to respect alignment, but for now we just assume
605 * that allocfunc provides the appropriate alignment...
608 /* Set the memory transport to use. */
609 if (supports_mem_transport) {
610 mem_transport = 0; /* REVISIT: use the constant here */
611 if (!ks_object_set_property (pin_handle, KSPROPSETID_MemoryTransport,
612 KSPROPERTY_MEMORY_TRANSPORT, &mem_transport,
613 sizeof (mem_transport), NULL)) {
614 GST_DEBUG ("failed to set memory transport, sticking with the default");
619 * Override the clock if we have one and the pin doesn't have any either.
621 if (priv->clock != NULL) {
622 HANDLE *cur_clock_handle = NULL;
623 gulong cur_clock_handle_size = sizeof (HANDLE);
625 if (ks_object_get_property (pin_handle, KSPROPSETID_Stream,
626 KSPROPERTY_STREAM_MASTERCLOCK, (gpointer *) & cur_clock_handle,
627 &cur_clock_handle_size, NULL)) {
628 GST_DEBUG ("current master clock handle: %p", *cur_clock_handle);
629 CloseHandle (*cur_clock_handle);
630 g_free (cur_clock_handle);
632 HANDLE new_clock_handle = gst_ks_clock_get_handle (priv->clock);
634 if (ks_object_set_property (pin_handle, KSPROPSETID_Stream,
635 KSPROPERTY_STREAM_MASTERCLOCK, &new_clock_handle,
636 sizeof (new_clock_handle), NULL)) {
637 gst_ks_clock_prepare (priv->clock);
639 GST_WARNING ("failed to set pin's master clock");
651 gst_ks_video_device_parse_win32_error ("KsCreatePin", ret, NULL, &str);
652 GST_ERROR ("%s", str);
662 if (ks_is_valid_handle (pin_handle))
663 CloseHandle (pin_handle);
666 return INVALID_HANDLE_VALUE;
671 gst_ks_video_device_close_current_pin (GstKsVideoDevice * self)
673 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
675 if (!ks_is_valid_handle (priv->pin_handle))
678 gst_ks_video_device_set_state (self, KSSTATE_STOP, NULL);
680 CloseHandle (priv->pin_handle);
681 priv->pin_handle = INVALID_HANDLE_VALUE;
685 gst_ks_video_device_reset_caps (GstKsVideoDevice * self)
687 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
689 gst_ks_video_device_close_current_pin (self);
691 ks_video_media_type_free (priv->cur_media_type);
692 priv->cur_media_type = NULL;
694 priv->width = priv->height = priv->fps_n = priv->fps_d = 0;
696 g_free (priv->rgb_swap_buf);
697 priv->rgb_swap_buf = NULL;
699 if (priv->cur_fixed_caps != NULL) {
700 gst_caps_unref (priv->cur_fixed_caps);
701 priv->cur_fixed_caps = NULL;
706 gst_ks_video_device_set_caps (GstKsVideoDevice * self, GstCaps * caps)
708 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
712 /* State to be committed on success */
713 KsVideoMediaType *media_type = NULL;
714 gint width, height, fps_n, fps_d;
715 HANDLE pin_handle = INVALID_HANDLE_VALUE;
719 gst_ks_video_device_reset_caps (self);
723 /* Validate the caps */
724 if (!gst_caps_is_subset (caps, priv->cached_caps)) {
725 gchar *string_caps = gst_caps_to_string (caps);
726 gchar *string_c_caps = gst_caps_to_string (priv->cached_caps);
728 GST_ERROR ("caps (%s) is not a subset of device caps (%s)",
729 string_caps, string_c_caps);
731 g_free (string_caps);
732 g_free (string_c_caps);
737 for (cur = priv->media_types; cur != NULL; cur = cur->next) {
738 KsVideoMediaType *mt = cur->data;
740 if (gst_caps_is_subset (caps, mt->translated_caps)) {
741 media_type = ks_video_media_type_dup (mt);
746 if (media_type == NULL)
749 s = gst_caps_get_structure (caps, 0);
750 if (!gst_structure_get_int (s, "width", &width) ||
751 !gst_structure_get_int (s, "height", &height) ||
752 !gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) {
753 gst_structure_get_boolean (s, "systemstream", &priv->is_muxed);
754 if (!priv->is_muxed) {
755 GST_ERROR ("Failed to get width/height/fps");
759 if (!ks_video_fixate_media_type (media_type->range,
760 media_type->format, width, height, fps_n, fps_d))
764 if (priv->cur_media_type != NULL) {
765 if (media_type->format_size == priv->cur_media_type->format_size &&
766 memcmp (media_type->format, priv->cur_media_type->format,
767 priv->cur_media_type->format_size) == 0) {
768 GST_DEBUG ("%s: re-using existing pin", G_STRFUNC);
771 GST_DEBUG ("%s: re-creating pin", G_STRFUNC);
775 gst_ks_video_device_close_current_pin (self);
777 pin_handle = gst_ks_video_device_create_pin (self, media_type,
778 &priv->num_requests);
779 if (!ks_is_valid_handle (pin_handle)) {
780 /* Re-create the old pin */
781 if (priv->cur_media_type != NULL)
782 priv->pin_handle = gst_ks_video_device_create_pin (self,
783 priv->cur_media_type, &priv->num_requests);
787 /* Commit state: no turning back past this */
788 gst_ks_video_device_reset_caps (self);
790 priv->cur_media_type = media_type;
792 priv->height = height;
796 if (media_type->is_rgb)
797 priv->rgb_swap_buf = g_malloc (media_type->sample_size / priv->height);
799 priv->rgb_swap_buf = NULL;
801 priv->pin_handle = pin_handle;
803 priv->cur_fixed_caps = gst_caps_copy (caps);
809 ks_video_media_type_free (media_type);
814 ks_video_media_type_free (media_type);
820 gst_ks_video_device_set_state (GstKsVideoDevice * self, KSSTATE state,
823 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
824 KSSTATE initial_state;
827 g_assert (priv->cur_media_type != NULL);
829 if (state == priv->state)
832 initial_state = priv->state;
833 addend = (state > priv->state) ? 1 : -1;
835 GST_DEBUG ("Initiating pin state change from %s to %s",
836 ks_state_to_string (priv->state), ks_state_to_string (state));
838 while (priv->state != state) {
839 KSSTATE next_state = priv->state + addend;
841 /* Skip the ACQUIRE step on the way down like DirectShow does */
842 if (addend < 0 && next_state == KSSTATE_ACQUIRE)
843 next_state = KSSTATE_STOP;
845 GST_DEBUG ("Changing pin state from %s to %s",
846 ks_state_to_string (priv->state), ks_state_to_string (next_state));
848 if (ks_object_set_connection_state (priv->pin_handle, next_state,
850 priv->state = next_state;
852 GST_DEBUG ("Changed pin state to %s", ks_state_to_string (priv->state));
854 if (priv->state == KSSTATE_PAUSE && addend > 0)
855 gst_ks_video_device_prepare_buffers (self);
856 else if (priv->state == KSSTATE_STOP && addend < 0)
857 gst_ks_video_device_clear_buffers (self);
859 GST_WARNING ("Failed to change pin state to %s",
860 ks_state_to_string (next_state));
866 GST_DEBUG ("Finished pin state change from %s to %s",
867 ks_state_to_string (initial_state), ks_state_to_string (state));
873 gst_ks_video_device_get_frame_size (GstKsVideoDevice * self)
875 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
877 g_assert (priv->cur_media_type != NULL);
879 return priv->cur_media_type->sample_size;
883 gst_ks_video_device_get_duration (GstKsVideoDevice * self)
885 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
887 g_assert (priv->cur_media_type != NULL);
889 return gst_util_uint64_scale_int (GST_SECOND, priv->fps_d, priv->fps_n);
893 gst_ks_video_device_get_latency (GstKsVideoDevice * self,
894 GstClockTime * min_latency, GstClockTime * max_latency)
896 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
898 if (priv->cur_media_type == NULL)
902 gst_util_uint64_scale_int (GST_SECOND, priv->fps_d, priv->fps_n);
903 *max_latency = *min_latency;
909 gst_ks_read_request_pick_buffer (GstKsVideoDevice * self, ReadRequest * req)
911 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
912 gboolean buffer_found = FALSE;
915 buffer_found = gst_buffer_is_writable (req->buf)
916 && gst_buffer_is_all_memory_writable (req->buf);
918 for (i = 0; !buffer_found && i < G_N_ELEMENTS (priv->spare_buffers); i++) {
919 if (gst_buffer_is_writable (priv->spare_buffers[i])
920 && gst_buffer_is_all_memory_writable (priv->spare_buffers[i])) {
924 req->buf = priv->spare_buffers[i];
925 priv->spare_buffers[i] = hold;
932 gst_buffer_unref (req->buf);
933 req->buf = self->allocfunc (gst_ks_video_device_get_frame_size (self),
934 KS_BUFFER_ALIGNMENT, self->allocfunc_data);
937 if (req->buf != NULL) {
938 GST_BUFFER_FLAGS (req->buf) = 0;
946 gst_ks_video_device_request_frame (GstKsVideoDevice * self, ReadRequest * req,
947 gulong * error_code, gchar ** error_str)
949 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
951 KSSTREAM_READ_PARAMS *params;
953 DWORD bytes_returned = 0;
956 if (!gst_ks_read_request_pick_buffer (self, req))
957 goto error_pick_buffer;
959 /* Reset the OVERLAPPED structure */
960 event = req->overlapped.hEvent;
961 memset (&req->overlapped, 0, sizeof (OVERLAPPED));
962 req->overlapped.hEvent = event;
964 /* Fill out KSSTREAM_HEADER and KS_FRAME_INFO */
965 params = &req->params;
966 memset (params, 0, sizeof (KSSTREAM_READ_PARAMS));
968 if (!gst_buffer_map (req->buf, &info, GST_MAP_WRITE))
971 params->header.Size = sizeof (KSSTREAM_HEADER);
972 if (!priv->is_muxed) {
973 params->header.Size += sizeof (KS_FRAME_INFO);
975 params->header.PresentationTime.Numerator = 1;
976 params->header.PresentationTime.Denominator = 1;
977 params->header.FrameExtent = gst_ks_video_device_get_frame_size (self);
978 params->header.Data = info.data;
979 params->frame_info.ExtendedHeaderSize = sizeof (KS_FRAME_INFO);
981 success = DeviceIoControl (priv->pin_handle, IOCTL_KS_READ_STREAM, NULL, 0,
982 params, params->header.Size, &bytes_returned, &req->overlapped);
983 gst_buffer_unmap (req->buf, &info);
984 if (!success && GetLastError () != ERROR_IO_PENDING)
992 if (error_code != NULL)
994 if (error_str != NULL)
1000 gst_ks_video_device_parse_win32_error ("DeviceIoControl", GetLastError (),
1001 error_code, error_str);
1011 gst_ks_video_device_read_frame (GstKsVideoDevice * self, GstBuffer ** buf,
1012 GstClockTime * presentation_time, gulong * error_code, gchar ** error_str)
1014 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
1018 DWORD bytes_returned;
1020 g_assert (priv->cur_media_type != NULL);
1022 /* First time we're called, submit the requests. */
1023 if (G_UNLIKELY (!priv->requests_submitted)) {
1024 priv->requests_submitted = TRUE;
1026 for (req_idx = 0; req_idx < priv->num_requests; req_idx++) {
1027 ReadRequest *req = &g_array_index (priv->requests, ReadRequest, req_idx);
1029 if (!gst_ks_video_device_request_frame (self, req, error_code, error_str))
1030 goto error_request_failed;
1037 /* Wait for either a request to complete, a cancel or a timeout */
1038 wait_ret = WaitForMultipleObjects (priv->request_events->len,
1039 (HANDLE *) priv->request_events->data, FALSE, READ_TIMEOUT);
1040 if (wait_ret == WAIT_TIMEOUT)
1042 else if (wait_ret == WAIT_FAILED)
1046 if (WaitForSingleObject (priv->cancel_event, 0) == WAIT_OBJECT_0)
1049 /* Find the last ReadRequest that finished and get the result, immediately
1050 * re-issuing each request that has completed. */
1051 for (req_idx = wait_ret - WAIT_OBJECT_0;
1052 req_idx < priv->num_requests; req_idx++) {
1053 ReadRequest *req = &g_array_index (priv->requests, ReadRequest, req_idx);
1056 * Completed? WaitForMultipleObjects() returns the lowest index if
1057 * multiple objects are in the signaled state, and we know that requests
1058 * are processed one by one so there's no point in looking further once
1059 * we've found the first that's non-signaled.
1061 if (WaitForSingleObject (req->overlapped.hEvent, 0) != WAIT_OBJECT_0)
1064 success = GetOverlappedResult (priv->pin_handle, &req->overlapped,
1065 &bytes_returned, TRUE);
1067 ResetEvent (req->overlapped.hEvent);
1070 KSSTREAM_HEADER *hdr = &req->params.header;
1071 KS_FRAME_INFO *frame_info = &req->params.frame_info;
1072 GstClockTime timestamp = GST_CLOCK_TIME_NONE;
1073 GstClockTime duration = GST_CLOCK_TIME_NONE;
1075 if (hdr->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID)
1076 timestamp = hdr->PresentationTime.Time * 100;
1078 if (hdr->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DURATIONVALID)
1079 duration = hdr->Duration * 100;
1083 if (G_LIKELY (hdr->DataUsed != 0)) {
1084 /* Assume it's a good frame */
1085 gst_buffer_set_size (req->buf, hdr->DataUsed);
1086 *buf = gst_buffer_ref (req->buf);
1089 if (G_LIKELY (presentation_time != NULL))
1090 *presentation_time = timestamp;
1092 if (G_UNLIKELY (GST_DEBUG_IS_ENABLED ())) {
1093 gchar *options_flags_str =
1094 ks_options_flags_to_string (hdr->OptionsFlags);
1096 GST_DEBUG ("PictureNumber=%" G_GUINT64_FORMAT ", DropCount=%"
1097 G_GUINT64_FORMAT ", PresentationTime=%" GST_TIME_FORMAT
1098 ", Duration=%" GST_TIME_FORMAT ", OptionsFlags=%s: %lu bytes",
1099 frame_info->PictureNumber, frame_info->DropCount,
1100 GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration),
1101 options_flags_str, hdr->DataUsed);
1103 g_free (options_flags_str);
1106 /* Protect against old frames. This should never happen, see previous
1107 * comment on last_timestamp. */
1108 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (timestamp))) {
1109 if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->last_timestamp) &&
1110 timestamp < priv->last_timestamp)) {
1111 GST_INFO ("got an old frame (last_timestamp=%" GST_TIME_FORMAT
1112 ", timestamp=%" GST_TIME_FORMAT ")",
1113 GST_TIME_ARGS (priv->last_timestamp),
1114 GST_TIME_ARGS (timestamp));
1117 priv->last_timestamp = timestamp;
1120 } else if (GetLastError () != ERROR_OPERATION_ABORTED) {
1121 goto error_get_result;
1124 /* Submit a new request immediately */
1125 if (!gst_ks_video_device_request_frame (self, req, error_code, error_str))
1126 goto error_request_failed;
1128 } while (*buf == NULL);
1133 error_request_failed:
1137 return GST_FLOW_ERROR;
1141 GST_DEBUG ("IOCTL_KS_READ_STREAM timed out");
1143 if (error_code != NULL)
1145 if (error_str != NULL)
1148 return GST_FLOW_CUSTOM_ERROR;
1152 gst_ks_video_device_parse_win32_error ("WaitForMultipleObjects",
1153 GetLastError (), error_code, error_str);
1155 return GST_FLOW_ERROR;
1159 if (error_code != NULL)
1161 if (error_str != NULL)
1164 return GST_FLOW_FLUSHING;
1168 gst_ks_video_device_parse_win32_error ("GetOverlappedResult",
1169 GetLastError (), error_code, error_str);
1171 return GST_FLOW_ERROR;
1176 gst_ks_video_device_postprocess_frame (GstKsVideoDevice * self,
1177 GstBuffer ** bufptr)
1179 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
1180 GstBuffer *buf = *bufptr;
1182 /* If it's RGB we need to flip the image */
1183 if (priv->rgb_swap_buf != NULL) {
1188 /* Need to make the buffer writable because
1189 * the pseudo-bufferpool of requests keeps a ref */
1190 buf = gst_buffer_make_writable (buf);
1192 if (!gst_buffer_map (buf, &info, GST_MAP_READWRITE))
1195 stride = info.size / priv->height;
1197 src = info.data + info.size - stride;
1199 for (line = 0; line < priv->height / 2; line++) {
1200 memcpy (priv->rgb_swap_buf, dst, stride);
1202 memcpy (dst, src, stride);
1203 memcpy (src, priv->rgb_swap_buf, stride);
1209 gst_buffer_unmap (buf, &info);
1217 gst_ks_video_device_cancel (GstKsVideoDevice * self)
1219 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
1221 SetEvent (priv->cancel_event);
1225 gst_ks_video_device_cancel_stop (GstKsVideoDevice * self)
1227 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
1229 ResetEvent (priv->cancel_event);
1233 gst_ks_video_device_stream_is_muxed (GstKsVideoDevice * self)
1235 GstKsVideoDevicePrivate *priv = GST_KS_VIDEO_DEVICE_GET_PRIVATE (self);
1237 return priv->is_muxed;