server: Add channel handling for camera device and enumerator channel
This commit is contained in:
parent
5637aeed91
commit
78fc60802c
23
channels/rdpecam/CMakeLists.txt
Normal file
23
channels/rdpecam/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel("camera-device")
|
||||
define_channel("camera-device-enumerator")
|
||||
|
||||
if(WITH_SERVER_CHANNELS)
|
||||
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
17
channels/rdpecam/ChannelOptions.cmake
Normal file
17
channels/rdpecam/ChannelOptions.cmake
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
set(OPTION_DEFAULT OFF)
|
||||
set(OPTION_CLIENT_DEFAULT OFF)
|
||||
set(OPTION_SERVER_DEFAULT ON)
|
||||
|
||||
define_channel_options(NAME "camera-device" TYPE "dynamic"
|
||||
DESCRIPTION "Video Capture Virtual Channel Extension"
|
||||
SPECIFICATIONS "[MS-RDPECAM]"
|
||||
DEFAULT ${OPTION_DEFAULT})
|
||||
|
||||
define_channel_options(NAME "camera-device-enumerator" TYPE "dynamic"
|
||||
DESCRIPTION "Video Capture Virtual Channel Extension"
|
||||
SPECIFICATIONS "[MS-RDPECAM]"
|
||||
DEFAULT ${OPTION_DEFAULT})
|
||||
|
||||
define_channel_server_options(${OPTION_SERVER_DEFAULT})
|
||||
|
28
channels/rdpecam/server/CMakeLists.txt
Normal file
28
channels/rdpecam/server/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel_server("camera-device")
|
||||
define_channel_server("camera-device-enumerator")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
camera_device_enumerator_main.c
|
||||
camera_device_main.c)
|
||||
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
610
channels/rdpecam/server/camera_device_enumerator_main.c
Normal file
610
channels/rdpecam/server/camera_device_enumerator_main.c
Normal file
@ -0,0 +1,610 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Capture Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/server/camera-device-enumerator.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("cam-dev-enum.server")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ENUMERATOR_INITIAL,
|
||||
ENUMERATOR_OPENED,
|
||||
} eEnumeratorChannelState;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CamDevEnumServerContext context;
|
||||
|
||||
HANDLE stopEvent;
|
||||
|
||||
HANDLE thread;
|
||||
void* enumerator_channel;
|
||||
|
||||
DWORD SessionId;
|
||||
|
||||
BOOL isOpened;
|
||||
BOOL externalThread;
|
||||
|
||||
/* Channel state */
|
||||
eEnumeratorChannelState state;
|
||||
|
||||
wStream* buffer;
|
||||
} enumerator_server;
|
||||
|
||||
static UINT enumerator_server_initialize(CamDevEnumServerContext* context, BOOL externalThread)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
enumerator_server* enumerator = (enumerator_server*)context;
|
||||
|
||||
WINPR_ASSERT(enumerator);
|
||||
|
||||
if (enumerator->isOpened)
|
||||
{
|
||||
WLog_WARN(TAG, "Application error: Camera Device Enumerator channel already initialized, "
|
||||
"calling in this state is not possible!");
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
enumerator->externalThread = externalThread;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT enumerator_server_open_channel(enumerator_server* enumerator)
|
||||
{
|
||||
CamDevEnumServerContext* context = &enumerator->context;
|
||||
DWORD Error = ERROR_SUCCESS;
|
||||
HANDLE hEvent;
|
||||
DWORD BytesReturned = 0;
|
||||
PULONG pSessionId = NULL;
|
||||
UINT32 channelId;
|
||||
BOOL status = TRUE;
|
||||
|
||||
WINPR_ASSERT(enumerator);
|
||||
|
||||
if (WTSQuerySessionInformationA(enumerator->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
enumerator->SessionId = (DWORD)*pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
hEvent = WTSVirtualChannelManagerGetEventHandle(enumerator->context.vcm);
|
||||
|
||||
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
||||
{
|
||||
Error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
|
||||
return Error;
|
||||
}
|
||||
|
||||
enumerator->enumerator_channel = WTSVirtualChannelOpenEx(
|
||||
enumerator->SessionId, CAM_DEVICE_ENUMERATOR_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
if (!enumerator->enumerator_channel)
|
||||
{
|
||||
Error = GetLastError();
|
||||
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
|
||||
return Error;
|
||||
}
|
||||
|
||||
channelId = WTSChannelGetIdByHandle(enumerator->enumerator_channel);
|
||||
|
||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
||||
if (!status)
|
||||
{
|
||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return Error;
|
||||
}
|
||||
|
||||
static UINT enumerator_server_handle_select_version_request(CamDevEnumServerContext* context,
|
||||
wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_SELECT_VERSION_REQUEST pdu = {};
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
IFCALLRET(context->SelectVersionRequest, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->SelectVersionRequest failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT enumerator_server_recv_device_added_notification(CamDevEnumServerContext* context,
|
||||
wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_DEVICE_ADDED_NOTIFICATION pdu;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
size_t remaining_length;
|
||||
WCHAR* channel_name_start;
|
||||
char* tmp;
|
||||
size_t i;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
/*
|
||||
* RequiredLength 4:
|
||||
*
|
||||
* Nullterminator DeviceName (2),
|
||||
* VirtualChannelName (>= 1),
|
||||
* Nullterminator VirtualChannelName (1)
|
||||
*/
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
pdu.DeviceName = (WCHAR*)Stream_Pointer(s);
|
||||
|
||||
remaining_length = Stream_GetRemainingLength(s);
|
||||
channel_name_start = (WCHAR*)Stream_Pointer(s);
|
||||
|
||||
/* Search for null terminator of DeviceName */
|
||||
for (i = 0; i < remaining_length; i += sizeof(WCHAR), ++channel_name_start)
|
||||
{
|
||||
if (*channel_name_start == L'\0')
|
||||
break;
|
||||
}
|
||||
|
||||
if (*channel_name_start != L'\0')
|
||||
{
|
||||
WLog_ERR(TAG, "enumerator_server_recv_device_added_notification: Invalid DeviceName!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
pdu.VirtualChannelName = (char*)++channel_name_start;
|
||||
++i;
|
||||
|
||||
if (i >= remaining_length || *pdu.VirtualChannelName == '\0')
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
tmp = pdu.VirtualChannelName;
|
||||
for (; i < remaining_length; ++i, ++tmp)
|
||||
{
|
||||
if (*tmp == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
if (*tmp != '\0')
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
IFCALLRET(context->DeviceAddedNotification, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->DeviceAddedNotification failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT enumerator_server_recv_device_removed_notification(CamDevEnumServerContext* context,
|
||||
wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_DEVICE_REMOVED_NOTIFICATION pdu;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
size_t remaining_length;
|
||||
char* tmp;
|
||||
size_t i;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
pdu.VirtualChannelName = (char*)Stream_Pointer(s);
|
||||
|
||||
remaining_length = Stream_GetRemainingLength(s);
|
||||
tmp = (char*)(Stream_Pointer(s) + 1);
|
||||
|
||||
for (i = 1; i < remaining_length; ++i, ++tmp)
|
||||
{
|
||||
if (*tmp == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
if (*tmp != '\0')
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"enumerator_server_recv_device_removed_notification: Invalid VirtualChannelName!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
IFCALLRET(context->DeviceRemovedNotification, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->DeviceRemovedNotification failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT enumerator_process_message(enumerator_server* enumerator)
|
||||
{
|
||||
BOOL rc;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
ULONG BytesReturned;
|
||||
CAM_SHARED_MSG_HEADER header = {};
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(enumerator);
|
||||
WINPR_ASSERT(enumerator->enumerator_channel);
|
||||
|
||||
s = enumerator->buffer;
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
rc = WTSVirtualChannelRead(enumerator->enumerator_channel, 0, NULL, 0, &BytesReturned);
|
||||
if (!rc)
|
||||
goto out;
|
||||
|
||||
if (BytesReturned < 1)
|
||||
{
|
||||
error = CHANNEL_RC_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(enumerator->enumerator_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_SetLength(s, BytesReturned);
|
||||
Stream_Read_UINT8(s, header.Version);
|
||||
Stream_Read_UINT8(s, header.MessageId);
|
||||
|
||||
switch (header.MessageId)
|
||||
{
|
||||
case CAM_MSG_ID_SelectVersionRequest:
|
||||
error =
|
||||
enumerator_server_handle_select_version_request(&enumerator->context, s, &header);
|
||||
break;
|
||||
case CAM_MSG_ID_DeviceAddedNotification:
|
||||
error =
|
||||
enumerator_server_recv_device_added_notification(&enumerator->context, s, &header);
|
||||
break;
|
||||
case CAM_MSG_ID_DeviceRemovedNotification:
|
||||
error = enumerator_server_recv_device_removed_notification(&enumerator->context, s,
|
||||
&header);
|
||||
break;
|
||||
default:
|
||||
WLog_ERR(TAG, "enumerator_process_message: unknown or invalid MessageId %" PRIu8 "",
|
||||
header.MessageId);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (error)
|
||||
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT enumerator_server_context_poll_int(CamDevEnumServerContext* context)
|
||||
{
|
||||
enumerator_server* enumerator = (enumerator_server*)context;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
|
||||
WINPR_ASSERT(enumerator);
|
||||
|
||||
switch (enumerator->state)
|
||||
{
|
||||
case ENUMERATOR_INITIAL:
|
||||
error = enumerator_server_open_channel(enumerator);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "enumerator_server_open_channel failed with error %" PRIu32 "!",
|
||||
error);
|
||||
else
|
||||
enumerator->state = ENUMERATOR_OPENED;
|
||||
break;
|
||||
case ENUMERATOR_OPENED:
|
||||
error = enumerator_process_message(enumerator);
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static HANDLE enumerator_server_get_channel_handle(enumerator_server* enumerator)
|
||||
{
|
||||
void* buffer = NULL;
|
||||
DWORD BytesReturned = 0;
|
||||
HANDLE ChannelEvent = NULL;
|
||||
|
||||
WINPR_ASSERT(enumerator);
|
||||
|
||||
if (WTSVirtualChannelQuery(enumerator->enumerator_channel, WTSVirtualEventHandle, &buffer,
|
||||
&BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
||||
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
|
||||
return ChannelEvent;
|
||||
}
|
||||
|
||||
static DWORD WINAPI enumerator_server_thread_func(LPVOID arg)
|
||||
{
|
||||
DWORD nCount;
|
||||
HANDLE events[2] = { 0 };
|
||||
enumerator_server* enumerator = (enumerator_server*)arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status;
|
||||
|
||||
WINPR_ASSERT(enumerator);
|
||||
|
||||
nCount = 0;
|
||||
events[nCount++] = enumerator->stopEvent;
|
||||
|
||||
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
|
||||
{
|
||||
switch (enumerator->state)
|
||||
{
|
||||
case ENUMERATOR_INITIAL:
|
||||
error = enumerator_server_context_poll_int(&enumerator->context);
|
||||
if (error == CHANNEL_RC_OK)
|
||||
{
|
||||
events[1] = enumerator_server_get_channel_handle(enumerator);
|
||||
nCount = 2;
|
||||
}
|
||||
break;
|
||||
case ENUMERATOR_OPENED:
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
||||
switch (status)
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
case WAIT_TIMEOUT:
|
||||
error = enumerator_server_context_poll_int(&enumerator->context);
|
||||
break;
|
||||
|
||||
case WAIT_FAILED:
|
||||
default:
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WTSVirtualChannelClose(enumerator->enumerator_channel);
|
||||
enumerator->enumerator_channel = NULL;
|
||||
|
||||
if (error && enumerator->context.rdpcontext)
|
||||
setChannelError(enumerator->context.rdpcontext, error,
|
||||
"enumerator_server_thread_func reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT enumerator_server_open(CamDevEnumServerContext* context)
|
||||
{
|
||||
enumerator_server* enumerator = (enumerator_server*)context;
|
||||
|
||||
WINPR_ASSERT(enumerator);
|
||||
|
||||
if (!enumerator->externalThread && (enumerator->thread == NULL))
|
||||
{
|
||||
enumerator->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!enumerator->stopEvent)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
enumerator->thread =
|
||||
CreateThread(NULL, 0, enumerator_server_thread_func, enumerator, 0, NULL);
|
||||
if (!enumerator->thread)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(enumerator->stopEvent);
|
||||
enumerator->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
enumerator->isOpened = TRUE;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT enumerator_server_close(CamDevEnumServerContext* context)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
enumerator_server* enumerator = (enumerator_server*)context;
|
||||
|
||||
WINPR_ASSERT(enumerator);
|
||||
|
||||
if (!enumerator->externalThread && enumerator->thread)
|
||||
{
|
||||
SetEvent(enumerator->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(enumerator->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(enumerator->thread);
|
||||
CloseHandle(enumerator->stopEvent);
|
||||
enumerator->thread = NULL;
|
||||
enumerator->stopEvent = NULL;
|
||||
}
|
||||
if (enumerator->externalThread)
|
||||
{
|
||||
if (enumerator->state != ENUMERATOR_INITIAL)
|
||||
{
|
||||
WTSVirtualChannelClose(enumerator->enumerator_channel);
|
||||
enumerator->enumerator_channel = NULL;
|
||||
enumerator->state = ENUMERATOR_INITIAL;
|
||||
}
|
||||
}
|
||||
enumerator->isOpened = FALSE;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT enumerator_server_context_poll(CamDevEnumServerContext* context)
|
||||
{
|
||||
enumerator_server* enumerator = (enumerator_server*)context;
|
||||
|
||||
WINPR_ASSERT(enumerator);
|
||||
|
||||
if (!enumerator->externalThread)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
return enumerator_server_context_poll_int(context);
|
||||
}
|
||||
|
||||
static BOOL enumerator_server_context_handle(CamDevEnumServerContext* context, HANDLE* handle)
|
||||
{
|
||||
enumerator_server* enumerator = (enumerator_server*)context;
|
||||
|
||||
WINPR_ASSERT(enumerator);
|
||||
WINPR_ASSERT(handle);
|
||||
|
||||
if (!enumerator->externalThread)
|
||||
return FALSE;
|
||||
if (enumerator->state == ENUMERATOR_INITIAL)
|
||||
return FALSE;
|
||||
|
||||
*handle = enumerator_server_get_channel_handle(enumerator);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT enumerator_server_packet_send(CamDevEnumServerContext* context, wStream* s)
|
||||
{
|
||||
enumerator_server* enumerator = (enumerator_server*)context;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
ULONG written;
|
||||
|
||||
if (!WTSVirtualChannelWrite(enumerator->enumerator_channel, (PCHAR)Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (written < Stream_GetPosition(s))
|
||||
{
|
||||
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
|
||||
Stream_GetPosition(s));
|
||||
}
|
||||
|
||||
out:
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT enumerator_send_select_version_response_pdu(
|
||||
CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
s = Stream_New(NULL, CAM_HEADER_SIZE);
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT8(s, selectVersionResponse->Header.Version);
|
||||
Stream_Write_UINT8(s, selectVersionResponse->Header.MessageId);
|
||||
|
||||
return enumerator_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm)
|
||||
{
|
||||
enumerator_server* enumerator = (enumerator_server*)calloc(1, sizeof(enumerator_server));
|
||||
|
||||
if (!enumerator)
|
||||
return NULL;
|
||||
|
||||
enumerator->context.vcm = vcm;
|
||||
enumerator->context.Initialize = enumerator_server_initialize;
|
||||
enumerator->context.Open = enumerator_server_open;
|
||||
enumerator->context.Close = enumerator_server_close;
|
||||
enumerator->context.Poll = enumerator_server_context_poll;
|
||||
enumerator->context.ChannelHandle = enumerator_server_context_handle;
|
||||
|
||||
enumerator->context.SelectVersionResponse = enumerator_send_select_version_response_pdu;
|
||||
|
||||
enumerator->buffer = Stream_New(NULL, 4096);
|
||||
if (!enumerator->buffer)
|
||||
goto fail;
|
||||
|
||||
return &enumerator->context;
|
||||
fail:
|
||||
cam_dev_enum_server_context_free(&enumerator->context);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cam_dev_enum_server_context_free(CamDevEnumServerContext* context)
|
||||
{
|
||||
enumerator_server* enumerator = (enumerator_server*)context;
|
||||
|
||||
if (enumerator)
|
||||
{
|
||||
enumerator_server_close(context);
|
||||
Stream_Free(enumerator->buffer, TRUE);
|
||||
}
|
||||
|
||||
free(enumerator);
|
||||
}
|
972
channels/rdpecam/server/camera_device_main.c
Normal file
972
channels/rdpecam/server/camera_device_main.c
Normal file
@ -0,0 +1,972 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Capture Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/server/camera-device.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("camera-device.server")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAMERA_DEVICE_INITIAL,
|
||||
CAMERA_DEVICE_OPENED,
|
||||
} eCameraDeviceChannelState;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CameraDeviceServerContext context;
|
||||
|
||||
HANDLE stopEvent;
|
||||
|
||||
HANDLE thread;
|
||||
void* device_channel;
|
||||
|
||||
DWORD SessionId;
|
||||
|
||||
BOOL isOpened;
|
||||
BOOL externalThread;
|
||||
|
||||
/* Channel state */
|
||||
eCameraDeviceChannelState state;
|
||||
|
||||
wStream* buffer;
|
||||
} device_server;
|
||||
|
||||
static UINT device_server_initialize(CameraDeviceServerContext* context, BOOL externalThread)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
device_server* device = (device_server*)context;
|
||||
|
||||
WINPR_ASSERT(device);
|
||||
|
||||
if (device->isOpened)
|
||||
{
|
||||
WLog_WARN(TAG, "Application error: Camera channel already initialized, "
|
||||
"calling in this state is not possible!");
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
device->externalThread = externalThread;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_open_channel(device_server* device)
|
||||
{
|
||||
CameraDeviceServerContext* context = &device->context;
|
||||
DWORD Error = ERROR_SUCCESS;
|
||||
HANDLE hEvent;
|
||||
DWORD BytesReturned = 0;
|
||||
PULONG pSessionId = NULL;
|
||||
UINT32 channelId;
|
||||
BOOL status = TRUE;
|
||||
|
||||
WINPR_ASSERT(device);
|
||||
|
||||
if (WTSQuerySessionInformationA(device->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
|
||||
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
device->SessionId = (DWORD)*pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
hEvent = WTSVirtualChannelManagerGetEventHandle(device->context.vcm);
|
||||
|
||||
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
|
||||
{
|
||||
Error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
|
||||
return Error;
|
||||
}
|
||||
|
||||
device->device_channel = WTSVirtualChannelOpenEx(device->SessionId, context->virtualChannelName,
|
||||
WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
if (!device->device_channel)
|
||||
{
|
||||
Error = GetLastError();
|
||||
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
|
||||
return Error;
|
||||
}
|
||||
|
||||
channelId = WTSChannelGetIdByHandle(device->device_channel);
|
||||
|
||||
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
|
||||
if (!status)
|
||||
{
|
||||
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return Error;
|
||||
}
|
||||
|
||||
static UINT device_server_handle_success_response(CameraDeviceServerContext* context, wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_SUCCESS_RESPONSE pdu = {};
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
IFCALLRET(context->SuccessResponse, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->SuccessResponse failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_recv_error_response(CameraDeviceServerContext* context, wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_ERROR_RESPONSE pdu = {};
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, pdu.ErrorCode);
|
||||
|
||||
IFCALLRET(context->ErrorResponse, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->ErrorResponse failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_recv_stream_list_response(CameraDeviceServerContext* context, wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_STREAM_LIST_RESPONSE pdu = {};
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
BYTE i;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
pdu.N_Descriptions = MIN(Stream_GetRemainingLength(s) / 5, 255);
|
||||
|
||||
for (i = 0; i < pdu.N_Descriptions; ++i)
|
||||
{
|
||||
CAM_STREAM_DESCRIPTION* StreamDescription = &pdu.StreamDescriptions[i];
|
||||
|
||||
Stream_Read_UINT16(s, StreamDescription->FrameSourceTypes);
|
||||
Stream_Read_UINT8(s, StreamDescription->StreamCategory);
|
||||
Stream_Read_UINT8(s, StreamDescription->Selected);
|
||||
Stream_Read_UINT8(s, StreamDescription->CanBeShared);
|
||||
}
|
||||
|
||||
IFCALLRET(context->StreamListResponse, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->StreamListResponse failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_recv_media_type_list_response(CameraDeviceServerContext* context,
|
||||
wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_MEDIA_TYPE_LIST_RESPONSE pdu = {};
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
BYTE i;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 26))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
pdu.N_Descriptions = Stream_GetRemainingLength(s) / 26;
|
||||
|
||||
pdu.MediaTypeDescriptions = calloc(pdu.N_Descriptions, sizeof(CAM_MEDIA_TYPE_DESCRIPTION));
|
||||
if (!pdu.MediaTypeDescriptions)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to allocate %zu CAM_MEDIA_TYPE_DESCRIPTION structs",
|
||||
pdu.N_Descriptions);
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdu.N_Descriptions; ++i)
|
||||
{
|
||||
CAM_MEDIA_TYPE_DESCRIPTION* MediaTypeDescriptions = &pdu.MediaTypeDescriptions[i];
|
||||
|
||||
Stream_Read_UINT8(s, MediaTypeDescriptions->Format);
|
||||
Stream_Read_UINT32(s, MediaTypeDescriptions->Width);
|
||||
Stream_Read_UINT32(s, MediaTypeDescriptions->Height);
|
||||
Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateNumerator);
|
||||
Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateDenominator);
|
||||
Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioNumerator);
|
||||
Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioDenominator);
|
||||
Stream_Read_UINT8(s, MediaTypeDescriptions->Flags);
|
||||
}
|
||||
|
||||
IFCALLRET(context->MediaTypeListResponse, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->MediaTypeListResponse failed with error %" PRIu32 "", error);
|
||||
|
||||
free(pdu.MediaTypeDescriptions);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_recv_current_media_type_response(CameraDeviceServerContext* context,
|
||||
wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_CURRENT_MEDIA_TYPE_RESPONSE pdu = {};
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 26))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT8(s, pdu.MediaTypeDescription.Format);
|
||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.Width);
|
||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.Height);
|
||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateNumerator);
|
||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateDenominator);
|
||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioNumerator);
|
||||
Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioDenominator);
|
||||
Stream_Read_UINT8(s, pdu.MediaTypeDescription.Flags);
|
||||
|
||||
IFCALLRET(context->CurrentMediaTypeResponse, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->CurrentMediaTypeResponse failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_recv_sample_response(CameraDeviceServerContext* context, wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_SAMPLE_RESPONSE pdu = {};
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT8(s, pdu.StreamIndex);
|
||||
|
||||
pdu.SampleSize = Stream_GetRemainingLength(s);
|
||||
pdu.Sample = Stream_Pointer(s);
|
||||
|
||||
IFCALLRET(context->SampleResponse, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->SampleResponse failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_recv_sample_error_response(CameraDeviceServerContext* context, wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_SAMPLE_ERROR_RESPONSE pdu = {};
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT8(s, pdu.StreamIndex);
|
||||
Stream_Read_UINT32(s, pdu.ErrorCode);
|
||||
|
||||
IFCALLRET(context->SampleErrorResponse, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->SampleErrorResponse failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_recv_property_list_response(CameraDeviceServerContext* context,
|
||||
wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_PROPERTY_LIST_RESPONSE pdu = {};
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
pdu.N_Properties = Stream_GetRemainingLength(s) / 19;
|
||||
|
||||
if (pdu.N_Properties > 0)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
pdu.Properties = calloc(pdu.N_Properties, sizeof(CAM_PROPERTY_DESCRIPTION));
|
||||
if (!pdu.Properties)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to allocate %zu CAM_PROPERTY_DESCRIPTION structs",
|
||||
pdu.N_Properties);
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdu.N_Properties; ++i)
|
||||
{
|
||||
Stream_Read_UINT8(s, pdu.Properties[i].PropertySet);
|
||||
Stream_Read_UINT8(s, pdu.Properties[i].PropertyId);
|
||||
Stream_Read_UINT8(s, pdu.Properties[i].Capabilities);
|
||||
Stream_Read_INT32(s, pdu.Properties[i].MinValue);
|
||||
Stream_Read_INT32(s, pdu.Properties[i].MaxValue);
|
||||
Stream_Read_INT32(s, pdu.Properties[i].Step);
|
||||
Stream_Read_INT32(s, pdu.Properties[i].DefaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
IFCALLRET(context->PropertyListResponse, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->PropertyListResponse failed with error %" PRIu32 "", error);
|
||||
|
||||
free(pdu.Properties);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_recv_property_value_response(CameraDeviceServerContext* context,
|
||||
wStream* s,
|
||||
const CAM_SHARED_MSG_HEADER* header)
|
||||
{
|
||||
CAM_PROPERTY_VALUE_RESPONSE pdu = {};
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(header);
|
||||
|
||||
pdu.Header = *header;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_Read_UINT8(s, pdu.PropertyValue.Mode);
|
||||
Stream_Read_INT32(s, pdu.PropertyValue.Value);
|
||||
|
||||
IFCALLRET(context->PropertyValueResponse, error, context, &pdu);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->PropertyValueResponse failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_process_message(device_server* device)
|
||||
{
|
||||
BOOL rc;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
ULONG BytesReturned;
|
||||
CAM_SHARED_MSG_HEADER header = {};
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(device);
|
||||
WINPR_ASSERT(device->device_channel);
|
||||
|
||||
s = device->buffer;
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
rc = WTSVirtualChannelRead(device->device_channel, 0, NULL, 0, &BytesReturned);
|
||||
if (!rc)
|
||||
goto out;
|
||||
|
||||
if (BytesReturned < 1)
|
||||
{
|
||||
error = CHANNEL_RC_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(device->device_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE))
|
||||
return ERROR_NO_DATA;
|
||||
|
||||
Stream_SetLength(s, BytesReturned);
|
||||
Stream_Read_UINT8(s, header.Version);
|
||||
Stream_Read_UINT8(s, header.MessageId);
|
||||
|
||||
switch (header.MessageId)
|
||||
{
|
||||
case CAM_MSG_ID_SuccessResponse:
|
||||
error = device_server_handle_success_response(&device->context, s, &header);
|
||||
break;
|
||||
case CAM_MSG_ID_ErrorResponse:
|
||||
error = device_server_recv_error_response(&device->context, s, &header);
|
||||
break;
|
||||
case CAM_MSG_ID_StreamListResponse:
|
||||
error = device_server_recv_stream_list_response(&device->context, s, &header);
|
||||
break;
|
||||
case CAM_MSG_ID_MediaTypeListResponse:
|
||||
error = device_server_recv_media_type_list_response(&device->context, s, &header);
|
||||
break;
|
||||
case CAM_MSG_ID_CurrentMediaTypeResponse:
|
||||
error = device_server_recv_current_media_type_response(&device->context, s, &header);
|
||||
break;
|
||||
case CAM_MSG_ID_SampleResponse:
|
||||
error = device_server_recv_sample_response(&device->context, s, &header);
|
||||
break;
|
||||
case CAM_MSG_ID_SampleErrorResponse:
|
||||
error = device_server_recv_sample_error_response(&device->context, s, &header);
|
||||
break;
|
||||
case CAM_MSG_ID_PropertyListResponse:
|
||||
error = device_server_recv_property_list_response(&device->context, s, &header);
|
||||
break;
|
||||
case CAM_MSG_ID_PropertyValueResponse:
|
||||
error = device_server_recv_property_value_response(&device->context, s, &header);
|
||||
break;
|
||||
default:
|
||||
WLog_ERR(TAG, "device_process_message: unknown or invalid MessageId %" PRIu8 "",
|
||||
header.MessageId);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (error)
|
||||
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_context_poll_int(CameraDeviceServerContext* context)
|
||||
{
|
||||
device_server* device = (device_server*)context;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
|
||||
WINPR_ASSERT(device);
|
||||
|
||||
switch (device->state)
|
||||
{
|
||||
case CAMERA_DEVICE_INITIAL:
|
||||
error = device_server_open_channel(device);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "device_server_open_channel failed with error %" PRIu32 "!", error);
|
||||
else
|
||||
device->state = CAMERA_DEVICE_OPENED;
|
||||
break;
|
||||
case CAMERA_DEVICE_OPENED:
|
||||
error = device_process_message(device);
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static HANDLE device_server_get_channel_handle(device_server* device)
|
||||
{
|
||||
void* buffer = NULL;
|
||||
DWORD BytesReturned = 0;
|
||||
HANDLE ChannelEvent = NULL;
|
||||
|
||||
WINPR_ASSERT(device);
|
||||
|
||||
if (WTSVirtualChannelQuery(device->device_channel, WTSVirtualEventHandle, &buffer,
|
||||
&BytesReturned) == TRUE)
|
||||
{
|
||||
if (BytesReturned == sizeof(HANDLE))
|
||||
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
|
||||
|
||||
WTSFreeMemory(buffer);
|
||||
}
|
||||
|
||||
return ChannelEvent;
|
||||
}
|
||||
|
||||
static DWORD WINAPI device_server_thread_func(LPVOID arg)
|
||||
{
|
||||
DWORD nCount;
|
||||
HANDLE events[2] = { 0 };
|
||||
device_server* device = (device_server*)arg;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status;
|
||||
|
||||
WINPR_ASSERT(device);
|
||||
|
||||
nCount = 0;
|
||||
events[nCount++] = device->stopEvent;
|
||||
|
||||
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
|
||||
{
|
||||
switch (device->state)
|
||||
{
|
||||
case CAMERA_DEVICE_INITIAL:
|
||||
error = device_server_context_poll_int(&device->context);
|
||||
if (error == CHANNEL_RC_OK)
|
||||
{
|
||||
events[1] = device_server_get_channel_handle(device);
|
||||
nCount = 2;
|
||||
}
|
||||
break;
|
||||
case CAMERA_DEVICE_OPENED:
|
||||
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
||||
switch (status)
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
case WAIT_TIMEOUT:
|
||||
error = device_server_context_poll_int(&device->context);
|
||||
break;
|
||||
|
||||
case WAIT_FAILED:
|
||||
default:
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WTSVirtualChannelClose(device->device_channel);
|
||||
device->device_channel = NULL;
|
||||
|
||||
if (error && device->context.rdpcontext)
|
||||
setChannelError(device->context.rdpcontext, error,
|
||||
"device_server_thread_func reported an error");
|
||||
|
||||
ExitThread(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_open(CameraDeviceServerContext* context)
|
||||
{
|
||||
device_server* device = (device_server*)context;
|
||||
|
||||
WINPR_ASSERT(device);
|
||||
|
||||
if (!device->externalThread && (device->thread == NULL))
|
||||
{
|
||||
device->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!device->stopEvent)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
device->thread = CreateThread(NULL, 0, device_server_thread_func, device, 0, NULL);
|
||||
if (!device->thread)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(device->stopEvent);
|
||||
device->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
device->isOpened = TRUE;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT device_server_close(CameraDeviceServerContext* context)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
device_server* device = (device_server*)context;
|
||||
|
||||
WINPR_ASSERT(device);
|
||||
|
||||
if (!device->externalThread && device->thread)
|
||||
{
|
||||
SetEvent(device->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(device->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(device->thread);
|
||||
CloseHandle(device->stopEvent);
|
||||
device->thread = NULL;
|
||||
device->stopEvent = NULL;
|
||||
}
|
||||
if (device->externalThread)
|
||||
{
|
||||
if (device->state != CAMERA_DEVICE_INITIAL)
|
||||
{
|
||||
WTSVirtualChannelClose(device->device_channel);
|
||||
device->device_channel = NULL;
|
||||
device->state = CAMERA_DEVICE_INITIAL;
|
||||
}
|
||||
}
|
||||
device->isOpened = FALSE;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_context_poll(CameraDeviceServerContext* context)
|
||||
{
|
||||
device_server* device = (device_server*)context;
|
||||
|
||||
WINPR_ASSERT(device);
|
||||
|
||||
if (!device->externalThread)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
return device_server_context_poll_int(context);
|
||||
}
|
||||
|
||||
static BOOL device_server_context_handle(CameraDeviceServerContext* context, HANDLE* handle)
|
||||
{
|
||||
device_server* device = (device_server*)context;
|
||||
|
||||
WINPR_ASSERT(device);
|
||||
WINPR_ASSERT(handle);
|
||||
|
||||
if (!device->externalThread)
|
||||
return FALSE;
|
||||
if (device->state == CAMERA_DEVICE_INITIAL)
|
||||
return FALSE;
|
||||
|
||||
*handle = device_server_get_channel_handle(device);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static wStream* device_server_packet_new(size_t size, BYTE version, BYTE messageId)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(size > 0);
|
||||
|
||||
s = Stream_New(NULL, size);
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Stream_Write_UINT8(s, version);
|
||||
Stream_Write_UINT8(s, messageId);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static UINT device_server_packet_send(CameraDeviceServerContext* context, wStream* s)
|
||||
{
|
||||
device_server* device = (device_server*)context;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
ULONG written;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
if (!WTSVirtualChannelWrite(device->device_channel, (PCHAR)Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (written < Stream_GetPosition(s))
|
||||
{
|
||||
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
|
||||
Stream_GetPosition(s));
|
||||
}
|
||||
|
||||
out:
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
static UINT device_server_write_and_send_header(CameraDeviceServerContext* context, BYTE messageId)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
s = device_server_packet_new(CAM_HEADER_SIZE, context->protocolVersion, messageId);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
return device_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
static UINT
|
||||
device_send_activate_device_request_pdu(CameraDeviceServerContext* context,
|
||||
const CAM_ACTIVATE_DEVICE_REQUEST* activateDeviceRequest)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
return device_server_write_and_send_header(context, CAM_MSG_ID_ActivateDeviceRequest);
|
||||
}
|
||||
|
||||
static UINT device_send_deactivate_device_request_pdu(
|
||||
CameraDeviceServerContext* context,
|
||||
const CAM_DEACTIVATE_DEVICE_REQUEST* deactivateDeviceRequest)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
return device_server_write_and_send_header(context, CAM_MSG_ID_DeactivateDeviceRequest);
|
||||
}
|
||||
|
||||
static UINT device_send_stream_list_request_pdu(CameraDeviceServerContext* context,
|
||||
const CAM_STREAM_LIST_REQUEST* streamListRequest)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
return device_server_write_and_send_header(context, CAM_MSG_ID_StreamListRequest);
|
||||
}
|
||||
|
||||
static UINT
|
||||
device_send_media_type_list_request_pdu(CameraDeviceServerContext* context,
|
||||
const CAM_MEDIA_TYPE_LIST_REQUEST* mediaTypeListRequest)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(mediaTypeListRequest);
|
||||
|
||||
s = device_server_packet_new(CAM_HEADER_SIZE + 1, context->protocolVersion,
|
||||
CAM_MSG_ID_MediaTypeListRequest);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
Stream_Write_UINT8(s, mediaTypeListRequest->StreamIndex);
|
||||
|
||||
return device_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
static UINT device_send_current_media_type_request_pdu(
|
||||
CameraDeviceServerContext* context,
|
||||
const CAM_CURRENT_MEDIA_TYPE_REQUEST* currentMediaTypeRequest)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(currentMediaTypeRequest);
|
||||
|
||||
s = device_server_packet_new(CAM_HEADER_SIZE + 1, context->protocolVersion,
|
||||
CAM_MSG_ID_CurrentMediaTypeRequest);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
Stream_Write_UINT8(s, currentMediaTypeRequest->StreamIndex);
|
||||
|
||||
return device_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
static UINT
|
||||
device_send_start_streams_request_pdu(CameraDeviceServerContext* context,
|
||||
const CAM_START_STREAMS_REQUEST* startStreamsRequest)
|
||||
{
|
||||
wStream* s;
|
||||
size_t i;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(startStreamsRequest);
|
||||
|
||||
s = device_server_packet_new(CAM_HEADER_SIZE + startStreamsRequest->N_Infos * 27,
|
||||
context->protocolVersion, CAM_MSG_ID_StartStreamsRequest);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
for (i = 0; i < startStreamsRequest->N_Infos; ++i)
|
||||
{
|
||||
const CAM_START_STREAM_INFO* info = &startStreamsRequest->StartStreamsInfo[i];
|
||||
const CAM_MEDIA_TYPE_DESCRIPTION* description = &info->MediaTypeDescription;
|
||||
|
||||
Stream_Write_UINT8(s, info->StreamIndex);
|
||||
|
||||
Stream_Write_UINT8(s, description->Format);
|
||||
Stream_Write_UINT32(s, description->Width);
|
||||
Stream_Write_UINT32(s, description->Height);
|
||||
Stream_Write_UINT32(s, description->FrameRateNumerator);
|
||||
Stream_Write_UINT32(s, description->FrameRateDenominator);
|
||||
Stream_Write_UINT32(s, description->PixelAspectRatioNumerator);
|
||||
Stream_Write_UINT32(s, description->PixelAspectRatioDenominator);
|
||||
Stream_Write_UINT8(s, description->Flags);
|
||||
}
|
||||
|
||||
return device_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
static UINT device_send_stop_streams_request_pdu(CameraDeviceServerContext* context,
|
||||
const CAM_STOP_STREAMS_REQUEST* stopStreamsRequest)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
return device_server_write_and_send_header(context, CAM_MSG_ID_StopStreamsRequest);
|
||||
}
|
||||
|
||||
static UINT device_send_sample_request_pdu(CameraDeviceServerContext* context,
|
||||
const CAM_SAMPLE_REQUEST* sampleRequest)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(sampleRequest);
|
||||
|
||||
s = device_server_packet_new(CAM_HEADER_SIZE + 1, context->protocolVersion,
|
||||
CAM_MSG_ID_SampleRequest);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
Stream_Write_UINT8(s, sampleRequest->StreamIndex);
|
||||
|
||||
return device_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
static UINT
|
||||
device_send_property_list_request_pdu(CameraDeviceServerContext* context,
|
||||
const CAM_PROPERTY_LIST_REQUEST* propertyListRequest)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
return device_server_write_and_send_header(context, CAM_MSG_ID_PropertyListRequest);
|
||||
}
|
||||
|
||||
static UINT
|
||||
device_send_property_value_request_pdu(CameraDeviceServerContext* context,
|
||||
const CAM_PROPERTY_VALUE_REQUEST* propertyValueRequest)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(propertyValueRequest);
|
||||
|
||||
s = device_server_packet_new(CAM_HEADER_SIZE + 2, context->protocolVersion,
|
||||
CAM_MSG_ID_PropertyValueRequest);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
Stream_Write_UINT8(s, propertyValueRequest->PropertySet);
|
||||
Stream_Write_UINT8(s, propertyValueRequest->PropertyId);
|
||||
|
||||
return device_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
static UINT device_send_set_property_value_request_pdu(
|
||||
CameraDeviceServerContext* context,
|
||||
const CAM_SET_PROPERTY_VALUE_REQUEST* setPropertyValueRequest)
|
||||
{
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(setPropertyValueRequest);
|
||||
|
||||
s = device_server_packet_new(CAM_HEADER_SIZE + 2 + 5, context->protocolVersion,
|
||||
CAM_MSG_ID_SetPropertyValueRequest);
|
||||
if (!s)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
Stream_Write_UINT8(s, setPropertyValueRequest->PropertySet);
|
||||
Stream_Write_UINT8(s, setPropertyValueRequest->PropertyId);
|
||||
|
||||
Stream_Write_UINT8(s, setPropertyValueRequest->PropertyValue.Mode);
|
||||
Stream_Write_INT32(s, setPropertyValueRequest->PropertyValue.Value);
|
||||
|
||||
return device_server_packet_send(context, s);
|
||||
}
|
||||
|
||||
CameraDeviceServerContext* camera_device_server_context_new(HANDLE vcm)
|
||||
{
|
||||
device_server* device = (device_server*)calloc(1, sizeof(device_server));
|
||||
|
||||
if (!device)
|
||||
return NULL;
|
||||
|
||||
device->context.vcm = vcm;
|
||||
device->context.Initialize = device_server_initialize;
|
||||
device->context.Open = device_server_open;
|
||||
device->context.Close = device_server_close;
|
||||
device->context.Poll = device_server_context_poll;
|
||||
device->context.ChannelHandle = device_server_context_handle;
|
||||
|
||||
device->context.ActivateDeviceRequest = device_send_activate_device_request_pdu;
|
||||
device->context.DeactivateDeviceRequest = device_send_deactivate_device_request_pdu;
|
||||
|
||||
device->context.StreamListRequest = device_send_stream_list_request_pdu;
|
||||
device->context.MediaTypeListRequest = device_send_media_type_list_request_pdu;
|
||||
device->context.CurrentMediaTypeRequest = device_send_current_media_type_request_pdu;
|
||||
|
||||
device->context.StartStreamsRequest = device_send_start_streams_request_pdu;
|
||||
device->context.StopStreamsRequest = device_send_stop_streams_request_pdu;
|
||||
device->context.SampleRequest = device_send_sample_request_pdu;
|
||||
|
||||
device->context.PropertyListRequest = device_send_property_list_request_pdu;
|
||||
device->context.PropertyValueRequest = device_send_property_value_request_pdu;
|
||||
device->context.SetPropertyValueRequest = device_send_set_property_value_request_pdu;
|
||||
|
||||
device->buffer = Stream_New(NULL, 4096);
|
||||
if (!device->buffer)
|
||||
goto fail;
|
||||
|
||||
return &device->context;
|
||||
fail:
|
||||
camera_device_server_context_free(&device->context);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void camera_device_server_context_free(CameraDeviceServerContext* context)
|
||||
{
|
||||
device_server* device = (device_server*)context;
|
||||
|
||||
if (device)
|
||||
{
|
||||
device_server_close(context);
|
||||
Stream_Free(device->buffer, TRUE);
|
||||
}
|
||||
|
||||
free(context->virtualChannelName);
|
||||
|
||||
free(device);
|
||||
}
|
@ -51,7 +51,12 @@
|
||||
#include <freerdp/server/telemetry.h>
|
||||
#include <freerdp/server/rdpgfx.h>
|
||||
#include <freerdp/server/disp.h>
|
||||
#include <freerdp/server/camera-device-enumerator.h>
|
||||
#include <freerdp/server/camera-device.h>
|
||||
|
||||
#ifdef WITH_CHANNEL_GFXREDIR
|
||||
#include <freerdp/server/gfxredir.h>
|
||||
#endif /* WITH_CHANNEL_GFXREDIR */
|
||||
|
||||
#if defined(CHANNEL_AINPUT_SERVER)
|
||||
#include <freerdp/server/ainput.h>
|
||||
@ -74,6 +79,8 @@ void freerdp_channels_dummy(void)
|
||||
TelemetryServerContext* telemetry;
|
||||
RdpgfxServerContext* rdpgfx;
|
||||
DispServerContext* disp;
|
||||
CamDevEnumServerContext* camera_enumerator;
|
||||
CameraDeviceServerContext* camera_device;
|
||||
#ifdef WITH_CHANNEL_GFXREDIR
|
||||
GfxRedirServerContext* gfxredir;
|
||||
#endif // WITH_CHANNEL_GFXREDIR
|
||||
@ -103,6 +110,10 @@ void freerdp_channels_dummy(void)
|
||||
rdpgfx_server_context_free(rdpgfx);
|
||||
disp = disp_server_context_new(NULL);
|
||||
disp_server_context_free(disp);
|
||||
camera_enumerator = cam_dev_enum_server_context_new(NULL);
|
||||
cam_dev_enum_server_context_free(camera_enumerator);
|
||||
camera_device = camera_device_server_context_new(NULL);
|
||||
camera_device_server_context_free(camera_device);
|
||||
#ifdef WITH_CHANNEL_GFXREDIR
|
||||
gfxredir = gfxredir_server_context_new(NULL);
|
||||
gfxredir_server_context_free(gfxredir);
|
||||
|
@ -62,6 +62,9 @@
|
||||
#cmakedefine CHANNEL_AUDIN
|
||||
#cmakedefine CHANNEL_AUDIN_CLIENT
|
||||
#cmakedefine CHANNEL_AUDIN_SERVER
|
||||
#cmakedefine CHANNEL_CAMERA
|
||||
#cmakedefine CHANNEL_CAMERA_CLIENT
|
||||
#cmakedefine CHANNEL_CAMERA_SERVER
|
||||
#cmakedefine CHANNEL_CLIPRDR
|
||||
#cmakedefine CHANNEL_CLIPRDR_CLIENT
|
||||
#cmakedefine CHANNEL_CLIPRDR_SERVER
|
||||
|
335
include/freerdp/channels/camera-device.h
Normal file
335
include/freerdp/channels/camera-device.h
Normal file
@ -0,0 +1,335 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Capture Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_CAMERA_DEVICE_H
|
||||
#define FREERDP_CHANNEL_CAMERA_DEVICE_H
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/types.h>
|
||||
|
||||
#define CAM_DEVICE_ENUMERATOR_DVC_CHANNEL_NAME "RDCamera_Device_Enumerator"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAM_MSG_ID_SuccessResponse = 0x01,
|
||||
CAM_MSG_ID_ErrorResponse = 0x02,
|
||||
CAM_MSG_ID_SelectVersionRequest = 0x03,
|
||||
CAM_MSG_ID_SelectVersionResponse = 0x04,
|
||||
CAM_MSG_ID_DeviceAddedNotification = 0x05,
|
||||
CAM_MSG_ID_DeviceRemovedNotification = 0x06,
|
||||
CAM_MSG_ID_ActivateDeviceRequest = 0x07,
|
||||
CAM_MSG_ID_DeactivateDeviceRequest = 0x08,
|
||||
CAM_MSG_ID_StreamListRequest = 0x09,
|
||||
CAM_MSG_ID_StreamListResponse = 0x0A,
|
||||
CAM_MSG_ID_MediaTypeListRequest = 0x0B,
|
||||
CAM_MSG_ID_MediaTypeListResponse = 0x0C,
|
||||
CAM_MSG_ID_CurrentMediaTypeRequest = 0x0D,
|
||||
CAM_MSG_ID_CurrentMediaTypeResponse = 0x0E,
|
||||
CAM_MSG_ID_StartStreamsRequest = 0x0F,
|
||||
CAM_MSG_ID_StopStreamsRequest = 0x10,
|
||||
CAM_MSG_ID_SampleRequest = 0x11,
|
||||
CAM_MSG_ID_SampleResponse = 0x12,
|
||||
CAM_MSG_ID_SampleErrorResponse = 0x13,
|
||||
CAM_MSG_ID_PropertyListRequest = 0x14,
|
||||
CAM_MSG_ID_PropertyListResponse = 0x15,
|
||||
CAM_MSG_ID_PropertyValueRequest = 0x16,
|
||||
CAM_MSG_ID_PropertyValueResponse = 0x17,
|
||||
CAM_MSG_ID_SetPropertyValueRequest = 0x18,
|
||||
} CAM_MSG_ID;
|
||||
|
||||
#define CAM_HEADER_SIZE 2
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BYTE Version;
|
||||
CAM_MSG_ID MessageId;
|
||||
} CAM_SHARED_MSG_HEADER;
|
||||
|
||||
/* Messages Exchanged on the Device Enumeration Channel (2.2.2) */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
} CAM_SELECT_VERSION_REQUEST;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
} CAM_SELECT_VERSION_RESPONSE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
WCHAR* DeviceName;
|
||||
char* VirtualChannelName;
|
||||
} CAM_DEVICE_ADDED_NOTIFICATION;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
char* VirtualChannelName;
|
||||
} CAM_DEVICE_REMOVED_NOTIFICATION;
|
||||
|
||||
/* Messages Exchanged on Device Channels (2.2.3) */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
} CAM_SUCCESS_RESPONSE;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAM_ERROR_CODE_UnexpectedError = 0x00000001,
|
||||
CAM_ERROR_CODE_InvalidMessage = 0x00000002,
|
||||
CAM_ERROR_CODE_NotInitialized = 0x00000003,
|
||||
CAM_ERROR_CODE_InvalidRequest = 0x00000004,
|
||||
CAM_ERROR_CODE_InvalidStreamNumber = 0x00000005,
|
||||
CAM_ERROR_CODE_InvalidMediaType = 0x00000006,
|
||||
CAM_ERROR_CODE_OutOfMemory = 0x00000007,
|
||||
CAM_ERROR_CODE_ItemNotFound = 0x00000008,
|
||||
CAM_ERROR_CODE_SetNotFound = 0x00000009,
|
||||
CAM_ERROR_CODE_OperationNotSupported = 0x0000000A,
|
||||
} CAM_ERROR_CODE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
CAM_ERROR_CODE ErrorCode;
|
||||
} CAM_ERROR_RESPONSE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
} CAM_ACTIVATE_DEVICE_REQUEST;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
} CAM_DEACTIVATE_DEVICE_REQUEST;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
} CAM_STREAM_LIST_REQUEST;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAM_STREAM_FRAME_SOURCE_TYPE_Color = 0x0001,
|
||||
CAM_STREAM_FRAME_SOURCE_TYPE_Infrared = 0x0002,
|
||||
CAM_STREAM_FRAME_SOURCE_TYPE_Custom = 0x0008,
|
||||
} CAM_STREAM_FRAME_SOURCE_TYPES;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAM_STREAM_CATEGORY_Capture = 0x01,
|
||||
} CAM_STREAM_CATEGORY;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_STREAM_FRAME_SOURCE_TYPES FrameSourceTypes;
|
||||
CAM_STREAM_CATEGORY StreamCategory;
|
||||
BYTE Selected;
|
||||
BYTE CanBeShared;
|
||||
} CAM_STREAM_DESCRIPTION;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
BYTE N_Descriptions;
|
||||
CAM_STREAM_DESCRIPTION StreamDescriptions[255];
|
||||
} CAM_STREAM_LIST_RESPONSE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
BYTE StreamIndex;
|
||||
} CAM_MEDIA_TYPE_LIST_REQUEST;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAM_MEDIA_FORMAT_H264 = 0x01,
|
||||
CAM_MEDIA_FORMAT_MJPG = 0x02,
|
||||
CAM_MEDIA_FORMAT_YUY2 = 0x03,
|
||||
CAM_MEDIA_FORMAT_NV12 = 0x04,
|
||||
CAM_MEDIA_FORMAT_I420 = 0x05,
|
||||
CAM_MEDIA_FORMAT_RGB24 = 0x06,
|
||||
CAM_MEDIA_FORMAT_RGB32 = 0x07,
|
||||
} CAM_MEDIA_FORMAT;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAM_MEDIA_TYPE_DESCRIPTION_FLAG_DecodingRequired = 0x01,
|
||||
CAM_MEDIA_TYPE_DESCRIPTION_FLAG_BottomUpImage = 0x02,
|
||||
} CAM_MEDIA_TYPE_DESCRIPTION_FLAGS;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_MEDIA_FORMAT Format;
|
||||
UINT32 Width;
|
||||
UINT32 Height;
|
||||
UINT32 FrameRateNumerator;
|
||||
UINT32 FrameRateDenominator;
|
||||
UINT32 PixelAspectRatioNumerator;
|
||||
UINT32 PixelAspectRatioDenominator;
|
||||
CAM_MEDIA_TYPE_DESCRIPTION_FLAGS Flags;
|
||||
} CAM_MEDIA_TYPE_DESCRIPTION;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
size_t N_Descriptions;
|
||||
CAM_MEDIA_TYPE_DESCRIPTION* MediaTypeDescriptions;
|
||||
} CAM_MEDIA_TYPE_LIST_RESPONSE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
BYTE StreamIndex;
|
||||
} CAM_CURRENT_MEDIA_TYPE_REQUEST;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
CAM_MEDIA_TYPE_DESCRIPTION MediaTypeDescription;
|
||||
} CAM_CURRENT_MEDIA_TYPE_RESPONSE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BYTE StreamIndex;
|
||||
CAM_MEDIA_TYPE_DESCRIPTION MediaTypeDescription;
|
||||
} CAM_START_STREAM_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
BYTE N_Infos;
|
||||
CAM_START_STREAM_INFO StartStreamsInfo[255];
|
||||
} CAM_START_STREAMS_REQUEST;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
} CAM_STOP_STREAMS_REQUEST;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
BYTE StreamIndex;
|
||||
} CAM_SAMPLE_REQUEST;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
BYTE StreamIndex;
|
||||
size_t SampleSize;
|
||||
BYTE* Sample;
|
||||
} CAM_SAMPLE_RESPONSE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
BYTE StreamIndex;
|
||||
CAM_ERROR_CODE ErrorCode;
|
||||
} CAM_SAMPLE_ERROR_RESPONSE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
} CAM_PROPERTY_LIST_REQUEST;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAM_PROPERTY_SET_CameraControl = 0x01,
|
||||
CAM_PROPERTY_SET_VideoProcAmp = 0x02,
|
||||
} CAM_PROPERTY_SET;
|
||||
|
||||
/* CameraControl properties */
|
||||
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Exposure 0x01
|
||||
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Focus 0x02
|
||||
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Pan 0x03
|
||||
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Roll 0x04
|
||||
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Tilt 0x05
|
||||
#define CAM_PROPERTY_ID_CAMERA_CONTROL_Zoom 0x06
|
||||
|
||||
/* VideoProcAmp properties */
|
||||
#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_BacklightCompensation 0x01
|
||||
#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Brightness 0x02
|
||||
#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Contrast 0x03
|
||||
#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Hue 0x04
|
||||
#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_WhiteBalance 0x05
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAM_PROPERTY_CAPABILITY_Manual = 0x01,
|
||||
CAM_PROPERTY_CAPABILITY_Auto = 0x02,
|
||||
} CAM_PROPERTY_CAPABILITIES;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_PROPERTY_SET PropertySet;
|
||||
BYTE PropertyId;
|
||||
CAM_PROPERTY_CAPABILITIES Capabilities;
|
||||
INT32 MinValue;
|
||||
INT32 MaxValue;
|
||||
INT32 Step;
|
||||
INT32 DefaultValue;
|
||||
} CAM_PROPERTY_DESCRIPTION;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
size_t N_Properties;
|
||||
CAM_PROPERTY_DESCRIPTION* Properties;
|
||||
} CAM_PROPERTY_LIST_RESPONSE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
CAM_PROPERTY_SET PropertySet;
|
||||
BYTE PropertyId;
|
||||
} CAM_PROPERTY_VALUE_REQUEST;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAM_PROPERTY_MODE_Manual = 0x01,
|
||||
CAM_PROPERTY_MODE_Auto = 0x02,
|
||||
} CAM_PROPERTY_MODE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_PROPERTY_MODE Mode;
|
||||
INT32 Value;
|
||||
} CAM_PROPERTY_VALUE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
CAM_PROPERTY_VALUE PropertyValue;
|
||||
} CAM_PROPERTY_VALUE_RESPONSE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CAM_SHARED_MSG_HEADER Header;
|
||||
CAM_PROPERTY_SET PropertySet;
|
||||
BYTE PropertyId;
|
||||
CAM_PROPERTY_VALUE PropertyValue;
|
||||
} CAM_SET_PROPERTY_VALUE_REQUEST;
|
||||
|
||||
#endif /* FREERDP_CHANNEL_CAMERA_DEVICE_H */
|
134
include/freerdp/server/camera-device-enumerator.h
Normal file
134
include/freerdp/server/camera-device-enumerator.h
Normal file
@ -0,0 +1,134 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Capture Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H
|
||||
#define FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H
|
||||
|
||||
#include <freerdp/channels/camera-device.h>
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
|
||||
typedef struct _cam_dev_enum_server_context CamDevEnumServerContext;
|
||||
|
||||
typedef UINT (*psCamDevEnumServerServerOpen)(CamDevEnumServerContext* context);
|
||||
typedef UINT (*psCamDevEnumServerServerClose)(CamDevEnumServerContext* context);
|
||||
|
||||
typedef BOOL (*psCamDevEnumServerServerChannelIdAssigned)(CamDevEnumServerContext* context,
|
||||
UINT32 channelId);
|
||||
|
||||
typedef UINT (*psCamDevEnumServerServerInitialize)(CamDevEnumServerContext* context,
|
||||
BOOL externalThread);
|
||||
typedef UINT (*psCamDevEnumServerServerPoll)(CamDevEnumServerContext* context);
|
||||
typedef BOOL (*psCamDevEnumServerServerChannelHandle)(CamDevEnumServerContext* context,
|
||||
HANDLE* handle);
|
||||
|
||||
typedef UINT (*psCamDevEnumServerServerSelectVersionRequest)(
|
||||
CamDevEnumServerContext* context, const CAM_SELECT_VERSION_REQUEST* selectVersionRequest);
|
||||
typedef UINT (*psCamDevEnumServerServerSelectVersionResponse)(
|
||||
CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse);
|
||||
|
||||
typedef UINT (*psCamDevEnumServerServerDeviceAddedNotification)(
|
||||
CamDevEnumServerContext* context, const CAM_DEVICE_ADDED_NOTIFICATION* deviceAddedNotification);
|
||||
typedef UINT (*psCamDevEnumServerServerDeviceRemovedNotification)(
|
||||
CamDevEnumServerContext* context,
|
||||
const CAM_DEVICE_REMOVED_NOTIFICATION* deviceRemovedNotification);
|
||||
|
||||
struct _cam_dev_enum_server_context
|
||||
{
|
||||
HANDLE vcm;
|
||||
|
||||
/* Server self-defined pointer. */
|
||||
void* userdata;
|
||||
|
||||
/*** APIs called by the server. ***/
|
||||
|
||||
/**
|
||||
* Optional: Set thread handling.
|
||||
* When externalThread=TRUE, the application is responsible to call
|
||||
* Poll() periodically to process channel events.
|
||||
*
|
||||
* Defaults to externalThread=FALSE
|
||||
*/
|
||||
psCamDevEnumServerServerInitialize Initialize;
|
||||
|
||||
/**
|
||||
* Open the camera device enumerator channel.
|
||||
*/
|
||||
psCamDevEnumServerServerOpen Open;
|
||||
|
||||
/**
|
||||
* Close the camera device enumerator channel.
|
||||
*/
|
||||
psCamDevEnumServerServerClose Close;
|
||||
|
||||
/**
|
||||
* Poll
|
||||
* When externalThread=TRUE, call Poll() periodically from your main loop.
|
||||
* If externalThread=FALSE do not call.
|
||||
*/
|
||||
psCamDevEnumServerServerPoll Poll;
|
||||
|
||||
/**
|
||||
* Retrieve the channel handle for use in conjunction with Poll().
|
||||
* If externalThread=FALSE do not call.
|
||||
*/
|
||||
psCamDevEnumServerServerChannelHandle ChannelHandle;
|
||||
|
||||
/*
|
||||
* Send a Select Version Response PDU.
|
||||
*/
|
||||
psCamDevEnumServerServerSelectVersionResponse SelectVersionResponse;
|
||||
|
||||
/*** Callbacks registered by the server. ***/
|
||||
|
||||
/**
|
||||
* Callback, when the channel got its id assigned.
|
||||
*/
|
||||
psCamDevEnumServerServerChannelIdAssigned ChannelIdAssigned;
|
||||
|
||||
/**
|
||||
* Callback for the Select Version Request PDU.
|
||||
*/
|
||||
psCamDevEnumServerServerSelectVersionRequest SelectVersionRequest;
|
||||
|
||||
/**
|
||||
* Callback for the Device Added Notification PDU.
|
||||
*/
|
||||
psCamDevEnumServerServerDeviceAddedNotification DeviceAddedNotification;
|
||||
|
||||
/**
|
||||
* Callback for the Device Removed Notification PDU.
|
||||
*/
|
||||
psCamDevEnumServerServerDeviceRemovedNotification DeviceRemovedNotification;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
FREERDP_API CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm);
|
||||
FREERDP_API void cam_dev_enum_server_context_free(CamDevEnumServerContext* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H */
|
278
include/freerdp/server/camera-device.h
Normal file
278
include/freerdp/server/camera-device.h
Normal file
@ -0,0 +1,278 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Video Capture Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H
|
||||
#define FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H
|
||||
|
||||
#include <freerdp/channels/camera-device.h>
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
|
||||
typedef struct _camera_device_server_context CameraDeviceServerContext;
|
||||
|
||||
typedef UINT (*psCameraDeviceServerOpen)(CameraDeviceServerContext* context);
|
||||
typedef UINT (*psCameraDeviceServerClose)(CameraDeviceServerContext* context);
|
||||
|
||||
typedef BOOL (*psCameraDeviceServerChannelIdAssigned)(CameraDeviceServerContext* context,
|
||||
UINT32 channelId);
|
||||
|
||||
typedef UINT (*psCameraDeviceServerInitialize)(CameraDeviceServerContext* context,
|
||||
BOOL externalThread);
|
||||
typedef UINT (*psCameraDeviceServerPoll)(CameraDeviceServerContext* context);
|
||||
typedef BOOL (*psCameraDeviceServerChannelHandle)(CameraDeviceServerContext* context,
|
||||
HANDLE* handle);
|
||||
|
||||
typedef UINT (*psCameraDeviceServerSuccessResponse)(CameraDeviceServerContext* context,
|
||||
const CAM_SUCCESS_RESPONSE* successResponse);
|
||||
typedef UINT (*psCameraDeviceServerErrorResponse)(CameraDeviceServerContext* context,
|
||||
const CAM_ERROR_RESPONSE* errorResponse);
|
||||
|
||||
typedef UINT (*psCameraDeviceServerActivateDeviceRequest)(
|
||||
CameraDeviceServerContext* context, const CAM_ACTIVATE_DEVICE_REQUEST* activateDeviceRequest);
|
||||
typedef UINT (*psCameraDeviceServerDeactivateDeviceRequest)(
|
||||
CameraDeviceServerContext* context,
|
||||
const CAM_DEACTIVATE_DEVICE_REQUEST* deactivateDeviceRequest);
|
||||
|
||||
typedef UINT (*psCameraDeviceServerStreamListRequest)(
|
||||
CameraDeviceServerContext* context, const CAM_STREAM_LIST_REQUEST* streamListRequest);
|
||||
typedef UINT (*psCameraDeviceServerStreamListResponse)(
|
||||
CameraDeviceServerContext* context, const CAM_STREAM_LIST_RESPONSE* streamListResponse);
|
||||
|
||||
typedef UINT (*psCameraDeviceServerMediaTypeListRequest)(
|
||||
CameraDeviceServerContext* context, const CAM_MEDIA_TYPE_LIST_REQUEST* mediaTypeListRequest);
|
||||
typedef UINT (*psCameraDeviceServerMediaTypeListResponse)(
|
||||
CameraDeviceServerContext* context, const CAM_MEDIA_TYPE_LIST_RESPONSE* mediaTypeListResponse);
|
||||
|
||||
typedef UINT (*psCameraDeviceServerCurrentMediaTypeRequest)(
|
||||
CameraDeviceServerContext* context,
|
||||
const CAM_CURRENT_MEDIA_TYPE_REQUEST* currentMediaTypeRequest);
|
||||
typedef UINT (*psCameraDeviceServerCurrentMediaTypeResponse)(
|
||||
CameraDeviceServerContext* context,
|
||||
const CAM_CURRENT_MEDIA_TYPE_RESPONSE* currentMediaTypeResponse);
|
||||
|
||||
typedef UINT (*psCameraDeviceServerStartStreamsRequest)(
|
||||
CameraDeviceServerContext* context, const CAM_START_STREAMS_REQUEST* startStreamsRequest);
|
||||
typedef UINT (*psCameraDeviceServerStopStreamsRequest)(
|
||||
CameraDeviceServerContext* context, const CAM_STOP_STREAMS_REQUEST* stopStreamsRequest);
|
||||
|
||||
typedef UINT (*psCameraDeviceServerSampleRequest)(CameraDeviceServerContext* context,
|
||||
const CAM_SAMPLE_REQUEST* sampleRequest);
|
||||
typedef UINT (*psCameraDeviceServerSampleResponse)(CameraDeviceServerContext* context,
|
||||
const CAM_SAMPLE_RESPONSE* sampleResponse);
|
||||
typedef UINT (*psCameraDeviceServerSampleErrorResponse)(
|
||||
CameraDeviceServerContext* context, const CAM_SAMPLE_ERROR_RESPONSE* sampleErrorResponse);
|
||||
|
||||
typedef UINT (*psCameraDeviceServerPropertyListRequest)(
|
||||
CameraDeviceServerContext* context, const CAM_PROPERTY_LIST_REQUEST* propertyListRequest);
|
||||
typedef UINT (*psCameraDeviceServerPropertyListResponse)(
|
||||
CameraDeviceServerContext* context, const CAM_PROPERTY_LIST_RESPONSE* propertyListResponse);
|
||||
|
||||
typedef UINT (*psCameraDeviceServerPropertyValueRequest)(
|
||||
CameraDeviceServerContext* context, const CAM_PROPERTY_VALUE_REQUEST* propertyValueRequest);
|
||||
typedef UINT (*psCameraDeviceServerPropertyValueResponse)(
|
||||
CameraDeviceServerContext* context, const CAM_PROPERTY_VALUE_RESPONSE* propertyValueResponse);
|
||||
|
||||
typedef UINT (*psCameraDeviceServerSetPropertyValueRequest)(
|
||||
CameraDeviceServerContext* context,
|
||||
const CAM_SET_PROPERTY_VALUE_REQUEST* setPropertyValueRequest);
|
||||
|
||||
struct _camera_device_server_context
|
||||
{
|
||||
HANDLE vcm;
|
||||
|
||||
/* Server self-defined pointer. */
|
||||
void* userdata;
|
||||
|
||||
/**
|
||||
* Name of the virtual channel. Pointer owned by the CameraDeviceServerContext,
|
||||
* meaning camera_device_server_context_free() takes care of freeing the pointer.
|
||||
*
|
||||
* Server implementations should sanitize the virtual channel name for invalid
|
||||
* names, like names for other known channels
|
||||
* ("ECHO", "AUDIO_PLAYBACK_DVC", etc.)
|
||||
*/
|
||||
char* virtualChannelName;
|
||||
|
||||
/**
|
||||
* Protocol version to be used. Every sent server to client PDU has the
|
||||
* version value in the Header set to the following value.
|
||||
*/
|
||||
BYTE protocolVersion;
|
||||
|
||||
/*** APIs called by the server. ***/
|
||||
|
||||
/**
|
||||
* Optional: Set thread handling.
|
||||
* When externalThread=TRUE, the application is responsible to call
|
||||
* Poll() periodically to process channel events.
|
||||
*
|
||||
* Defaults to externalThread=FALSE
|
||||
*/
|
||||
psCameraDeviceServerInitialize Initialize;
|
||||
|
||||
/**
|
||||
* Open the camera device channel.
|
||||
*/
|
||||
psCameraDeviceServerOpen Open;
|
||||
|
||||
/**
|
||||
* Close the camera device channel.
|
||||
*/
|
||||
psCameraDeviceServerClose Close;
|
||||
|
||||
/**
|
||||
* Poll
|
||||
* When externalThread=TRUE, call Poll() periodically from your main loop.
|
||||
* If externalThread=FALSE do not call.
|
||||
*/
|
||||
psCameraDeviceServerPoll Poll;
|
||||
|
||||
/**
|
||||
* Retrieve the channel handle for use in conjunction with Poll().
|
||||
* If externalThread=FALSE do not call.
|
||||
*/
|
||||
psCameraDeviceServerChannelHandle ChannelHandle;
|
||||
|
||||
/**
|
||||
* For the following server to client PDUs,
|
||||
* the message header does not have to be set.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Send a Activate Device Request PDU.
|
||||
*/
|
||||
psCameraDeviceServerActivateDeviceRequest ActivateDeviceRequest;
|
||||
|
||||
/**
|
||||
* Send a Deactivate Device Request PDU.
|
||||
*/
|
||||
psCameraDeviceServerDeactivateDeviceRequest DeactivateDeviceRequest;
|
||||
|
||||
/**
|
||||
* Send a Stream List Request PDU.
|
||||
*/
|
||||
psCameraDeviceServerStreamListRequest StreamListRequest;
|
||||
|
||||
/**
|
||||
* Send a Media Type List Request PDU.
|
||||
*/
|
||||
psCameraDeviceServerMediaTypeListRequest MediaTypeListRequest;
|
||||
|
||||
/**
|
||||
* Send a Current Media Type Request PDU.
|
||||
*/
|
||||
psCameraDeviceServerCurrentMediaTypeRequest CurrentMediaTypeRequest;
|
||||
|
||||
/**
|
||||
* Send a Start Streams Request PDU.
|
||||
*/
|
||||
psCameraDeviceServerStartStreamsRequest StartStreamsRequest;
|
||||
|
||||
/**
|
||||
* Send a Stop Streams Request PDU.
|
||||
*/
|
||||
psCameraDeviceServerStopStreamsRequest StopStreamsRequest;
|
||||
|
||||
/**
|
||||
* Send a Sample Request PDU.
|
||||
*/
|
||||
psCameraDeviceServerSampleRequest SampleRequest;
|
||||
|
||||
/**
|
||||
* Send a Property List Request PDU.
|
||||
*/
|
||||
psCameraDeviceServerPropertyListRequest PropertyListRequest;
|
||||
|
||||
/**
|
||||
* Send a Property Value Request PDU.
|
||||
*/
|
||||
psCameraDeviceServerPropertyValueRequest PropertyValueRequest;
|
||||
|
||||
/**
|
||||
* Send a Set Property Value Request PDU.
|
||||
*/
|
||||
psCameraDeviceServerSetPropertyValueRequest SetPropertyValueRequest;
|
||||
|
||||
/*** Callbacks registered by the server. ***/
|
||||
|
||||
/**
|
||||
* Callback, when the channel got its id assigned.
|
||||
*/
|
||||
psCameraDeviceServerChannelIdAssigned ChannelIdAssigned;
|
||||
|
||||
/**
|
||||
* Callback for the Success Response PDU.
|
||||
*/
|
||||
psCameraDeviceServerSuccessResponse SuccessResponse;
|
||||
|
||||
/**
|
||||
* Callback for the Error Response PDU.
|
||||
*/
|
||||
psCameraDeviceServerErrorResponse ErrorResponse;
|
||||
|
||||
/**
|
||||
* Callback for the Stream List Response PDU.
|
||||
*/
|
||||
psCameraDeviceServerStreamListResponse StreamListResponse;
|
||||
|
||||
/**
|
||||
* Callback for the Media Type List Response PDU.
|
||||
*/
|
||||
psCameraDeviceServerMediaTypeListResponse MediaTypeListResponse;
|
||||
|
||||
/**
|
||||
* Callback for the Current Media Type Response PDU.
|
||||
*/
|
||||
psCameraDeviceServerCurrentMediaTypeResponse CurrentMediaTypeResponse;
|
||||
|
||||
/**
|
||||
* Callback for the Sample Response PDU.
|
||||
*/
|
||||
psCameraDeviceServerSampleResponse SampleResponse;
|
||||
|
||||
/**
|
||||
* Callback for the Sample Error Response PDU.
|
||||
*/
|
||||
psCameraDeviceServerSampleErrorResponse SampleErrorResponse;
|
||||
|
||||
/**
|
||||
* Callback for the Property List Response PDU.
|
||||
*/
|
||||
psCameraDeviceServerPropertyListResponse PropertyListResponse;
|
||||
|
||||
/**
|
||||
* Callback for the Property Value Response PDU.
|
||||
*/
|
||||
psCameraDeviceServerPropertyValueResponse PropertyValueResponse;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
FREERDP_API CameraDeviceServerContext* camera_device_server_context_new(HANDLE vcm);
|
||||
FREERDP_API void camera_device_server_context_free(CameraDeviceServerContext* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H */
|
Loading…
Reference in New Issue
Block a user