Hi Martin,
Thanks for your feedback!
On Wed, Jan 23, 2019 at 12:54:51PM -0200, Martin Pieuchot wrote:
> Hello Raphael,
>
> On 16/01/19(Wed) 12:41, Raphael Graf wrote:
> > 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
>
> Did you test all of them?
Yes, I have tested all encodings with these two devices:
uvideo0 at uhub0 port 6 configuration 1 interface 0
"Chicony Electronics Co., Ltd. product 0x480c" rev 2.00/31.34 addr 2
uvideo0 at uhub1 port 8 configuration 1 interface 0 "SC20A38485AA4629RX
Integrated Camera" rev 2.00/0.21 addr 4
>
> > 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)
>
> What about fixing the manpage then? ;)
The new diff below contains the changes to 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)
>
> Nice work, some comments below.
>
> Make sure the code you're writing follows style(9) :o)
>
> > 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')
>
> Why do we need this define and not the others? Aren't we missing an
> include?
Actually, I don't know why this define is missing in videoio.h.
This YUY2 format (id:0x32595559) is reported by Xv.
The uvideo(4) driver reports YUYV which has a define in videoio.h.
(YUY2 and YUYV are equivalent)
In the new diff, I have changed the variable names enc_in/enc_out to enc/enc_xv
as the second 'enc' is only used for Xv output.
I also fixed the style(9) issues and added a CONV_NONE define.
Note that I'm not sure if the algorithm used in the yuy2_to_yv12 function is
correct in respect to color theory..
(It is a conversion from 16 to 12 bits/pixel)
Sorry, it is still a big diff, feedback is welcome.
Index: video.1
===================================================================
RCS file: /cvs/xenocara/app/video/video.1,v
retrieving revision 1.13
diff -u -p -u -p -r1.13 video.1
--- video.1 4 Jun 2016 07:44:32 -0000 1.13
+++ video.1 25 Jan 2019 09:51:12 -0000
@@ -84,18 +84,19 @@ The default is 0, the first adaptor repo
.It Fl e Ar encoding
Lowercase FOURCC name of video encoding to use.
Valid arguments are
+.Ql yuyv ,
+.Ql yuy2 ,
.Ql uyvy
and
-.Ql yuy2 .
+.Ql yv12 .
The default is
-.Ql yuy2
-unless
-.Ar file
-is being used and only supports
-.Ql uyvy ,
-in which case
-.Ql uyvy
-will be used by default.
+.Ql yuyv .
+If reading from a
+.Xr video 4
+device and
+.Ql yuyv
+is not supported, the default is
+.Ql uyvy .
.It Fl f Ar file
.Xr video 4
device from which frames will be read.
@@ -338,6 +339,6 @@ Note that with the first three commands,
does not support 640x480 pixels sized frames, the largest frame size
smaller than 640x480 will be used, and if
.Ar /dev/video
-does not support yuy2 encoding, uyvy will be used.
+does not support yuyv encoding, uyvy will be used.
.Sh SEE ALSO
.Xr video 4
Index: video.c
===================================================================
RCS file: /cvs/xenocara/app/video/video.c,v
retrieving revision 1.27
diff -u -p -u -p -r1.27 video.c
--- video.c 22 Jan 2019 20:02:40 -0000 1.27
+++ video.c 25 Jan 2019 09:51:12 -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 {
@@ -162,9 +167,12 @@ struct video {
char iofile[FILENAME_MAX];
int iofile_fd;
char *sz_str;
+#define CONV_NONE 0x0
#define CONV_SWAP 0x1
+#define CONV_YV12 0x2
int conv_type;
int enc;
+ int enc_xv;
int full_screen;
int net_wm;
int width;
@@ -216,6 +224,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 +251,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;
@@ -250,8 +270,7 @@ xv_get_info(struct video *vid)
XvEncodingInfo *xv_encs;
struct xv_adap *adap;
unsigned int nenc, p;
- int num_xvformats, nadaps, i, j, ret;
- char fmtName[5];
+ int num_xvformats, nadaps, i, j, ret, enc;
if ((x->dpy = XOpenDisplay(NULL)) == NULL) {
warnx("cannot open display %s", XDisplayName(NULL));
@@ -312,16 +331,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;
+ 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 +381,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_xv].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_xv].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_xv].id) {
x->cur_adap = i;
break;
}
@@ -445,7 +457,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 +514,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_xv].id,
vid->frame_buffer, vid->width, vid->height);
return 1;
@@ -780,36 +792,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 +820,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].id;
while (ioctl(d->fd, VIDIOC_ENUM_FRAMESIZES, &fsize) == 0) {
switch (fsize.type) {
case V4L2_FRMSIZE_TYPE_DISCRETE:
@@ -915,7 +911,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].id;
ival.width = d->sizes[i].w;
ival.height = d->sizes[i].h;
ival.index = 0;
@@ -1072,7 +1068,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 +1132,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].id;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
if (ioctl(d->fd, VIDIOC_S_FMT, &fmt) < 0) {
warn("VIDIOC_S_FMT");
@@ -1289,58 +1285,64 @@ int
choose_enc(struct video *vid)
{
int i;
+ int enc, enc_xv;
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;
- }
+ if (encs[i].flags & SW_DEV)
+ vid->enc = i;
+ }
+ }
+ if (vid->enc_xv < 0) {
+ for (i = 0; vid->enc_xv < 0 && i < ENC_LAST; i++) {
+ if (encs[i].flags & SW_XV)
+ vid->enc_xv = i;
}
+ }
+ if ((vid->mode & M_IN_DEV) && (vid->mode & M_OUT_XV)) {
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 ((encs[i].flags & SW_DEV) &&
+ (encs[i].flags & SW_XV)) {
+ vid->enc = i;
+ vid->enc_xv = i;
}
}
}
- if (vid->enc < 0) {
- warnx("could not find a usable encoding");
- return 0;
- }
- if ((vid->mode & M_IN_DEV) && encs[vid->enc].dev_id == -1) {
+ if (vid->enc < 0 && vid->mode & M_IN_FILE)
+ vid->enc = ENC_YUYV;
+ if ((vid->mode & M_IN_DEV) && !(encs[vid->enc].flags & SW_DEV)) {
warnx("device %s can't supply %s", vid->dev.path,
encs[vid->enc].name);
return 0;
}
- if ((vid->mode & M_OUT_XV) && encs[vid->enc].xv_id == -1) {
+ if (vid->mode & M_OUT_XV && vid->enc != vid->enc_xv) {
+ /* check if conversion is possible */
+ enc = (vid->enc == ENC_YUYV) ? ENC_YUY2 : vid->enc;
+ enc_xv = (vid->enc_xv == ENC_YUYV) ? ENC_YUY2 : vid->enc_xv;
+
+ if (enc == enc_xv)
+ vid->conv_type = CONV_NONE;
+ else if ((enc == ENC_YUY2 && enc_xv == ENC_UYVY) ||
+ (enc == ENC_UYVY && enc_xv == ENC_YUY2))
+ vid->conv_type = CONV_SWAP;
+ else if (enc == ENC_YUY2 && enc_xv == ENC_YV12)
+ vid->conv_type = CONV_YV12;
+ else {
+ warn("conversion from %s to %s is unsupported",
+ encs[vid->enc].name, encs[vid->enc_xv].name);
+ return 0;
+ }
+ }
+ if (vid->enc < 0) {
+ warnx("could not find a usable encoding");
+ return 0;
+ }
+ if ((vid->mode & M_OUT_XV) && !(encs[vid->enc_xv].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_xv].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;
}
@@ -1491,12 +1493,8 @@ 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_xv].bpp) / NBBY;
if ((vid->conv_buffer = calloc(1, vid->conv_bufsz)) == NULL) {
warn("conv_buffer");
return 0;
@@ -1633,6 +1631,34 @@ got_shutdown(int s)
}
int
+yuy2_to_yv12(uint8_t *src, uint8_t *dst, int width, int height)
+{
+ int row, col, c, o;
+ int iy = 0;
+ int iu = width * height;
+ int iv = iu + iu / 4;
+
+ for (row = 0; row < height * 2; row += 2) {
+ c = row * width;
+ for (col = 0; col < width * 2 ; col += 4) {
+ o = c + col;
+ dst[iy++] = src[o];
+ dst[iy++] = src[o + 2];
+ if ((row & 0x03) == 0) {
+ o++;
+ dst[iv++] = (src[o] + src[o + width * 2]) / 2;
+ o += 2;
+ dst[iu++] = (src[o] + src[o + width * 2]) / 2;
+ if (o + width * 2 >= iu*2)
+ printf("hello\n");
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
stream(struct video *vid)
{
struct xdsp *x = &vid->xdsp;
@@ -1721,7 +1747,9 @@ 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 +1771,8 @@ 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;
@@ -1863,6 +1893,7 @@ main(int argc, char *argv[])
vid.dev.fd = vid.iofile_fd = -1;
vid.mode = M_IN_DEV | M_OUT_XV;
vid.enc = -1;
+ vid.enc_xv = -1;
vid.mmap_on = 1; /* mmap method is default */
wout = 1;