00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "avdevice.h"
00023
00024 #undef __STRICT_ANSI__ //workaround due to broken kernel headers
00025 #include "config.h"
00026 #include "libavutil/rational.h"
00027 #include "libavutil/imgutils.h"
00028 #include "libavutil/log.h"
00029 #include "libavutil/opt.h"
00030 #include "libavformat/internal.h"
00031 #include "libavcodec/dsputil.h"
00032 #include <unistd.h>
00033 #include <fcntl.h>
00034 #include <sys/ioctl.h>
00035 #include <sys/mman.h>
00036 #include <sys/time.h>
00037 #define _LINUX_TIME_H 1
00038 #include <linux/videodev.h>
00039 #include <time.h>
00040 #include "avdevice.h"
00041
00042 typedef struct {
00043 AVClass *class;
00044 int fd;
00045 int frame_format;
00046 int use_mmap;
00047 AVRational time_base;
00048 int64_t time_frame;
00049 int frame_size;
00050 struct video_capability video_cap;
00051 struct video_audio audio_saved;
00052 struct video_window video_win;
00053 uint8_t *video_buf;
00054 struct video_mbuf gb_buffers;
00055 struct video_mmap gb_buf;
00056 int gb_frame;
00057 int standard;
00058 } VideoData;
00059
00060 static const struct {
00061 int palette;
00062 int depth;
00063 enum PixelFormat pix_fmt;
00064 } video_formats [] = {
00065 {.palette = VIDEO_PALETTE_YUV420P, .depth = 12, .pix_fmt = PIX_FMT_YUV420P },
00066 {.palette = VIDEO_PALETTE_YUV422, .depth = 16, .pix_fmt = PIX_FMT_YUYV422 },
00067 {.palette = VIDEO_PALETTE_UYVY, .depth = 16, .pix_fmt = PIX_FMT_UYVY422 },
00068 {.palette = VIDEO_PALETTE_YUYV, .depth = 16, .pix_fmt = PIX_FMT_YUYV422 },
00069
00070 {.palette = VIDEO_PALETTE_RGB24, .depth = 24, .pix_fmt = PIX_FMT_BGR24 },
00071 {.palette = VIDEO_PALETTE_RGB565, .depth = 16, .pix_fmt = PIX_FMT_BGR565 },
00072 {.palette = VIDEO_PALETTE_GREY, .depth = 8, .pix_fmt = PIX_FMT_GRAY8 },
00073 };
00074
00075
00076 static int grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
00077 {
00078 VideoData *s = s1->priv_data;
00079 AVStream *st;
00080 int video_fd;
00081 int desired_palette, desired_depth;
00082 struct video_tuner tuner;
00083 struct video_audio audio;
00084 struct video_picture pict;
00085 int j;
00086 int vformat_num = FF_ARRAY_ELEMS(video_formats);
00087
00088 av_log(s1, AV_LOG_WARNING, "V4L input device is deprecated and will be removed in the next release.");
00089
00090 if (ap->time_base.den <= 0) {
00091 av_log(s1, AV_LOG_ERROR, "Wrong time base (%d)\n", ap->time_base.den);
00092 return -1;
00093 }
00094 s->time_base = ap->time_base;
00095
00096 s->video_win.width = ap->width;
00097 s->video_win.height = ap->height;
00098
00099 st = avformat_new_stream(s1, NULL);
00100 if (!st)
00101 return AVERROR(ENOMEM);
00102 avpriv_set_pts_info(st, 64, 1, 1000000);
00103
00104 video_fd = open(s1->filename, O_RDWR);
00105 if (video_fd < 0) {
00106 av_log(s1, AV_LOG_ERROR, "%s: %s\n", s1->filename, strerror(errno));
00107 goto fail;
00108 }
00109
00110 if (ioctl(video_fd, VIDIOCGCAP, &s->video_cap) < 0) {
00111 av_log(s1, AV_LOG_ERROR, "VIDIOCGCAP: %s\n", strerror(errno));
00112 goto fail;
00113 }
00114
00115 if (!(s->video_cap.type & VID_TYPE_CAPTURE)) {
00116 av_log(s1, AV_LOG_ERROR, "Fatal: grab device does not handle capture\n");
00117 goto fail;
00118 }
00119
00120
00121 if (s->video_win.width <= 0 || s->video_win.height <= 0) {
00122 if (ioctl(video_fd, VIDIOCGWIN, &s->video_win, sizeof(s->video_win)) < 0) {
00123 av_log(s1, AV_LOG_ERROR, "VIDIOCGWIN: %s\n", strerror(errno));
00124 goto fail;
00125 }
00126 }
00127
00128 if(av_image_check_size(s->video_win.width, s->video_win.height, 0, s1) < 0)
00129 return -1;
00130
00131 desired_palette = -1;
00132 desired_depth = -1;
00133 for (j = 0; j < vformat_num; j++) {
00134 if (ap->pix_fmt == video_formats[j].pix_fmt) {
00135 desired_palette = video_formats[j].palette;
00136 desired_depth = video_formats[j].depth;
00137 break;
00138 }
00139 }
00140
00141
00142 if (!ioctl(video_fd, VIDIOCGTUNER, &tuner)) {
00143 tuner.mode = s->standard;
00144 ioctl(video_fd, VIDIOCSTUNER, &tuner);
00145 }
00146
00147
00148 audio.audio = 0;
00149 ioctl(video_fd, VIDIOCGAUDIO, &audio);
00150 memcpy(&s->audio_saved, &audio, sizeof(audio));
00151 audio.flags &= ~VIDEO_AUDIO_MUTE;
00152 ioctl(video_fd, VIDIOCSAUDIO, &audio);
00153
00154 ioctl(video_fd, VIDIOCGPICT, &pict);
00155 av_dlog(s1, "v4l: colour=%d hue=%d brightness=%d constrast=%d whiteness=%d\n",
00156 pict.colour, pict.hue, pict.brightness, pict.contrast, pict.whiteness);
00157
00158 pict.palette = desired_palette;
00159 pict.depth= desired_depth;
00160 if (desired_palette == -1 || ioctl(video_fd, VIDIOCSPICT, &pict) < 0) {
00161 for (j = 0; j < vformat_num; j++) {
00162 pict.palette = video_formats[j].palette;
00163 pict.depth = video_formats[j].depth;
00164 if (-1 != ioctl(video_fd, VIDIOCSPICT, &pict))
00165 break;
00166 }
00167 if (j >= vformat_num)
00168 goto fail1;
00169 }
00170
00171 if (ioctl(video_fd, VIDIOCGMBUF, &s->gb_buffers) < 0) {
00172
00173 int val;
00174
00175 s->video_win.x = 0;
00176 s->video_win.y = 0;
00177 s->video_win.chromakey = -1;
00178 s->video_win.flags = 0;
00179
00180 if (ioctl(video_fd, VIDIOCSWIN, s->video_win) < 0) {
00181 av_log(s1, AV_LOG_ERROR, "VIDIOCSWIN: %s\n", strerror(errno));
00182 goto fail;
00183 }
00184
00185 s->frame_format = pict.palette;
00186
00187 val = 1;
00188 if (ioctl(video_fd, VIDIOCCAPTURE, &val) < 0) {
00189 av_log(s1, AV_LOG_ERROR, "VIDIOCCAPTURE: %s\n", strerror(errno));
00190 goto fail;
00191 }
00192
00193 s->time_frame = av_gettime() * s->time_base.den / s->time_base.num;
00194 s->use_mmap = 0;
00195 } else {
00196 s->video_buf = mmap(0, s->gb_buffers.size, PROT_READ|PROT_WRITE, MAP_SHARED, video_fd, 0);
00197 if ((unsigned char*)-1 == s->video_buf) {
00198 s->video_buf = mmap(0, s->gb_buffers.size, PROT_READ|PROT_WRITE, MAP_PRIVATE, video_fd, 0);
00199 if ((unsigned char*)-1 == s->video_buf) {
00200 av_log(s1, AV_LOG_ERROR, "mmap: %s\n", strerror(errno));
00201 goto fail;
00202 }
00203 }
00204 s->gb_frame = 0;
00205 s->time_frame = av_gettime() * s->time_base.den / s->time_base.num;
00206
00207
00208 s->gb_buf.frame = s->gb_frame % s->gb_buffers.frames;
00209 s->gb_buf.height = s->video_win.height;
00210 s->gb_buf.width = s->video_win.width;
00211 s->gb_buf.format = pict.palette;
00212
00213 if (ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf) < 0) {
00214 if (errno != EAGAIN) {
00215 fail1:
00216 av_log(s1, AV_LOG_ERROR, "VIDIOCMCAPTURE: %s\n", strerror(errno));
00217 } else {
00218 av_log(s1, AV_LOG_ERROR, "Fatal: grab device does not receive any video signal\n");
00219 }
00220 goto fail;
00221 }
00222 for (j = 1; j < s->gb_buffers.frames; j++) {
00223 s->gb_buf.frame = j;
00224 ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf);
00225 }
00226 s->frame_format = s->gb_buf.format;
00227 s->use_mmap = 1;
00228 }
00229
00230 for (j = 0; j < vformat_num; j++) {
00231 if (s->frame_format == video_formats[j].palette) {
00232 s->frame_size = s->video_win.width * s->video_win.height * video_formats[j].depth / 8;
00233 st->codec->pix_fmt = video_formats[j].pix_fmt;
00234 break;
00235 }
00236 }
00237
00238 if (j >= vformat_num)
00239 goto fail;
00240
00241 s->fd = video_fd;
00242
00243 st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00244 st->codec->codec_id = CODEC_ID_RAWVIDEO;
00245 st->codec->width = s->video_win.width;
00246 st->codec->height = s->video_win.height;
00247 st->codec->time_base = s->time_base;
00248 st->codec->bit_rate = s->frame_size * 1/av_q2d(st->codec->time_base) * 8;
00249
00250 return 0;
00251 fail:
00252 if (video_fd >= 0)
00253 close(video_fd);
00254 return AVERROR(EIO);
00255 }
00256
00257 static int v4l_mm_read_picture(VideoData *s, uint8_t *buf)
00258 {
00259 uint8_t *ptr;
00260
00261 while (ioctl(s->fd, VIDIOCSYNC, &s->gb_frame) < 0 &&
00262 (errno == EAGAIN || errno == EINTR));
00263
00264 ptr = s->video_buf + s->gb_buffers.offsets[s->gb_frame];
00265 memcpy(buf, ptr, s->frame_size);
00266
00267
00268 s->gb_buf.frame = s->gb_frame;
00269 if (ioctl(s->fd, VIDIOCMCAPTURE, &s->gb_buf) < 0) {
00270 if (errno == EAGAIN)
00271 av_log(NULL, AV_LOG_ERROR, "Cannot Sync\n");
00272 else
00273 av_log(NULL, AV_LOG_ERROR, "VIDIOCMCAPTURE: %s\n", strerror(errno));
00274 return AVERROR(EIO);
00275 }
00276
00277
00278 s->gb_frame = (s->gb_frame + 1) % s->gb_buffers.frames;
00279
00280 return s->frame_size;
00281 }
00282
00283 static int grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
00284 {
00285 VideoData *s = s1->priv_data;
00286 int64_t curtime, delay;
00287 struct timespec ts;
00288
00289
00290 s->time_frame += INT64_C(1000000);
00291
00292
00293 for(;;) {
00294 curtime = av_gettime();
00295 delay = s->time_frame * s->time_base.num / s->time_base.den - curtime;
00296 if (delay <= 0) {
00297 if (delay < INT64_C(-1000000) * s->time_base.num / s->time_base.den) {
00298
00299 s->time_frame += INT64_C(1000000);
00300 }
00301 break;
00302 }
00303 ts.tv_sec = delay / 1000000;
00304 ts.tv_nsec = (delay % 1000000) * 1000;
00305 nanosleep(&ts, NULL);
00306 }
00307
00308 if (av_new_packet(pkt, s->frame_size) < 0)
00309 return AVERROR(EIO);
00310
00311 pkt->pts = curtime;
00312
00313
00314 if (s->use_mmap) {
00315 return v4l_mm_read_picture(s, pkt->data);
00316 } else {
00317 if (read(s->fd, pkt->data, pkt->size) != pkt->size)
00318 return AVERROR(EIO);
00319 return s->frame_size;
00320 }
00321 }
00322
00323 static int grab_read_close(AVFormatContext *s1)
00324 {
00325 VideoData *s = s1->priv_data;
00326
00327 if (s->use_mmap)
00328 munmap(s->video_buf, s->gb_buffers.size);
00329
00330
00331
00332 s->audio_saved.flags |= VIDEO_AUDIO_MUTE;
00333 ioctl(s->fd, VIDIOCSAUDIO, &s->audio_saved);
00334
00335 close(s->fd);
00336 return 0;
00337 }
00338
00339 static const AVOption options[] = {
00340 { "standard", "", offsetof(VideoData, standard), AV_OPT_TYPE_INT, {.dbl = VIDEO_MODE_NTSC}, VIDEO_MODE_PAL, VIDEO_MODE_NTSC, AV_OPT_FLAG_DECODING_PARAM, "standard" },
00341 { "PAL", "", 0, AV_OPT_TYPE_CONST, {.dbl = VIDEO_MODE_PAL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "standard" },
00342 { "SECAM", "", 0, AV_OPT_TYPE_CONST, {.dbl = VIDEO_MODE_SECAM}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "standard" },
00343 { "NTSC", "", 0, AV_OPT_TYPE_CONST, {.dbl = VIDEO_MODE_NTSC}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "standard" },
00344 { NULL },
00345 };
00346
00347 static const AVClass v4l_class = {
00348 .class_name = "V4L indev",
00349 .item_name = av_default_item_name,
00350 .option = options,
00351 .version = LIBAVUTIL_VERSION_INT,
00352 };
00353
00354 AVInputFormat ff_v4l_demuxer = {
00355 .name = "video4linux,v4l",
00356 .long_name = NULL_IF_CONFIG_SMALL("Video4Linux device grab"),
00357 .priv_data_size = sizeof(VideoData),
00358 .read_header = grab_read_header,
00359 .read_packet = grab_read_packet,
00360 .read_close = grab_read_close,
00361 .flags = AVFMT_NOFILE,
00362 .priv_class = &v4l_class,
00363 };