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