⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 player.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#include <u.h>#include <libc.h>#include <thread.h>#include <fcall.h>#include "pool.h"#include "playlist.h"enum {	Pac,	Mp3,	Pcm,	Ogg,};typedef struct Playfd Playfd;struct Playfd {	/* Describes a file to play for starting up pac4dec/mp3,... */	char	*filename;	/* mallocated */	int	fd;		/* filedesc to use */	int	cfd;		/* fildesc to close */};Channel *full, *empty, *playout, *spare;Channel	*playc, *pacc;char *playprog[] = {[Pac] = "/bin/games/pac4dec",[Mp3] = "/bin/games/mp3dec",[Pcm] = "/bin/cp",[Ogg] = "/bin/games/vorbisdec",};ulong totbytes, totbuffers;static char curfile[8192];voidpac4dec(void *a){	Playfd *pfd;	Pacbuf *pb;	int fd, type;	char *ext, buf[256];	static char args[6][32];	char *argv[6] = {args[0], args[1], args[2], args[3], args[4], args[5]};	threadsetname("pac4dec");	pfd = a;	close(pfd->cfd);	/* read fd */	ext = strrchr(pfd->filename, '.');	fd = open(pfd->filename, OREAD);	if (fd < 0 && ext == nil){		// Try the alternatives		ext = buf + strlen(pfd->filename);		snprint(buf, sizeof buf, "%s.pac", pfd->filename);		fd = open(buf, OREAD);		if (fd < 0){			snprint(buf, sizeof buf, "%s.mp3", pfd->filename);			fd = open(buf, OREAD);		}		if (fd < 0){			snprint(buf, sizeof buf, "%s.ogg", pfd->filename);			fd = open(buf, OREAD);		}		if (fd < 0){			snprint(buf, sizeof buf, "%s.pcm", pfd->filename);			fd = open(buf, OREAD);		}	}	if (fd < 0){		if (debug & DbgPlayer)			fprint(2, "pac4dec: %s: %r", pfd->filename);		pb = nbrecvp(spare);		pb->cmd = Error;		pb->off = 0;		pb->len = snprint(pb->data, sizeof(pb->data), "startplay: %s: %r", pfd->filename);		sendp(full, pb);		threadexits("open");	}	dup(pfd->fd, 1);	close(pfd->fd);	if(ext == nil || strcmp(ext, ".pac") == 0){		type = Pac;		snprint(args[0], sizeof args[0], "pac4dec");		snprint(args[1], sizeof args[1], "/fd/%d", fd);		snprint(args[2], sizeof args[2], "/fd/1");		argv[3] = nil;	}else if(strcmp(ext, ".mp3") == 0){		type = Mp3;		snprint(args[0], sizeof args[0], "mp3dec");		snprint(args[1], sizeof args[1], "-q");		snprint(args[2], sizeof args[1], "-s");		snprint(args[3], sizeof args[1], "/fd/%d", fd);		argv[4] = nil;	}else if(strcmp(ext, ".ogg") == 0){		type = Ogg;		snprint(args[0], sizeof args[0], "vorbisdec");		argv[1] = nil;		argv[2] = nil;		argv[3] = nil;		dup(fd, 0);	}else{		type = Pcm;		snprint(args[0], sizeof args[0], "cat");		snprint(args[1], sizeof args[1], "/fd/%d", fd);		argv[2] = nil;		argv[3] = nil;	}	free(pfd->filename);	free(pfd);	if (debug & DbgPlayer)		fprint(2, "procexecl %s %s %s %s\n",			playprog[type], argv[0], argv[1], argv[2]);	procexec(nil, playprog[type], argv);	if((pb = nbrecvp(spare)) == nil)		pb = malloc(sizeof(Pacbuf));	pb->cmd = Error;	pb->off = 0;	pb->len = snprint(pb->data, sizeof(pb->data), "startplay: %s: exec", playprog[type]);	sendp(full, pb);	threadexits(playprog[type]);}static intstartplay(ushort n){	int fd[2];	Playfd *pfd;	char *file;	file = getplaylist(n);	if(file == nil)		return Undef;	if (debug & DbgPlayer)		fprint(2, "startplay: file is `%s'\n", file);	if(pipe(fd) < 0)		sysfatal("pipe: %r");	pfd = malloc(sizeof(Playfd));	pfd->filename = file;	/* mallocated already */	pfd->fd = fd[1];	pfd->cfd = fd[0];	procrfork(pac4dec, pfd, 4096, RFFDG);	close(fd[1]);	/* write fd, for pac4dec */	return fd[0];	/* read fd */}static voidrtsched(void){	int fd;	char *ctl;	ctl = smprint("/proc/%ud/ctl", getpid());	if((fd = open(ctl, ORDWR)) < 0) 		sysfatal("%s: %r", ctl);	if(fprint(fd, "period 20ms") < 0)		sysfatal("%s: %r", ctl);	if(fprint(fd, "cost 100µs") < 0)		sysfatal("%s: %r", ctl);	if(fprint(fd, "sporadic") < 0)		sysfatal("%s: %r", ctl);	if(fprint(fd, "admit") < 0)		sysfatal("%s: %r", ctl);	close(fd);	free(ctl);}voidpacproc(void*){	Pmsg playstate, newstate;	int fd;	Pacbuf *pb;	Alt a[3] = {		{empty, &pb, CHANNOP},		{playc, &newstate.m, CHANRCV},		{nil, nil, CHANEND},	};	threadsetname("pacproc");	close(srvfd[1]);	newstate.cmd = playstate.cmd = Stop;	newstate.off = playstate.off = 0;	fd = -1;	for(;;){		switch(alt(a)){		case 0:			/* Play out next buffer (pb points to one already) */			assert(fd >= 0);	/* Because we must be in Play mode */			pb->m = playstate.m;			pb->len = read(fd, pb->data, sizeof pb->data);			if(pb->len > 0){				sendp(full, pb);				break;			}			if(pb->len < 0){				if(debug & DbgPlayer)					fprint(2, "pac, error: %d\n", playstate.off);				pb->cmd = Error;				pb->len = snprint(pb->data, sizeof pb->data, "%s: %r", curfile);				sendp(full, pb);			}else{				/* Simple end of file */				sendp(empty, pb); /* Don't need buffer after all */			}			close(fd);			fd = -1;			if(debug & DbgPlayer)				fprint(2, "pac, eof: %d\n", playstate.off);			/* End of file, do next by falling through */			newstate.cmd = playstate.cmd;			newstate.off = playstate.off + 1;		case 1:			if((debug & DbgPac) && newstate.cmd)				fprint(2, "Pacproc: newstate %s-%d, playstate %s-%d\n",					statetxt[newstate.cmd], newstate.off,					statetxt[playstate.cmd], playstate.off);			/* Deal with an incoming command */			if(newstate.cmd == Pause || newstate.cmd == Resume){				/* Just pass them on, don't change local state */				pb = recvp(spare);				pb->m = newstate.m;				sendp(full, pb);				break;			}			/* Stop whatever we're doing */			if(fd >= 0){				if(debug & DbgPlayer)					fprint(2, "pac, stop\n");				/* Stop any active (pac) decoders */				close(fd);				fd = -1;			}			a[0].op = CHANNOP;			switch(newstate.cmd){			default:				sysfatal("pacproc: unexpected newstate %d", newstate.cmd);			case Stop:				/* Wait for state to change */				break;			case Skip:			case Play:				fd = startplay(newstate.off);				if(fd >=0){					playstate = newstate;					a[0].op = CHANRCV;					continue;	/* Start reading */				}				newstate.cmd = Stop;			}			pb = recvp(spare);			pb->m = newstate.m;			sendp(full, pb);			playstate = newstate;		}	}}voidpcmproc(void*){	Pmsg localstate, newstate, prevstate;	int fd, n;	Pacbuf *pb, *b;	Alt a[3] = {		{full, &pb, CHANRCV},		{playout, &pb, CHANRCV},		{nil, nil, CHANEND},	};	/*	 * This is the real-time proc.	 * It gets its input from two sources, full data/control buffers from the pacproc	 * which mixes decoded data with control messages, and data buffers from the pcmproc's	 * (*this* proc's) own internal playout buffer.	 * When a command is received on the `full' channel containing a command that warrants	 * an immediate change of audio source (e.g., to silence or to another number), we just	 * toss everything in the pipeline -- i.e., the playout channel	 * Finally, we report all state changes using `playupdate' (another message channel)	 */	threadsetname("pcmproc");	close(srvfd[1]);	fd = open("/dev/audio", OWRITE);	if (fd < 0)		sysfatal("/dev/audio: %r");	localstate.cmd = 0;	/* Force initial playupdate */	newstate.cmd = Stop;	newstate.off = 0;//	rtsched();	for(;;){		if(newstate.m != localstate.m){			playupdate(newstate, nil);			localstate = newstate;		}		switch(alt(a)){		case 0:			/* buffer received from pacproc */			if((debug & DbgPcm) && localstate.m != prevstate.m){				fprint(2, "pcm, full: %s-%d, local state is %s-%d\n",					statetxt[pb->cmd], pb->off,					statetxt[localstate.cmd], localstate.off);				prevstate.m = localstate.m;			}			switch(pb->cmd){			default:				sysfatal("pcmproc: unknown newstate: %s-%d", statetxt[pb->cmd], pb->off);			case Resume:				a[1].op = CHANRCV;				newstate.cmd = Play;				break;			case Pause:				a[1].op = CHANNOP;				newstate.cmd = Pause;				break;			case Stop:				/* Dump all data in the buffer */				while(b = nbrecvp(playout))					if(b->cmd == Error){						playupdate(b->Pmsg, b->data);						sendp(spare, b);					}else						sendp(empty, b);				newstate.m = pb->m;				a[1].op = CHANRCV;				break;			case Skip:				/* Dump all data in the buffer, then fall through */				while(b = nbrecvp(playout))					if(b->cmd == Error){						playupdate(pb->Pmsg, pb->data);						sendp(spare, pb);					}else						sendp(empty, b);				a[1].op = CHANRCV;				newstate.cmd = Play;			case Error:			case Play:				/* deal with at playout, just requeue */				sendp(playout, pb);				pb = nil;				localstate = newstate;				break;			}			/* If we still have a buffer, free it */			if(pb)				sendp(spare, pb);			break;		case 1:			/* internal buffer */			if((debug & DbgPlayer) && localstate.m != prevstate.m){				fprint(2, "pcm, playout: %s-%d, local state is %s-%d\n",					statetxt[pb->cmd], pb->off,					statetxt[localstate.cmd], localstate.off);				prevstate.m = localstate.m;			}			switch(pb->cmd){			default:				sysfatal("pcmproc: unknown newstate: %s-%d", statetxt[pb->cmd], pb->off);			case Error:				playupdate(pb->Pmsg, pb->data);				localstate = newstate;				sendp(spare, pb);				break;			case Play:				/* play out this buffer */				totbytes += pb->len;				totbuffers++;				n = write(fd, pb->data, pb->len);				if (n != pb->len){					if (debug & DbgPlayer)						fprint(2, "pcmproc: file %d: %r\n", pb->off);					if (n < 0)						sysfatal("pcmproc: write: %r");				}				newstate.m = pb->m;				sendp(empty, pb);				break;			}			break;		}	}}voidplayinit(void){	int i;	full = chancreate(sizeof(Pacbuf*), 1);	empty = chancreate(sizeof(Pacbuf*), NPacbuf);	spare = chancreate(sizeof(Pacbuf*), NSparebuf);	playout = chancreate(sizeof(Pacbuf*), NPacbuf+NSparebuf);	for(i = 0; i < NPacbuf; i++)		sendp(empty, malloc(sizeof(Pacbuf)));	for(i = 0; i < NSparebuf; i++)		sendp(spare, malloc(sizeof(Pacbuf)));	playc = chancreate(sizeof(Pmsg), 1);	procrfork(pacproc, nil, 32*1024, RFFDG);	procrfork(pcmproc, nil, 32*1024, RFFDG);}char *getplaystat(char *p, char *e){	p = seprint(p, e, "empty buffers %d of %d\n", empty->n, empty->s);	p = seprint(p, e, "full buffers %d of %d\n", full->n, full->s);	p = seprint(p, e, "playout buffers %d of %d\n", playout->n, playout->s);	p = seprint(p, e, "spare buffers %d of %d\n", spare->n, spare->s);	p = seprint(p, e, "bytes %lud / buffers %lud played\n", totbytes, totbuffers);	return p;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -