/* * Copy a YUV4MPEG stream to a v4l2 output device. * The stream is read from standard input. * The device can be specified as argument; it defaults to /dev/video0. * * Example using mplayer as a producer for the v4l2loopback driver: * * $ mkfifo /tmp/pipe * $ ./yuv4mpeg_to_v4l2 < /tmp/pipe & * $ mplayer movie.mp4 -vo yuv4mpeg:file=/tmp/pipe * * Copyright (C) 2011 Eric C. Cooper * Released under the GNU General Public License */ #include #include #include #include #include #include #include char *prog; char *device; int dev_fd; int frame_width; int frame_height; int frame_bytes; void usage(void) { fprintf(stderr, "Usage: %s [/dev/videoN]\n", prog); exit(1); } void process_args(int argc, char **argv) { prog = argv[0]; switch (argc) { case 1: device = "/dev/video0"; break; case 2: device = argv[1]; break; default: usage(); break; } } void sysfail(char *msg) { perror(msg); exit(1); } void fail(char *msg) { fprintf(stderr, "%s: %s\n", prog, msg); exit(1); } void bad_header(char *kind) { char msg[64]; sprintf(msg, "malformed %s header", kind); fail(msg); } void do_tag(char tag, char *value) { switch (tag) { case 'W': frame_width = strtoul(value, NULL, 10); break; case 'H': frame_height = strtoul(value, NULL, 10); break; } } int read_header(char *magic) { char *p, *q, *p0; size_t n; int first, done; p0 = NULL; if (getline(&p0, &n, stdin) == -1) { free(p0); return 0; } q = p = p0; first = 1; done = 0; while (!done) { while (*q != ' ' && *q != '\n') if (*q++ == '\0') bad_header(magic); done = (*q == '\n'); *q = '\0'; if (first) if (strcmp(p, magic) == 0) first = 0; else bad_header(magic); else do_tag(*p, p + 1); p = ++q; } free(p0); return 1; } void process_header(void) { if (!read_header("YUV4MPEG2")) fail("missing YUV4MPEG2 header"); frame_bytes = 3 * frame_width * frame_height / 2; if (frame_bytes == 0) fail("frame width or height is missing"); } void copy_frames(void) { char *frame; frame = malloc(frame_bytes); if (frame == NULL) fail("cannot malloc frame"); while (read_header("FRAME")) { if (fread(frame, 1, frame_bytes, stdin) != frame_bytes) { free(frame); fail("malformed frame"); } else if (write(dev_fd, frame, frame_bytes) != frame_bytes) { free(frame); sysfail("write"); } } free(frame); } #define vidioc(op, arg) \ if (ioctl(dev_fd, VIDIOC_##op, arg) == -1) \ sysfail(#op); \ else void open_video(void) { struct v4l2_format v; dev_fd = open(device, O_RDWR); if (dev_fd == -1) sysfail(device); v.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; vidioc(G_FMT, &v); v.fmt.pix.width = frame_width; v.fmt.pix.height = frame_height; v.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; v.fmt.pix.sizeimage = frame_bytes; vidioc(S_FMT, &v); } int main(int argc, char **argv) { process_args(argc, argv); process_header(); open_video(); copy_frames(); return 0; }