Add experimental extension to the RDPSND protocol that allows recording.
git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/trunk/rdesktop@1358 423420c4-83ab-492f-b58f-81f9feb106b5
This commit is contained in:
parent
66f1511672
commit
0dbec16985
181
doc/rdpsnd-rec.txt
Normal file
181
doc/rdpsnd-rec.txt
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
This is a rdesktop specific extension to the RDPSND protocol to
|
||||||
|
support recording.
|
||||||
|
|
||||||
|
The basic protocol is the same as RDPSND (described in rdpsnd.txt),
|
||||||
|
but with a bunch of new opcodes.
|
||||||
|
|
||||||
|
A client indicates to the server that it can support recording by
|
||||||
|
setting bit 24 (0x00800000) in "Flags" in RDPSND_NEGOTIATE.
|
||||||
|
|
||||||
|
New opcodes
|
||||||
|
|
||||||
|
0x27 RDPSND_REC_NEGOTIATE
|
||||||
|
0x28 RDPSND_REC_START
|
||||||
|
0x29 RDPSND_REC_STOP
|
||||||
|
0x2A RDPSND_REC_DATA
|
||||||
|
0x2B RDPSND_REC_SET_VOLUME
|
||||||
|
|
||||||
|
Opcodes
|
||||||
|
=======
|
||||||
|
|
||||||
|
The following is a list of the new opcodes and their payload.
|
||||||
|
|
||||||
|
RDPSND_REC_NEGOTIATE
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Sent immediatly after RDPSND_NEGOTIATE when the client indicates that
|
||||||
|
it supports recording. Allows the server to determine the
|
||||||
|
capabilities of the client.
|
||||||
|
|
||||||
|
The client should reply with an identical packet, with the relevant
|
||||||
|
fields filled in, and a filtered list of formats (based on what the
|
||||||
|
client supports).
|
||||||
|
|
||||||
|
0 1 2 3
|
||||||
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Flags |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Left channel | Right channel |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Format count | Version |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| ... |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Format tag | Channels |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Frames per sec. |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Bytes per sec. |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Block align | Bits per sample |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Extra size | Extra data ... |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| ... |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
Flags
|
||||||
|
|
||||||
|
Flags for capabilities. Currently unused.
|
||||||
|
|
||||||
|
Left channel
|
||||||
|
|
||||||
|
Initial volume for left channel. Reserved when sent from server.
|
||||||
|
|
||||||
|
Right channel
|
||||||
|
|
||||||
|
Initial volume for right channel. Reserved when sent from server.
|
||||||
|
|
||||||
|
Format count
|
||||||
|
|
||||||
|
Number of format structures following the header.
|
||||||
|
|
||||||
|
Version
|
||||||
|
|
||||||
|
Version of the RDPSND record protocol. Current version is 1.
|
||||||
|
|
||||||
|
Format tag
|
||||||
|
|
||||||
|
Audio format type as registered at Microsoft.
|
||||||
|
|
||||||
|
Channels
|
||||||
|
|
||||||
|
Number of channels per frame.
|
||||||
|
|
||||||
|
Frames per sec.
|
||||||
|
|
||||||
|
Frames per second in Hz.
|
||||||
|
|
||||||
|
Bytes per sec.
|
||||||
|
|
||||||
|
Number of bytes per second. Should be the product of
|
||||||
|
"Frames per sec." and "Block align".
|
||||||
|
|
||||||
|
Block align
|
||||||
|
|
||||||
|
The size of each frame. Note that not all bytes may contain
|
||||||
|
useful data.
|
||||||
|
|
||||||
|
Bits per sample
|
||||||
|
|
||||||
|
Number of bits per sample. Commonly 8 or 16.
|
||||||
|
|
||||||
|
Extra size
|
||||||
|
|
||||||
|
Number of bytes of extra information following this format
|
||||||
|
description.
|
||||||
|
|
||||||
|
Extra data
|
||||||
|
|
||||||
|
Optional extra format data. Contents specific to each format
|
||||||
|
type.
|
||||||
|
|
||||||
|
RDPSND_REC_START
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Sent from the server to tell the client to start recording.
|
||||||
|
|
||||||
|
0 1 2 3
|
||||||
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Format index | |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
Format index
|
||||||
|
|
||||||
|
Waveform data format in the form of an index to the previously
|
||||||
|
negotiated format list.
|
||||||
|
|
||||||
|
RDPSND_REC_STOP
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Tells the client to stop sending record data. Must be sent before a
|
||||||
|
new RDPSND_REC_START is sent.
|
||||||
|
|
||||||
|
No payload and no response.
|
||||||
|
|
||||||
|
RDPSND_REC_DATA
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Chunk of recorded data. The client is free to choose how much data
|
||||||
|
should be queued up before a packet is sent. The payload must not
|
||||||
|
exceed 32768 bytes though.
|
||||||
|
|
||||||
|
No response.
|
||||||
|
|
||||||
|
0 1 2 3
|
||||||
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Waveform data |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| ... |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
Waveform data
|
||||||
|
|
||||||
|
Binary waveform data in the format specified by the format index.
|
||||||
|
Size defined by the packet boundary.
|
||||||
|
|
||||||
|
RDPSND_REC_SET_VOLUME
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Request from the server to the client to change the input volume.
|
||||||
|
No response.
|
||||||
|
|
||||||
|
0 1 2 3
|
||||||
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Left channel | Right channel |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
Left channel
|
||||||
|
|
||||||
|
Volume of left channel in the range [0, 65535].
|
||||||
|
|
||||||
|
Right channel
|
||||||
|
|
||||||
|
Volume of right channel in the range [0, 65535].
|
1
proto.h
1
proto.h
@ -166,6 +166,7 @@ struct async_iorequest *rdpdr_remove_iorequest(struct async_iorequest *prev,
|
|||||||
void rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out);
|
void rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out);
|
||||||
BOOL rdpdr_abort_io(uint32 fd, uint32 major, NTSTATUS status);
|
BOOL rdpdr_abort_io(uint32 fd, uint32 major, NTSTATUS status);
|
||||||
/* rdpsnd.c */
|
/* rdpsnd.c */
|
||||||
|
void rdpsnd_record(const void *data, unsigned int size);
|
||||||
BOOL rdpsnd_init(char *optarg);
|
BOOL rdpsnd_init(char *optarg);
|
||||||
void rdpsnd_show_help(void);
|
void rdpsnd_show_help(void);
|
||||||
void rdpsnd_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv);
|
void rdpsnd_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv);
|
||||||
|
259
rdpsnd.c
259
rdpsnd.c
@ -34,20 +34,38 @@
|
|||||||
#define RDPSND_PING 6
|
#define RDPSND_PING 6
|
||||||
#define RDPSND_NEGOTIATE 7
|
#define RDPSND_NEGOTIATE 7
|
||||||
|
|
||||||
|
#define RDPSND_REC_NEGOTIATE 39
|
||||||
|
#define RDPSND_REC_START 40
|
||||||
|
#define RDPSND_REC_STOP 41
|
||||||
|
#define RDPSND_REC_DATA 42
|
||||||
|
#define RDPSND_REC_SET_VOLUME 43
|
||||||
|
|
||||||
|
#define RDPSND_FLAG_RECORD 0x00800000
|
||||||
|
|
||||||
#define MAX_FORMATS 10
|
#define MAX_FORMATS 10
|
||||||
#define MAX_QUEUE 10
|
#define MAX_QUEUE 50
|
||||||
|
|
||||||
static VCHANNEL *rdpsnd_channel;
|
static VCHANNEL *rdpsnd_channel;
|
||||||
|
static VCHANNEL *rdpsnddbg_channel;
|
||||||
static struct audio_driver *drivers = NULL;
|
static struct audio_driver *drivers = NULL;
|
||||||
struct audio_driver *current_driver = NULL;
|
struct audio_driver *current_driver = NULL;
|
||||||
|
|
||||||
static BOOL device_open;
|
static BOOL device_open;
|
||||||
|
static BOOL rec_device_open;
|
||||||
|
|
||||||
static WAVEFORMATEX formats[MAX_FORMATS];
|
static WAVEFORMATEX formats[MAX_FORMATS];
|
||||||
static unsigned int format_count;
|
static unsigned int format_count;
|
||||||
static unsigned int current_format;
|
static unsigned int current_format;
|
||||||
|
|
||||||
|
static WAVEFORMATEX rec_formats[MAX_FORMATS];
|
||||||
|
static unsigned int rec_format_count;
|
||||||
|
|
||||||
unsigned int queue_hi, queue_lo, queue_pending;
|
unsigned int queue_hi, queue_lo, queue_pending;
|
||||||
struct audio_packet packet_queue[MAX_QUEUE];
|
struct audio_packet packet_queue[MAX_QUEUE];
|
||||||
|
|
||||||
|
static char record_buffer[8192];
|
||||||
|
static int record_buffer_size;
|
||||||
|
|
||||||
static uint8 packet_opcode;
|
static uint8 packet_opcode;
|
||||||
static struct stream packet;
|
static struct stream packet;
|
||||||
|
|
||||||
@ -91,6 +109,74 @@ rdpsnd_send_completion(uint16 tick, uint8 packet_index)
|
|||||||
(unsigned) tick, (unsigned) packet_index));
|
(unsigned) tick, (unsigned) packet_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rdpsnd_flush_record(void)
|
||||||
|
{
|
||||||
|
STREAM s;
|
||||||
|
unsigned int chunk_size;
|
||||||
|
char *data;
|
||||||
|
|
||||||
|
if (record_buffer_size == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert(record_buffer_size <= sizeof(record_buffer));
|
||||||
|
|
||||||
|
data = record_buffer;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Microsoft's RDP server keeps dropping chunks, so we need to
|
||||||
|
* transmit everything inside one channel fragment or we risk
|
||||||
|
* making the rdpsnd server go out of sync with the byte stream.
|
||||||
|
*/
|
||||||
|
while (record_buffer_size)
|
||||||
|
{
|
||||||
|
if (record_buffer_size < 1596)
|
||||||
|
chunk_size = record_buffer_size;
|
||||||
|
else
|
||||||
|
chunk_size = 1596;
|
||||||
|
|
||||||
|
s = rdpsnd_init_packet(RDPSND_REC_DATA, chunk_size);
|
||||||
|
out_uint8p(s, data, chunk_size);
|
||||||
|
|
||||||
|
s_mark_end(s);
|
||||||
|
rdpsnd_send(s);
|
||||||
|
|
||||||
|
data = data + chunk_size;
|
||||||
|
record_buffer_size -= chunk_size;
|
||||||
|
|
||||||
|
DEBUG_SOUND(("RDPSND: -> RDPSND_REC_DATA(length: %u)\n", (unsigned) chunk_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
record_buffer_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rdpsnd_record(const void *data, unsigned int size)
|
||||||
|
{
|
||||||
|
int remain;
|
||||||
|
|
||||||
|
assert(rec_device_open);
|
||||||
|
|
||||||
|
while (size)
|
||||||
|
{
|
||||||
|
remain = sizeof(record_buffer) - record_buffer_size;
|
||||||
|
|
||||||
|
if (size >= remain)
|
||||||
|
{
|
||||||
|
memcpy(record_buffer + record_buffer_size, data, remain);
|
||||||
|
record_buffer_size += remain;
|
||||||
|
rdpsnd_flush_record();
|
||||||
|
data = (const char *) data + remain;
|
||||||
|
size -= remain;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(record_buffer + record_buffer_size, data, size);
|
||||||
|
record_buffer_size += size;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL
|
static BOOL
|
||||||
rdpsnd_auto_select(void)
|
rdpsnd_auto_select(void)
|
||||||
@ -187,7 +273,7 @@ rdpsnd_process_negotiate(STREAM in)
|
|||||||
}
|
}
|
||||||
|
|
||||||
out = rdpsnd_init_packet(RDPSND_NEGOTIATE | 0x200, 20 + 18 * format_count);
|
out = rdpsnd_init_packet(RDPSND_NEGOTIATE | 0x200, 20 + 18 * format_count);
|
||||||
out_uint32_le(out, 3); /* flags */
|
out_uint32_le(out, 0x00800003); /* flags */
|
||||||
out_uint32(out, 0xffffffff); /* volume */
|
out_uint32(out, 0xffffffff); /* volume */
|
||||||
out_uint32(out, 0); /* pitch */
|
out_uint32(out, 0); /* pitch */
|
||||||
out_uint16(out, 0); /* UDP port */
|
out_uint16(out, 0); /* UDP port */
|
||||||
@ -235,6 +321,95 @@ rdpsnd_process_ping(STREAM in)
|
|||||||
DEBUG_SOUND(("RDPSND: -> (tick: 0x%04x)\n", (unsigned) tick));
|
DEBUG_SOUND(("RDPSND: -> (tick: 0x%04x)\n", (unsigned) tick));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rdpsnd_process_rec_negotiate(STREAM in)
|
||||||
|
{
|
||||||
|
uint16 in_format_count, i;
|
||||||
|
uint16 version;
|
||||||
|
WAVEFORMATEX *format;
|
||||||
|
STREAM out;
|
||||||
|
BOOL device_available = False;
|
||||||
|
int readcnt;
|
||||||
|
int discardcnt;
|
||||||
|
|
||||||
|
in_uint8s(in, 8); /* initial bytes not valid from server */
|
||||||
|
in_uint16_le(in, in_format_count);
|
||||||
|
in_uint16_le(in, version);
|
||||||
|
|
||||||
|
DEBUG_SOUND(("RDPSND: RDPSND_REC_NEGOTIATE(formats: %d, version: %x)\n",
|
||||||
|
(int) in_format_count, (unsigned) version));
|
||||||
|
|
||||||
|
if (!current_driver)
|
||||||
|
device_available = rdpsnd_auto_select();
|
||||||
|
|
||||||
|
if (current_driver && !device_available && current_driver->wave_in_open
|
||||||
|
&& current_driver->wave_in_open())
|
||||||
|
{
|
||||||
|
current_driver->wave_in_close();
|
||||||
|
device_available = True;
|
||||||
|
}
|
||||||
|
|
||||||
|
rec_format_count = 0;
|
||||||
|
if (s_check_rem(in, 18 * in_format_count))
|
||||||
|
{
|
||||||
|
for (i = 0; i < in_format_count; i++)
|
||||||
|
{
|
||||||
|
format = &rec_formats[rec_format_count];
|
||||||
|
in_uint16_le(in, format->wFormatTag);
|
||||||
|
in_uint16_le(in, format->nChannels);
|
||||||
|
in_uint32_le(in, format->nSamplesPerSec);
|
||||||
|
in_uint32_le(in, format->nAvgBytesPerSec);
|
||||||
|
in_uint16_le(in, format->nBlockAlign);
|
||||||
|
in_uint16_le(in, format->wBitsPerSample);
|
||||||
|
in_uint16_le(in, format->cbSize);
|
||||||
|
|
||||||
|
/* read in the buffer of unknown use */
|
||||||
|
readcnt = format->cbSize;
|
||||||
|
discardcnt = 0;
|
||||||
|
if (format->cbSize > MAX_CBSIZE)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "cbSize too large for buffer: %d\n",
|
||||||
|
format->cbSize);
|
||||||
|
readcnt = MAX_CBSIZE;
|
||||||
|
discardcnt = format->cbSize - MAX_CBSIZE;
|
||||||
|
}
|
||||||
|
in_uint8a(in, format->cb, readcnt);
|
||||||
|
in_uint8s(in, discardcnt);
|
||||||
|
|
||||||
|
if (device_available && current_driver->wave_in_format_supported(format))
|
||||||
|
{
|
||||||
|
rec_format_count++;
|
||||||
|
if (rec_format_count == MAX_FORMATS)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out = rdpsnd_init_packet(RDPSND_REC_NEGOTIATE, 12 + 18 * rec_format_count);
|
||||||
|
out_uint32_le(out, 0x00000000); /* flags */
|
||||||
|
out_uint32_le(out, 0xffffffff); /* volume */
|
||||||
|
out_uint16_le(out, rec_format_count);
|
||||||
|
out_uint16_le(out, 1); /* version */
|
||||||
|
|
||||||
|
for (i = 0; i < rec_format_count; i++)
|
||||||
|
{
|
||||||
|
format = &rec_formats[i];
|
||||||
|
out_uint16_le(out, format->wFormatTag);
|
||||||
|
out_uint16_le(out, format->nChannels);
|
||||||
|
out_uint32_le(out, format->nSamplesPerSec);
|
||||||
|
out_uint32_le(out, format->nAvgBytesPerSec);
|
||||||
|
out_uint16_le(out, format->nBlockAlign);
|
||||||
|
out_uint16_le(out, format->wBitsPerSample);
|
||||||
|
out_uint16(out, 0); /* cbSize */
|
||||||
|
}
|
||||||
|
|
||||||
|
s_mark_end(out);
|
||||||
|
|
||||||
|
DEBUG_SOUND(("RDPSND: -> RDPSND_REC_NEGOTIATE(formats: %d)\n", (int) rec_format_count));
|
||||||
|
|
||||||
|
rdpsnd_send(out);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rdpsnd_process_packet(uint8 opcode, STREAM s)
|
rdpsnd_process_packet(uint8 opcode, STREAM s)
|
||||||
{
|
{
|
||||||
@ -309,6 +484,50 @@ rdpsnd_process_packet(uint8 opcode, STREAM s)
|
|||||||
if (device_open)
|
if (device_open)
|
||||||
current_driver->wave_out_volume(vol_left, vol_right);
|
current_driver->wave_out_volume(vol_left, vol_right);
|
||||||
break;
|
break;
|
||||||
|
case RDPSND_REC_NEGOTIATE:
|
||||||
|
rdpsnd_process_rec_negotiate(s);
|
||||||
|
break;
|
||||||
|
case RDPSND_REC_START:
|
||||||
|
in_uint16_le(s, format);
|
||||||
|
DEBUG_SOUND(("RDPSND: RDPSND_REC_START(format: %u)\n", (unsigned) format));
|
||||||
|
|
||||||
|
if (format >= MAX_FORMATS)
|
||||||
|
{
|
||||||
|
error("RDPSND: Invalid format index\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rec_device_open)
|
||||||
|
{
|
||||||
|
error("RDPSND: Multiple RDPSND_REC_START\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current_driver->wave_in_open())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!current_driver->wave_in_set_format(&rec_formats[format]))
|
||||||
|
{
|
||||||
|
error("RDPSND: Device not accepting format\n");
|
||||||
|
current_driver->wave_in_close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rec_device_open = True;
|
||||||
|
break;
|
||||||
|
case RDPSND_REC_STOP:
|
||||||
|
DEBUG_SOUND(("RDPSND: RDPSND_REC_STOP()\n"));
|
||||||
|
rdpsnd_flush_record();
|
||||||
|
if (rec_device_open)
|
||||||
|
current_driver->wave_in_close();
|
||||||
|
rec_device_open = False;
|
||||||
|
break;
|
||||||
|
case RDPSND_REC_SET_VOLUME:
|
||||||
|
in_uint16_le(s, vol_left);
|
||||||
|
in_uint16_le(s, vol_right);
|
||||||
|
DEBUG_SOUND(("RDPSND: RDPSND_REC_VOLUME(left: 0x%04x (%u %%), right: 0x%04x (%u %%))\n", (unsigned) vol_left, (unsigned) vol_left / 655, (unsigned) vol_right, (unsigned) vol_right / 655));
|
||||||
|
if (rec_device_open)
|
||||||
|
current_driver->wave_in_volume(vol_left, vol_right);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
unimpl("RDPSND packet type %x\n", opcode);
|
unimpl("RDPSND packet type %x\n", opcode);
|
||||||
break;
|
break;
|
||||||
@ -373,6 +592,32 @@ rdpsnd_process(STREAM s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOOL
|
||||||
|
rdpsnddbg_line_handler(const char *line, void *data)
|
||||||
|
{
|
||||||
|
#ifdef WITH_DEBUG_SOUND
|
||||||
|
fprintf(stderr, "SNDDBG: %s\n", line);
|
||||||
|
#endif
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rdpsnddbg_process(STREAM s)
|
||||||
|
{
|
||||||
|
unsigned int pkglen;
|
||||||
|
static char *rest = NULL;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
pkglen = s->end - s->p;
|
||||||
|
/* str_handle_lines requires null terminated strings */
|
||||||
|
buf = xmalloc(pkglen + 1);
|
||||||
|
STRNCPY(buf, (char *) s->p, pkglen + 1);
|
||||||
|
|
||||||
|
str_handle_lines(buf, &rest, rdpsnddbg_line_handler, NULL);
|
||||||
|
|
||||||
|
xfree(buf);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rdpsnd_register_drivers(char *options)
|
rdpsnd_register_drivers(char *options)
|
||||||
{
|
{
|
||||||
@ -425,7 +670,11 @@ rdpsnd_init(char *optarg)
|
|||||||
channel_register("rdpsnd", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
|
channel_register("rdpsnd", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
|
||||||
rdpsnd_process);
|
rdpsnd_process);
|
||||||
|
|
||||||
if (rdpsnd_channel == NULL)
|
rdpsnddbg_channel =
|
||||||
|
channel_register("snddbg", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
|
||||||
|
rdpsnddbg_process);
|
||||||
|
|
||||||
|
if ((rdpsnd_channel == NULL) || (rdpsnddbg_channel == NULL))
|
||||||
{
|
{
|
||||||
error("channel_register\n");
|
error("channel_register\n");
|
||||||
return False;
|
return False;
|
||||||
@ -489,7 +738,7 @@ rdpsnd_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv)
|
|||||||
{
|
{
|
||||||
long next_pending;
|
long next_pending;
|
||||||
|
|
||||||
if (device_open)
|
if (device_open || rec_device_open)
|
||||||
current_driver->add_fds(n, rfds, wfds, tv);
|
current_driver->add_fds(n, rfds, wfds, tv);
|
||||||
|
|
||||||
next_pending = rdpsnd_queue_next_completion();
|
next_pending = rdpsnd_queue_next_completion();
|
||||||
@ -511,7 +760,7 @@ rdpsnd_check_fds(fd_set * rfds, fd_set * wfds)
|
|||||||
{
|
{
|
||||||
rdpsnd_queue_complete_pending();
|
rdpsnd_queue_complete_pending();
|
||||||
|
|
||||||
if (device_open)
|
if (device_open || rec_device_open)
|
||||||
current_driver->check_fds(rfds, wfds);
|
current_driver->check_fds(rfds, wfds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
rdpsnd.h
6
rdpsnd.h
@ -39,6 +39,12 @@ struct audio_driver
|
|||||||
BOOL(*wave_out_set_format) (WAVEFORMATEX * pwfx);
|
BOOL(*wave_out_set_format) (WAVEFORMATEX * pwfx);
|
||||||
void (*wave_out_volume) (uint16 left, uint16 right);
|
void (*wave_out_volume) (uint16 left, uint16 right);
|
||||||
|
|
||||||
|
BOOL(*wave_in_open) (void);
|
||||||
|
void (*wave_in_close) (void);
|
||||||
|
BOOL(*wave_in_format_supported) (WAVEFORMATEX * pwfx);
|
||||||
|
BOOL(*wave_in_set_format) (WAVEFORMATEX * pwfx);
|
||||||
|
void (*wave_in_volume) (uint16 left, uint16 right);
|
||||||
|
|
||||||
char *name;
|
char *name;
|
||||||
char *description;
|
char *description;
|
||||||
int need_byteswap_on_be;
|
int need_byteswap_on_be;
|
||||||
|
Loading…
Reference in New Issue
Block a user