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