~moody/drawterm

e7c6d21bccfb2ad48ff3050c80b72b185240e2fa — Jacob Moody 3 months ago 6c58f8e pipewire-framebuffer
experimental pipewire camera api
5 files changed, 342 insertions(+), 0 deletions(-)

M cpu.c
M kern/Makefile
A kern/devframe-pipewire.c
A kern/devframe.c
M kern/devtab.c
M cpu.c => cpu.c +2 -0
@@ 348,6 348,8 @@ cpubody(void)
			panic("bind #i: %r");
		if(bind("#m", "/dev", MBEFORE) < 0)
			panic("bind #m: %r");
		if(bind("#f", "/dev", MBEFORE) < 0)
			panic("bind #f: %r");
		if(cmd == nil)
			atexit(ending);
	}

M kern/Makefile => kern/Makefile +2 -0
@@ 10,6 10,8 @@ OFILES=\
	dev.$O\
	devaudio.$O\
	devaudio-$(AUDIO).$O\
	devframe.$O\
	devframe-pipewire.$O\
	devcmd.$O\
	devcons.$O\
	devdraw.$O\

A kern/devframe-pipewire.c => kern/devframe-pipewire.c +196 -0
@@ 0,0 1,196 @@
#include	"u.h"
#include	"lib.h"
#include	"dat.h"
#include	"fns.h"
#include	"error.h"
#include	"devaudio.h"

#undef long
#include <spa/param/video/format-utils.h>
#include <spa/debug/types.h>
#include <spa/param/video/type-info.h>
 
#include <pipewire/pipewire.h>

void	framedevopen(char **format, int *width, int *height);
long	framedevread(void *va, long n);
void	framedevclose(void);

static struct {
	Lock lk;
	Rendez z;
	int haveformat;
	int haveframe;
	struct pw_main_loop *loop;
	struct pw_stream *stream;
	struct spa_video_info format;
	struct pw_buffer *pwbuf;

	Rendez newframe;
} pwstate;

static char *argv[] = { "drawterm" };
static int argc = 1;

static void
frame_process(void *data)
{
	fprintf(stderr, "frame process\n");
	lock(&pwstate.lk);
	if(pwstate.pwbuf != NULL)
		pw_stream_queue_buffer(pwstate.stream, pwstate.pwbuf);
	pwstate.pwbuf = pw_stream_dequeue_buffer(pwstate.stream);
	pwstate.haveframe = 1;
	unlock(&pwstate.lk);
	wakeup(&pwstate.newframe);
}

static void
on_param_changed(void *data, uint32_t id, const struct spa_pod *param)
{
	if(param == NULL || id != SPA_PARAM_Format)
		return;

	fprintf(stderr, "got param changed\n");
	lock(&pwstate.lk);
	if(spa_format_parse(param, &pwstate.format.media_type, &pwstate.format.media_subtype) < 0)
		sysfatal("piewire could not parse format");
	
	if(pwstate.format.media_type != SPA_MEDIA_TYPE_video || pwstate.format.media_subtype != SPA_MEDIA_SUBTYPE_raw){
		unlock(&pwstate.lk);
		return;
	}
	if(spa_format_video_raw_parse(param, &pwstate.format.info.raw) < 0)
		sysfatal("pipewire could not parse raw video");

	pwstate.haveformat = 1;
	unlock(&pwstate.lk);
	wakeup(&pwstate.z);
	fprintf(stderr, "leaving param changed\n");
	/* TODO we need to tell pipewire what formats are cool with us,
	 * or tell it that we are perfectly cool with the format advertised */
}

static const struct pw_stream_events stream_events = {
	PW_VERSION_STREAM_EVENTS,
	.param_changed = on_param_changed,
	.process = frame_process,
};

static void
pwproc(void *arg)
{
	struct pw_main_loop *loop;

	loop = arg;
	fprintf(stderr, "pipewire looping\n");
	pw_main_loop_run(loop);
}

static int
haveformat(void *arg)
{
	return pwstate.haveformat;
}

void
framedevopen(char **format, int *width, int *height)
{
	const struct spa_pod *params[1];
	char buffer[1024];
	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
	int err;

	pw_init(&argc, &argv);
	fprintf(stderr, "opening\n");

	lock(&pwstate.lk);
	pwstate.loop = pw_main_loop_new(NULL);
	pwstate.stream = pw_stream_new_simple(
		pw_main_loop_get_loop(pwstate.loop),
		"drawterm-capture",
		pw_properties_new(
			PW_KEY_MEDIA_TYPE, "Video",
			PW_KEY_MEDIA_CATEGORY, "Monitor",
			PW_KEY_MEDIA_ROLE, "Screen",
			NULL),
		&stream_events,
		NULL);

	if(pwstate.stream == NULL)
		sysfatal("could not get a stream");

        params[0] = spa_pod_builder_add_object(&b,
                SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
                SPA_FORMAT_mediaType,       SPA_POD_Id(SPA_MEDIA_TYPE_video),
                SPA_FORMAT_mediaSubtype,    SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
                SPA_FORMAT_VIDEO_format,    SPA_POD_CHOICE_ENUM_Id(7,
                                                SPA_VIDEO_FORMAT_RGB,
                                                SPA_VIDEO_FORMAT_RGB,
                                                SPA_VIDEO_FORMAT_RGBA,
                                                SPA_VIDEO_FORMAT_RGBx,
                                                SPA_VIDEO_FORMAT_BGRx,
                                                SPA_VIDEO_FORMAT_YUY2,
                                                SPA_VIDEO_FORMAT_I420),
                SPA_FORMAT_VIDEO_size,      SPA_POD_CHOICE_RANGE_Rectangle(
                                                &SPA_RECTANGLE(320, 240),
                                                &SPA_RECTANGLE(1, 1),
                                                &SPA_RECTANGLE(4096, 4096)),
                SPA_FORMAT_VIDEO_framerate, SPA_POD_CHOICE_RANGE_Fraction(
                                                &SPA_FRACTION(25, 1),
                                                &SPA_FRACTION(0, 1),
                                                &SPA_FRACTION(1000, 1)));

	err = pw_stream_connect(pwstate.stream,
			PW_DIRECTION_INPUT,
			PW_ID_ANY,
			PW_STREAM_FLAG_AUTOCONNECT |
			PW_STREAM_FLAG_MAP_BUFFERS,
			params, 1);
	if(err < 0)
		sysfatal("failed to connect");
	unlock(&pwstate.lk);
	kproc("pipewire frame buffer loop", pwproc, pwstate.loop);
	sleep(&pwstate.z, haveformat, 0);
	lock(&pwstate.lk);
	*width = pwstate.format.info.raw.size.width;
	*height = pwstate.format.info.raw.size.height;
	unlock(&pwstate.lk);
	fprintf(stderr, "leaving open\n");
}

void
framedevclose(void)
{
	lock(&pwstate.lk);
	pw_main_loop_quit(pwstate.loop);
	unlock(&pwstate.lk);
}

static int
haveframe(void *arg)
{
	return pwstate.haveframe;
}

long
framedevread(void *v, long n)
{
	fprintf(stderr, "entering read\n");
	long size;
	lock(&pwstate.lk);
	if(pwstate.haveframe == 0){
		unlock(&pwstate.lk);
		sleep(&pwstate.newframe, haveframe, 0);
		lock(&pwstate.lk);
	}
	size = pwstate.pwbuf->buffer->datas[0].chunk->size;
	if(n < size){
		unlock(&pwstate.lk);
		error(Eshort);
		return -1;
	}
	memmove(v, pwstate.pwbuf->buffer->datas[0].data, size);
	unlock(&pwstate.lk);
	return size;
}

A kern/devframe.c => kern/devframe.c +140 -0
@@ 0,0 1,140 @@

#include	"u.h"
#include	"lib.h"
#include	"dat.h"
#include	"fns.h"
#include	"error.h"

enum {
	Qdir,
	Qformat,
	Qframe,
};

void	framedevopen(char **format, int *width, int *height);
long	framedevread(void *va, long n);
void	framedevclose(void);

static struct {
	int open;
	char *format;
	int width, height;
} state;

Dirtab framedir[]={
	".",		{Qdir, 0, QTDIR},	0,	DMDIR|0555,
	"format",	{Qformat},		0,	0444,
	"framebuffer",	{Qframe},		0,	0444,
};

#define NFRAME 	(sizeof(framedir)/sizeof(Dirtab))

static void
frameinit(void)
{
}

static Chan*
frameattach(char *spec)
{
	return devattach('f', spec);
}

static Walkqid*
framewalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, framedir, NFRAME, devgen);
}

static int
framestat(Chan *c, uchar *db, int n)
{
	return devstat(c, db, n, framedir, NFRAME, devgen);
}

static Chan*
frameopen(Chan *c, int omode)
{
	switch((long)c->qid.path){
	case Qdir:
		if(omode != OREAD)
			error(Eperm);
		break;
	case Qformat:
		framedevopen(&state.format, &state.width, &state.height);
		state.open = 1;
		break;
	case Qframe:
		if(state.open == 1){
			break;
		}
		framedevopen(&state.format, &state.width, &state.height);
		state.open = 1;
		break;
	}
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void
frameclose(Chan *c)
{
	if(!(c->flag&COPEN))
		return;
	if(c->qid.path == Qdir)
		return;
	state.open = 0;
	framedevclose();
}

long
frameread(Chan *c, void *va, long n, vlong offset)
{
	char buf[8192];
	int nbuf;

	switch((long)c->qid.path){
	case Qdir:
		return devdirread(c, va, n, framedir, NFRAME, devgen);
	case Qframe:
		return framedevread(va, n);
	case Qformat:
		nbuf = snprint(buf, sizeof buf - 1, "%dx%d@%s\n", state.width, state.height, state.format);
		if(n < nbuf)
			error(Eshort);
		n = nbuf;
		memmove(va, buf, n);
		return n;
	}
	return 0;
}

long
framewrite(Chan *c, void *va, long n, vlong offset)
{
	error(Eperm);
	return -1;
}

Dev framedevtab = {
	'f',
	"framebuffer",

	devreset,
	frameinit,
	devshutdown,
	frameattach,
	framewalk,
	framestat,
	frameopen,
	devcreate,
	frameclose,
	frameread,
	devbread,
	framewrite,
	devbwrite,
	devremove,
	devwstat,
};

M kern/devtab.c => kern/devtab.c +2 -0
@@ 19,6 19,7 @@ extern Dev audiodevtab;
extern Dev kbddevtab;
extern Dev cmddevtab;
extern Dev envdevtab;
extern Dev framedevtab;

Dev *devtab[] = {
	&rootdevtab,


@@ 36,6 37,7 @@ Dev *devtab[] = {
	&kbddevtab,
	&cmddevtab,
	&envdevtab,
	&framedevtab,
	0
};