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?
> 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? ;)
> 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?
> +
> /* 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)
What about a small diff that introduce `enc_out' and `enc_in' to
reduce the number of chunks in this one?
> 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;
Please define your variable first then do the assignment. This will
also make your lines fit in 80 chars :)
> +
> + if (enc_in == enc_out)
> + vid->conv_type = 0; /* same pixelformat, different id */
Could we use a define for no conversion? CONV_NONE? This would
implicitly document your code so you can get rid of the comment above :)
> + 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);
Is it impossible to convert or is it unsupported?
> + 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;
Please reformat this line to fit in 80 chars :)
> 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) {
This function definition should respect style(9) :)
> + 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
Please do not use C++-style comments. You could also pick better name
for your variables, if `i' represents the rows and `j' the columns then
use `row' and `col' ;) What does `c' represent? And the others?
> + 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;
Please reformat this line to fit in 80 chars ;)
> 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++;
> }
>
>