Commit | Line | Data |
---|---|---|
de6d9b64 FB |
1 | /* |
2 | * Linux video grab interface | |
19720f15 | 3 | * Copyright (c) 2000,2001 Fabrice Bellard. |
de6d9b64 | 4 | * |
19720f15 FB |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public | |
7 | * License as published by the Free Software Foundation; either | |
8 | * version 2 of the License, or (at your option) any later version. | |
de6d9b64 | 9 | * |
19720f15 | 10 | * This library is distributed in the hope that it will be useful, |
de6d9b64 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19720f15 FB |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. | |
de6d9b64 | 14 | * |
19720f15 FB |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library; if not, write to the Free Software | |
5509bffa | 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
de6d9b64 | 18 | */ |
8be1c656 | 19 | #include "avformat.h" |
de6d9b64 FB |
20 | #include <unistd.h> |
21 | #include <fcntl.h> | |
22 | #include <sys/ioctl.h> | |
23 | #include <sys/mman.h> | |
de6d9b64 | 24 | #include <sys/time.h> |
72a476c1 MN |
25 | #define _LINUX_TIME_H 1 |
26 | #include <linux/videodev.h> | |
608d0dee | 27 | #include <time.h> |
de6d9b64 FB |
28 | |
29 | typedef struct { | |
30 | int fd; | |
31 | int frame_format; /* see VIDEO_PALETTE_xxx */ | |
32 | int use_mmap; | |
33 | int width, height; | |
4972b26f | 34 | int frame_rate; |
14bea432 | 35 | int frame_rate_base; |
0c1a9eda | 36 | int64_t time_frame; |
4972b26f | 37 | int frame_size; |
8949367e FB |
38 | struct video_capability video_cap; |
39 | struct video_audio audio_saved; | |
0c1a9eda | 40 | uint8_t *video_buf; |
8949367e FB |
41 | struct video_mbuf gb_buffers; |
42 | struct video_mmap gb_buf; | |
43 | int gb_frame; | |
44 | ||
45 | /* ATI All In Wonder specific stuff */ | |
46 | /* XXX: remove and merge in libavcodec/imgconvert.c */ | |
47 | int aiw_enabled; | |
48 | int deint; | |
49 | int halfw; | |
0c1a9eda ZK |
50 | uint8_t *src_mem; |
51 | uint8_t *lum_m4_mem; | |
de6d9b64 FB |
52 | } VideoData; |
53 | ||
8949367e FB |
54 | static int aiw_init(VideoData *s); |
55 | static int aiw_read_picture(VideoData *s, uint8_t *data); | |
56 | static int aiw_close(VideoData *s); | |
de6d9b64 | 57 | |
4972b26f | 58 | static int grab_read_header(AVFormatContext *s1, AVFormatParameters *ap) |
de6d9b64 | 59 | { |
c9a65ca8 | 60 | VideoData *s = s1->priv_data; |
4972b26f | 61 | AVStream *st; |
de6d9b64 | 62 | int width, height; |
de6d9b64 | 63 | int video_fd, frame_size; |
14bea432 | 64 | int ret, frame_rate, frame_rate_base; |
4606ac8d | 65 | int desired_palette; |
e3ee3283 | 66 | struct video_tuner tuner; |
8949367e | 67 | struct video_audio audio; |
7f172339 | 68 | const char *video_device; |
7aae3168 | 69 | int j; |
4972b26f | 70 | |
c0df9d75 | 71 | if (!ap || ap->width <= 0 || ap->height <= 0 || ap->time_base.den <= 0) |
4972b26f | 72 | return -1; |
115329f1 | 73 | |
4972b26f FB |
74 | width = ap->width; |
75 | height = ap->height; | |
c0df9d75 MN |
76 | frame_rate = ap->time_base.den; |
77 | frame_rate_base = ap->time_base.num; | |
4972b26f | 78 | |
568e18b1 MN |
79 | if((unsigned)width > 32767 || (unsigned)height > 32767) |
80 | return -1; | |
115329f1 | 81 | |
c9a65ca8 FB |
82 | st = av_new_stream(s1, 0); |
83 | if (!st) | |
4972b26f | 84 | return -ENOMEM; |
0a7b514f | 85 | av_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ |
4972b26f FB |
86 | |
87 | s->width = width; | |
88 | s->height = height; | |
14bea432 MN |
89 | s->frame_rate = frame_rate; |
90 | s->frame_rate_base = frame_rate_base; | |
de6d9b64 | 91 | |
7f172339 FB |
92 | video_device = ap->device; |
93 | if (!video_device) | |
94 | video_device = "/dev/video"; | |
8aa3ee32 | 95 | video_fd = open(video_device, O_RDWR); |
de6d9b64 | 96 | if (video_fd < 0) { |
8aa3ee32 | 97 | perror(video_device); |
4972b26f | 98 | goto fail; |
de6d9b64 | 99 | } |
115329f1 | 100 | |
8949367e | 101 | if (ioctl(video_fd,VIDIOCGCAP, &s->video_cap) < 0) { |
de6d9b64 FB |
102 | perror("VIDIOCGCAP"); |
103 | goto fail; | |
104 | } | |
105 | ||
8949367e | 106 | if (!(s->video_cap.type & VID_TYPE_CAPTURE)) { |
bb270c08 | 107 | av_log(s1, AV_LOG_ERROR, "Fatal: grab device does not handle capture\n"); |
de6d9b64 FB |
108 | goto fail; |
109 | } | |
4606ac8d ZK |
110 | |
111 | desired_palette = -1; | |
01f4895c | 112 | if (st->codec->pix_fmt == PIX_FMT_YUV420P) { |
4606ac8d | 113 | desired_palette = VIDEO_PALETTE_YUV420P; |
01f4895c | 114 | } else if (st->codec->pix_fmt == PIX_FMT_YUV422) { |
4606ac8d | 115 | desired_palette = VIDEO_PALETTE_YUV422; |
01f4895c | 116 | } else if (st->codec->pix_fmt == PIX_FMT_BGR24) { |
4606ac8d | 117 | desired_palette = VIDEO_PALETTE_RGB24; |
115329f1 | 118 | } |
e3ee3283 AB |
119 | |
120 | /* set tv standard */ | |
121 | if (ap->standard && !ioctl(video_fd, VIDIOCGTUNER, &tuner)) { | |
bb270c08 DB |
122 | if (!strcasecmp(ap->standard, "pal")) |
123 | tuner.mode = VIDEO_MODE_PAL; | |
124 | else if (!strcasecmp(ap->standard, "secam")) | |
125 | tuner.mode = VIDEO_MODE_SECAM; | |
126 | else | |
127 | tuner.mode = VIDEO_MODE_NTSC; | |
128 | ioctl(video_fd, VIDIOCSTUNER, &tuner); | |
e3ee3283 | 129 | } |
115329f1 | 130 | |
de6d9b64 | 131 | /* unmute audio */ |
8949367e | 132 | audio.audio = 0; |
de6d9b64 | 133 | ioctl(video_fd, VIDIOCGAUDIO, &audio); |
8949367e | 134 | memcpy(&s->audio_saved, &audio, sizeof(audio)); |
de6d9b64 FB |
135 | audio.flags &= ~VIDEO_AUDIO_MUTE; |
136 | ioctl(video_fd, VIDIOCSAUDIO, &audio); | |
137 | ||
8949367e | 138 | ret = ioctl(video_fd,VIDIOCGMBUF,&s->gb_buffers); |
de6d9b64 FB |
139 | if (ret < 0) { |
140 | /* try to use read based access */ | |
141 | struct video_window win; | |
142 | struct video_picture pict; | |
143 | int val; | |
144 | ||
145 | win.x = 0; | |
146 | win.y = 0; | |
147 | win.width = width; | |
148 | win.height = height; | |
149 | win.chromakey = -1; | |
150 | win.flags = 0; | |
151 | ||
152 | ioctl(video_fd, VIDIOCSWIN, &win); | |
153 | ||
154 | ioctl(video_fd, VIDIOCGPICT, &pict); | |
155 | #if 0 | |
156 | printf("v4l: colour=%d hue=%d brightness=%d constrast=%d whiteness=%d\n", | |
157 | pict.colour, | |
158 | pict.hue, | |
159 | pict.brightness, | |
160 | pict.contrast, | |
161 | pict.whiteness); | |
115329f1 | 162 | #endif |
de6d9b64 | 163 | /* try to choose a suitable video format */ |
4606ac8d ZK |
164 | pict.palette = desired_palette; |
165 | if (desired_palette == -1 || (ret = ioctl(video_fd, VIDIOCSPICT, &pict)) < 0) { | |
166 | pict.palette=VIDEO_PALETTE_YUV420P; | |
de6d9b64 FB |
167 | ret = ioctl(video_fd, VIDIOCSPICT, &pict); |
168 | if (ret < 0) { | |
4606ac8d | 169 | pict.palette=VIDEO_PALETTE_YUV422; |
de6d9b64 | 170 | ret = ioctl(video_fd, VIDIOCSPICT, &pict); |
4606ac8d ZK |
171 | if (ret < 0) { |
172 | pict.palette=VIDEO_PALETTE_RGB24; | |
173 | ret = ioctl(video_fd, VIDIOCSPICT, &pict); | |
115329f1 | 174 | if (ret < 0) |
4606ac8d ZK |
175 | goto fail1; |
176 | } | |
de6d9b64 FB |
177 | } |
178 | } | |
179 | ||
180 | s->frame_format = pict.palette; | |
181 | ||
182 | val = 1; | |
183 | ioctl(video_fd, VIDIOCCAPTURE, &val); | |
184 | ||
3834be58 | 185 | s->time_frame = av_gettime() * s->frame_rate / s->frame_rate_base; |
de6d9b64 | 186 | s->use_mmap = 0; |
115329f1 | 187 | |
8949367e FB |
188 | /* ATI All In Wonder automatic activation */ |
189 | if (!strcmp(s->video_cap.name, "Km")) { | |
190 | if (aiw_init(s) < 0) | |
191 | goto fail; | |
192 | s->aiw_enabled = 1; | |
193 | /* force 420P format because convertion from YUV422 to YUV420P | |
194 | is done in this driver (ugly) */ | |
195 | s->frame_format = VIDEO_PALETTE_YUV420P; | |
196 | } | |
de6d9b64 | 197 | } else { |
8949367e FB |
198 | s->video_buf = mmap(0,s->gb_buffers.size,PROT_READ|PROT_WRITE,MAP_SHARED,video_fd,0); |
199 | if ((unsigned char*)-1 == s->video_buf) { | |
de6d9b64 FB |
200 | perror("mmap"); |
201 | goto fail; | |
202 | } | |
8949367e | 203 | s->gb_frame = 0; |
3834be58 | 204 | s->time_frame = av_gettime() * s->frame_rate / s->frame_rate_base; |
115329f1 | 205 | |
de6d9b64 | 206 | /* start to grab the first frame */ |
8949367e FB |
207 | s->gb_buf.frame = s->gb_frame % s->gb_buffers.frames; |
208 | s->gb_buf.height = height; | |
209 | s->gb_buf.width = width; | |
210 | s->gb_buf.format = desired_palette; | |
4606ac8d | 211 | |
8949367e FB |
212 | if (desired_palette == -1 || (ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf)) < 0) { |
213 | s->gb_buf.format = VIDEO_PALETTE_YUV420P; | |
115329f1 | 214 | |
8949367e | 215 | ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf); |
4606ac8d ZK |
216 | if (ret < 0 && errno != EAGAIN) { |
217 | /* try YUV422 */ | |
8949367e | 218 | s->gb_buf.format = VIDEO_PALETTE_YUV422; |
115329f1 | 219 | |
8949367e | 220 | ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf); |
4606ac8d ZK |
221 | if (ret < 0 && errno != EAGAIN) { |
222 | /* try RGB24 */ | |
8949367e FB |
223 | s->gb_buf.format = VIDEO_PALETTE_RGB24; |
224 | ret = ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf); | |
4606ac8d ZK |
225 | } |
226 | } | |
de6d9b64 FB |
227 | } |
228 | if (ret < 0) { | |
229 | if (errno != EAGAIN) { | |
230 | fail1: | |
bc874dae | 231 | av_log(s1, AV_LOG_ERROR, "Fatal: grab device does not support suitable format\n"); |
de6d9b64 | 232 | } else { |
bc874dae | 233 | av_log(s1, AV_LOG_ERROR,"Fatal: grab device does not receive any video signal\n"); |
de6d9b64 FB |
234 | } |
235 | goto fail; | |
236 | } | |
bb270c08 DB |
237 | for (j = 1; j < s->gb_buffers.frames; j++) { |
238 | s->gb_buf.frame = j; | |
239 | ioctl(video_fd, VIDIOCMCAPTURE, &s->gb_buf); | |
240 | } | |
8949367e | 241 | s->frame_format = s->gb_buf.format; |
de6d9b64 FB |
242 | s->use_mmap = 1; |
243 | } | |
244 | ||
245 | switch(s->frame_format) { | |
246 | case VIDEO_PALETTE_YUV420P: | |
247 | frame_size = (width * height * 3) / 2; | |
01f4895c | 248 | st->codec->pix_fmt = PIX_FMT_YUV420P; |
de6d9b64 FB |
249 | break; |
250 | case VIDEO_PALETTE_YUV422: | |
251 | frame_size = width * height * 2; | |
01f4895c | 252 | st->codec->pix_fmt = PIX_FMT_YUV422; |
de6d9b64 FB |
253 | break; |
254 | case VIDEO_PALETTE_RGB24: | |
255 | frame_size = width * height * 3; | |
01f4895c | 256 | st->codec->pix_fmt = PIX_FMT_BGR24; /* NOTE: v4l uses BGR24, not RGB24 ! */ |
de6d9b64 FB |
257 | break; |
258 | default: | |
259 | goto fail; | |
260 | } | |
261 | s->fd = video_fd; | |
4972b26f | 262 | s->frame_size = frame_size; |
115329f1 | 263 | |
01f4895c MN |
264 | st->codec->codec_type = CODEC_TYPE_VIDEO; |
265 | st->codec->codec_id = CODEC_ID_RAWVIDEO; | |
266 | st->codec->width = width; | |
267 | st->codec->height = height; | |
268 | st->codec->time_base.den = frame_rate; | |
269 | st->codec->time_base.num = frame_rate_base; | |
4209fad9 | 270 | st->codec->bit_rate = frame_size * 1/av_q2d(st->codec->time_base) * 8; |
45dd5c69 | 271 | |
de6d9b64 FB |
272 | return 0; |
273 | fail: | |
4972b26f FB |
274 | if (video_fd >= 0) |
275 | close(video_fd); | |
1ea4f593 | 276 | av_free(st); |
0bd586c5 | 277 | return AVERROR_IO; |
de6d9b64 FB |
278 | } |
279 | ||
0c1a9eda | 280 | static int v4l_mm_read_picture(VideoData *s, uint8_t *buf) |
de6d9b64 | 281 | { |
0c1a9eda | 282 | uint8_t *ptr; |
de6d9b64 | 283 | |
7aae3168 MN |
284 | while (ioctl(s->fd, VIDIOCSYNC, &s->gb_frame) < 0 && |
285 | (errno == EAGAIN || errno == EINTR)); | |
286 | ||
287 | ptr = s->video_buf + s->gb_buffers.offsets[s->gb_frame]; | |
288 | memcpy(buf, ptr, s->frame_size); | |
289 | ||
4606ac8d | 290 | /* Setup to capture the next frame */ |
7aae3168 | 291 | s->gb_buf.frame = s->gb_frame; |
8949367e | 292 | if (ioctl(s->fd, VIDIOCMCAPTURE, &s->gb_buf) < 0) { |
28c66901 | 293 | if (errno == EAGAIN) |
bc874dae | 294 | av_log(NULL, AV_LOG_ERROR, "Cannot Sync\n"); |
28c66901 | 295 | else |
de6d9b64 | 296 | perror("VIDIOCMCAPTURE"); |
0bd586c5 | 297 | return AVERROR_IO; |
de6d9b64 | 298 | } |
4606ac8d | 299 | |
4606ac8d | 300 | /* This is now the grabbing frame */ |
7aae3168 | 301 | s->gb_frame = (s->gb_frame + 1) % s->gb_buffers.frames; |
4606ac8d | 302 | |
4972b26f | 303 | return s->frame_size; |
de6d9b64 FB |
304 | } |
305 | ||
4972b26f | 306 | static int grab_read_packet(AVFormatContext *s1, AVPacket *pkt) |
de6d9b64 | 307 | { |
4972b26f | 308 | VideoData *s = s1->priv_data; |
0c1a9eda | 309 | int64_t curtime, delay; |
4972b26f | 310 | struct timespec ts; |
4606ac8d ZK |
311 | |
312 | /* Calculate the time of the next frame */ | |
3834be58 | 313 | s->time_frame += int64_t_C(1000000); |
de6d9b64 FB |
314 | |
315 | /* wait based on the frame rate */ | |
45dd5c69 | 316 | for(;;) { |
a11bf0bd | 317 | curtime = av_gettime(); |
3834be58 | 318 | delay = s->time_frame * s->frame_rate_base / s->frame_rate - curtime; |
4606ac8d | 319 | if (delay <= 0) { |
3834be58 | 320 | if (delay < int64_t_C(-1000000) * s->frame_rate_base / s->frame_rate) { |
4606ac8d | 321 | /* printf("grabbing is %d frames late (dropping)\n", (int) -(delay / 16666)); */ |
3834be58 | 322 | s->time_frame += int64_t_C(1000000); |
4606ac8d | 323 | } |
4972b26f | 324 | break; |
115329f1 | 325 | } |
4972b26f FB |
326 | ts.tv_sec = delay / 1000000; |
327 | ts.tv_nsec = (delay % 1000000) * 1000; | |
328 | nanosleep(&ts, NULL); | |
329 | } | |
330 | ||
331 | if (av_new_packet(pkt, s->frame_size) < 0) | |
0bd586c5 | 332 | return AVERROR_IO; |
de6d9b64 | 333 | |
0a7b514f | 334 | pkt->pts = curtime; |
45dd5c69 | 335 | |
de6d9b64 | 336 | /* read one frame */ |
8949367e FB |
337 | if (s->aiw_enabled) { |
338 | return aiw_read_picture(s, pkt->data); | |
339 | } else if (s->use_mmap) { | |
4972b26f | 340 | return v4l_mm_read_picture(s, pkt->data); |
de6d9b64 | 341 | } else { |
4972b26f | 342 | if (read(s->fd, pkt->data, pkt->size) != pkt->size) |
0bd586c5 | 343 | return AVERROR_IO; |
4972b26f | 344 | return s->frame_size; |
de6d9b64 FB |
345 | } |
346 | } | |
347 | ||
4972b26f | 348 | static int grab_read_close(AVFormatContext *s1) |
de6d9b64 | 349 | { |
4972b26f | 350 | VideoData *s = s1->priv_data; |
e61efa24 | 351 | |
8949367e FB |
352 | if (s->aiw_enabled) |
353 | aiw_close(s); | |
354 | ||
e61efa24 | 355 | if (s->use_mmap) |
8949367e | 356 | munmap(s->video_buf, s->gb_buffers.size); |
e61efa24 | 357 | |
8949367e FB |
358 | /* mute audio. we must force it because the BTTV driver does not |
359 | return its state correctly */ | |
360 | s->audio_saved.flags |= VIDEO_AUDIO_MUTE; | |
361 | ioctl(s->fd, VIDIOCSAUDIO, &s->audio_saved); | |
5a56c87c | 362 | |
de6d9b64 | 363 | close(s->fd); |
de6d9b64 FB |
364 | return 0; |
365 | } | |
366 | ||
c18a2692 | 367 | static AVInputFormat video_grab_device_format = { |
8aa3ee32 | 368 | "video4linux", |
4972b26f | 369 | "video grab", |
c9a65ca8 | 370 | sizeof(VideoData), |
4972b26f | 371 | NULL, |
4972b26f FB |
372 | grab_read_header, |
373 | grab_read_packet, | |
374 | grab_read_close, | |
bb76a117 | 375 | .flags = AVFMT_NOFILE, |
de6d9b64 | 376 | }; |
c9a65ca8 | 377 | |
8949367e FB |
378 | /* All in Wonder specific stuff */ |
379 | /* XXX: remove and merge in libavcodec/imgconvert.c */ | |
3f9bff71 | 380 | |
8949367e | 381 | static int aiw_init(VideoData *s) |
3f9bff71 | 382 | { |
3f9bff71 | 383 | int width, height; |
3f9bff71 | 384 | |
8949367e FB |
385 | width = s->width; |
386 | height = s->height; | |
3f9bff71 | 387 | |
8949367e FB |
388 | if ((width == s->video_cap.maxwidth && height == s->video_cap.maxheight) || |
389 | (width == s->video_cap.maxwidth && height == s->video_cap.maxheight*2) || | |
390 | (width == s->video_cap.maxwidth/2 && height == s->video_cap.maxheight)) { | |
115329f1 | 391 | |
8949367e FB |
392 | s->deint=0; |
393 | s->halfw=0; | |
394 | if (height == s->video_cap.maxheight*2) s->deint=1; | |
395 | if (width == s->video_cap.maxwidth/2) s->halfw=1; | |
3f9bff71 | 396 | } else { |
bc874dae MB |
397 | av_log(NULL, AV_LOG_ERROR, "\nIncorrect Grab Size Supplied - Supported Sizes Are:\n"); |
398 | av_log(NULL, AV_LOG_ERROR, " %dx%d %dx%d %dx%d\n\n", | |
8949367e FB |
399 | s->video_cap.maxwidth,s->video_cap.maxheight, |
400 | s->video_cap.maxwidth,s->video_cap.maxheight*2, | |
401 | s->video_cap.maxwidth/2,s->video_cap.maxheight); | |
3f9bff71 MN |
402 | goto fail; |
403 | } | |
404 | ||
3f9bff71 MN |
405 | if (s->halfw == 0) { |
406 | s->src_mem = av_malloc(s->width*2); | |
407 | } else { | |
408 | s->src_mem = av_malloc(s->width*4); | |
409 | } | |
410 | if (!s->src_mem) goto fail; | |
411 | ||
412 | s->lum_m4_mem = av_malloc(s->width); | |
8949367e | 413 | if (!s->lum_m4_mem) |
3f9bff71 | 414 | goto fail; |
3f9bff71 MN |
415 | return 0; |
416 | fail: | |
8949367e FB |
417 | av_freep(&s->src_mem); |
418 | av_freep(&s->lum_m4_mem); | |
419 | return -1; | |
3f9bff71 MN |
420 | } |
421 | ||
3f9bff71 | 422 | #ifdef HAVE_MMX |
6f72dcf7 | 423 | #include "libavcodec/i386/mmx.h" |
3f9bff71 MN |
424 | |
425 | #define LINE_WITH_UV \ | |
426 | movq_m2r(ptr[0],mm0); \ | |
427 | movq_m2r(ptr[8],mm1); \ | |
428 | movq_r2r(mm0, mm4); \ | |
429 | punpcklbw_r2r(mm1,mm0); \ | |
430 | punpckhbw_r2r(mm1,mm4); \ | |
431 | movq_r2r(mm0,mm5); \ | |
432 | punpcklbw_r2r(mm4,mm0); \ | |
433 | punpckhbw_r2r(mm4,mm5); \ | |
434 | movq_r2r(mm0,mm1); \ | |
435 | punpcklbw_r2r(mm5,mm1); \ | |
436 | movq_r2m(mm1,lum[0]); \ | |
437 | movq_m2r(ptr[16],mm2); \ | |
438 | movq_m2r(ptr[24],mm1); \ | |
439 | movq_r2r(mm2,mm4); \ | |
440 | punpcklbw_r2r(mm1,mm2); \ | |
441 | punpckhbw_r2r(mm1,mm4); \ | |
442 | movq_r2r(mm2,mm3); \ | |
443 | punpcklbw_r2r(mm4,mm2); \ | |
444 | punpckhbw_r2r(mm4,mm3); \ | |
445 | movq_r2r(mm2,mm1); \ | |
446 | punpcklbw_r2r(mm3,mm1); \ | |
447 | movq_r2m(mm1,lum[8]); \ | |
448 | punpckhdq_r2r(mm2,mm0); \ | |
449 | punpckhdq_r2r(mm3,mm5); \ | |
450 | movq_r2m(mm0,cb[0]); \ | |
451 | movq_r2m(mm5,cr[0]); | |
452 | ||
453 | #define LINE_NO_UV \ | |
454 | movq_m2r(ptr[0],mm0);\ | |
455 | movq_m2r(ptr[8],mm1);\ | |
456 | movq_r2r(mm0, mm4);\ | |
457 | punpcklbw_r2r(mm1,mm0); \ | |
458 | punpckhbw_r2r(mm1,mm4);\ | |
459 | movq_r2r(mm0,mm5);\ | |
460 | punpcklbw_r2r(mm4,mm0);\ | |
461 | punpckhbw_r2r(mm4,mm5);\ | |
462 | movq_r2r(mm0,mm1);\ | |
463 | punpcklbw_r2r(mm5,mm1);\ | |
464 | movq_r2m(mm1,lum[0]);\ | |
465 | movq_m2r(ptr[16],mm2);\ | |
466 | movq_m2r(ptr[24],mm1);\ | |
467 | movq_r2r(mm2,mm4);\ | |
468 | punpcklbw_r2r(mm1,mm2);\ | |
469 | punpckhbw_r2r(mm1,mm4);\ | |
470 | movq_r2r(mm2,mm3);\ | |
471 | punpcklbw_r2r(mm4,mm2);\ | |
472 | punpckhbw_r2r(mm4,mm3);\ | |
473 | movq_r2r(mm2,mm1);\ | |
474 | punpcklbw_r2r(mm3,mm1);\ | |
475 | movq_r2m(mm1,lum[8]); | |
476 | ||
477 | #define LINE_WITHUV_AVG \ | |
478 | movq_m2r(ptr[0], mm0);\ | |
479 | movq_m2r(ptr[8], mm1);\ | |
480 | movq_r2r(mm0, mm4);\ | |
481 | punpcklbw_r2r(mm1,mm0);\ | |
482 | punpckhbw_r2r(mm1,mm4);\ | |
483 | movq_r2r(mm0,mm5);\ | |
484 | punpcklbw_r2r(mm4,mm0);\ | |
485 | punpckhbw_r2r(mm4,mm5);\ | |
486 | movq_r2r(mm0,mm1);\ | |
487 | movq_r2r(mm5,mm2);\ | |
488 | punpcklbw_r2r(mm7,mm1);\ | |
489 | punpcklbw_r2r(mm7,mm2);\ | |
490 | paddw_r2r(mm6,mm1);\ | |
491 | paddw_r2r(mm2,mm1);\ | |
492 | psraw_i2r(1,mm1);\ | |
493 | packuswb_r2r(mm7,mm1);\ | |
494 | movd_r2m(mm1,lum[0]);\ | |
495 | movq_m2r(ptr[16],mm2);\ | |
496 | movq_m2r(ptr[24],mm1);\ | |
497 | movq_r2r(mm2,mm4);\ | |
498 | punpcklbw_r2r(mm1,mm2);\ | |
499 | punpckhbw_r2r(mm1,mm4);\ | |
500 | movq_r2r(mm2,mm3);\ | |
501 | punpcklbw_r2r(mm4,mm2);\ | |
502 | punpckhbw_r2r(mm4,mm3);\ | |
503 | movq_r2r(mm2,mm1);\ | |
504 | movq_r2r(mm3,mm4);\ | |
505 | punpcklbw_r2r(mm7,mm1);\ | |
506 | punpcklbw_r2r(mm7,mm4);\ | |
507 | paddw_r2r(mm6,mm1);\ | |
508 | paddw_r2r(mm4,mm1);\ | |
509 | psraw_i2r(1,mm1);\ | |
510 | packuswb_r2r(mm7,mm1);\ | |
511 | movd_r2m(mm1,lum[4]);\ | |
512 | punpckhbw_r2r(mm7,mm0);\ | |
513 | punpckhbw_r2r(mm7,mm2);\ | |
514 | paddw_r2r(mm6,mm0);\ | |
515 | paddw_r2r(mm2,mm0);\ | |
516 | psraw_i2r(1,mm0);\ | |
517 | packuswb_r2r(mm7,mm0);\ | |
518 | punpckhbw_r2r(mm7,mm5);\ | |
519 | punpckhbw_r2r(mm7,mm3);\ | |
520 | paddw_r2r(mm6,mm5);\ | |
521 | paddw_r2r(mm3,mm5);\ | |
522 | psraw_i2r(1,mm5);\ | |
523 | packuswb_r2r(mm7,mm5);\ | |
524 | movd_r2m(mm0,cb[0]);\ | |
525 | movd_r2m(mm5,cr[0]); | |
526 | ||
527 | #define LINE_NOUV_AVG \ | |
528 | movq_m2r(ptr[0],mm0);\ | |
529 | movq_m2r(ptr[8],mm1);\ | |
530 | pand_r2r(mm5,mm0);\ | |
531 | pand_r2r(mm5,mm1);\ | |
532 | pmaddwd_r2r(mm6,mm0);\ | |
533 | pmaddwd_r2r(mm6,mm1);\ | |
534 | packssdw_r2r(mm1,mm0);\ | |
535 | paddw_r2r(mm6,mm0);\ | |
536 | psraw_i2r(1,mm0);\ | |
537 | movq_m2r(ptr[16],mm2);\ | |
538 | movq_m2r(ptr[24],mm3);\ | |
539 | pand_r2r(mm5,mm2);\ | |
540 | pand_r2r(mm5,mm3);\ | |
541 | pmaddwd_r2r(mm6,mm2);\ | |
542 | pmaddwd_r2r(mm6,mm3);\ | |
543 | packssdw_r2r(mm3,mm2);\ | |
544 | paddw_r2r(mm6,mm2);\ | |
545 | psraw_i2r(1,mm2);\ | |
546 | packuswb_r2r(mm2,mm0);\ | |
547 | movq_r2m(mm0,lum[0]); | |
548 | ||
549 | #define DEINT_LINE_LUM(ptroff) \ | |
550 | movd_m2r(lum_m4[(ptroff)],mm0);\ | |
551 | movd_m2r(lum_m3[(ptroff)],mm1);\ | |
552 | movd_m2r(lum_m2[(ptroff)],mm2);\ | |
553 | movd_m2r(lum_m1[(ptroff)],mm3);\ | |
554 | movd_m2r(lum[(ptroff)],mm4);\ | |
555 | punpcklbw_r2r(mm7,mm0);\ | |
556 | movd_r2m(mm2,lum_m4[(ptroff)]);\ | |
557 | punpcklbw_r2r(mm7,mm1);\ | |
558 | punpcklbw_r2r(mm7,mm2);\ | |
559 | punpcklbw_r2r(mm7,mm3);\ | |
560 | punpcklbw_r2r(mm7,mm4);\ | |
561 | psllw_i2r(2,mm1);\ | |
562 | psllw_i2r(1,mm2);\ | |
563 | paddw_r2r(mm6,mm1);\ | |
564 | psllw_i2r(2,mm3);\ | |
565 | paddw_r2r(mm2,mm1);\ | |
566 | paddw_r2r(mm4,mm0);\ | |
567 | paddw_r2r(mm3,mm1);\ | |
568 | psubusw_r2r(mm0,mm1);\ | |
569 | psrlw_i2r(3,mm1);\ | |
570 | packuswb_r2r(mm7,mm1);\ | |
571 | movd_r2m(mm1,lum_m2[(ptroff)]); | |
572 | ||
573 | #else | |
6f72dcf7 | 574 | #include "libavcodec/dsputil.h" |
3f9bff71 MN |
575 | |
576 | #define LINE_WITH_UV \ | |
577 | lum[0]=ptr[0];lum[1]=ptr[2];lum[2]=ptr[4];lum[3]=ptr[6];\ | |
578 | cb[0]=ptr[1];cb[1]=ptr[5];\ | |
579 | cr[0]=ptr[3];cr[1]=ptr[7];\ | |
580 | lum[4]=ptr[8];lum[5]=ptr[10];lum[6]=ptr[12];lum[7]=ptr[14];\ | |
581 | cb[2]=ptr[9];cb[3]=ptr[13];\ | |
582 | cr[2]=ptr[11];cr[3]=ptr[15];\ | |
583 | lum[8]=ptr[16];lum[9]=ptr[18];lum[10]=ptr[20];lum[11]=ptr[22];\ | |
584 | cb[4]=ptr[17];cb[5]=ptr[21];\ | |
585 | cr[4]=ptr[19];cr[5]=ptr[23];\ | |
586 | lum[12]=ptr[24];lum[13]=ptr[26];lum[14]=ptr[28];lum[15]=ptr[30];\ | |
587 | cb[6]=ptr[25];cb[7]=ptr[29];\ | |
588 | cr[6]=ptr[27];cr[7]=ptr[31]; | |
589 | ||
590 | #define LINE_NO_UV \ | |
591 | lum[0]=ptr[0];lum[1]=ptr[2];lum[2]=ptr[4];lum[3]=ptr[6];\ | |
592 | lum[4]=ptr[8];lum[5]=ptr[10];lum[6]=ptr[12];lum[7]=ptr[14];\ | |
593 | lum[8]=ptr[16];lum[9]=ptr[18];lum[10]=ptr[20];lum[11]=ptr[22];\ | |
594 | lum[12]=ptr[24];lum[13]=ptr[26];lum[14]=ptr[28];lum[15]=ptr[30]; | |
595 | ||
596 | #define LINE_WITHUV_AVG \ | |
597 | sum=(ptr[0]+ptr[2]+1) >> 1;lum[0]=sum; \ | |
598 | sum=(ptr[4]+ptr[6]+1) >> 1;lum[1]=sum; \ | |
599 | sum=(ptr[1]+ptr[5]+1) >> 1;cb[0]=sum; \ | |
600 | sum=(ptr[3]+ptr[7]+1) >> 1;cr[0]=sum; \ | |
601 | sum=(ptr[8]+ptr[10]+1) >> 1;lum[2]=sum; \ | |
602 | sum=(ptr[12]+ptr[14]+1) >> 1;lum[3]=sum; \ | |
603 | sum=(ptr[9]+ptr[13]+1) >> 1;cb[1]=sum; \ | |
604 | sum=(ptr[11]+ptr[15]+1) >> 1;cr[1]=sum; \ | |
605 | sum=(ptr[16]+ptr[18]+1) >> 1;lum[4]=sum; \ | |
606 | sum=(ptr[20]+ptr[22]+1) >> 1;lum[5]=sum; \ | |
607 | sum=(ptr[17]+ptr[21]+1) >> 1;cb[2]=sum; \ | |
608 | sum=(ptr[19]+ptr[23]+1) >> 1;cr[2]=sum; \ | |
609 | sum=(ptr[24]+ptr[26]+1) >> 1;lum[6]=sum; \ | |
610 | sum=(ptr[28]+ptr[30]+1) >> 1;lum[7]=sum; \ | |
611 | sum=(ptr[25]+ptr[29]+1) >> 1;cb[3]=sum; \ | |
115329f1 | 612 | sum=(ptr[27]+ptr[31]+1) >> 1;cr[3]=sum; |
3f9bff71 MN |
613 | |
614 | #define LINE_NOUV_AVG \ | |
615 | sum=(ptr[0]+ptr[2]+1) >> 1;lum[0]=sum; \ | |
616 | sum=(ptr[4]+ptr[6]+1) >> 1;lum[1]=sum; \ | |
617 | sum=(ptr[8]+ptr[10]+1) >> 1;lum[2]=sum; \ | |
618 | sum=(ptr[12]+ptr[14]+1) >> 1;lum[3]=sum; \ | |
619 | sum=(ptr[16]+ptr[18]+1) >> 1;lum[4]=sum; \ | |
620 | sum=(ptr[20]+ptr[22]+1) >> 1;lum[5]=sum; \ | |
621 | sum=(ptr[24]+ptr[26]+1) >> 1;lum[6]=sum; \ | |
115329f1 | 622 | sum=(ptr[28]+ptr[30]+1) >> 1;lum[7]=sum; |
3f9bff71 MN |
623 | |
624 | #define DEINT_LINE_LUM(ptroff) \ | |
625 | sum=(-lum_m4[(ptroff)]+(lum_m3[(ptroff)]<<2)+(lum_m2[(ptroff)]<<1)+(lum_m1[(ptroff)]<<2)-lum[(ptroff)]); \ | |
626 | lum_m4[(ptroff)]=lum_m2[(ptroff)];\ | |
627 | lum_m2[(ptroff)]=cm[(sum+4)>>3];\ | |
628 | sum=(-lum_m4[(ptroff)+1]+(lum_m3[(ptroff)+1]<<2)+(lum_m2[(ptroff)+1]<<1)+(lum_m1[(ptroff)+1]<<2)-lum[(ptroff)+1]); \ | |
629 | lum_m4[(ptroff)+1]=lum_m2[(ptroff)+1];\ | |
630 | lum_m2[(ptroff)+1]=cm[(sum+4)>>3];\ | |
631 | sum=(-lum_m4[(ptroff)+2]+(lum_m3[(ptroff)+2]<<2)+(lum_m2[(ptroff)+2]<<1)+(lum_m1[(ptroff)+2]<<2)-lum[(ptroff)+2]); \ | |
632 | lum_m4[(ptroff)+2]=lum_m2[(ptroff)+2];\ | |
633 | lum_m2[(ptroff)+2]=cm[(sum+4)>>3];\ | |
634 | sum=(-lum_m4[(ptroff)+3]+(lum_m3[(ptroff)+3]<<2)+(lum_m2[(ptroff)+3]<<1)+(lum_m1[(ptroff)+3]<<2)-lum[(ptroff)+3]); \ | |
635 | lum_m4[(ptroff)+3]=lum_m2[(ptroff)+3];\ | |
636 | lum_m2[(ptroff)+3]=cm[(sum+4)>>3]; | |
637 | ||
638 | #endif | |
639 | ||
640 | ||
8949367e FB |
641 | /* Read two fields separately. */ |
642 | static int aiw_read_picture(VideoData *s, uint8_t *data) | |
3f9bff71 | 643 | { |
0c1a9eda | 644 | uint8_t *ptr, *lum, *cb, *cr; |
8949367e | 645 | int h; |
3f9bff71 | 646 | #ifndef HAVE_MMX |
8949367e | 647 | int sum; |
3f9bff71 | 648 | #endif |
0c1a9eda ZK |
649 | uint8_t* src = s->src_mem; |
650 | uint8_t *ptrend = &src[s->width*2]; | |
8949367e FB |
651 | lum=data; |
652 | cb=&lum[s->width*s->height]; | |
653 | cr=&cb[(s->width*s->height)/4]; | |
654 | if (s->deint == 0 && s->halfw == 0) { | |
655 | while (read(s->fd,src,s->width*2) < 0) { | |
656 | usleep(100); | |
657 | } | |
658 | for (h = 0; h < s->height-2; h+=2) { | |
3f9bff71 MN |
659 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) { |
660 | LINE_WITH_UV | |
8949367e | 661 | } |
3f9bff71 MN |
662 | read(s->fd,src,s->width*2); |
663 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16) { | |
664 | LINE_NO_UV | |
8949367e FB |
665 | } |
666 | read(s->fd,src,s->width*2); | |
667 | } | |
668 | /* | |
669 | * Do last two lines | |
670 | */ | |
671 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) { | |
672 | LINE_WITH_UV | |
3f9bff71 | 673 | } |
8949367e FB |
674 | read(s->fd,src,s->width*2); |
675 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16) { | |
676 | LINE_NO_UV | |
677 | } | |
678 | /* drop second field */ | |
679 | while (read(s->fd,src,s->width*2) < 0) { | |
680 | usleep(100); | |
681 | } | |
682 | for (h = 0; h < s->height - 1; h++) { | |
683 | read(s->fd,src,s->width*2); | |
684 | } | |
685 | } else if (s->halfw == 1) { | |
3f9bff71 | 686 | #ifdef HAVE_MMX |
8949367e FB |
687 | mmx_t rounder; |
688 | mmx_t masker; | |
689 | rounder.uw[0]=1; | |
690 | rounder.uw[1]=1; | |
691 | rounder.uw[2]=1; | |
692 | rounder.uw[3]=1; | |
693 | masker.ub[0]=0xff; | |
694 | masker.ub[1]=0; | |
695 | masker.ub[2]=0xff; | |
696 | masker.ub[3]=0; | |
697 | masker.ub[4]=0xff; | |
698 | masker.ub[5]=0; | |
699 | masker.ub[6]=0xff; | |
700 | masker.ub[7]=0; | |
701 | pxor_r2r(mm7,mm7); | |
702 | movq_m2r(rounder,mm6); | |
3f9bff71 | 703 | #endif |
8949367e FB |
704 | while (read(s->fd,src,s->width*4) < 0) { |
705 | usleep(100); | |
706 | } | |
707 | ptrend = &src[s->width*4]; | |
708 | for (h = 0; h < s->height-2; h+=2) { | |
3f9bff71 MN |
709 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8, cb+=4, cr+=4) { |
710 | LINE_WITHUV_AVG | |
8949367e | 711 | } |
3f9bff71 MN |
712 | read(s->fd,src,s->width*4); |
713 | #ifdef HAVE_MMX | |
714 | movq_m2r(masker,mm5); | |
715 | #endif | |
716 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8) { | |
717 | LINE_NOUV_AVG | |
8949367e FB |
718 | } |
719 | read(s->fd,src,s->width*4); | |
720 | } | |
721 | /* | |
722 | * Do last two lines | |
723 | */ | |
724 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8, cb+=4, cr+=4) { | |
725 | LINE_WITHUV_AVG | |
726 | } | |
727 | read(s->fd,src,s->width*4); | |
728 | #ifdef HAVE_MMX | |
729 | movq_m2r(masker,mm5); | |
730 | #endif | |
731 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=8) { | |
732 | LINE_NOUV_AVG | |
733 | } | |
734 | /* drop second field */ | |
735 | while (read(s->fd,src,s->width*4) < 0) { | |
736 | usleep(100); | |
737 | } | |
738 | for (h = 0; h < s->height - 1; h++) { | |
739 | read(s->fd,src,s->width*4); | |
740 | } | |
741 | } else { | |
0c1a9eda | 742 | uint8_t *lum_m1, *lum_m2, *lum_m3, *lum_m4; |
3f9bff71 | 743 | #ifdef HAVE_MMX |
8949367e FB |
744 | mmx_t rounder; |
745 | rounder.uw[0]=4; | |
746 | rounder.uw[1]=4; | |
747 | rounder.uw[2]=4; | |
748 | rounder.uw[3]=4; | |
749 | movq_m2r(rounder,mm6); | |
750 | pxor_r2r(mm7,mm7); | |
3f9bff71 | 751 | #else |
0c1a9eda | 752 | uint8_t *cm = cropTbl + MAX_NEG_CROP; |
3f9bff71 MN |
753 | #endif |
754 | ||
8949367e FB |
755 | /* read two fields and deinterlace them */ |
756 | while (read(s->fd,src,s->width*2) < 0) { | |
757 | usleep(100); | |
758 | } | |
759 | for (h = 0; h < (s->height/2)-2; h+=2) { | |
3f9bff71 MN |
760 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) { |
761 | LINE_WITH_UV | |
8949367e | 762 | } |
3f9bff71 | 763 | read(s->fd,src,s->width*2); |
8949367e FB |
764 | /* skip a luminance line - will be filled in later */ |
765 | lum += s->width; | |
3f9bff71 MN |
766 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) { |
767 | LINE_WITH_UV | |
8949367e FB |
768 | } |
769 | /* skip a luminance line - will be filled in later */ | |
770 | lum += s->width; | |
771 | read(s->fd,src,s->width*2); | |
772 | } | |
773 | /* | |
774 | * Do last two lines | |
775 | */ | |
776 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) { | |
777 | LINE_WITH_UV | |
778 | } | |
779 | /* skip a luminance line - will be filled in later */ | |
780 | lum += s->width; | |
781 | read(s->fd,src,s->width*2); | |
782 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, cb+=8, cr+=8) { | |
783 | LINE_WITH_UV | |
784 | } | |
785 | /* | |
3f9bff71 MN |
786 | * |
787 | * SECOND FIELD | |
788 | * | |
789 | */ | |
8949367e FB |
790 | lum=&data[s->width]; |
791 | while (read(s->fd,src,s->width*2) < 0) { | |
792 | usleep(10); | |
793 | } | |
794 | /* First (and last) two lines not interlaced */ | |
795 | for (h = 0; h < 2; h++) { | |
796 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16) { | |
797 | LINE_NO_UV | |
798 | } | |
799 | read(s->fd,src,s->width*2); | |
800 | /* skip a luminance line */ | |
801 | lum += s->width; | |
802 | } | |
803 | lum_m1=&lum[-s->width]; | |
804 | lum_m2=&lum_m1[-s->width]; | |
805 | lum_m3=&lum_m2[-s->width]; | |
806 | memmove(s->lum_m4_mem,&lum_m3[-s->width],s->width); | |
807 | for (; h < (s->height/2)-1; h++) { | |
808 | lum_m4=s->lum_m4_mem; | |
809 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16,lum_m1+=16,lum_m2+=16,lum_m3+=16,lum_m4+=16) { | |
810 | LINE_NO_UV | |
3f9bff71 MN |
811 | |
812 | DEINT_LINE_LUM(0) | |
813 | DEINT_LINE_LUM(4) | |
814 | DEINT_LINE_LUM(8) | |
815 | DEINT_LINE_LUM(12) | |
8949367e FB |
816 | } |
817 | read(s->fd,src,s->width*2); | |
818 | /* skip a luminance line */ | |
819 | lum += s->width; | |
820 | lum_m1 += s->width; | |
821 | lum_m2 += s->width; | |
822 | lum_m3 += s->width; | |
823 | // lum_m4 += s->width; | |
824 | } | |
825 | /* | |
3f9bff71 MN |
826 | * Do last line |
827 | */ | |
8949367e FB |
828 | lum_m4=s->lum_m4_mem; |
829 | for (ptr = &src[0]; ptr < ptrend; ptr+=32, lum+=16, lum_m1+=16, lum_m2+=16, lum_m3+=16, lum_m4+=16) { | |
830 | LINE_NO_UV | |
3f9bff71 MN |
831 | |
832 | DEINT_LINE_LUM(0) | |
833 | DEINT_LINE_LUM(4) | |
834 | DEINT_LINE_LUM(8) | |
835 | DEINT_LINE_LUM(12) | |
8949367e FB |
836 | } |
837 | } | |
3f9bff71 | 838 | #ifdef HAVE_MMX |
8949367e | 839 | emms(); |
3f9bff71 | 840 | #endif |
3f9bff71 MN |
841 | return s->frame_size; |
842 | } | |
843 | ||
8949367e | 844 | static int aiw_close(VideoData *s) |
3f9bff71 | 845 | { |
8949367e FB |
846 | av_freep(&s->lum_m4_mem); |
847 | av_freep(&s->src_mem); | |
3f9bff71 MN |
848 | return 0; |
849 | } | |
850 | ||
c9a65ca8 FB |
851 | int video_grab_init(void) |
852 | { | |
853 | av_register_input_format(&video_grab_device_format); | |
854 | return 0; | |
855 | } |