mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
1784 lines
59 KiB
C
1784 lines
59 KiB
C
/*
|
|
* Copyright (C) 2025 Pascal Nowack
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program 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
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "grd-rdp-dvc-camera-device.h"
|
|
|
|
#include "grd-pipewire-utils.h"
|
|
#include "grd-rdp-camera-stream.h"
|
|
#include "grd-sample-buffer.h"
|
|
#include "grd-utils.h"
|
|
|
|
#define SAMPLE_TIMEOUT_MS (2 * 1000)
|
|
|
|
enum
|
|
{
|
|
ERROR,
|
|
|
|
N_SIGNALS
|
|
};
|
|
|
|
static guint signals[N_SIGNALS];
|
|
|
|
typedef enum
|
|
{
|
|
DEVICE_STATE_FATAL_ERROR,
|
|
DEVICE_STATE_PENDING_ACTIVATION,
|
|
DEVICE_STATE_PENDING_ACTIVATION_RESPONSE,
|
|
DEVICE_STATE_PENDING_STREAM_LIST_RESPONSE,
|
|
DEVICE_STATE_PENDING_MEDIA_TYPE_LIST_RESPONSE,
|
|
DEVICE_STATE_PENDING_PROPERTY_LIST_RESPONSE,
|
|
DEVICE_STATE_PENDING_PROPERTY_VALUE_RESPONSE,
|
|
DEVICE_STATE_PENDING_STREAM_PREPARATION,
|
|
DEVICE_STATE_INITIALIZATION_DONE,
|
|
DEVICE_STATE_IN_SHUTDOWN,
|
|
} DeviceState;
|
|
|
|
typedef enum
|
|
{
|
|
DEVICE_EVENT_ACK_STARTED_STREAMS,
|
|
DEVICE_EVENT_STOP_STREAMS,
|
|
DEVICE_EVENT_ACK_STOPPED_STREAMS,
|
|
} DeviceEvent;
|
|
|
|
typedef enum
|
|
{
|
|
CLIENT_REQUEST_TYPE_NONE,
|
|
CLIENT_REQUEST_TYPE_START_STREAMS,
|
|
CLIENT_REQUEST_TYPE_STOP_STREAMS,
|
|
} ClientRequestType;
|
|
|
|
typedef struct
|
|
{
|
|
CAM_MEDIA_TYPE_DESCRIPTION *media_type_description;
|
|
uint32_t run_sequence;
|
|
} StreamRunContext;
|
|
|
|
typedef struct
|
|
{
|
|
GrdRdpDvcCameraDevice *device;
|
|
|
|
GList *media_type_descriptions;
|
|
GrdRdpCameraStream *camera_stream;
|
|
|
|
GAsyncQueue *pending_samples;
|
|
} StreamContext;
|
|
|
|
struct _GrdRdpDvcCameraDevice
|
|
{
|
|
GrdRdpDvc parent;
|
|
|
|
CameraDeviceServerContext *device_context;
|
|
gboolean channel_opened;
|
|
|
|
char *dvc_name;
|
|
char *device_name;
|
|
|
|
GThread *camera_thread;
|
|
GMainContext *camera_context;
|
|
gboolean in_shutdown;
|
|
|
|
GrdSyncPoint sync_point;
|
|
|
|
GSource *initialization_source;
|
|
GMutex state_mutex;
|
|
DeviceState state;
|
|
GQueue *pending_media_type_lists;
|
|
GQueue *pending_property_values;
|
|
|
|
GHashTable *stream_contexts;
|
|
|
|
GSource *pipewire_source;
|
|
|
|
struct pw_context *pipewire_context;
|
|
struct pw_core *pipewire_core;
|
|
struct spa_hook pipewire_core_listener;
|
|
|
|
GHashTable *queued_stream_starts;
|
|
GHashTable *pending_stream_starts;
|
|
GHashTable *running_streams;
|
|
|
|
GSource *event_source;
|
|
|
|
GMutex event_mutex;
|
|
GHashTable *pending_events;
|
|
|
|
GMutex client_request_mutex;
|
|
gboolean pending_client_request;
|
|
ClientRequestType pending_client_request_type;
|
|
|
|
GMutex sample_request_mutex;
|
|
GHashTable *pending_samples;
|
|
|
|
GSource *sample_timeout_source;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GrdRdpDvcCameraDevice, grd_rdp_dvc_camera_device,
|
|
GRD_TYPE_RDP_DVC)
|
|
|
|
static StreamRunContext *
|
|
stream_run_context_new (CAM_MEDIA_TYPE_DESCRIPTION *media_type_description,
|
|
uint32_t run_sequence)
|
|
{
|
|
StreamRunContext *stream_run_context;
|
|
|
|
stream_run_context = g_new0 (StreamRunContext, 1);
|
|
stream_run_context->media_type_description = media_type_description;
|
|
stream_run_context->run_sequence = run_sequence;
|
|
|
|
return stream_run_context;
|
|
}
|
|
|
|
static void
|
|
stream_run_context_free (StreamRunContext *stream_run_context)
|
|
{
|
|
g_free (stream_run_context);
|
|
}
|
|
|
|
static StreamContext *
|
|
stream_context_new (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
StreamContext *stream_context;
|
|
|
|
stream_context = g_new0 (StreamContext, 1);
|
|
stream_context->device = device;
|
|
|
|
stream_context->pending_samples = g_async_queue_new ();
|
|
|
|
return stream_context;
|
|
}
|
|
|
|
static void
|
|
discard_pending_sample_requests (StreamContext *stream_context)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = stream_context->device;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
GrdSampleBuffer *sample_buffer;
|
|
|
|
locker = g_mutex_locker_new (&device->state_mutex);
|
|
while ((sample_buffer = g_async_queue_try_pop (stream_context->pending_samples)))
|
|
{
|
|
g_mutex_lock (&device->sample_request_mutex);
|
|
g_hash_table_remove (device->pending_samples, sample_buffer);
|
|
g_mutex_unlock (&device->sample_request_mutex);
|
|
|
|
grd_rdp_camera_stream_submit_sample (stream_context->camera_stream,
|
|
sample_buffer, FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
stream_context_free (StreamContext *stream_context)
|
|
{
|
|
if (stream_context->camera_stream)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = stream_context->device;
|
|
uint8_t stream_index =
|
|
grd_rdp_camera_stream_get_stream_index (stream_context->camera_stream);
|
|
|
|
discard_pending_sample_requests (stream_context);
|
|
|
|
g_hash_table_remove (device->queued_stream_starts,
|
|
GUINT_TO_POINTER (stream_index));
|
|
g_hash_table_remove (device->pending_stream_starts,
|
|
GUINT_TO_POINTER (stream_index));
|
|
g_hash_table_remove (device->running_streams,
|
|
GUINT_TO_POINTER (stream_index));
|
|
}
|
|
|
|
g_clear_object (&stream_context->camera_stream);
|
|
g_clear_list (&stream_context->media_type_descriptions, g_free);
|
|
|
|
g_clear_pointer (&stream_context->pending_samples, g_async_queue_unref);
|
|
|
|
g_free (stream_context);
|
|
}
|
|
|
|
const char *
|
|
grd_rdp_dvc_camera_device_get_dvc_name (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
return device->dvc_name;
|
|
}
|
|
|
|
void
|
|
grd_rdp_dvc_camera_device_start_stream (GrdRdpDvcCameraDevice *device,
|
|
GrdRdpCameraStream *camera_stream,
|
|
CAM_MEDIA_TYPE_DESCRIPTION *media_type_description,
|
|
uint32_t run_sequence)
|
|
{
|
|
uint8_t stream_index = grd_rdp_camera_stream_get_stream_index (camera_stream);
|
|
|
|
g_hash_table_insert (device->queued_stream_starts,
|
|
GUINT_TO_POINTER (stream_index),
|
|
stream_run_context_new (media_type_description,
|
|
run_sequence));
|
|
|
|
g_source_set_ready_time (device->event_source, 0);
|
|
}
|
|
|
|
static gboolean
|
|
queue_stream_restart (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = user_data;
|
|
uint8_t stream_index = GPOINTER_TO_UINT (key);
|
|
StreamRunContext *stream_run_context = value;
|
|
StreamContext *stream_context = NULL;
|
|
|
|
if (!g_hash_table_lookup_extended (device->stream_contexts,
|
|
GUINT_TO_POINTER (stream_index),
|
|
NULL, (gpointer *) &stream_context))
|
|
g_assert_not_reached ();
|
|
|
|
grd_rdp_camera_stream_inhibit_camera_loop (stream_context->camera_stream);
|
|
|
|
if (!g_hash_table_contains (device->queued_stream_starts,
|
|
GUINT_TO_POINTER (stream_index)))
|
|
{
|
|
g_hash_table_insert (device->queued_stream_starts,
|
|
GUINT_TO_POINTER (stream_index),
|
|
g_memdup2 (stream_run_context,
|
|
sizeof (StreamRunContext)));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
grd_rdp_dvc_camera_device_stop_stream (GrdRdpDvcCameraDevice *device,
|
|
GrdRdpCameraStream *camera_stream)
|
|
{
|
|
uint8_t stream_index = grd_rdp_camera_stream_get_stream_index (camera_stream);
|
|
|
|
g_hash_table_remove (device->running_streams,
|
|
GUINT_TO_POINTER (stream_index));
|
|
g_hash_table_remove (device->queued_stream_starts,
|
|
GUINT_TO_POINTER (stream_index));
|
|
|
|
/*
|
|
* Stopping a stream can mean one of the two things:
|
|
* 1) The stream is stopped, because there is no consumer left
|
|
* 2) The stream format was changed
|
|
*
|
|
* For case 2, all streams need to be stopped, because the [MS-RDPECAM]
|
|
* protocol only supports stopping all streams and not stopping one individual
|
|
* stream.
|
|
*/
|
|
g_hash_table_foreach_remove (device->running_streams,
|
|
queue_stream_restart, device);
|
|
|
|
g_mutex_lock (&device->event_mutex);
|
|
g_hash_table_add (device->pending_events,
|
|
GUINT_TO_POINTER (DEVICE_EVENT_STOP_STREAMS));
|
|
g_mutex_unlock (&device->event_mutex);
|
|
|
|
g_source_set_ready_time (device->event_source, 0);
|
|
}
|
|
|
|
void
|
|
grd_rdp_dvc_camera_device_request_sample (GrdRdpDvcCameraDevice *device,
|
|
GrdRdpCameraStream *camera_stream,
|
|
GrdSampleBuffer *sample_buffer)
|
|
{
|
|
CameraDeviceServerContext *device_context = device->device_context;
|
|
uint8_t stream_index = grd_rdp_camera_stream_get_stream_index (camera_stream);
|
|
StreamContext *stream_context = NULL;
|
|
CAM_SAMPLE_REQUEST sample_request = {};
|
|
|
|
g_assert (device->pending_client_request_type !=
|
|
CLIENT_REQUEST_TYPE_STOP_STREAMS);
|
|
|
|
if (!g_hash_table_lookup_extended (device->stream_contexts,
|
|
GUINT_TO_POINTER (stream_index),
|
|
NULL, (gpointer *) &stream_context))
|
|
g_assert_not_reached ();
|
|
|
|
g_mutex_lock (&device->sample_request_mutex);
|
|
g_hash_table_add (device->pending_samples, sample_buffer);
|
|
g_mutex_unlock (&device->sample_request_mutex);
|
|
|
|
g_async_queue_push (stream_context->pending_samples, sample_buffer);
|
|
|
|
sample_request.StreamIndex = stream_index;
|
|
|
|
device_context->SampleRequest (device_context, &sample_request);
|
|
}
|
|
|
|
static void
|
|
dvc_creation_status_cb (gpointer user_data,
|
|
int32_t creation_status)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = user_data;
|
|
|
|
if (creation_status < 0)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Failed to open %s channel "
|
|
"(CreationStatus %i, device name: \"%s\"). "
|
|
"Terminating protocol",
|
|
device->dvc_name, creation_status, device->device_name);
|
|
g_signal_emit (device, signals[ERROR], 0);
|
|
return;
|
|
}
|
|
|
|
g_message ("[RDP.CAM_DEVICE] Successfully opened %s channel "
|
|
"(CreationStatus %i, device name: \"%s\"). ",
|
|
device->dvc_name, creation_status, device->device_name);
|
|
|
|
g_source_set_ready_time (device->initialization_source, 0);
|
|
}
|
|
|
|
static BOOL
|
|
device_channel_id_assigned (CameraDeviceServerContext *device_context,
|
|
uint32_t channel_id)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = device_context->userdata;
|
|
GrdRdpDvc *dvc = GRD_RDP_DVC (device);
|
|
|
|
g_debug ("[RDP.CAM_DEVICE] DVC channel id for channel %s assigned to id %u",
|
|
device->dvc_name, channel_id);
|
|
|
|
grd_rdp_dvc_subscribe_creation_status (dvc, channel_id,
|
|
dvc_creation_status_cb,
|
|
device);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static const char *
|
|
device_state_to_string (DeviceState state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case DEVICE_STATE_FATAL_ERROR:
|
|
return "FATAL_ERROR";
|
|
case DEVICE_STATE_PENDING_ACTIVATION:
|
|
return "PENDING_ACTIVATION";
|
|
case DEVICE_STATE_PENDING_ACTIVATION_RESPONSE:
|
|
return "PENDING_ACTIVATION_RESPONSE";
|
|
case DEVICE_STATE_PENDING_STREAM_LIST_RESPONSE:
|
|
return "PENDING_STREAM_LIST_RESPONSE";
|
|
case DEVICE_STATE_PENDING_MEDIA_TYPE_LIST_RESPONSE:
|
|
return "PENDING_MEDIA_TYPE_LIST_RESPONSE";
|
|
case DEVICE_STATE_PENDING_PROPERTY_LIST_RESPONSE:
|
|
return "PENDING_PROPERTY_LIST_RESPONSE";
|
|
case DEVICE_STATE_PENDING_PROPERTY_VALUE_RESPONSE:
|
|
return "PENDING_PROPERTY_VALUE_RESPONSE";
|
|
case DEVICE_STATE_PENDING_STREAM_PREPARATION:
|
|
return "PENDING_STREAM_PREPARATION";
|
|
case DEVICE_STATE_INITIALIZATION_DONE:
|
|
return "INITIALIZATION_DONE";
|
|
case DEVICE_STATE_IN_SHUTDOWN:
|
|
return "IN_SHUTDOWN";
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static void
|
|
transition_into_fatal_error_state (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
device->state = DEVICE_STATE_FATAL_ERROR;
|
|
g_signal_emit (device, signals[ERROR], 0);
|
|
}
|
|
|
|
static void
|
|
request_stream_list (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
CameraDeviceServerContext *device_context = device->device_context;
|
|
CAM_STREAM_LIST_REQUEST stream_list_request = {};
|
|
|
|
device->state = DEVICE_STATE_PENDING_STREAM_LIST_RESPONSE;
|
|
device_context->StreamListRequest (device_context, &stream_list_request);
|
|
}
|
|
|
|
static gboolean
|
|
try_handle_runtime_success_response (GrdRdpDvcCameraDevice *device,
|
|
GError **error)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->client_request_mutex);
|
|
if (!device->pending_client_request)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Protocol violation: "
|
|
"Received stray success response in state %s for device "
|
|
"\"%s\". There was no runtime request. Removing device...",
|
|
device_state_to_string (device->state), device->device_name);
|
|
return FALSE;
|
|
}
|
|
|
|
g_mutex_lock (&device->event_mutex);
|
|
switch (device->pending_client_request_type)
|
|
{
|
|
case CLIENT_REQUEST_TYPE_NONE:
|
|
g_assert_not_reached ();
|
|
break;
|
|
case CLIENT_REQUEST_TYPE_START_STREAMS:
|
|
g_hash_table_add (device->pending_events,
|
|
GUINT_TO_POINTER (DEVICE_EVENT_ACK_STARTED_STREAMS));
|
|
break;
|
|
case CLIENT_REQUEST_TYPE_STOP_STREAMS:
|
|
g_hash_table_add (device->pending_events,
|
|
GUINT_TO_POINTER (DEVICE_EVENT_ACK_STOPPED_STREAMS));
|
|
break;
|
|
}
|
|
g_mutex_unlock (&device->event_mutex);
|
|
|
|
g_source_set_ready_time (device->event_source, 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Actions for success:
|
|
*
|
|
* - Activate Device Request
|
|
* - Deactivate Device Request
|
|
* - Start Streams Request
|
|
* - Stop Streams Request
|
|
* - Set Property Value Request
|
|
*/
|
|
static uint32_t
|
|
device_success_response (CameraDeviceServerContext *device_context,
|
|
const CAM_SUCCESS_RESPONSE *success_response)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = device_context->userdata;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->state_mutex);
|
|
switch (device->state)
|
|
{
|
|
case DEVICE_STATE_FATAL_ERROR:
|
|
case DEVICE_STATE_IN_SHUTDOWN:
|
|
break;
|
|
case DEVICE_STATE_PENDING_ACTIVATION:
|
|
case DEVICE_STATE_PENDING_STREAM_LIST_RESPONSE:
|
|
case DEVICE_STATE_PENDING_MEDIA_TYPE_LIST_RESPONSE:
|
|
case DEVICE_STATE_PENDING_PROPERTY_LIST_RESPONSE:
|
|
case DEVICE_STATE_PENDING_PROPERTY_VALUE_RESPONSE:
|
|
case DEVICE_STATE_PENDING_STREAM_PREPARATION:
|
|
g_warning ("[RDP.CAM_DEVICE] Protocol violation: Received stray success "
|
|
"response in state %s for device \"%s\". Removing device...",
|
|
device_state_to_string (device->state), device->device_name);
|
|
transition_into_fatal_error_state (device);
|
|
break;
|
|
case DEVICE_STATE_PENDING_ACTIVATION_RESPONSE:
|
|
g_debug ("[RDP.CAM_DEVICE] Activated device \"%s\"", device->device_name);
|
|
request_stream_list (device);
|
|
break;
|
|
case DEVICE_STATE_INITIALIZATION_DONE:
|
|
if (!try_handle_runtime_success_response (device, &error))
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] %s", error->message);
|
|
transition_into_fatal_error_state (device);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static const char *
|
|
error_code_to_string (CAM_ERROR_CODE error_code)
|
|
{
|
|
switch (error_code)
|
|
{
|
|
case CAM_ERROR_CODE_UnexpectedError:
|
|
return "Unexpected Error";
|
|
case CAM_ERROR_CODE_InvalidMessage:
|
|
return "Invalid Message";
|
|
case CAM_ERROR_CODE_NotInitialized:
|
|
return "Not Initialized";
|
|
case CAM_ERROR_CODE_InvalidRequest:
|
|
return "Invalid Request";
|
|
case CAM_ERROR_CODE_InvalidStreamNumber:
|
|
return "Invalid Stream Number";
|
|
case CAM_ERROR_CODE_InvalidMediaType:
|
|
return "Invalid MediaType";
|
|
case CAM_ERROR_CODE_OutOfMemory:
|
|
return "Out Of Memory";
|
|
case CAM_ERROR_CODE_ItemNotFound:
|
|
return "Item Not Found";
|
|
case CAM_ERROR_CODE_SetNotFound:
|
|
return "Set Not Found";
|
|
case CAM_ERROR_CODE_OperationNotSupported:
|
|
return "Operation Not Supported";
|
|
default:
|
|
return "Unknown Error";
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static const char *
|
|
device_error_to_string (const CAM_ERROR_RESPONSE *error_response)
|
|
{
|
|
return error_code_to_string (error_response->ErrorCode);
|
|
}
|
|
|
|
static const char *
|
|
sample_error_to_string (const CAM_SAMPLE_ERROR_RESPONSE *sample_error_response)
|
|
{
|
|
return error_code_to_string (sample_error_response->ErrorCode);
|
|
}
|
|
|
|
static void
|
|
fetch_runtime_error_response (GrdRdpDvcCameraDevice *device,
|
|
const CAM_ERROR_RESPONSE *error_response,
|
|
GError **error)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->client_request_mutex);
|
|
if (!device->pending_client_request)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Protocol violation: "
|
|
"Received stray error response \"%s\" in state %s for "
|
|
"device \"%s\". There was no runtime request. Removing "
|
|
"device...", device_error_to_string (error_response),
|
|
device_state_to_string (device->state), device->device_name);
|
|
return;
|
|
}
|
|
|
|
switch (device->pending_client_request_type)
|
|
{
|
|
case CLIENT_REQUEST_TYPE_NONE:
|
|
g_assert_not_reached ();
|
|
break;
|
|
case CLIENT_REQUEST_TYPE_START_STREAMS:
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to start "
|
|
"streams on device \"%s\": %s. Removing device...",
|
|
device->device_name, device_error_to_string (error_response));
|
|
break;
|
|
case CLIENT_REQUEST_TYPE_STOP_STREAMS:
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to stop "
|
|
"streams on device \"%s\": %s. Removing device...",
|
|
device->device_name, device_error_to_string (error_response));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Actions for failure:
|
|
*
|
|
* - Activate Device Request
|
|
* - Deactivate Device Request
|
|
* - Start Streams Request
|
|
* - Stop Streams Request
|
|
* - Set Property Value Request
|
|
*
|
|
* - Stream List Request
|
|
* - Media Type List Request
|
|
* - Current Media Type Request
|
|
* - Property List Request
|
|
* - Property Value Request
|
|
*/
|
|
static uint32_t
|
|
device_error_response (CameraDeviceServerContext *device_context,
|
|
const CAM_ERROR_RESPONSE *error_response)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = device_context->userdata;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->state_mutex);
|
|
switch (device->state)
|
|
{
|
|
case DEVICE_STATE_FATAL_ERROR:
|
|
case DEVICE_STATE_IN_SHUTDOWN:
|
|
break;
|
|
case DEVICE_STATE_PENDING_ACTIVATION:
|
|
case DEVICE_STATE_PENDING_STREAM_PREPARATION:
|
|
g_warning ("[RDP.CAM_DEVICE] Protocol violation: Received stray error "
|
|
"response \"%s\" in state %s for device \"%s\". "
|
|
"Removing device...", device_error_to_string (error_response),
|
|
device_state_to_string (device->state), device->device_name);
|
|
break;
|
|
case DEVICE_STATE_PENDING_ACTIVATION_RESPONSE:
|
|
g_warning ("[RDP.CAM_DEVICE] Failed to activate device \"%s\": %s."
|
|
"Removing device...",
|
|
device->device_name, device_error_to_string (error_response));
|
|
break;
|
|
case DEVICE_STATE_PENDING_STREAM_LIST_RESPONSE:
|
|
g_warning ("[RDP.CAM_DEVICE] Failed to fetch stream list from device "
|
|
"\"%s\": %s. Removing device...",
|
|
device->device_name, device_error_to_string (error_response));
|
|
break;
|
|
case DEVICE_STATE_PENDING_MEDIA_TYPE_LIST_RESPONSE:
|
|
g_warning ("[RDP.CAM_DEVICE] Failed to fetch media type list from device "
|
|
"\"%s\": %s. Removing device...",
|
|
device->device_name, device_error_to_string (error_response));
|
|
break;
|
|
case DEVICE_STATE_PENDING_PROPERTY_LIST_RESPONSE:
|
|
g_warning ("[RDP.CAM_DEVICE] Failed to fetch property list from device "
|
|
"\"%s\": %s. Removing device...",
|
|
device->device_name, device_error_to_string (error_response));
|
|
break;
|
|
case DEVICE_STATE_PENDING_PROPERTY_VALUE_RESPONSE:
|
|
g_warning ("[RDP.CAM_DEVICE] Failed to fetch property value from device "
|
|
"\"%s\": %s. Removing device...",
|
|
device->device_name, device_error_to_string (error_response));
|
|
break;
|
|
case DEVICE_STATE_INITIALIZATION_DONE:
|
|
fetch_runtime_error_response (device, error_response, &error);
|
|
g_warning ("[RDP.CAM_DEVICE] %s", error->message);
|
|
break;
|
|
}
|
|
|
|
transition_into_fatal_error_state (device);
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static void
|
|
request_next_media_type_list (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
CameraDeviceServerContext *device_context = device->device_context;
|
|
CAM_MEDIA_TYPE_LIST_REQUEST media_type_list_request = {};
|
|
|
|
g_assert (g_queue_get_length (device->pending_media_type_lists) > 0);
|
|
|
|
media_type_list_request.StreamIndex =
|
|
GPOINTER_TO_UINT (g_queue_peek_head (device->pending_media_type_lists));
|
|
|
|
device->state = DEVICE_STATE_PENDING_MEDIA_TYPE_LIST_RESPONSE;
|
|
device_context->MediaTypeListRequest (device_context,
|
|
&media_type_list_request);
|
|
}
|
|
|
|
static uint32_t
|
|
device_stream_list_response (CameraDeviceServerContext *device_context,
|
|
const CAM_STREAM_LIST_RESPONSE *stream_list_response)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = device_context->userdata;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
uint16_t i;
|
|
|
|
locker = g_mutex_locker_new (&device->state_mutex);
|
|
if (device->state == DEVICE_STATE_FATAL_ERROR ||
|
|
device->state == DEVICE_STATE_IN_SHUTDOWN)
|
|
return CHANNEL_RC_OK;
|
|
|
|
if (device->state != DEVICE_STATE_PENDING_STREAM_LIST_RESPONSE)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Protocol violation: Received stray stream "
|
|
"list response in state %s for device \"%s\". "
|
|
"Removing device...",
|
|
device_state_to_string (device->state), device->device_name);
|
|
transition_into_fatal_error_state (device);
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
if (stream_list_response->N_Descriptions < 1)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Device \"%s\": Received invalid stream list "
|
|
"response. Removing device...", device->device_name);
|
|
transition_into_fatal_error_state (device);
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
for (i = 0; i < stream_list_response->N_Descriptions; ++i)
|
|
{
|
|
g_debug ("[RDP.CAM_DEVICE] Device \"%s\": Stream %u: FrameSourceTypes: %u, "
|
|
"StreamCategory, %u, Selected: %u, CanBeShared: %u",
|
|
device->device_name, i,
|
|
stream_list_response->StreamDescriptions[i].FrameSourceTypes,
|
|
stream_list_response->StreamDescriptions[i].StreamCategory,
|
|
stream_list_response->StreamDescriptions[i].Selected,
|
|
stream_list_response->StreamDescriptions[i].CanBeShared);
|
|
|
|
g_hash_table_insert (device->stream_contexts,
|
|
GUINT_TO_POINTER (i), stream_context_new (device));
|
|
g_queue_push_tail (device->pending_media_type_lists,
|
|
GUINT_TO_POINTER (i));
|
|
}
|
|
|
|
request_next_media_type_list (device);
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static void
|
|
request_property_list (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
CameraDeviceServerContext *device_context = device->device_context;
|
|
CAM_PROPERTY_LIST_REQUEST property_list_request = {};
|
|
|
|
device->state = DEVICE_STATE_PENDING_PROPERTY_LIST_RESPONSE;
|
|
device_context->PropertyListRequest (device_context, &property_list_request);
|
|
}
|
|
|
|
static void
|
|
start_preparing_streams (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
device->state = DEVICE_STATE_PENDING_STREAM_PREPARATION;
|
|
g_source_set_ready_time (device->initialization_source, 0);
|
|
}
|
|
|
|
static uint32_t
|
|
device_media_type_list_response (CameraDeviceServerContext *device_context,
|
|
const CAM_MEDIA_TYPE_LIST_RESPONSE *media_type_list_response)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = device_context->userdata;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
StreamContext *stream_context = NULL;
|
|
uint8_t stream_index;
|
|
size_t i;
|
|
|
|
locker = g_mutex_locker_new (&device->state_mutex);
|
|
if (device->state == DEVICE_STATE_FATAL_ERROR ||
|
|
device->state == DEVICE_STATE_IN_SHUTDOWN)
|
|
return CHANNEL_RC_OK;
|
|
|
|
if (device->state != DEVICE_STATE_PENDING_MEDIA_TYPE_LIST_RESPONSE)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Protocol violation: Received stray media "
|
|
"type list response in state %s for device \"%s\". "
|
|
"Removing device...",
|
|
device_state_to_string (device->state), device->device_name);
|
|
transition_into_fatal_error_state (device);
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
if (media_type_list_response->N_Descriptions < 1)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Device \"%s\": Received invalid media type "
|
|
"list response. Removing device...", device->device_name);
|
|
transition_into_fatal_error_state (device);
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
stream_index =
|
|
GPOINTER_TO_UINT (g_queue_pop_head (device->pending_media_type_lists));
|
|
if (!g_hash_table_lookup_extended (device->stream_contexts,
|
|
GUINT_TO_POINTER (stream_index),
|
|
NULL, (gpointer *) &stream_context))
|
|
g_assert_not_reached ();
|
|
|
|
for (i = 0; i < media_type_list_response->N_Descriptions; ++i)
|
|
{
|
|
CAM_MEDIA_TYPE_DESCRIPTION *media_type_description =
|
|
&media_type_list_response->MediaTypeDescriptions[i];
|
|
|
|
g_debug ("[RDP.CAM_DEVICE] Device \"%s\": Stream %u: MediaTypeList %zu: "
|
|
"Format: %u, Width: %u, Height: %u, FrameRateNumerator: %u, "
|
|
"FrameRateDenominator: %u, PixelAspectRatioNumerator: %u, "
|
|
"PixelAspectRatioDenominator: %u, Flags: 0x%08X",
|
|
device->device_name, stream_index, i,
|
|
media_type_description->Format,
|
|
media_type_description->Width,
|
|
media_type_description->Height,
|
|
media_type_description->FrameRateNumerator,
|
|
media_type_description->FrameRateDenominator,
|
|
media_type_description->PixelAspectRatioNumerator,
|
|
media_type_description->PixelAspectRatioDenominator,
|
|
media_type_description->Flags);
|
|
|
|
stream_context->media_type_descriptions =
|
|
g_list_append (stream_context->media_type_descriptions,
|
|
g_memdup2 (media_type_description,
|
|
sizeof (CAM_MEDIA_TYPE_DESCRIPTION)));
|
|
}
|
|
|
|
if (g_queue_get_length (device->pending_media_type_lists) > 0)
|
|
request_next_media_type_list (device);
|
|
else if (device_context->protocolVersion >= 2)
|
|
request_property_list (device);
|
|
else
|
|
start_preparing_streams (device);
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static uint32_t
|
|
device_current_media_type_response (CameraDeviceServerContext *device_context,
|
|
const CAM_CURRENT_MEDIA_TYPE_RESPONSE *current_media_type_response)
|
|
{
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static gboolean
|
|
has_pending_event_unlocked (GrdRdpDvcCameraDevice *device,
|
|
DeviceEvent event)
|
|
{
|
|
return g_hash_table_contains (device->pending_events,
|
|
GUINT_TO_POINTER (event));
|
|
}
|
|
|
|
static gboolean
|
|
has_pending_event (GrdRdpDvcCameraDevice *device,
|
|
DeviceEvent event)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->event_mutex);
|
|
return has_pending_event_unlocked (device, event);
|
|
}
|
|
|
|
static uint32_t
|
|
device_sample_response (CameraDeviceServerContext *device_context,
|
|
const CAM_SAMPLE_RESPONSE *sample_response)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = device_context->userdata;
|
|
uint8_t stream_index = sample_response->StreamIndex;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
StreamContext *stream_context = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
GrdSampleBuffer *sample_buffer;
|
|
uint32_t n_pending_samples;
|
|
gboolean success = FALSE;
|
|
|
|
locker = g_mutex_locker_new (&device->state_mutex);
|
|
if (device->state == DEVICE_STATE_FATAL_ERROR ||
|
|
device->state == DEVICE_STATE_IN_SHUTDOWN)
|
|
return CHANNEL_RC_OK;
|
|
|
|
if (!g_hash_table_lookup_extended (device->stream_contexts,
|
|
GUINT_TO_POINTER (stream_index),
|
|
NULL, (gpointer *) &stream_context))
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Protocol violation: Received sample "
|
|
"response for unknown stream in state %s for device \"%s\". "
|
|
"Removing device...",
|
|
device_state_to_string (device->state), device->device_name);
|
|
transition_into_fatal_error_state (device);
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
sample_buffer = g_async_queue_try_pop (stream_context->pending_samples);
|
|
if (!sample_buffer)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Protocol violation: Received stray sample "
|
|
"response for stream %u on device \"%s\". Ignoring...",
|
|
stream_index, device->device_name);
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
g_mutex_lock (&device->sample_request_mutex);
|
|
g_hash_table_remove (device->pending_samples, sample_buffer);
|
|
|
|
n_pending_samples = g_hash_table_size (device->pending_samples);
|
|
g_mutex_unlock (&device->sample_request_mutex);
|
|
|
|
if (grd_rdp_camera_stream_announce_new_sample (stream_context->camera_stream,
|
|
sample_buffer))
|
|
{
|
|
success = grd_sample_buffer_load_sample (sample_buffer,
|
|
sample_response->Sample,
|
|
sample_response->SampleSize,
|
|
&error);
|
|
if (!success)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Device \"%s\", stream %u: Failed to "
|
|
"load sample: %s ", device->device_name, stream_index,
|
|
error->message);
|
|
}
|
|
}
|
|
|
|
grd_rdp_camera_stream_submit_sample (stream_context->camera_stream,
|
|
sample_buffer, success);
|
|
|
|
if (n_pending_samples == 0 &&
|
|
has_pending_event (device, DEVICE_EVENT_STOP_STREAMS))
|
|
g_source_set_ready_time (device->event_source, 0);
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static uint32_t
|
|
device_sample_error_response (CameraDeviceServerContext *device_context,
|
|
const CAM_SAMPLE_ERROR_RESPONSE *sample_error_response)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = device_context->userdata;
|
|
uint8_t stream_index = sample_error_response->StreamIndex;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
StreamContext *stream_context = NULL;
|
|
GrdSampleBuffer *sample_buffer;
|
|
uint32_t n_pending_samples;
|
|
|
|
locker = g_mutex_locker_new (&device->state_mutex);
|
|
if (device->state == DEVICE_STATE_FATAL_ERROR ||
|
|
device->state == DEVICE_STATE_IN_SHUTDOWN)
|
|
return CHANNEL_RC_OK;
|
|
|
|
if (!g_hash_table_lookup_extended (device->stream_contexts,
|
|
GUINT_TO_POINTER (stream_index),
|
|
NULL, (gpointer *) &stream_context))
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Protocol violation: Received sample error "
|
|
"response \"%s\" for unknown stream in state %s for device "
|
|
"\"%s\". Removing device...",
|
|
sample_error_to_string (sample_error_response),
|
|
device_state_to_string (device->state), device->device_name);
|
|
transition_into_fatal_error_state (device);
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
sample_buffer = g_async_queue_try_pop (stream_context->pending_samples);
|
|
if (!sample_buffer)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Protocol violation: Received stray sample "
|
|
"error response \"%s\" for stream %u on device \"%s\". "
|
|
"Ignoring...", sample_error_to_string (sample_error_response),
|
|
stream_index, device->device_name);
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
g_mutex_lock (&device->sample_request_mutex);
|
|
g_hash_table_remove (device->pending_samples, sample_buffer);
|
|
|
|
n_pending_samples = g_hash_table_size (device->pending_samples);
|
|
g_mutex_unlock (&device->sample_request_mutex);
|
|
|
|
g_warning ("[RDP.CAM_DEVICE] Device \"%s\", stream %u: Failed to retrieve "
|
|
"sample from client: %s ", device->device_name, stream_index,
|
|
sample_error_to_string (sample_error_response));
|
|
|
|
grd_rdp_camera_stream_submit_sample (stream_context->camera_stream,
|
|
sample_buffer, FALSE);
|
|
|
|
if (n_pending_samples == 0 &&
|
|
has_pending_event (device, DEVICE_EVENT_STOP_STREAMS))
|
|
g_source_set_ready_time (device->event_source, 0);
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static void
|
|
request_next_property_value (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
CameraDeviceServerContext *device_context = device->device_context;
|
|
CAM_PROPERTY_VALUE_REQUEST *property_value_request;
|
|
|
|
g_assert (g_queue_get_length (device->pending_property_values) > 0);
|
|
|
|
property_value_request = g_queue_peek_head (device->pending_property_values);
|
|
|
|
device->state = DEVICE_STATE_PENDING_PROPERTY_VALUE_RESPONSE;
|
|
device_context->PropertyValueRequest (device_context, property_value_request);
|
|
}
|
|
|
|
static uint32_t
|
|
device_property_list_response (CameraDeviceServerContext *device_context,
|
|
const CAM_PROPERTY_LIST_RESPONSE *property_list_response)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = device_context->userdata;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
size_t i;
|
|
|
|
locker = g_mutex_locker_new (&device->state_mutex);
|
|
if (device->state == DEVICE_STATE_FATAL_ERROR ||
|
|
device->state == DEVICE_STATE_IN_SHUTDOWN)
|
|
return CHANNEL_RC_OK;
|
|
|
|
if (device->state != DEVICE_STATE_PENDING_PROPERTY_LIST_RESPONSE)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Protocol violation: Received stray "
|
|
"property list response in state %s for device \"%s\". "
|
|
"Removing device...",
|
|
device_state_to_string (device->state), device->device_name);
|
|
transition_into_fatal_error_state (device);
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
for (i = 0; i < property_list_response->N_Properties; ++i)
|
|
{
|
|
CAM_PROPERTY_DESCRIPTION *property =
|
|
&property_list_response->Properties[i];
|
|
CAM_PROPERTY_VALUE_REQUEST *property_value_request;
|
|
|
|
g_debug ("[RDP.CAM_DEVICE] Device \"%s\": Property %zu: "
|
|
"PropertySet: 0x%02X, PropertyId: 0x%02X, Capabilities: 0x%02X, "
|
|
"MinValue: %i, MaxValue: %i, Step: %i, DefaultValue: %i",
|
|
device->device_name, i,
|
|
property->PropertySet, property->PropertyId,
|
|
property->Capabilities, property->MinValue, property->MaxValue,
|
|
property->Step, property->DefaultValue);
|
|
|
|
property_value_request = g_new0 (CAM_PROPERTY_VALUE_REQUEST, 1);
|
|
property_value_request->PropertySet = property->PropertySet;
|
|
property_value_request->PropertyId = property->PropertyId;
|
|
|
|
g_queue_push_tail (device->pending_property_values,
|
|
property_value_request);
|
|
}
|
|
|
|
if (g_queue_get_length (device->pending_property_values) > 0)
|
|
request_next_property_value (device);
|
|
else
|
|
start_preparing_streams (device);
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static uint32_t
|
|
device_property_value_response (CameraDeviceServerContext *device_context,
|
|
const CAM_PROPERTY_VALUE_RESPONSE *property_value_response)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = device_context->userdata;
|
|
const CAM_PROPERTY_VALUE *property_value =
|
|
&property_value_response->PropertyValue;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
g_autofree CAM_PROPERTY_VALUE_REQUEST *property_value_request = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->state_mutex);
|
|
if (device->state == DEVICE_STATE_FATAL_ERROR ||
|
|
device->state == DEVICE_STATE_IN_SHUTDOWN)
|
|
return CHANNEL_RC_OK;
|
|
|
|
if (device->state != DEVICE_STATE_PENDING_PROPERTY_VALUE_RESPONSE)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Protocol violation: Received stray "
|
|
"property value response in state %s for device \"%s\". "
|
|
"Removing device...",
|
|
device_state_to_string (device->state), device->device_name);
|
|
transition_into_fatal_error_state (device);
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
property_value_request = g_queue_pop_head (device->pending_property_values);
|
|
g_debug ("[RDP.CAM_DEVICE] Device \"%s\": PropertySet: 0x%02X, "
|
|
"PropertyId: 0x%02X, Mode: 0x%02X, Value: %i",
|
|
device->device_name, property_value_request->PropertySet,
|
|
property_value_request->PropertyId,
|
|
property_value->Mode, property_value->Value);
|
|
|
|
if (g_queue_get_length (device->pending_property_values) > 0)
|
|
request_next_property_value (device);
|
|
else
|
|
start_preparing_streams (device);
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
GrdRdpDvcCameraDevice *
|
|
grd_rdp_dvc_camera_device_new (GrdRdpDvcHandler *dvc_handler,
|
|
HANDLE vcm,
|
|
rdpContext *rdp_context,
|
|
uint8_t protocol_version,
|
|
const char *dvc_name,
|
|
const char *device_name)
|
|
{
|
|
g_autoptr (GrdRdpDvcCameraDevice) device = NULL;
|
|
CameraDeviceServerContext *device_context;
|
|
|
|
device = g_object_new (GRD_TYPE_RDP_DVC_CAMERA_DEVICE, NULL);
|
|
device_context = camera_device_server_context_new (vcm);
|
|
if (!device_context)
|
|
g_error ("[RDP.CAM_DEVICE] Failed to allocate server context (OOM)");
|
|
|
|
device->device_context = device_context;
|
|
device->dvc_name = g_strdup (dvc_name);
|
|
device->device_name = g_strdup (device_name);
|
|
|
|
grd_rdp_dvc_initialize_base (GRD_RDP_DVC (device),
|
|
dvc_handler, NULL,
|
|
GRD_RDP_CHANNEL_CAMERA);
|
|
|
|
device_context->virtualChannelName = g_strdup (dvc_name);
|
|
device_context->protocolVersion = protocol_version;
|
|
|
|
device_context->ChannelIdAssigned = device_channel_id_assigned;
|
|
device_context->SuccessResponse = device_success_response;
|
|
device_context->ErrorResponse = device_error_response;
|
|
device_context->StreamListResponse = device_stream_list_response;
|
|
device_context->MediaTypeListResponse = device_media_type_list_response;
|
|
device_context->CurrentMediaTypeResponse = device_current_media_type_response;
|
|
device_context->SampleResponse = device_sample_response;
|
|
device_context->SampleErrorResponse = device_sample_error_response;
|
|
device_context->PropertyListResponse = device_property_list_response;
|
|
device_context->PropertyValueResponse = device_property_value_response;
|
|
device_context->rdpcontext = rdp_context;
|
|
device_context->userdata = device;
|
|
|
|
/* Check whether camera thread was able to successfully initialize PipeWire */
|
|
if (!grd_sync_point_wait_for_completion (&device->sync_point))
|
|
return NULL;
|
|
|
|
/*
|
|
* The DRDYNVC virtual channel is already initialized here,
|
|
* as the enumerator channel also uses it
|
|
*/
|
|
if (device_context->Open (device_context))
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Device \"%s\": Failed to open the %s "
|
|
"channel. Terminating protocol", device_name, dvc_name);
|
|
return NULL;
|
|
}
|
|
device->channel_opened = TRUE;
|
|
|
|
return g_steal_pointer (&device);
|
|
}
|
|
|
|
static gboolean
|
|
tear_down_streams (gpointer user_data)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = user_data;
|
|
|
|
g_hash_table_remove_all (device->stream_contexts);
|
|
if (device->sample_timeout_source)
|
|
{
|
|
g_source_destroy (device->sample_timeout_source);
|
|
g_clear_pointer (&device->sample_timeout_source, g_source_unref);
|
|
}
|
|
|
|
grd_sync_point_complete (&device->sync_point, TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static gboolean
|
|
source_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
g_source_set_ready_time (source, -1);
|
|
|
|
return callback (user_data);
|
|
}
|
|
|
|
static GSourceFuncs source_funcs =
|
|
{
|
|
.dispatch = source_dispatch,
|
|
};
|
|
|
|
static void
|
|
destroy_camera_streams_in_camera_thread (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
GSource *stream_teardown_source;
|
|
|
|
g_mutex_lock (&device->state_mutex);
|
|
device->state = DEVICE_STATE_IN_SHUTDOWN;
|
|
g_mutex_unlock (&device->state_mutex);
|
|
|
|
grd_sync_point_reset (&device->sync_point);
|
|
|
|
stream_teardown_source = g_source_new (&source_funcs, sizeof (GSource));
|
|
g_source_set_callback (stream_teardown_source, tear_down_streams,
|
|
device, NULL);
|
|
g_source_set_ready_time (stream_teardown_source, 0);
|
|
g_source_attach (stream_teardown_source, device->camera_context);
|
|
g_source_unref (stream_teardown_source);
|
|
|
|
grd_sync_point_wait_for_completion (&device->sync_point);
|
|
}
|
|
|
|
static void
|
|
stop_camera_thread (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
destroy_camera_streams_in_camera_thread (device);
|
|
|
|
device->in_shutdown = TRUE;
|
|
|
|
g_main_context_wakeup (device->camera_context);
|
|
g_clear_pointer (&device->camera_thread, g_thread_join);
|
|
}
|
|
|
|
static void
|
|
grd_rdp_dvc_camera_device_dispose (GObject *object)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = GRD_RDP_DVC_CAMERA_DEVICE (object);
|
|
GrdRdpDvc *dvc = GRD_RDP_DVC (device);
|
|
|
|
if (device->camera_thread)
|
|
stop_camera_thread (device);
|
|
|
|
g_assert (g_hash_table_size (device->stream_contexts) == 0);
|
|
g_assert (!device->sample_timeout_source);
|
|
|
|
if (device->channel_opened)
|
|
{
|
|
device->device_context->Close (device->device_context);
|
|
device->channel_opened = FALSE;
|
|
}
|
|
grd_rdp_dvc_maybe_unsubscribe_creation_status (dvc);
|
|
|
|
g_assert (!device->pipewire_core);
|
|
g_assert (!device->pipewire_context);
|
|
g_assert (!device->pipewire_source);
|
|
|
|
if (device->event_source)
|
|
{
|
|
g_source_destroy (device->event_source);
|
|
g_clear_pointer (&device->event_source, g_source_unref);
|
|
}
|
|
if (device->initialization_source)
|
|
{
|
|
g_source_destroy (device->initialization_source);
|
|
g_clear_pointer (&device->initialization_source, g_source_unref);
|
|
}
|
|
|
|
g_clear_pointer (&device->camera_context, g_main_context_unref);
|
|
|
|
g_clear_pointer (&device->pending_samples, g_hash_table_unref);
|
|
g_clear_pointer (&device->pending_events, g_hash_table_unref);
|
|
g_clear_pointer (&device->running_streams, g_hash_table_unref);
|
|
g_clear_pointer (&device->pending_stream_starts, g_hash_table_unref);
|
|
g_clear_pointer (&device->queued_stream_starts, g_hash_table_unref);
|
|
|
|
if (device->pending_property_values)
|
|
{
|
|
g_queue_free_full (device->pending_property_values, g_free);
|
|
device->pending_property_values = NULL;
|
|
}
|
|
g_clear_pointer (&device->pending_media_type_lists, g_queue_free);
|
|
|
|
g_clear_pointer (&device->device_name, g_free);
|
|
g_clear_pointer (&device->dvc_name, g_free);
|
|
|
|
g_clear_pointer (&device->device_context, camera_device_server_context_free);
|
|
|
|
G_OBJECT_CLASS (grd_rdp_dvc_camera_device_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
grd_rdp_dvc_camera_device_finalize (GObject *object)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = GRD_RDP_DVC_CAMERA_DEVICE (object);
|
|
|
|
grd_sync_point_clear (&device->sync_point);
|
|
|
|
g_mutex_clear (&device->sample_request_mutex);
|
|
g_mutex_clear (&device->client_request_mutex);
|
|
g_mutex_clear (&device->event_mutex);
|
|
g_mutex_clear (&device->state_mutex);
|
|
|
|
g_assert (g_hash_table_size (device->stream_contexts) == 0);
|
|
g_clear_pointer (&device->stream_contexts, g_hash_table_unref);
|
|
|
|
G_OBJECT_CLASS (grd_rdp_dvc_camera_device_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
pipewire_core_error (void *user_data,
|
|
uint32_t id,
|
|
int seq,
|
|
int res,
|
|
const char *message)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = user_data;
|
|
|
|
g_warning ("[RDP.CAM_DEVICE] Device \"%s\": PipeWire core error: "
|
|
"id: %u, seq: %i, res: %i, %s",
|
|
device->device_name, id, seq, res, message);
|
|
|
|
if (id == PW_ID_CORE && res == -EPIPE)
|
|
g_signal_emit (device, signals[ERROR], 0);
|
|
}
|
|
|
|
static const struct pw_core_events pipewire_core_events =
|
|
{
|
|
.version = PW_VERSION_CORE_EVENTS,
|
|
.error = pipewire_core_error,
|
|
};
|
|
|
|
static gboolean
|
|
set_up_pipewire (GrdRdpDvcCameraDevice *device,
|
|
GError **error)
|
|
{
|
|
GrdPipeWireSource *pipewire_source;
|
|
|
|
pipewire_source = grd_pipewire_source_new ("RDP.CAM_DEVICE", error);
|
|
if (!pipewire_source)
|
|
return FALSE;
|
|
|
|
device->pipewire_source = (GSource *) pipewire_source;
|
|
|
|
device->pipewire_context =
|
|
pw_context_new (pipewire_source->pipewire_loop, NULL, 0);
|
|
if (!device->pipewire_context)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create PipeWire context");
|
|
return FALSE;
|
|
}
|
|
|
|
device->pipewire_core =
|
|
pw_context_connect (device->pipewire_context, NULL, 0);
|
|
if (!device->pipewire_core)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create PipeWire core");
|
|
return FALSE;
|
|
}
|
|
|
|
pw_core_add_listener (device->pipewire_core,
|
|
&device->pipewire_core_listener,
|
|
&pipewire_core_events, device);
|
|
|
|
g_source_attach (device->pipewire_source, device->camera_context);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
stop_pipewire (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
if (device->pipewire_core)
|
|
{
|
|
spa_hook_remove (&device->pipewire_core_listener);
|
|
g_clear_pointer (&device->pipewire_core, pw_core_disconnect);
|
|
}
|
|
g_clear_pointer (&device->pipewire_context, pw_context_destroy);
|
|
|
|
if (device->pipewire_source)
|
|
{
|
|
g_source_destroy (device->pipewire_source);
|
|
g_clear_pointer (&device->pipewire_source, g_source_unref);
|
|
}
|
|
}
|
|
|
|
static gpointer
|
|
camera_thread_func (gpointer data)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = data;
|
|
g_autoptr (GError) error = NULL;
|
|
gboolean success;
|
|
|
|
pw_init (NULL, NULL);
|
|
|
|
success = set_up_pipewire (device, &error);
|
|
if (!success)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Device \"%s\": Failed to set up PipeWire: "
|
|
"%s", device->device_name, error->message);
|
|
}
|
|
grd_sync_point_complete (&device->sync_point, success);
|
|
|
|
while (!device->in_shutdown)
|
|
g_main_context_iteration (device->camera_context, TRUE);
|
|
|
|
stop_pipewire (device);
|
|
|
|
pw_deinit ();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
activate_device (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
CameraDeviceServerContext *device_context = device->device_context;
|
|
CAM_ACTIVATE_DEVICE_REQUEST activate_device_request = {};
|
|
|
|
device->state = DEVICE_STATE_PENDING_ACTIVATION_RESPONSE;
|
|
device_context->ActivateDeviceRequest (device_context,
|
|
&activate_device_request);
|
|
}
|
|
|
|
static gboolean
|
|
has_supported_media_type (GList *media_type_descriptions)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = media_type_descriptions; l; l = l->next)
|
|
{
|
|
CAM_MEDIA_TYPE_DESCRIPTION *media_type_description = l->data;
|
|
|
|
/* Sanitize format */
|
|
if (media_type_description->FrameRateNumerator == 0 ||
|
|
media_type_description->FrameRateDenominator == 0 ||
|
|
media_type_description->PixelAspectRatioDenominator == 0)
|
|
continue;
|
|
|
|
if (media_type_description->Format == CAM_MEDIA_FORMAT_H264)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
create_streams (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
StreamContext *stream_context = NULL;
|
|
gpointer key = NULL;
|
|
GHashTableIter iter;
|
|
|
|
g_hash_table_iter_init (&iter, device->stream_contexts);
|
|
while (g_hash_table_iter_next (&iter, &key, (gpointer *) &stream_context))
|
|
{
|
|
uint8_t stream_index = GPOINTER_TO_UINT (key);
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (!has_supported_media_type (stream_context->media_type_descriptions))
|
|
continue;
|
|
|
|
stream_context->camera_stream =
|
|
grd_rdp_camera_stream_new (device,
|
|
device->camera_context,
|
|
device->pipewire_core,
|
|
device->device_name,
|
|
stream_index,
|
|
stream_context->media_type_descriptions,
|
|
&error);
|
|
if (!stream_context->camera_stream)
|
|
{
|
|
g_warning ("[RDP.CAM_DEVICE] Device \"%s\": Failed to create camera "
|
|
"stream for stream %u: %s ", device->device_name,
|
|
stream_index, error->message);
|
|
}
|
|
}
|
|
|
|
device->state = DEVICE_STATE_INITIALIZATION_DONE;
|
|
}
|
|
|
|
static gboolean
|
|
initialize_device (gpointer user_data)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = user_data;
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->state_mutex);
|
|
switch (device->state)
|
|
{
|
|
case DEVICE_STATE_FATAL_ERROR:
|
|
case DEVICE_STATE_PENDING_ACTIVATION_RESPONSE:
|
|
case DEVICE_STATE_PENDING_STREAM_LIST_RESPONSE:
|
|
case DEVICE_STATE_PENDING_MEDIA_TYPE_LIST_RESPONSE:
|
|
case DEVICE_STATE_PENDING_PROPERTY_LIST_RESPONSE:
|
|
case DEVICE_STATE_PENDING_PROPERTY_VALUE_RESPONSE:
|
|
case DEVICE_STATE_INITIALIZATION_DONE:
|
|
case DEVICE_STATE_IN_SHUTDOWN:
|
|
break;
|
|
case DEVICE_STATE_PENDING_ACTIVATION:
|
|
activate_device (device);
|
|
break;
|
|
case DEVICE_STATE_PENDING_STREAM_PREPARATION:
|
|
create_streams (device);
|
|
break;
|
|
}
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static gboolean
|
|
has_pending_ack_event (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->event_mutex);
|
|
return has_pending_event_unlocked (device, DEVICE_EVENT_ACK_STARTED_STREAMS) ||
|
|
has_pending_event_unlocked (device, DEVICE_EVENT_ACK_STOPPED_STREAMS);
|
|
}
|
|
|
|
static gboolean
|
|
has_pending_client_success_response (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
return device->pending_client_request && !has_pending_ack_event (device);
|
|
}
|
|
|
|
static void
|
|
set_pending_client_request (GrdRdpDvcCameraDevice *device,
|
|
ClientRequestType request_type)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->client_request_mutex);
|
|
g_assert (!device->pending_client_request);
|
|
g_assert (device->pending_client_request_type == CLIENT_REQUEST_TYPE_NONE);
|
|
|
|
device->pending_client_request_type = request_type;
|
|
device->pending_client_request = TRUE;
|
|
}
|
|
|
|
static void
|
|
reset_pending_client_request (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->client_request_mutex);
|
|
device->pending_client_request = FALSE;
|
|
device->pending_client_request_type = CLIENT_REQUEST_TYPE_NONE;
|
|
}
|
|
|
|
static gboolean
|
|
try_pop_event (GrdRdpDvcCameraDevice *device,
|
|
DeviceEvent event)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->event_mutex);
|
|
return g_hash_table_remove (device->pending_events, GUINT_TO_POINTER (event));
|
|
}
|
|
|
|
static void
|
|
ack_started_streams (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
StreamRunContext *stream_run_context = NULL;
|
|
gpointer key = NULL;
|
|
GHashTableIter iter;
|
|
|
|
g_hash_table_iter_init (&iter, device->pending_stream_starts);
|
|
while (g_hash_table_iter_next (&iter, &key, (gpointer *) &stream_run_context))
|
|
{
|
|
uint8_t stream_index = GPOINTER_TO_UINT (key);
|
|
StreamContext *stream_context = NULL;
|
|
GrdRdpCameraStream *camera_stream;
|
|
uint32_t run_sequence;
|
|
|
|
if (!g_hash_table_lookup_extended (device->stream_contexts,
|
|
GUINT_TO_POINTER (stream_index),
|
|
NULL, (gpointer *) &stream_context))
|
|
g_assert_not_reached ();
|
|
|
|
camera_stream = stream_context->camera_stream;
|
|
run_sequence = stream_run_context->run_sequence;
|
|
|
|
if (grd_rdp_camera_stream_notify_stream_started (camera_stream,
|
|
run_sequence))
|
|
{
|
|
grd_rdp_camera_stream_uninhibit_camera_loop (camera_stream);
|
|
|
|
g_hash_table_insert (device->running_streams,
|
|
GUINT_TO_POINTER (stream_index),
|
|
stream_run_context);
|
|
g_hash_table_iter_steal (&iter);
|
|
}
|
|
else
|
|
{
|
|
g_hash_table_iter_remove (&iter);
|
|
}
|
|
}
|
|
|
|
g_debug ("[RDP.CAM_DEVICE] Device \"%s\": Started streams",
|
|
device->device_name);
|
|
}
|
|
|
|
static void
|
|
ack_stopped_streams (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
g_debug ("[RDP.CAM_DEVICE] Device \"%s\": Stopped streams",
|
|
device->device_name);
|
|
}
|
|
|
|
static void
|
|
maybe_handle_client_success_response (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
if (!has_pending_ack_event (device))
|
|
return;
|
|
|
|
reset_pending_client_request (device);
|
|
|
|
if (try_pop_event (device, DEVICE_EVENT_ACK_STARTED_STREAMS))
|
|
ack_started_streams (device);
|
|
if (try_pop_event (device, DEVICE_EVENT_ACK_STOPPED_STREAMS))
|
|
ack_stopped_streams (device);
|
|
}
|
|
|
|
static gboolean
|
|
has_pending_sample_requests (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
g_autoptr (GMutexLocker) locker = NULL;
|
|
|
|
locker = g_mutex_locker_new (&device->sample_request_mutex);
|
|
return g_hash_table_size (device->pending_samples) > 0;
|
|
}
|
|
|
|
static gboolean
|
|
discard_sample_requests (gpointer user_data)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = user_data;
|
|
StreamContext *stream_context = NULL;
|
|
GHashTableIter iter;
|
|
|
|
g_warning ("[RDP.CAM_DEVICE] Device \"%s\": Protocol violation: Got no "
|
|
"sample response after timeout. Discarding requests...",
|
|
device->device_name);
|
|
|
|
g_clear_pointer (&device->sample_timeout_source, g_source_unref);
|
|
|
|
g_hash_table_iter_init (&iter, device->stream_contexts);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &stream_context))
|
|
discard_pending_sample_requests (stream_context);
|
|
|
|
g_source_set_ready_time (device->event_source, 0);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
maybe_set_sample_timeout (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
if (device->sample_timeout_source)
|
|
return;
|
|
|
|
g_debug ("[RDP.CAM_DEVICE] Device \"%s\": Waiting for remaining samples "
|
|
"before sending stop-streams-request", device->device_name);
|
|
|
|
device->sample_timeout_source = g_timeout_source_new (SAMPLE_TIMEOUT_MS);
|
|
g_source_set_callback (device->sample_timeout_source, discard_sample_requests,
|
|
device, NULL);
|
|
g_source_attach (device->sample_timeout_source, device->camera_context);
|
|
}
|
|
|
|
static void
|
|
stop_camera_streams (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
CameraDeviceServerContext *device_context = device->device_context;
|
|
CAM_STOP_STREAMS_REQUEST stop_streams_request = {};
|
|
|
|
if (device->sample_timeout_source)
|
|
{
|
|
g_source_destroy (device->sample_timeout_source);
|
|
g_clear_pointer (&device->sample_timeout_source, g_source_unref);
|
|
}
|
|
|
|
g_debug ("[RDP.CAM_DEVICE] Device \"%s\": Sending stop-streams-request",
|
|
device->device_name);
|
|
|
|
set_pending_client_request (device, CLIENT_REQUEST_TYPE_STOP_STREAMS);
|
|
|
|
device_context->StopStreamsRequest (device_context, &stop_streams_request);
|
|
}
|
|
|
|
static void
|
|
start_queued_camera_streams (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
CameraDeviceServerContext *device_context = device->device_context;
|
|
CAM_START_STREAMS_REQUEST start_streams_request = {};
|
|
StreamRunContext *stream_run_context = NULL;
|
|
gpointer key = NULL;
|
|
GHashTableIter iter;
|
|
uint16_t i = 0;
|
|
|
|
g_assert (g_hash_table_size (device->pending_stream_starts) == 0);
|
|
g_assert (g_hash_table_size (device->queued_stream_starts) > 0);
|
|
|
|
start_streams_request.N_Infos =
|
|
g_hash_table_size (device->queued_stream_starts);
|
|
|
|
g_hash_table_iter_init (&iter, device->queued_stream_starts);
|
|
while (g_hash_table_iter_next (&iter, &key, (gpointer *) &stream_run_context))
|
|
{
|
|
CAM_START_STREAM_INFO *start_stream_info;
|
|
|
|
start_stream_info = &start_streams_request.StartStreamsInfo[i++];
|
|
start_stream_info->StreamIndex = GPOINTER_TO_UINT (key);
|
|
start_stream_info->MediaTypeDescription =
|
|
*stream_run_context->media_type_description;
|
|
|
|
g_hash_table_insert (device->pending_stream_starts,
|
|
key, stream_run_context);
|
|
g_hash_table_iter_steal (&iter);
|
|
}
|
|
g_assert (start_streams_request.N_Infos == i);
|
|
|
|
g_debug ("[RDP.CAM_DEVICE] Device \"%s\": Sending start-streams-request",
|
|
device->device_name);
|
|
|
|
set_pending_client_request (device, CLIENT_REQUEST_TYPE_START_STREAMS);
|
|
|
|
device_context->StartStreamsRequest (device_context, &start_streams_request);
|
|
}
|
|
|
|
static gboolean
|
|
handle_device_events (gpointer user_data)
|
|
{
|
|
GrdRdpDvcCameraDevice *device = user_data;
|
|
|
|
if (has_pending_client_success_response (device))
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
maybe_handle_client_success_response (device);
|
|
|
|
if (has_pending_sample_requests (device) &&
|
|
has_pending_event (device, DEVICE_EVENT_STOP_STREAMS))
|
|
maybe_set_sample_timeout (device);
|
|
|
|
if (!has_pending_sample_requests (device) &&
|
|
try_pop_event (device, DEVICE_EVENT_STOP_STREAMS))
|
|
stop_camera_streams (device);
|
|
else if (g_hash_table_size (device->queued_stream_starts) > 0 &&
|
|
!has_pending_event (device, DEVICE_EVENT_STOP_STREAMS))
|
|
start_queued_camera_streams (device);
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static void
|
|
grd_rdp_dvc_camera_device_init (GrdRdpDvcCameraDevice *device)
|
|
{
|
|
GSource *initialization_source;
|
|
GSource *event_source;
|
|
|
|
device->state = DEVICE_STATE_PENDING_ACTIVATION;
|
|
|
|
device->stream_contexts =
|
|
g_hash_table_new_full (NULL, NULL,
|
|
NULL, (GDestroyNotify) stream_context_free);
|
|
device->queued_stream_starts =
|
|
g_hash_table_new_full (NULL, NULL,
|
|
NULL, (GDestroyNotify) stream_run_context_free);
|
|
device->pending_stream_starts =
|
|
g_hash_table_new_full (NULL, NULL,
|
|
NULL, (GDestroyNotify) stream_run_context_free);
|
|
device->running_streams =
|
|
g_hash_table_new_full (NULL, NULL,
|
|
NULL, (GDestroyNotify) stream_run_context_free);
|
|
device->pending_events = g_hash_table_new (NULL, NULL);
|
|
device->pending_samples = g_hash_table_new (NULL, NULL);
|
|
|
|
device->pending_media_type_lists = g_queue_new ();
|
|
device->pending_property_values = g_queue_new ();
|
|
|
|
g_mutex_init (&device->state_mutex);
|
|
g_mutex_init (&device->event_mutex);
|
|
g_mutex_init (&device->client_request_mutex);
|
|
g_mutex_init (&device->sample_request_mutex);
|
|
|
|
grd_sync_point_init (&device->sync_point);
|
|
|
|
device->camera_context = g_main_context_new ();
|
|
device->camera_thread = g_thread_new ("RDP camera device thread",
|
|
camera_thread_func,
|
|
device);
|
|
|
|
initialization_source = g_source_new (&source_funcs, sizeof (GSource));
|
|
g_source_set_callback (initialization_source, initialize_device,
|
|
device, NULL);
|
|
g_source_set_ready_time (initialization_source, -1);
|
|
g_source_attach (initialization_source, device->camera_context);
|
|
device->initialization_source = initialization_source;
|
|
|
|
event_source = g_source_new (&source_funcs, sizeof (GSource));
|
|
g_source_set_callback (event_source, handle_device_events, device, NULL);
|
|
g_source_set_ready_time (event_source, -1);
|
|
g_source_attach (event_source, device->camera_context);
|
|
device->event_source = event_source;
|
|
}
|
|
|
|
static void
|
|
grd_rdp_dvc_camera_device_class_init (GrdRdpDvcCameraDeviceClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = grd_rdp_dvc_camera_device_dispose;
|
|
object_class->finalize = grd_rdp_dvc_camera_device_finalize;
|
|
|
|
signals[ERROR] = g_signal_new ("error",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
}
|