Merge pull request #1369 from jsorg71/mic-drdynvc
implement [MS-RDPEAI], mstsc compatible audio input
This commit is contained in:
commit
cc2f918cd4
@ -70,7 +70,9 @@ xrdp_chansrv_SOURCES = \
|
||||
sound.c \
|
||||
sound.h \
|
||||
xcommon.c \
|
||||
xcommon.h
|
||||
xcommon.h \
|
||||
audin.c \
|
||||
audin.h
|
||||
|
||||
xrdp_chansrv_LDFLAGS = \
|
||||
$(X_LIBS)
|
||||
|
518
sesman/chansrv/audin.c
Normal file
518
sesman/chansrv/audin.c
Normal file
@ -0,0 +1,518 @@
|
||||
/**
|
||||
* 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"
|
||||
#include "fifo.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 */
|
||||
|
||||
extern FIFO g_in_fifo; /* in sound.c */
|
||||
extern int g_bytes_in_fifo; /* in sound.c */
|
||||
|
||||
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_audin_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]);
|
||||
index++;
|
||||
}
|
||||
g_free(g_client_formats);
|
||||
g_client_formats = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static int
|
||||
audin_send_version(int chan_id)
|
||||
{
|
||||
int error;
|
||||
int bytes;
|
||||
struct stream *s;
|
||||
|
||||
LOG(0, ("audin_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
|
||||
audin_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, ("audin_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];
|
||||
LOG(0, ("audin_send_formats: sending format wFormatTag 0x%4.4x "
|
||||
"nChannels %d nSamplesPerSec %d",
|
||||
wf->wFormatTag, wf->nChannels, wf->nSamplesPerSec));
|
||||
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);
|
||||
out_uint16_le(s, wf->cbSize);
|
||||
if (wf->cbSize > 0)
|
||||
{
|
||||
out_uint8p(s, wf->data, wf->cbSize);
|
||||
}
|
||||
}
|
||||
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_send_open(int chan_id)
|
||||
{
|
||||
int error;
|
||||
int bytes;
|
||||
struct stream *s;
|
||||
struct xr_wave_format_ex *wf;
|
||||
|
||||
LOG(0, ("audin_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 audin_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);
|
||||
LOG(0, ("audin_process_formats: recved format wFormatTag 0x%4.4x "
|
||||
"nChannels %d nSamplesPerSec %d",
|
||||
wf->wFormatTag, wf->nChannels, wf->nSamplesPerSec));
|
||||
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);
|
||||
}
|
||||
}
|
||||
audin_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 0x%8.8x", result));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static int
|
||||
audin_process_incoming_data(int chan_id, struct stream *s)
|
||||
{
|
||||
LOG(10, ("audin_process_incoming_data:"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static int
|
||||
audin_process_data(int chan_id, struct stream *s)
|
||||
{
|
||||
int data_bytes;
|
||||
struct stream *ls;
|
||||
|
||||
data_bytes = (int) (s->end - s->p);
|
||||
LOG(10, ("audin_process_data: data_bytes %d", data_bytes));
|
||||
|
||||
xstream_new(ls, data_bytes);
|
||||
g_memcpy(ls->data, s->p, data_bytes);
|
||||
ls->p += data_bytes;
|
||||
s_mark_end(ls);
|
||||
fifo_insert(&g_in_fifo, (void *) ls);
|
||||
g_bytes_in_fifo += 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);
|
||||
LOG(0, ("audin_process_format_change: g_current_format %d",
|
||||
g_current_format));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static int
|
||||
audin_process_msg(int chan_id, struct stream *s)
|
||||
{
|
||||
int code;
|
||||
|
||||
LOG(10, ("audin_process_msg:"));
|
||||
if (!s_check_rem(s, 1))
|
||||
{
|
||||
LOG(0, ("audin_process_msg: parse error"));
|
||||
return 1;
|
||||
}
|
||||
in_uint8(s, code);
|
||||
LOG(10, ("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 0x%8.8x", creation_status));
|
||||
if (creation_status == 0)
|
||||
{
|
||||
return audin_send_version(chan_id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static int
|
||||
audin_close_response(int chan_id)
|
||||
{
|
||||
LOG(0, ("audin_close_response:"));
|
||||
g_audin_chanid = 0;
|
||||
cleanup_client_formats();
|
||||
free_stream(g_in_s);
|
||||
g_in_s = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static int
|
||||
audin_data_fragment(int chan_id, char *data, int bytes)
|
||||
{
|
||||
int rv;
|
||||
int left;
|
||||
|
||||
LOG(10, ("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(10, ("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(10, ("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)
|
||||
{
|
||||
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_audin_chanid = 0;
|
||||
g_in_s = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int
|
||||
audin_deinit(void)
|
||||
{
|
||||
LOG(0, ("audin_deinit:"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int
|
||||
audin_start(void)
|
||||
{
|
||||
int error;
|
||||
struct stream* s;
|
||||
|
||||
LOG(0, ("audin_start:"));
|
||||
if (g_audin_chanid != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if there is any data in FIFO, discard it */
|
||||
while ((s = (struct stream *) fifo_remove(&g_in_fifo)) != NULL)
|
||||
{
|
||||
xstream_free(s);
|
||||
}
|
||||
g_bytes_in_fifo = 0;
|
||||
|
||||
error = chansrv_drdynvc_open(AUDIN_NAME, AUDIN_FLAGS,
|
||||
&g_audin_info, /* callback functions */
|
||||
&g_audin_chanid); /* chansrv chan_id */
|
||||
LOG(0, ("audin_start: error %d g_audin_chanid %d", error, g_audin_chanid));
|
||||
return error;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int
|
||||
audin_stop(void)
|
||||
{
|
||||
LOG(0, ("audin_stop:"));
|
||||
chansrv_drdynvc_close(g_audin_chanid);
|
||||
return 0;
|
||||
}
|
34
sesman/chansrv/audin.h
Normal file
34
sesman/chansrv/audin.h
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AUDIN_H_
|
||||
#define _AUDIN_H_
|
||||
|
||||
int
|
||||
audin_init(void);
|
||||
int
|
||||
audin_deinit(void);
|
||||
int
|
||||
audin_start(void);
|
||||
int
|
||||
audin_stop(void);
|
||||
|
||||
#endif
|
@ -37,6 +37,7 @@
|
||||
#include "xcommon.h"
|
||||
#include "chansrv_fuse.h"
|
||||
#include "xrdp_sockets.h"
|
||||
#include "audin.h"
|
||||
|
||||
static struct trans *g_lis_trans = 0;
|
||||
static struct trans *g_con_trans = 0;
|
||||
@ -415,6 +416,8 @@ process_message_channel_setup(struct stream *s)
|
||||
rail_init();
|
||||
}
|
||||
|
||||
audin_init();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "xrdp_sockets.h"
|
||||
#include "chansrv_common.h"
|
||||
#include "list.h"
|
||||
#include "audin.h"
|
||||
|
||||
#if defined(XRDP_FDK_AAC)
|
||||
#include <fdk-aac/aacenc_lib.h>
|
||||
@ -71,8 +72,8 @@ static struct trans *g_audio_c_trans_in = 0; /* connection */
|
||||
static int g_training_sent_time = 0;
|
||||
static int g_cBlockNo = 0;
|
||||
static int g_bytes_in_stream = 0;
|
||||
static FIFO g_in_fifo;
|
||||
static int g_bytes_in_fifo = 0;
|
||||
FIFO g_in_fifo;
|
||||
int g_bytes_in_fifo = 0;
|
||||
static int g_time_diff = 0;
|
||||
static int g_best_time_diff = 0;
|
||||
|
||||
@ -240,6 +241,8 @@ static struct xr_wave_format_ex *g_wave_inp_formats[] =
|
||||
0
|
||||
};
|
||||
|
||||
static int g_rdpsnd_can_rec = 0;
|
||||
|
||||
static int g_client_input_format_index = 0;
|
||||
static int g_server_input_format_index = 0;
|
||||
|
||||
@ -1560,6 +1563,10 @@ sound_process_input_formats(struct stream *s, int size)
|
||||
|
||||
LOG(10, ("sound_process_input_formats: size=%d", size));
|
||||
|
||||
if (g_getenv("XRDP_NO_RDPSND_REC") == NULL)
|
||||
{
|
||||
g_rdpsnd_can_rec = 1;
|
||||
}
|
||||
in_uint8s(s, 8); /* skip 8 bytes */
|
||||
in_uint16_le(s, num_formats);
|
||||
in_uint8s(s, 2); /* skip version */
|
||||
@ -1771,13 +1778,27 @@ sound_sndsrvr_source_data_in(struct trans *trans)
|
||||
trans_force_write_s(trans, s);
|
||||
}
|
||||
else if (cmd == PA_CMD_START_REC)
|
||||
{
|
||||
if (g_rdpsnd_can_rec)
|
||||
{
|
||||
sound_input_start_recording();
|
||||
}
|
||||
else
|
||||
{
|
||||
audin_start();
|
||||
}
|
||||
}
|
||||
else if (cmd == PA_CMD_STOP_REC)
|
||||
{
|
||||
if (g_rdpsnd_can_rec)
|
||||
{
|
||||
sound_input_stop_recording();
|
||||
}
|
||||
else
|
||||
{
|
||||
audin_stop();
|
||||
}
|
||||
}
|
||||
|
||||
xstream_free(s);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user