Here is an attempt to make video(1) work with the modesetting driver.
See https://marc.info/?l=openbsd-bugs&m=152231686416039&w=2
The general idea:
If there is no common encoding for input (device) and output (Xv), the
encoding is converted to something supported by the output.
No conversion is done if the output is sent to file (options -o/-O).
The option -e is now used as the input format. The output format
is selected automatically (if output goes to Xv).
The following encodings are supported: yuy2, yuyv, uyuy, yv12
yuy2 and yuyv are treated as two different encodings, they share the same
pixelformat, but have different ids. (This behaviour is different from the
current description of '-e' in the manpage)
I have tested on two lenovo laptops using both drivers (modesetting and intel).
On my laptop (lenovo X1 Carbon), with modesetting diver:
yuyv is converted to yv12 before sending to Xv.
On the same laptop With intel driver:
yuyv is converted to yuy2 (no-op)
Index: video.c
===================================================================
RCS file: /cvs/xenocara/app/video/video.c,v
retrieving revision 1.26
diff -u -p -u -p -r1.26 video.c
--- video.c 4 Jan 2019 17:45:00 -0000 1.26
+++ video.c 16 Jan 2019 11:30:21 -0000
@@ -38,6 +38,8 @@
#include <X11/extensions/Xvlib.h>
#include <X11/Xatom.h>
+#define V_V4L2_PIX_FMT_YUY2 v4l2_fourcc('Y', 'U', 'Y', '2')
+
/* Xv(3) adaptor properties */
struct xv_adap {
char name[128];
@@ -133,20 +135,23 @@ struct dev {
/* video encodingss */
struct encodings {
char *name;
+ int id;
int bpp;
- int xv_id;
- int dev_id;
#define SW_DEV 0x1
#define SW_XV 0x2
#define SW_MASK (SW_DEV | SW_XV)
int flags;
} encs[] = {
#define ENC_YUY2 0
- { "yuy2", 16, -1, -1, 0 },
-#define ENC_UYVY 1
- { "uyvy", 16, -1, -1, 0 },
-#define ENC_LAST 2
- { NULL, 0, 0, 0, 0 }
+ { "yuy2", V_V4L2_PIX_FMT_YUY2, 16, 0 },
+#define ENC_YUYV 1
+ { "yuyv", V4L2_PIX_FMT_YUYV, 16, 0 },
+#define ENC_UYVY 2
+ { "uyvy", V4L2_PIX_FMT_UYVY, 16, 0 },
+#define ENC_YV12 3
+ { "yv12", V4L2_PIX_FMT_YVU420, 12, 0 },
+#define ENC_LAST 4
+ { NULL, 0, 0, 0 }
};
struct video {
@@ -163,8 +168,10 @@ struct video {
int iofile_fd;
char *sz_str;
#define CONV_SWAP 0x1
+#define CONV_YV12 0x2
int conv_type;
- int enc;
+ int enc_in;
+ int enc_out;
int full_screen;
int net_wm;
int width;
@@ -216,6 +223,7 @@ int stream(struct video *);
void got_frame(int);
void got_shutdown(int);
int find_enc(char *);
+int find_by_id(int);
void usage(void);
static volatile sig_atomic_t play, shutdown, hold, wout;
@@ -242,6 +250,17 @@ find_enc(char *name)
}
int
+find_enc_by_id(int id)
+{
+ int i;
+
+ for (i = 0; i < ENC_LAST; i++)
+ if (encs[i].id == id)
+ break;
+ return i;
+}
+
+int
xv_get_info(struct video *vid)
{
struct xdsp *x = &vid->xdsp;
@@ -251,7 +270,6 @@ xv_get_info(struct video *vid)
struct xv_adap *adap;
unsigned int nenc, p;
int num_xvformats, nadaps, i, j, ret;
- char fmtName[5];
if ((x->dpy = XOpenDisplay(NULL)) == NULL) {
warnx("cannot open display %s", XDisplayName(NULL));
@@ -312,16 +330,9 @@ xv_get_info(struct video *vid)
&num_xvformats);
adap->nfmts = 0;
for (j = 0; j < num_xvformats; j++) {
- snprintf(fmtName, sizeof(fmtName), "%c%c%c%c",
- xvformats[j].id & 0xff,
- (xvformats[j].id >> 8) & 0xff,
- (xvformats[j].id >> 16) & 0xff,
- (xvformats[j].id >> 24) & 0xff);
- if (!strcmp(fmtName, "YUY2")) {
- encs[ENC_YUY2].xv_id = xvformats[j].id;
- adap->fmts[adap->nfmts++] = xvformats[j].id;
- } else if (!strcmp(fmtName, "UYVY")) {
- encs[ENC_UYVY].xv_id = xvformats[j].id;
+ int enc = find_enc_by_id(xvformats[j].id);
+ if (enc < ENC_LAST) {
+ encs[enc].flags |= SW_XV;
adap->fmts[adap->nfmts++] = xvformats[j].id;
}
if (adap->nfmts >= MAX_FMTS)
@@ -369,20 +380,20 @@ xv_sel_adap(struct video *vid)
x->cur_adap = i;
adap = &x->adaps[i];
for (i = 0; i < adap->nfmts; i++) {
- if (adap->fmts[i] == encs[vid->enc].xv_id)
+ if (adap->fmts[i] == encs[vid->enc_out].id)
break;
}
if (i >= adap->nfmts) {
warnx("Xv adaptor '%d' doesn't support %s",
x->adaps[x->cur_adap].adap_index,
- encs[vid->enc].name);
+ encs[vid->enc_out].name);
return 0;
}
}
for (i = 0; i < x->nadaps && x->cur_adap == -1; i++) {
adap = &x->adaps[i];
for (j = 0; j < adap->nfmts; j++) {
- if (adap->fmts[j] == encs[vid->enc].xv_id) {
+ if (adap->fmts[j] == encs[vid->enc_out].id) {
x->cur_adap = i;
break;
}
@@ -445,7 +456,7 @@ xv_dump_info(struct video *vid)
fprintf(stderr, " encodings: ");
for (i = 0, j = 0; i < ENC_LAST; i++) {
- if (encs[i].xv_id != -1 && !(encs[i].flags & SW_XV)) {
+ if (encs[i].id != -1 && (encs[i].flags & SW_XV)) {
if (j)
fprintf(stderr, ", ");
fprintf(stderr, "%s", encs[i].name);
@@ -502,7 +513,7 @@ xv_init(struct video *vid)
resize_window(vid, 0);
- x->xv_image = XvCreateImage(x->dpy, x->port, encs[vid->enc].xv_id,
+ x->xv_image = XvCreateImage(x->dpy, x->port, encs[vid->enc_out].id,
vid->frame_buffer, vid->width, vid->height);
return 1;
@@ -780,36 +791,20 @@ dev_get_encs(struct video *vid)
fmtdesc.index = 0;
fmtdesc.type = d->buf_type;
while (ioctl(d->fd, VIDIOC_ENUM_FMT, &fmtdesc) >= 0) {
- if (fmtdesc.pixelformat == V4L2_PIX_FMT_YUYV) {
- i = find_enc("yuy2");
- if (i < ENC_LAST)
- encs[i].dev_id = fmtdesc.pixelformat;
- }
- if (fmtdesc.pixelformat == V4L2_PIX_FMT_UYVY) {
- i = find_enc("uyvy");
- if (i < ENC_LAST)
- encs[i].dev_id = fmtdesc.pixelformat;
+ i = find_enc_by_id(fmtdesc.pixelformat);
+ if (i < ENC_LAST) {
+ encs[i].flags |= SW_DEV;
}
fmtdesc.index++;
}
for (i = 0; encs[i].name; i++) {
- if (encs[i].dev_id != -1)
+ if (encs[i].flags & SW_DEV)
break;
}
if (i >= ENC_LAST) {
warnx("%s has no usable YUV encodings", d->path);
return 0;
}
- if (encs[ENC_YUY2].dev_id == -1 &&
- encs[ENC_UYVY].dev_id != -1) {
- encs[ENC_YUY2].dev_id = encs[ENC_UYVY].dev_id;
- encs[ENC_YUY2].flags |= SW_DEV;
- }
- if (encs[ENC_UYVY].dev_id == -1 &&
- encs[ENC_YUY2].dev_id != -1) {
- encs[ENC_UYVY].dev_id = encs[ENC_YUY2].dev_id;
- encs[ENC_UYVY].flags |= SW_DEV;
- }
return 1;
}
@@ -824,7 +819,7 @@ dev_get_sizes(struct video *vid)
nsizes = 0;
fsize.index = 0;
- fsize.pixel_format = encs[vid->enc].dev_id;
+ fsize.pixel_format = encs[vid->enc_in].id;
while (ioctl(d->fd, VIDIOC_ENUM_FRAMESIZES, &fsize) == 0) {
switch (fsize.type) {
case V4L2_FRMSIZE_TYPE_DISCRETE:
@@ -915,7 +910,7 @@ dev_get_rates(struct video *vid)
for (i = 0; i < d->nsizes; i++) {
bzero(&ival, sizeof(ival));
- ival.pixel_format = encs[vid->enc].dev_id;
+ ival.pixel_format = encs[vid->enc_in].id;
ival.width = d->sizes[i].w;
ival.height = d->sizes[i].h;
ival.index = 0;
@@ -1072,7 +1067,7 @@ dev_dump_info(struct video *vid)
fprintf(stderr, " encodings: ");
for (i = 0, j = 0; i < ENC_LAST; i++) {
- if (encs[i].dev_id != -1 && !(encs[i].flags & SW_DEV)) {
+ if (encs[i].flags & SW_DEV) {
if (j)
fprintf(stderr, ", ");
fprintf(stderr, "%s", encs[i].name);
@@ -1136,7 +1131,7 @@ dev_init(struct video *vid)
fmt.type = d->buf_type;
fmt.fmt.pix.width = vid->width;
fmt.fmt.pix.height = vid->height;
- fmt.fmt.pix.pixelformat = encs[vid->enc].dev_id;
+ fmt.fmt.pix.pixelformat = encs[vid->enc_in].id;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
if (ioctl(d->fd, VIDIOC_S_FMT, &fmt) < 0) {
warn("VIDIOC_S_FMT");
@@ -1290,57 +1285,67 @@ choose_enc(struct video *vid)
{
int i;
- if (vid->enc < 0) {
- for (i = 0; vid->enc < 0 && i < ENC_LAST; i++) {
- if ((vid->mode & M_IN_DEV) && (vid->mode & M_OUT_XV)) {
- if (encs[i].dev_id != -1 &&
- encs[i].xv_id != -1 &&
- (encs[i].flags & SW_MASK) == 0)
- vid->enc = i;
- } else if (vid->mode & M_IN_DEV) {
- if (encs[i].dev_id != -1 &&
- (encs[i].flags & SW_MASK) == 0)
- vid->enc = i;
- } else if (vid->mode & M_OUT_XV) {
- if (encs[i].xv_id != -1 &&
- (encs[i].flags & SW_MASK) == 0)
- vid->enc = i;
- }
- }
- for (i = 0; vid->enc < 0 && i < ENC_LAST; i++) {
- if ((vid->mode & M_IN_DEV) && (vid->mode & M_OUT_XV)) {
- if (encs[i].dev_id != -1 &&
- encs[i].xv_id != -1)
- vid->enc = i;
- } else if (vid->mode & M_IN_DEV) {
- if (encs[i].dev_id != -1)
- vid->enc = i;
- } else if (vid->mode & M_OUT_XV) {
- if (encs[i].xv_id != -1)
- vid->enc = i;
+ if (vid->enc_in < 0) {
+ for (i = 0; vid->enc_in < 0 && i < ENC_LAST; i++) {
+ if (encs[i].flags & SW_DEV)
+ vid->enc_in = i;
+ }
+ }
+
+ if (vid->enc_out < 0) {
+ for (i = 0; vid->enc_out < 0 && i < ENC_LAST; i++) {
+ if (encs[i].flags & SW_XV)
+ vid->enc_out = i;
+ }
+ }
+
+ if ((vid->mode & M_IN_DEV) && (vid->mode & M_OUT_XV)) {
+ for (i = 0; vid->enc_in < 0 && i < ENC_LAST; i++) {
+ if ((encs[i].flags & SW_DEV) &&
+ (encs[i].flags & SW_XV)) {
+ vid->enc_in = i;
+ vid->enc_out = i;
}
}
}
- if (vid->enc < 0) {
+
+ if (vid->enc_in < 0 && vid->mode & M_IN_FILE)
+ vid->enc_in = ENC_YUY2;
+
+ if (vid->mode & M_OUT_XV && vid->enc_in != vid->enc_out) {
+ /* check if conversion is possible */
+ int enc_in = (vid->enc_in == ENC_YUYV) ? ENC_YUY2 : vid->enc_in;
+ int enc_out = (vid->enc_out == ENC_YUYV) ? ENC_YUY2 :
vid->enc_out;
+
+ if (enc_in == enc_out)
+ vid->conv_type = 0; /* same pixelformat, different id */
+ else if ((enc_in == ENC_YUY2 && enc_out == ENC_UYVY) ||
+ (enc_in == ENC_UYVY && enc_out == ENC_YUY2))
+ vid->conv_type = CONV_SWAP;
+ else if (enc_in == ENC_YUY2 && enc_out == ENC_YV12)
+ vid->conv_type = CONV_YV12;
+ else {
+ warn("can't convert from %s to %s",
+ encs[vid->enc_in].name, encs[vid->enc_out].name);
+ return 0;
+ }
+ }
+
+ if (vid->enc_in < 0) {
warnx("could not find a usable encoding");
return 0;
}
- if ((vid->mode & M_IN_DEV) && encs[vid->enc].dev_id == -1) {
+ if ((vid->mode & M_IN_DEV) && !(encs[vid->enc_in].flags & SW_DEV)) {
warnx("device %s can't supply %s", vid->dev.path,
- encs[vid->enc].name);
+ encs[vid->enc_in].name);
return 0;
}
- if ((vid->mode & M_OUT_XV) && encs[vid->enc].xv_id == -1) {
+ if ((vid->mode & M_OUT_XV) && !(encs[vid->enc_out].flags & SW_XV)) {
warnx("Xv adaptor %d can't display %s",
vid->xdsp.adaps[vid->xdsp.cur_adap].adap_index,
- encs[vid->enc].name);
+ encs[vid->enc_out].name);
return 0;
}
- if (((vid->mode & M_IN_DEV) &&
- (encs[vid->enc].flags & SW_MASK) == SW_DEV) ||
- ((vid->mode & M_OUT_XV) &&
- (encs[vid->enc].flags & SW_MASK) == SW_XV))
- vid->conv_type = CONV_SWAP;
return 1;
}
@@ -1469,14 +1474,14 @@ setup(struct video *vid)
if (!parse_size(vid) || !choose_size(vid))
return 0;
- vid->bpf = vid->width * vid->height * encs[vid->enc].bpp / NBBY;
+ vid->bpf = (vid->width * vid->height * encs[vid->enc_in].bpp) / NBBY;
if (vid->verbose > 0) {
if (vid->mode & M_IN_DEV)
dev_dump_info(vid);
if (vid->mode & M_OUT_XV)
xv_dump_info(vid);
- fprintf(stderr, "using %s encoding\n", encs[vid->enc].name);
+ fprintf(stderr, "using %s encoding\n", encs[vid->enc_in].name);
fprintf(stderr, "using frame size %dx%d (%d bytes)\n",
vid->width, vid->height, vid->bpf);
if (vid->fps)
@@ -1491,12 +1496,7 @@ setup(struct video *vid)
}
if (vid->conv_type) {
- if (vid->conv_type == CONV_SWAP) {
- vid->conv_bufsz = vid->bpf;
- } else {
- warnx("invalid conversion type");
- return 0;
- }
+ vid->conv_bufsz = (vid->width * vid->height *
encs[vid->enc_out].bpp) / NBBY;
if ((vid->conv_buffer = calloc(1, vid->conv_bufsz)) == NULL) {
warn("conv_buffer");
return 0;
@@ -1539,8 +1539,9 @@ ioctl_input(struct video *vid)
}
/* copy frame buffer */
- if (buf.bytesused > vid->bpf)
+ if (buf.bytesused > vid->bpf) {
return 0;
+ }
memcpy(vid->frame_buffer, vid->mmap_buffer[buf.index], buf.bytesused);
/* requeue buffer */
@@ -1632,6 +1633,29 @@ got_shutdown(int s)
shutdown = 1;
}
+int yuy2_to_yv12(uint8_t *src, uint8_t *dst, int width, int height) {
+ int i, j;
+ int iy = 0, iu, h1, h2, c;
+ int bpf = width * height * 2;
+
+ for (i = 0; i < bpf; i+= 2)
+ dst[iy++] = src[i]; //Y component
+
+ iu = iy + bpf / 8;
+ for (i = 0; i < height / 2; i++) { //row
+ c = i * width * 4;
+ for (j = 0; j < width / 2; j++) { //col
+ h1 = c + j * 4 + 1;
+ h2 = h1 + width * 2;
+ dst[iu++] = (src[h1] + src[h2]) / 2;
+ h1+= 2; h2+= 2;
+ dst[iy++] = (src[h1] + src[h2]) / 2;
+ }
+ }
+
+ return 0;
+}
+
int
stream(struct video *vid)
{
@@ -1721,7 +1745,8 @@ stream(struct video *vid)
src = vid->frame_buffer;
if (vid->conv_type == CONV_SWAP) {
swab(src, vid->conv_buffer, vid->bpf);
- src = vid->conv_buffer;
+ } else if (vid->conv_type == CONV_YV12) {
+ yuy2_to_yv12(src, vid->conv_buffer, vid->width,
vid->height);
}
if ((vid->mode & M_OUT_FILE) && wout) {
@@ -1743,6 +1768,7 @@ stream(struct video *vid)
}
}
if (vid->mode & M_OUT_XV) {
+ src = (vid->conv_type) ? vid->conv_buffer :
vid->frame_buffer;
x->xv_image->data = src;
if (x->resized) {
x->resized = 0;
@@ -1862,7 +1888,8 @@ main(int argc, char *argv[])
x->cur_adap = -1;
vid.dev.fd = vid.iofile_fd = -1;
vid.mode = M_IN_DEV | M_OUT_XV;
- vid.enc = -1;
+ vid.enc_in = -1;
+ vid.enc_out = -1;
vid.mmap_on = 1; /* mmap method is default */
wout = 1;
@@ -1876,8 +1903,8 @@ main(int argc, char *argv[])
}
break;
case 'e':
- vid.enc = find_enc(optarg);
- if (vid.enc >= ENC_LAST) {
+ vid.enc_in = find_enc(optarg);
+ if (vid.enc_in >= ENC_LAST) {
warnx("encoding '%s' is invalid", optarg);
errs++;
}