1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
/*
* 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 <ecc@cmu.edu>
* Released under the GNU General Public License
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
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;
}
|