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