Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_posix/sys/src/games/music/playlistfs/player.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <thread.h>
4
#include <fcall.h>
5
#include "pool.h"
6
#include "playlist.h"
7
 
8
enum {
9
	Pac,
10
	Mp3,
11
	Pcm,
12
	Ogg,
13
};
14
 
15
typedef struct Playfd Playfd;
16
 
17
struct Playfd {
18
	/* Describes a file to play for starting up pac4dec/mp3,... */
19
	char	*filename;	/* mallocated */
20
	int	fd;		/* filedesc to use */
21
	int	cfd;		/* fildesc to close */
22
};
23
 
24
Channel *full, *empty, *playout, *spare;
25
Channel	*playc, *pacc;
26
 
27
char *playprog[] = {
28
[Pac] = "/bin/games/pac4dec",
29
[Mp3] = "/bin/games/mp3dec",
30
[Pcm] = "/bin/cp",
31
[Ogg] = "/bin/games/vorbisdec",
32
};
33
 
34
ulong totbytes, totbuffers;
35
 
36
static char curfile[8192];
37
 
38
void
39
pac4dec(void *a)
40
{
41
	Playfd *pfd;
42
	Pacbuf *pb;
43
	int fd, type;
44
	char *ext, buf[256];
45
	static char args[6][32];
46
	char *argv[6] = {args[0], args[1], args[2], args[3], args[4], args[5]};
47
 
48
	threadsetname("pac4dec");
49
	pfd = a;
50
	close(pfd->cfd);	/* read fd */
51
	ext = strrchr(pfd->filename, '.');
52
	fd = open(pfd->filename, OREAD);
53
	if (fd < 0 && ext == nil){
54
		// Try the alternatives
55
		ext = buf + strlen(pfd->filename);
56
		snprint(buf, sizeof buf, "%s.pac", pfd->filename);
57
		fd = open(buf, OREAD);
58
		if (fd < 0){
59
			snprint(buf, sizeof buf, "%s.mp3", pfd->filename);
60
			fd = open(buf, OREAD);
61
		}
62
		if (fd < 0){
63
			snprint(buf, sizeof buf, "%s.ogg", pfd->filename);
64
			fd = open(buf, OREAD);
65
		}
66
		if (fd < 0){
67
			snprint(buf, sizeof buf, "%s.pcm", pfd->filename);
68
			fd = open(buf, OREAD);
69
		}
70
	}
71
	if (fd < 0){
72
		if (debug & DbgPlayer)
73
			fprint(2, "pac4dec: %s: %r", pfd->filename);
74
		pb = nbrecvp(spare);
75
		pb->cmd = Error;
76
		pb->off = 0;
77
		pb->len = snprint(pb->data, sizeof(pb->data), "startplay: %s: %r", pfd->filename);
78
		sendp(full, pb);
79
		threadexits("open");
80
	}
81
	dup(pfd->fd, 1);
82
	close(pfd->fd);
83
	if(ext == nil || strcmp(ext, ".pac") == 0){
84
		type = Pac;
85
		snprint(args[0], sizeof args[0], "pac4dec");
86
		snprint(args[1], sizeof args[1], "/fd/%d", fd);
87
		snprint(args[2], sizeof args[2], "/fd/1");
88
		argv[3] = nil;
89
	}else if(strcmp(ext, ".mp3") == 0){
90
		type = Mp3;
91
		snprint(args[0], sizeof args[0], "mp3dec");
92
		snprint(args[1], sizeof args[1], "-q");
93
		snprint(args[2], sizeof args[1], "-s");
94
		snprint(args[3], sizeof args[1], "/fd/%d", fd);
95
		argv[4] = nil;
96
	}else if(strcmp(ext, ".ogg") == 0){
97
		type = Ogg;
98
		snprint(args[0], sizeof args[0], "vorbisdec");
99
		argv[1] = nil;
100
		argv[2] = nil;
101
		argv[3] = nil;
102
		dup(fd, 0);
103
	}else{
104
		type = Pcm;
105
		snprint(args[0], sizeof args[0], "cat");
106
		snprint(args[1], sizeof args[1], "/fd/%d", fd);
107
		argv[2] = nil;
108
		argv[3] = nil;
109
	}
110
	free(pfd->filename);
111
	free(pfd);
112
	if (debug & DbgPlayer)
113
		fprint(2, "procexecl %s %s %s %s\n",
114
			playprog[type], argv[0], argv[1], argv[2]);
115
	procexec(nil, playprog[type], argv);
116
	if((pb = nbrecvp(spare)) == nil)
117
		pb = malloc(sizeof(Pacbuf));
118
	pb->cmd = Error;
119
	pb->off = 0;
120
	pb->len = snprint(pb->data, sizeof(pb->data), "startplay: %s: exec", playprog[type]);
121
	sendp(full, pb);
122
	threadexits(playprog[type]);
123
}
124
 
125
static int
126
startplay(ushort n)
127
{
128
	int fd[2];
129
	Playfd *pfd;
130
	char *file;
131
 
132
	file = getplaylist(n);
133
	if(file == nil)
134
		return Undef;
135
	if (debug & DbgPlayer)
136
		fprint(2, "startplay: file is `%s'\n", file);
137
	if(pipe(fd) < 0)
138
		sysfatal("pipe: %r");
139
	pfd = malloc(sizeof(Playfd));
140
	pfd->filename = file;	/* mallocated already */
141
	pfd->fd = fd[1];
142
	pfd->cfd = fd[0];
143
	procrfork(pac4dec, pfd, STACKSIZE, RFFDG);
144
	close(fd[1]);	/* write fd, for pac4dec */
145
	return fd[0];	/* read fd */
146
}
147
 
148
static void
149
rtsched(void)
150
{
151
	int fd;
152
	char *ctl;
153
 
154
	ctl = smprint("/proc/%ud/ctl", getpid());
155
	if((fd = open(ctl, ORDWR)) < 0) 
156
		sysfatal("%s: %r", ctl);
157
	if(fprint(fd, "period 20ms") < 0)
158
		sysfatal("%s: %r", ctl);
159
	if(fprint(fd, "cost 100µs") < 0)
160
		sysfatal("%s: %r", ctl);
161
	if(fprint(fd, "sporadic") < 0)
162
		sysfatal("%s: %r", ctl);
163
	if(fprint(fd, "admit") < 0)
164
		sysfatal("%s: %r", ctl);
165
	close(fd);
166
	free(ctl);
167
}
168
 
169
void
170
pacproc(void*)
171
{
172
	Pmsg playstate, newstate;
173
	int fd;
174
	Pacbuf *pb;
175
	Alt a[3] = {
176
		{empty, &pb, CHANNOP},
177
		{playc, &newstate.m, CHANRCV},
178
		{nil, nil, CHANEND},
179
	};
180
 
181
	threadsetname("pacproc");
182
	close(srvfd[1]);
183
	newstate.cmd = playstate.cmd = Stop;
184
	newstate.off = playstate.off = 0;
185
	fd = -1;
186
	for(;;){
187
		switch(alt(a)){
188
		case 0:
189
			/* Play out next buffer (pb points to one already) */
190
			assert(fd >= 0);	/* Because we must be in Play mode */
191
			pb->m = playstate.m;
192
			pb->len = read(fd, pb->data, sizeof pb->data);
193
			if(pb->len > 0){
194
				sendp(full, pb);
195
				break;
196
			}
197
			if(pb->len < 0){
198
				if(debug & DbgPlayer)
199
					fprint(2, "pac, error: %d\n", playstate.off);
200
				pb->cmd = Error;
201
				pb->len = snprint(pb->data, sizeof pb->data, "%s: %r", curfile);
202
				sendp(full, pb);
203
			}else{
204
				/* Simple end of file */
205
				sendp(empty, pb); /* Don't need buffer after all */
206
			}
207
			close(fd);
208
			fd = -1;
209
			if(debug & DbgPlayer)
210
				fprint(2, "pac, eof: %d\n", playstate.off);
211
			/* End of file, do next by falling through */
212
			newstate.cmd = playstate.cmd;
213
			newstate.off = playstate.off + 1;
214
		case 1:
215
			if((debug & DbgPac) && newstate.cmd)
216
				fprint(2, "Pacproc: newstate %s-%d, playstate %s-%d\n",
217
					statetxt[newstate.cmd], newstate.off,
218
					statetxt[playstate.cmd], playstate.off);
219
			/* Deal with an incoming command */
220
			if(newstate.cmd == Pause || newstate.cmd == Resume){
221
				/* Just pass them on, don't change local state */
222
				pb = recvp(spare);
223
				pb->m = newstate.m;
224
				sendp(full, pb);
225
				break;
226
			}
227
			/* Stop whatever we're doing */
228
			if(fd >= 0){
229
				if(debug & DbgPlayer)
230
					fprint(2, "pac, stop\n");
231
				/* Stop any active (pac) decoders */
232
				close(fd);
233
				fd = -1;
234
			}
235
			a[0].op = CHANNOP;
236
			switch(newstate.cmd){
237
			default:
238
				sysfatal("pacproc: unexpected newstate %d", newstate.cmd);
239
			case Stop:
240
				/* Wait for state to change */
241
				break;
242
			case Skip:
243
			case Play:
244
				fd = startplay(newstate.off);
245
				if(fd >=0){
246
					playstate = newstate;
247
					a[0].op = CHANRCV;
248
					continue;	/* Start reading */
249
				}
250
				newstate.cmd = Stop;
251
			}
252
			pb = recvp(spare);
253
			pb->m = newstate.m;
254
			sendp(full, pb);
255
			playstate = newstate;
256
		}
257
	}
258
}
259
 
260
void
261
pcmproc(void*)
262
{
263
	Pmsg localstate, newstate, prevstate;
264
	int fd, n;
265
	Pacbuf *pb, *b;
266
	Alt a[3] = {
267
		{full, &pb, CHANRCV},
268
		{playout, &pb, CHANRCV},
269
		{nil, nil, CHANEND},
270
	};
271
 
272
	/*
273
	 * This is the real-time proc.
274
	 * It gets its input from two sources, full data/control buffers from the pacproc
275
	 * which mixes decoded data with control messages, and data buffers from the pcmproc's
276
	 * (*this* proc's) own internal playout buffer.
277
	 * When a command is received on the `full' channel containing a command that warrants
278
	 * an immediate change of audio source (e.g., to silence or to another number), we just
279
	 * toss everything in the pipeline -- i.e., the playout channel
280
	 * Finally, we report all state changes using `playupdate' (another message channel)
281
	 */
282
	threadsetname("pcmproc");
283
	close(srvfd[1]);
284
	fd = -1;
285
	localstate.cmd = 0;	/* Force initial playupdate */
286
	newstate.cmd = Stop;
287
	newstate.off = 0;
288
//	rtsched();
289
	for(;;){
290
		if(newstate.m != localstate.m){
291
			playupdate(newstate, nil);
292
			localstate = newstate;
293
		}
294
		switch(alt(a)){
295
		case 0:
296
			/* buffer received from pacproc */
297
			if((debug & DbgPcm) && localstate.m != prevstate.m){
298
				fprint(2, "pcm, full: %s-%d, local state is %s-%d\n",
299
					statetxt[pb->cmd], pb->off,
300
					statetxt[localstate.cmd], localstate.off);
301
				prevstate.m = localstate.m;
302
			}
303
			switch(pb->cmd){
304
			default:
305
				sysfatal("pcmproc: unknown newstate: %s-%d", statetxt[pb->cmd], pb->off);
306
			case Resume:
307
				a[1].op = CHANRCV;
308
				newstate.cmd = Play;
309
				break;
310
			case Pause:
311
				a[1].op = CHANNOP;
312
				newstate.cmd = Pause;
313
				if(fd >= 0){
314
					close(fd);
315
					fd = -1;
316
				}
317
				break;
318
			case Stop:
319
				/* Dump all data in the buffer */
320
				while(b = nbrecvp(playout))
321
					if(b->cmd == Error){
322
						playupdate(b->Pmsg, b->data);
323
						sendp(spare, b);
324
					}else
325
						sendp(empty, b);
326
				newstate.m = pb->m;
327
				a[1].op = CHANRCV;
328
				if(fd >= 0){
329
					close(fd);
330
					fd = -1;
331
				}
332
				break;
333
			case Skip:
334
				/* Dump all data in the buffer, then fall through */
335
				while(b = nbrecvp(playout))
336
					if(b->cmd == Error){
337
						playupdate(pb->Pmsg, pb->data);
338
						sendp(spare, pb);
339
					}else
340
						sendp(empty, b);
341
				a[1].op = CHANRCV;
342
				newstate.cmd = Play;
343
			case Error:
344
			case Play:
345
				/* deal with at playout, just requeue */
346
				sendp(playout, pb);
347
				pb = nil;
348
				localstate = newstate;
349
				break;
350
			}
351
			/* If we still have a buffer, free it */
352
			if(pb)
353
				sendp(spare, pb);
354
			break;
355
		case 1:
356
			/* internal buffer */
357
			if((debug & DbgPlayer) && localstate.m != prevstate.m){
358
				fprint(2, "pcm, playout: %s-%d, local state is %s-%d\n",
359
					statetxt[pb->cmd], pb->off,
360
					statetxt[localstate.cmd], localstate.off);
361
				prevstate.m = localstate.m;
362
			}
363
			switch(pb->cmd){
364
			default:
365
				sysfatal("pcmproc: unknown newstate: %s-%d", statetxt[pb->cmd], pb->off);
366
			case Error:
367
				playupdate(pb->Pmsg, pb->data);
368
				localstate = newstate;
369
				sendp(spare, pb);
370
				break;
371
			case Play:
372
				if(fd < 0 && (fd = open("/dev/audio", OWRITE)) < 0){
373
					a[1].op = CHANNOP;
374
					newstate.cmd = Pause;
375
					pb->cmd = Error;
376
					snprint(pb->data, sizeof(pb->data),
377
						"/dev/audio: %r");
378
					playupdate(pb->Pmsg, pb->data);
379
					sendp(empty, pb);
380
					break;
381
				}
382
				/* play out this buffer */
383
				totbytes += pb->len;
384
				totbuffers++;
385
				n = write(fd, pb->data, pb->len);
386
				if (n != pb->len){
387
					if (debug & DbgPlayer)
388
						fprint(2, "pcmproc: file %d: %r\n", pb->off);
389
					if (n < 0)
390
						sysfatal("pcmproc: write: %r");
391
				}
392
				newstate.m = pb->m;
393
				sendp(empty, pb);
394
				break;
395
			}
396
			break;
397
		}
398
	}
399
}
400
 
401
void
402
playinit(void)
403
{
404
	int i;
405
 
406
	full = chancreate(sizeof(Pacbuf*), 1);
407
	empty = chancreate(sizeof(Pacbuf*), NPacbuf);
408
	spare = chancreate(sizeof(Pacbuf*), NSparebuf);
409
	playout = chancreate(sizeof(Pacbuf*), NPacbuf+NSparebuf);
410
	for(i = 0; i < NPacbuf; i++)
411
		sendp(empty, malloc(sizeof(Pacbuf)));
412
	for(i = 0; i < NSparebuf; i++)
413
		sendp(spare, malloc(sizeof(Pacbuf)));
414
 
415
	playc = chancreate(sizeof(Pmsg), 1);
416
	procrfork(pacproc, nil, 8*STACKSIZE, RFFDG);
417
	procrfork(pcmproc, nil, 8*STACKSIZE, RFFDG);
418
}
419
 
420
char *
421
getplaystat(char *p, char *e)
422
{
423
	p = seprint(p, e, "empty buffers %d of %d\n", empty->n, empty->s);
424
	p = seprint(p, e, "full buffers %d of %d\n", full->n, full->s);
425
	p = seprint(p, e, "playout buffers %d of %d\n", playout->n, playout->s);
426
	p = seprint(p, e, "spare buffers %d of %d\n", spare->n, spare->s);
427
	p = seprint(p, e, "bytes %lud / buffers %lud played\n", totbytes, totbuffers);
428
	return p;
429
}