Plan 9 from Bell Labs’s /usr/web/sources/contrib/yk/lab/rd-audio/audio.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/* [MS-RDPEA]: Remote Desktop Protocol: Audio Output Virtual Channel Extension */
#include <u.h>
#include <libc.h>
#include "dat.h"
#include "fns.h"

/*
[MS-RDPEA] "6 Appendix A: Product Behavior":
<1> Section 2.1: In Windows, the client advertises the static virtual
channel named "RDPDR", as defined in [MS-RDPEFS].  If that channel is
not advertised, the server will not issue any communication on the
"RDPSND" channel.  Not supported on Windows XP and Windows Server
2003.
*/

static char	rdpsnd[]				= "RDPSND";

enum	/* Audiomsg.type */
{
	Awinf=	2,	/* WaveInfo PDU */
	Awav=	0,	/* Wave PDU - always follows Awinf */
	Awack=	5,	/* Wave Confirm PDU */
	Aping=	6,	/* Training PDU or Training Confirm PDU */
	Afmt=	7,	/* Client/Server Audio Formats and Version PDU */
	Afin=	1,	/* 2.2.3.9 Close PDU */
};

enum /* RFC2361 Appendix A */
{
	FmtPCM=	0x01,	/* WAVE_FORMAT_PCM */		
};

typedef	struct	Audiomsg Audiomsg;
struct Audiomsg
{
	uint	type;
	uint	nfmt;	/* Afmt */
	uint	seq;	/* Afmt, Awinf, Awack */
	uint	time;	/* Awinf, Awack, Aping */
	uint	ping;	/* Aping */
	uint	ver;	/* Afmt */
	uint	ndata;	/* Afmt, Awinf */
	uint	nxdata;	/* Awinf */
	uchar	*data;	/* Afmt, Awinf, Awav */
};
static	int	audiogetmsg(Audiomsg*,uchar*,int);
static	int	audioputmsg(uchar*,int, Audiomsg*);
static	int	audiosendmsg(Rdp*, Audiomsg*);
static	void	sfmtrcvd(Rdp*,Audiomsg*);
static	void	winfrcvd(Rdp*,Audiomsg*);
static	void	wavrcvd(Rdp*,Audiomsg*);
static	void	pingrcvd(Rdp*,Audiomsg*);
static	void	finrcvd(Rdp*,Audiomsg*);
static	void	sendcfmt(Rdp*);
static	void	sendwack(Rdp*);
static	void	sendping(Rdp*, uint, uint);

void
audiovcfn(Rdp* c, uchar* a, uint nb)
{
	Audiomsg r;

	if(audiogetmsg(&r, a, nb) < 0){
		fprint(2, "audio: %r\n");
		return;
	}
	switch(r.type){
	case Afmt:	sfmtrcvd(c, &r); break;
	case Awinf:	winfrcvd(c, &r); break;
	case Awav:	wavrcvd(c, &r); break;
	case Aping:	pingrcvd(c, &r); break;
	case Afin:	finrcvd(c, &r); break;
	}
}

static void
sfmtrcvd(Rdp* c, Audiomsg* r)
{
	c->audio.seq = r->seq;
	sendcfmt(c);
}

static void
winfrcvd(Rdp* c, Audiomsg* r)
{
	c->audio.seq = r->seq;
	c->audio.time = r->time;
	playsound(c, r->data, r->ndata);
}

static void
wavrcvd(Rdp* c, Audiomsg* r)
{
	playsound(c, r->data, r->ndata);
	sendwack(c);
}

static void
pingrcvd(Rdp* c, Audiomsg* r)
{
	sendping(c, r->time, r->ping);
}

static void
finrcvd(Rdp* c, Audiomsg* r)
{
	USED(c);
	USED(r);
}

static void
sendcfmt(Rdp* c)
{
	Audiomsg t;

	t.type = Afmt;
	if(audiosendmsg(c, &t) < 0)
		fprint(2, "audio: sendcfmt: %r\n");
}

static void
sendwack(Rdp* c)
{
	Audiomsg t;

	t.type = Awack;
	t.seq = c->audio.seq;
	t.time = c->audio.time;
	if(audiosendmsg(c, &t) < 0)
		fprint(2, "audio: sendwack: %r\n");
}

static void
sendping(Rdp* c, uint time, uint v)
{
	Audiomsg t;

	t.type = Aping;
	t.time = time;
	t.ping = v;
	if(audiosendmsg(c, &t) < 0)
		fprint(2, "audio: sendping: %r\n");
}

/*
 * 2.2.1 RDPSND PDU Header (SNDPROLOG)
 *	msgtype[1] pad[1] bodysize[2]
 *
 * 2.2.2.1 Server Audio Formats and Version PDU
 *	hdr[4] flags[4] vol[4] pitch[4] dgport[2] nfmt[2] ack[1] ver[2] pad[1] nfmt*(afmt[*])
 *
 * 2.2.2.1.1 Audio Format (AUDIO_FORMAT)
 *	ftag[2] nchan[2] samphz[4] avgbytehz[4] blocksz[2] sampbitsz[2] ndata[2] data[ndata]
 */

static int
audiogetmsg(Audiomsg* m, uchar* a, int nb)
{
	ulong len;

	if(nb < 4){
		werrstr(Eshort);
		return -1;
	}
	
	m->type = *a;
	len = GSHORT(a+2);
	switch(m->type){
	case Afmt:
		if(len < 20 || nb < len+4){
			werrstr(Eshort);
			return -1;
		}
		m->nfmt = GSHORT(a+18);
		m->seq = a[20];
		m->ver = GSHORT(a+21);
		m->data = a+24;
		m->ndata = len-20;
		return len+4;
	case Awinf:
		if(len < 16 || nb < 16){
			werrstr(Eshort);
			return -1;
		}
		m->time = GSHORT(a+4);
		m->seq = a[8];
		m->data = a+12;
		m->ndata = 4;
		m->nxdata = len-16;
		return 16;
	case Awav:
		m->data = a+4;
		m->ndata = nb-4;
		return nb;
	case Aping:
		if(nb < 8){
			werrstr(Eshort);
			return -1;
		}
		m->time = GSHORT(a+4);
		m->ping = GSHORT(a+6);
		return 8;	
	case Afin:
		return 4;
	}
	werrstr("unrecognized audio msg type(%ud)", m->type);
	return -1;
}

static int
audioputmsg(uchar* a, int n, Audiomsg* m)
{
	int len;

	switch(m->type){
	case Afmt:
		len = 42-4;
		if(n < 42){
			werrstr(Esmall);
			return -1;
		}
		a[0] = m->type;
		a[1] = 0; // pad
		PSHORT(a+2, len);
		PLONG(a+4, 1);	// flags: 1=alive
		PLONG(a+8, 0xffffffff);	// volume: l=100, r=100
		PLONG(a+12, 1<<16);	// pitch
		PSHORT(a+16, 0);	// dgport
		PSHORT(a+18, 1);	// nfmt
		a[20] = 0;	// seq
		PSHORT(a+21, 5);	// ver
		a[23] = 0;	// pad
		// afmt[0]
		PSHORT(a+24, FmtPCM);	// ftag
		PSHORT(a+26, 2);	// nchan
		PLONG(a+28, 44100);	// samphz
		PLONG(a+32, 44100*2*2);	// avgbytehz
		PSHORT(a+36, 4);	// blocksz (nchan*sampbitsz/8)
		PSHORT(a+38, 16);	// sampbitsz
		PSHORT(a+40, 0);	// ndata
		/* NOTE: we shouldn't send this unless the server
		 * also declared support for the exact same format */
		return 42;
	case Awack:
		len = 8-4;
		if(n < 8){
			werrstr(Esmall);
			return -1;
		}
		a[0] = m->type;
		a[1] = 0; // pad
		PSHORT(a+2, len);
		PSHORT(a+4, m->time);
		a[6] = m->seq;
		a[7] = 0;	// pad
		return 8;
	case Aping:
		len = 8-4;
		if(n < 8){
			werrstr(Esmall);
			return -1;
		}
		a[0] = m->type;
		a[1] = 0; // pad
		PSHORT(a+2, len);
		PSHORT(a+4, m->time);
		PSHORT(a+6, m->ping);
		return 8;
	}
	werrstr("unrecognized audio msg type(%ud)", m->type);
	return -1;
}


static int
audiosendmsg(Rdp* c, Audiomsg* m)
{
	int n;
	uchar buf[64];

	n = audioputmsg(buf, sizeof buf, m);
	if(n < 0)
		return n;
	return sendvc(c, rdpsnd, buf, n);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].