shithub: treason

ref: 28ef2a222c95ee8826d9eed152964bfcc90e30a7
dir: /stream_audio.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include "misc.h"
#include "stream.h"

enum {
	Onesec = 2*2*44100, /* 16-bit, stereo */
	Framesz = Onesec/10, /* 1/10th second */
};

static char *progs[] = {
	[FmtAAC] = "/bin/audio/aacdec",
	[FmtOpus] = "/bin/audio/opusdec",
	[FmtVorbis] = "/bin/audio/oggdec",
};

extern Streamops audops;

int
audopenfd(int fd, Stream *s, int *failed)
{
	int p[2], pid;
	char *prog;
	Dir *d;

	if(fd < 0)
		return -1;
	if(s->fmt >= nelem(progs) || (prog = progs[s->fmt]) == nil){
		werrstr("unsupported audio format %d", s->fmt);
		goto err;
	}

	*failed = 1;
	if((d = dirstat(prog)) == nil){
		werrstr("can't decode, %s isn't available", prog);
		goto err;
	}
	free(d);

	pipe(p);
	if((pid = rfork(RFFDG|RFPROC)) == 0){
		dup(fd, 0); close(fd);
		close(p[0]);
		dup(p[1], 1); close(p[1]);
		if(!debug){
			dup(fd = open("/dev/null", OWRITE), 2);
			close(fd);
		}
		execl(prog, prog, nil);
		sysfatal("exec: %r");
	}
	close(p[1]);
	if(pid < 0){
		close(p[0]);
		goto err;
	}
	close(fd);

	s->type = Saudio;
	s->timebase.denum = 1;
	s->timebase.num = 1;
	s->b = Bfdopen(p[0], OREAD);
	memmove(&s->ops, &audops, sizeof(audops));
	*failed = 0;

	return 0;
err:
	close(fd);
	return -1;
}

static Stream *
audopen(char *path, int *num, int *failed)
{
	Stream *s;

	if((s = calloc(1, sizeof(*s))) == nil){
		*failed = 1;
		return nil;
	}

	*num = 1;
	if(audopenfd(open(path, OREAD), s, failed) != 0){
		free(s);
		s = nil;
	}

	return s;
}

static int
audread(Stream *s, Streamframe *f)
{
	if(s->buf == nil)
		s->buf = malloc(Framesz);
	f->buf = s->buf;
	f->timestamp = s->timestamp;
	if((f->sz = Bread(s->b, f->buf, Framesz)) < 0){ /* eof */
		f->sz = 0;
		return -1;
	}

	f->dt = f->sz * 1000000000ULL / Onesec;
	s->timestamp += f->dt;

	return 0;
}

static void
audclose(Stream *s)
{
	Bterm(s->b);
	free(s->buf);
}

Streamops audops = {
	.open = audopen,
	.read = audread,
	.close = audclose,
};