xrdp/sesman/chansrv/audin.c
2019-07-04 23:52:18 -07:00

456 lines
12 KiB
C

/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2019
*
* 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.
*
* MS-RDPEAI
*
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "os_calls.h"
#include "chansrv.h"
#include "log.h"
#include "xrdp_constants.h"
#define MSG_SNDIN_VERSION 1
#define MSG_SNDIN_FORMATS 2
#define MSG_SNDIN_OPEN 3
#define MSG_SNDIN_OPEN_REPLY 4
#define MSG_SNDIN_DATA_INCOMING 5
#define MSG_SNDIN_DATA 6
#define MSG_SNDIN_FORMATCHANGE 7
#define AUDIN_VERSION 0x00000001
#define AUDIN_NAME "AUDIO_INPUT"
#define AUDIN_FLAGS 1 /* WTS_CHANNEL_OPTION_DYNAMIC */
struct xr_wave_format_ex
{
int wFormatTag;
int nChannels;
int nSamplesPerSec;
int nAvgBytesPerSec;
int nBlockAlign;
int wBitsPerSample;
int cbSize;
uint8_t *data;
};
static uint8_t g_pcm_44100_data[] = { 0 };
static struct xr_wave_format_ex g_pcm_44100 =
{
WAVE_FORMAT_PCM, /* wFormatTag */
2, /* num of channels */
44100, /* samples per sec */
176400, /* avg bytes per sec */
4, /* block align */
16, /* bits per sample */
0, /* data size */
g_pcm_44100_data /* data */
};
static struct chansrv_drdynvc_procs g_audin_info;
static int g_audio_chanid;
static struct stream *g_in_s;
static struct xr_wave_format_ex *g_server_formats[] =
{
&g_pcm_44100,
NULL
};
static struct xr_wave_format_ex **g_client_formats = NULL;
static int g_current_format = 0; /* index in g_client_formats */
/*****************************************************************************/
static int
cleanup_client_formats(void)
{
int index;
if (g_client_formats == NULL)
{
return 0;
}
index = 0;
while (g_client_formats[index] != NULL)
{
g_free(g_client_formats[index]->data);
g_free(g_client_formats[index]);
}
g_free(g_client_formats);
g_client_formats = NULL;
return 0;
}
/*****************************************************************************/
static int
audio_send_version(int chan_id)
{
int error;
int bytes;
struct stream *s;
LOG(0, ("audio_send_version:"));
make_stream(s);
init_stream(s, 32);
out_uint8(s, MSG_SNDIN_VERSION);
out_uint32_le(s, AUDIN_VERSION);
s_mark_end(s);
bytes = (int) (s->end - s->data);
error = chansrv_drdynvc_data(chan_id, s->data, bytes);
free_stream(s);
return error;
}
/*****************************************************************************/
static int
audio_send_formats(int chan_id)
{
int error;
int bytes;
int num_formats;
int index;
struct stream *s;
struct xr_wave_format_ex *wf;
LOG(0, ("audio_send_formats:"));
num_formats = sizeof(g_server_formats) /
sizeof(g_server_formats[0]) - 1;
make_stream(s);
init_stream(s, 8192 * num_formats);
out_uint8(s, MSG_SNDIN_FORMATS);
out_uint32_le(s, num_formats);
out_uint32_le(s, 0); /* cbSizeFormatsPacket */
for (index = 0; index < num_formats; index++)
{
wf = g_server_formats[index];
out_uint16_le(s, wf->wFormatTag);
out_uint16_le(s, wf->nChannels);
out_uint32_le(s, wf->nSamplesPerSec);
out_uint32_le(s, wf->nAvgBytesPerSec);
out_uint16_le(s, wf->nBlockAlign);
out_uint16_le(s, wf->wBitsPerSample);
bytes = wf->cbSize;
out_uint16_le(s, bytes);
if (bytes > 0)
{
out_uint8p(s, wf->data, bytes);
}
}
s_mark_end(s);
bytes = (int) (s->end - s->data);
error = chansrv_drdynvc_data(chan_id, s->data, bytes);
free_stream(s);
return error;
}
/*****************************************************************************/
static int
audio_send_open(int chan_id)
{
int error;
int bytes;
struct stream *s;
struct xr_wave_format_ex *wf;
LOG(0, ("audio_send_open:"));
make_stream(s);
init_stream(s, 8192);
out_uint8(s, MSG_SNDIN_OPEN);
out_uint32_le(s, 2048); /* FramesPerPacket */
out_uint32_le(s, g_current_format); /* initialFormat */
wf = g_client_formats[g_current_format];
out_uint16_le(s, wf->wFormatTag);
out_uint16_le(s, wf->nChannels);
out_uint32_le(s, wf->nSamplesPerSec);
out_uint32_le(s, wf->nAvgBytesPerSec);
out_uint16_le(s, wf->nBlockAlign);
out_uint16_le(s, wf->wBitsPerSample);
bytes = wf->cbSize;
out_uint16_le(s, bytes);
if (bytes > 0)
{
out_uint8p(s, wf->data, bytes);
}
s_mark_end(s);
bytes = (int) (s->end - s->data);
error = chansrv_drdynvc_data(chan_id, s->data, bytes);
free_stream(s);
return error;
}
/*****************************************************************************/
static int
audin_process_version(int chan_id, struct stream *s)
{
int version;
LOG(0, ("audin_process_version:"));
if (!s_check_rem(s, 4))
{
LOG(0, ("audin_process_version: parse error"));
return 1;
}
in_uint32_le(s, version);
LOG(0, ("audin_process_version: version %d", version));
return audio_send_formats(chan_id);
}
/*****************************************************************************/
static int
audin_process_formats(int chan_id, struct stream *s)
{
int index;
int num_formats;
struct xr_wave_format_ex *wf;
LOG(0, ("audin_process_formats:"));
cleanup_client_formats();
if (!s_check_rem(s, 8))
{
LOG(0, ("audin_process_formats: parse error"));
return 1;
}
in_uint32_le(s, num_formats);
in_uint8s(s, 4); /* cbSizeFormatsPacket */
g_client_formats = g_new0(struct xr_wave_format_ex *, num_formats + 1);
for (index = 0; index < num_formats; index++)
{
if (!s_check_rem(s, 18))
{
LOG(0, ("audin_process_formats: parse error"));
return 1;
}
wf = g_new0(struct xr_wave_format_ex, 1);
g_client_formats[index] = wf;
in_uint16_le(s, wf->wFormatTag);
in_uint16_le(s, wf->nChannels);
in_uint32_le(s, wf->nSamplesPerSec);
in_uint32_le(s, wf->nAvgBytesPerSec);
in_uint16_le(s, wf->nBlockAlign);
in_uint16_le(s, wf->wBitsPerSample);
in_uint16_le(s, wf->cbSize);
if (wf->cbSize > 0)
{
if (!s_check_rem(s, wf->cbSize))
{
LOG(0, ("audin_process_formats: parse error"));
return 1;
}
wf->data = g_new0(uint8_t, wf->cbSize);
in_uint8a(s, wf->data, wf->cbSize);
}
}
audio_send_open(chan_id);
return 0;
}
/*****************************************************************************/
static int
audin_process_open_reply(int chan_id, struct stream *s)
{
int result;
if (!s_check_rem(s, 4))
{
LOG(0, ("audin_process_open_reply: parse error"));
return 1;
}
in_uint32_le(s, result);
LOG(0, ("audin_process_open_reply: result %d", result));
return 0;
}
/*****************************************************************************/
static int
audin_process_incoming_data(int chan_id, struct stream *s)
{
LOG(0, ("audin_process_incoming_data:"));
return 0;
}
/*****************************************************************************/
static int
audin_process_data(int chan_id, struct stream *s)
{
int data_bytes;
data_bytes = (int) (s->end - s->p);
LOG(0, ("audin_process_data: data_bytes %d", data_bytes));
return 0;
}
/*****************************************************************************/
static int
audin_process_format_change(int chan_id, struct stream *s)
{
LOG(0, ("audin_process_format_change:"));
if (!s_check_rem(s, 4))
{
LOG(0, ("audin_process_format_change: parse error"));
return 1;
}
in_uint32_le(s, g_current_format);
return 0;
}
/*****************************************************************************/
static int
audin_process_msg(int chan_id, struct stream *s)
{
int code;
LOG(0, ("audin_process_msg:"));
if (!s_check_rem(s, 1))
{
LOG(0, ("audin_process_msg: parse error"));
return 1;
}
in_uint8(s, code);
LOG(0, ("audin_process_msg: code %d", code));
switch (code)
{
case MSG_SNDIN_VERSION:
return audin_process_version(chan_id, s);
case MSG_SNDIN_FORMATS:
return audin_process_formats(chan_id, s);
case MSG_SNDIN_OPEN_REPLY:
return audin_process_open_reply(chan_id, s);
case MSG_SNDIN_DATA_INCOMING:
return audin_process_incoming_data(chan_id, s);
case MSG_SNDIN_DATA:
return audin_process_data(chan_id, s);
case MSG_SNDIN_FORMATCHANGE:
return audin_process_format_change(chan_id, s);
default:
LOG(0, ("audin_process_msg: unprocessed code %d", code));
break;
}
return 0;
}
/*****************************************************************************/
static int
audin_open_response(int chan_id, int creation_status)
{
LOG(0, ("audin_open_response: creation_status %d", creation_status));
if (creation_status == 0)
{
return audio_send_version(chan_id);
}
return 0;
}
/*****************************************************************************/
static int
audin_close_response(int chan_id)
{
LOG(0, ("audin_close_response:"));
return 0;
}
/*****************************************************************************/
static int
audin_data_fragment(int chan_id, char *data, int bytes)
{
int rv;
int left;
LOG(0, ("audin_data_fragment:"));
if (!s_check_rem(g_in_s, bytes))
{
left = (int) (g_in_s->end - g_in_s->p);
LOG(0, ("audin_data_fragment: error bytes %d left %d", bytes, left));
return 1;
}
out_uint8a(g_in_s, data, bytes);
if (g_in_s->p == g_in_s->end)
{
g_in_s->p = g_in_s->data;
rv = audin_process_msg(chan_id, g_in_s);
free_stream(g_in_s);
g_in_s = NULL;
return rv;
}
return 0;
}
/*****************************************************************************/
static int
audin_data_first(int chan_id, char *data, int bytes, int total_bytes)
{
LOG(0, ("audin_data_first:"));
if (g_in_s != NULL)
{
LOG(0, ("audin_data_first: warning g_in_s is not nil"));
free_stream(g_in_s);
}
make_stream(g_in_s);
init_stream(g_in_s, total_bytes);
g_in_s->end = g_in_s->data + total_bytes;
return audin_data_fragment(chan_id, data, bytes);
}
/*****************************************************************************/
static int
audin_data(int chan_id, char *data, int bytes)
{
struct stream ls;
LOG(0, ("audin_data:"));
//g_hexdump(data, bytes);
if (g_in_s == NULL)
{
g_memset(&ls, 0, sizeof(ls));
ls.data = data;
ls.p = ls.data;
ls.end = ls.p + bytes;
return audin_process_msg(chan_id, &ls);
}
return audin_data_fragment(chan_id, data, bytes);
}
/*****************************************************************************/
int
audin_init(void)
{
int error;
LOG(0, ("audin_init:"));
g_memset(&g_audin_info, 0, sizeof(g_audin_info));
g_audin_info.open_response = audin_open_response;
g_audin_info.close_response = audin_close_response;
g_audin_info.data_first = audin_data_first;
g_audin_info.data = audin_data;
g_audio_chanid = 0;
g_in_s = NULL;
error = chansrv_drdynvc_open(AUDIN_NAME, AUDIN_FLAGS,
&g_audin_info, /* callback functions */
&g_audio_chanid); /* chansrv chan_id */
LOG(0, ("audin_init: error %d", error));
return error;
}