1 |
#include "EXTERN.h" |
2 |
#include "perl.h" |
3 |
#include "XSUB.h" |
4 |
|
5 |
#include <sys/types.h> |
6 |
#include <sys/time.h> |
7 |
#include <unistd.h> |
8 |
#include <sys/mman.h> |
9 |
|
10 |
#include <string.h> |
11 |
#include <pthread.h> |
12 |
#include <linux/videodev.h> |
13 |
|
14 |
#define NEED_newCONSTSUB |
15 |
#include "../gppport.h" |
16 |
|
17 |
#ifndef pTHX_ |
18 |
#define pTHX_ |
19 |
#endif |
20 |
|
21 |
#define XSRETURN_bool(bool) if (bool) XSRETURN_YES; else XSRETURN_NO; |
22 |
|
23 |
#define VBI_BPF (2048*32) |
24 |
|
25 |
typedef struct video_capability *Video__Capture__V4l__Capability; |
26 |
typedef struct video_channel *Video__Capture__V4l__Channel; |
27 |
typedef struct video_audio *Video__Capture__V4l__Audio; |
28 |
typedef struct video_picture *Video__Capture__V4l__Picture; |
29 |
typedef struct video_tuner *Video__Capture__V4l__Tuner; |
30 |
|
31 |
static void |
32 |
attach_struct (SV *sv, size_t bytes) |
33 |
{ |
34 |
void *ptr; |
35 |
|
36 |
sv = SvRV (sv); |
37 |
Newz (0, ptr, bytes, void*); |
38 |
|
39 |
sv_magic (sv, 0, '~', 0, bytes); |
40 |
mg_find(sv, '~')->mg_ptr = ptr; |
41 |
} |
42 |
|
43 |
static SV * |
44 |
new_struct (SV *sv, size_t bytes, const char *pkg) |
45 |
{ |
46 |
SV *rv = newRV_noinc (sv); |
47 |
attach_struct (rv, bytes); |
48 |
return sv_bless (rv, gv_stashpv ((char *)pkg, TRUE)); |
49 |
} |
50 |
|
51 |
static void * |
52 |
old_struct (SV *sv, const char *name) |
53 |
{ |
54 |
/* TODO: check name */ |
55 |
return mg_find (SvRV(sv), '~')->mg_ptr; |
56 |
} |
57 |
|
58 |
static int |
59 |
framesize (unsigned int format, unsigned int pixels) |
60 |
{ |
61 |
if (format==VIDEO_PALETTE_RGB565) return pixels*2; |
62 |
if (format==VIDEO_PALETTE_RGB24) return pixels*3; |
63 |
if (format==VIDEO_PALETTE_RGB555) return pixels*2; |
64 |
if (format==VIDEO_PALETTE_HI240) return pixels*1; |
65 |
if (format==VIDEO_PALETTE_GREY) return pixels*1; |
66 |
if (format==VIDEO_PALETTE_RGB32) return pixels*4; |
67 |
if (format==VIDEO_PALETTE_UYVY) return pixels*2; |
68 |
if (format==VIDEO_PALETTE_YUYV) return pixels*2; |
69 |
/* everything below is very probably WRONG */ |
70 |
if (format==VIDEO_PALETTE_YUV410P) return pixels*2; |
71 |
if (format==VIDEO_PALETTE_YUV411) return pixels*2; |
72 |
if (format==VIDEO_PALETTE_YUV411P) return pixels*2; |
73 |
if (format==VIDEO_PALETTE_YUV420) return pixels*3/2; |
74 |
if (format==VIDEO_PALETTE_YUV420P) return pixels*3/2; |
75 |
if (format==VIDEO_PALETTE_YUV422) return pixels*2; |
76 |
if (format==VIDEO_PALETTE_YUV422P) return pixels*2; |
77 |
if (format==VIDEO_PALETTE_PLANAR) return pixels*2; |
78 |
if (format==VIDEO_PALETTE_RAW) return pixels*8; |
79 |
return 0; |
80 |
} |
81 |
|
82 |
struct private { |
83 |
int fd; |
84 |
unsigned char *mmap_base; |
85 |
struct video_mbuf vm; |
86 |
}; |
87 |
|
88 |
static int |
89 |
private_free (pTHX_ SV *obj, MAGIC *mg) |
90 |
{ |
91 |
struct private *p = (struct private *)mg->mg_ptr; |
92 |
munmap (p->mmap_base, p->vm.size); |
93 |
return 0; |
94 |
} |
95 |
|
96 |
static MGVTBL vtbl_private = {0, 0, 0, 0, private_free}; |
97 |
|
98 |
static struct private * |
99 |
find_private (SV *sv) |
100 |
{ |
101 |
HV *hv = (HV*)SvRV(sv); |
102 |
MAGIC *mg = mg_find ((SV*)hv, '~'); |
103 |
|
104 |
if (!mg) |
105 |
{ |
106 |
struct private p; |
107 |
p.fd = SvIV (*hv_fetch (hv, "fd", 2, 0)); |
108 |
if (ioctl (p.fd, VIDIOCGMBUF, &p.vm) == 0) |
109 |
{ |
110 |
p.mmap_base = (unsigned char *)mmap (0, p.vm.size, PROT_READ|PROT_WRITE, MAP_SHARED, p.fd, 0); |
111 |
if (p.mmap_base) |
112 |
{ |
113 |
sv_magic ((SV*)hv, 0, '~', (char*)&p, sizeof p); |
114 |
mg = mg_find ((SV*)hv, '~'); |
115 |
mg->mg_virtual = &vtbl_private; |
116 |
} |
117 |
} |
118 |
} |
119 |
|
120 |
return (struct private *) (mg ? mg->mg_ptr : 0); |
121 |
} |
122 |
|
123 |
typedef unsigned char u8; |
124 |
typedef unsigned int UI; |
125 |
|
126 |
#define get_field(field) (*hv_fetch ((HV*)SvRV (self), #field, strlen(#field), 0)) |
127 |
|
128 |
/* only one thread currently */ |
129 |
typedef struct vbi_frame { |
130 |
struct vbi_frame *next; |
131 |
int size; |
132 |
char data[VBI_BPF]; |
133 |
} vbi_frame; |
134 |
static vbi_frame *vbi_head, *vbi_tail, |
135 |
*vbi_free; |
136 |
static int vbi_fd; |
137 |
static UI vbi_max; |
138 |
static pthread_t vbi_snatcher; |
139 |
static pthread_mutex_t vbi_lock = PTHREAD_MUTEX_INITIALIZER; |
140 |
static pthread_cond_t vbi_cond = PTHREAD_COND_INITIALIZER; |
141 |
|
142 |
static void * |
143 |
vbi_snatcher_thread (void *arg) |
144 |
{ |
145 |
/* try to become a realtime process. */ |
146 |
#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING |
147 |
{ |
148 |
struct sched_param sp; |
149 |
|
150 |
sp.sched_priority = (sched_get_priority_max (SCHED_FIFO) |
151 |
+ sched_get_priority_min (SCHED_FIFO)) / 2 - 1; |
152 |
pthread_setschedparam (pthread_self (), SCHED_FIFO, &sp); |
153 |
} |
154 |
#endif |
155 |
for(;;) |
156 |
{ |
157 |
vbi_frame *next; |
158 |
|
159 |
pthread_mutex_lock (&vbi_lock); |
160 |
if (vbi_free) |
161 |
{ |
162 |
next = vbi_free; |
163 |
vbi_free = vbi_free->next; |
164 |
pthread_mutex_unlock (&vbi_lock); |
165 |
|
166 |
next->next = 0; |
167 |
next->size = read (vbi_fd, next->data, VBI_BPF); |
168 |
|
169 |
pthread_mutex_lock (&vbi_lock); |
170 |
|
171 |
if (vbi_tail) |
172 |
vbi_tail->next = next; |
173 |
else |
174 |
vbi_head = vbi_tail = next; |
175 |
|
176 |
vbi_tail = next; |
177 |
vbi_max--; |
178 |
|
179 |
pthread_cond_signal (&vbi_cond); |
180 |
pthread_mutex_unlock (&vbi_lock); |
181 |
} |
182 |
else |
183 |
{ |
184 |
static struct timespec to = { 0, 1000000000 / 70 }; /* skip almost a frame */ |
185 |
|
186 |
pthread_mutex_unlock (&vbi_lock); |
187 |
pthread_testcancel (); |
188 |
nanosleep (&to, 0); |
189 |
} |
190 |
} |
191 |
} |
192 |
|
193 |
MODULE = Video::Capture::V4l PACKAGE = Video::Capture::V4l::VBI |
194 |
|
195 |
PROTOTYPES: ENABLE |
196 |
|
197 |
SV * |
198 |
field(self) |
199 |
SV * self |
200 |
CODE: |
201 |
int fd = SvIV(get_field(fd)); |
202 |
|
203 |
if (vbi_fd == fd) |
204 |
{ |
205 |
vbi_frame *next; |
206 |
|
207 |
pthread_mutex_lock (&vbi_lock); |
208 |
while (!vbi_head) |
209 |
pthread_cond_wait (&vbi_cond, &vbi_lock); |
210 |
|
211 |
RETVAL = newSVpvn (vbi_head->data, vbi_head->size); |
212 |
|
213 |
vbi_max++; |
214 |
next = vbi_head->next; |
215 |
|
216 |
vbi_head->next = vbi_free; |
217 |
vbi_free = vbi_head; |
218 |
|
219 |
vbi_head = next; |
220 |
|
221 |
if (!next) |
222 |
vbi_tail = vbi_head; |
223 |
|
224 |
pthread_mutex_unlock (&vbi_lock); |
225 |
} |
226 |
else |
227 |
{ |
228 |
int len; |
229 |
|
230 |
RETVAL = newSVpvn ("", 0); |
231 |
SvGROW (RETVAL, VBI_BPF); |
232 |
len = read (fd, SvPV_nolen (RETVAL), VBI_BPF); |
233 |
SvCUR_set (RETVAL, len); |
234 |
} |
235 |
|
236 |
OUTPUT: |
237 |
RETVAL |
238 |
|
239 |
void |
240 |
backlog(self,backlog) |
241 |
SV * self |
242 |
unsigned int backlog |
243 |
CODE: |
244 |
{ |
245 |
while (vbi_max != backlog) |
246 |
{ |
247 |
vbi_frame *f; |
248 |
|
249 |
pthread_mutex_lock (&vbi_lock); |
250 |
|
251 |
if (vbi_max < backlog) |
252 |
{ |
253 |
f = malloc (sizeof (vbi_frame)); |
254 |
f->next = vbi_free; |
255 |
vbi_free = f; |
256 |
vbi_max++; |
257 |
} |
258 |
else |
259 |
{ |
260 |
if (vbi_free) |
261 |
{ |
262 |
f = vbi_free; |
263 |
vbi_free = vbi_free->next; |
264 |
free (f); |
265 |
vbi_max--; |
266 |
} |
267 |
} |
268 |
|
269 |
pthread_mutex_unlock (&vbi_lock); |
270 |
} |
271 |
|
272 |
if (backlog) |
273 |
{ |
274 |
if (!vbi_fd) |
275 |
{ |
276 |
vbi_fd = SvIV(get_field(fd)); |
277 |
pthread_create (&vbi_snatcher, 0, vbi_snatcher_thread, 0); |
278 |
} |
279 |
} |
280 |
else |
281 |
{ |
282 |
if (vbi_fd) |
283 |
{ |
284 |
pthread_cancel (vbi_snatcher); |
285 |
pthread_join (vbi_snatcher, 0); |
286 |
vbi_fd = 0; |
287 |
} |
288 |
|
289 |
/* no locking necessary, in theory */ |
290 |
while (vbi_head) |
291 |
{ |
292 |
vbi_frame *next = vbi_head->next; |
293 |
|
294 |
free (vbi_head); |
295 |
vbi_head = next; |
296 |
} |
297 |
|
298 |
vbi_tail = 0; |
299 |
} |
300 |
} |
301 |
|
302 |
int |
303 |
queued(self) |
304 |
CODE: |
305 |
if (vbi_fd) |
306 |
{ |
307 |
/* FIXME: lock/unlock */ |
308 |
pthread_mutex_lock (&vbi_lock); |
309 |
RETVAL = !!vbi_head; |
310 |
pthread_mutex_unlock (&vbi_lock); |
311 |
} |
312 |
else |
313 |
RETVAL = 1; |
314 |
OUTPUT: |
315 |
RETVAL |
316 |
|
317 |
MODULE = Video::Capture::V4l PACKAGE = Video::Capture::V4l |
318 |
|
319 |
SV * |
320 |
capture(sv,frame,width,height,format = VIDEO_PALETTE_RGB24) |
321 |
SV *sv |
322 |
unsigned int frame |
323 |
unsigned int width |
324 |
unsigned int height |
325 |
unsigned int format |
326 |
CODE: |
327 |
{ |
328 |
struct private *p; |
329 |
if ((p = find_private (sv))) |
330 |
{ |
331 |
struct video_mmap vm; |
332 |
vm.frame = frame; |
333 |
vm.height = height; |
334 |
vm.width = width; |
335 |
vm.format = format; |
336 |
if (ioctl (p->fd, VIDIOCMCAPTURE, &vm) == 0) |
337 |
{ |
338 |
SV *fr = newSV (0); |
339 |
SvUPGRADE (fr, SVt_PV); |
340 |
SvREADONLY_on (fr); |
341 |
SvPVX (fr) = p->mmap_base + p->vm.offsets[frame]; |
342 |
SvCUR_set (fr, framesize (format, width*height)); |
343 |
SvLEN_set (fr, 0); |
344 |
SvPOK_only (fr); |
345 |
RETVAL = fr; |
346 |
} |
347 |
else |
348 |
XSRETURN_EMPTY; |
349 |
} |
350 |
else |
351 |
XSRETURN_EMPTY; |
352 |
} |
353 |
OUTPUT: |
354 |
RETVAL |
355 |
|
356 |
void |
357 |
sync(sv,frame) |
358 |
SV *sv |
359 |
int frame |
360 |
PPCODE: |
361 |
{ |
362 |
struct private *p; |
363 |
if ((p = find_private (sv)) |
364 |
&& ioctl (p->fd, VIDIOCSYNC, &frame) == 0) |
365 |
XSRETURN_YES; |
366 |
else |
367 |
XSRETURN_EMPTY; |
368 |
} |
369 |
|
370 |
unsigned long |
371 |
_freq (fd,fr) |
372 |
int fd |
373 |
unsigned long fr |
374 |
CODE: |
375 |
if (items > 1) |
376 |
{ |
377 |
fr = ((fr<<4)+499)/1000; |
378 |
ioctl (fd, VIDIOCSFREQ, &fr); |
379 |
} |
380 |
if (GIMME_V != G_VOID) |
381 |
{ |
382 |
if (ioctl (fd, VIDIOCGFREQ, &fr) == 0) |
383 |
RETVAL = (fr*1000+7)>>4; |
384 |
else |
385 |
XSRETURN_EMPTY; |
386 |
} |
387 |
else |
388 |
XSRETURN (0); |
389 |
OUTPUT: |
390 |
RETVAL |
391 |
|
392 |
|
393 |
SV * |
394 |
_capabilities_new(fd) |
395 |
int fd |
396 |
CODE: |
397 |
RETVAL = new_struct (newSViv (fd), sizeof (struct video_capability), "Video::Capture::V4l::Capability"); |
398 |
OUTPUT: |
399 |
RETVAL |
400 |
|
401 |
SV * |
402 |
_channel_new(fd) |
403 |
int fd |
404 |
CODE: |
405 |
RETVAL = new_struct (newSViv (fd), sizeof (struct video_channel), "Video::Capture::V4l::Channel"); |
406 |
OUTPUT: |
407 |
RETVAL |
408 |
|
409 |
SV * |
410 |
_tuner_new(fd) |
411 |
int fd |
412 |
CODE: |
413 |
RETVAL = new_struct (newSViv (fd), sizeof (struct video_tuner), "Video::Capture::V4l::Tuner"); |
414 |
OUTPUT: |
415 |
RETVAL |
416 |
|
417 |
SV * |
418 |
_audio_new(fd) |
419 |
int fd |
420 |
CODE: |
421 |
RETVAL = new_struct (newSViv (fd), sizeof (struct video_audio), "Video::Capture::V4l::Audio"); |
422 |
OUTPUT: |
423 |
RETVAL |
424 |
|
425 |
SV * |
426 |
_picture_new(fd) |
427 |
int fd |
428 |
CODE: |
429 |
RETVAL = new_struct (newSViv (fd), sizeof (struct video_picture), "Video::Capture::V4l::Picture"); |
430 |
OUTPUT: |
431 |
RETVAL |
432 |
|
433 |
MODULE = Video::Capture::V4l PACKAGE = Video::Capture::V4l::Capability |
434 |
|
435 |
void |
436 |
get(sv) |
437 |
SV * sv |
438 |
CODE: |
439 |
XSRETURN_bool (ioctl (SvIV (SvRV (sv)), VIDIOCGCAP, old_struct (sv, "Video::Capture::V4l::Capability")) == 0); |
440 |
|
441 |
MODULE = Video::Capture::V4l PACKAGE = Video::Capture::V4l::Channel |
442 |
|
443 |
void |
444 |
get(sv) |
445 |
SV * sv |
446 |
CODE: |
447 |
XSRETURN_bool (ioctl (SvIV (SvRV (sv)), VIDIOCGCHAN, old_struct (sv, "Video::Capture::V4l::Channel")) == 0); |
448 |
|
449 |
void |
450 |
set(sv) |
451 |
SV * sv |
452 |
CODE: |
453 |
XSRETURN_bool (ioctl (SvIV (SvRV (sv)), VIDIOCSCHAN, old_struct (sv, "Video::Capture::V4l::Channel")) == 0); |
454 |
|
455 |
MODULE = Video::Capture::V4l PACKAGE = Video::Capture::V4l::Tuner |
456 |
|
457 |
void |
458 |
get(sv) |
459 |
SV * sv |
460 |
CODE: |
461 |
XSRETURN_bool (ioctl (SvIV (SvRV (sv)), VIDIOCGTUNER, old_struct (sv, "Video::Capture::V4l::Tuner")) == 0); |
462 |
|
463 |
void |
464 |
set(sv) |
465 |
SV * sv |
466 |
CODE: |
467 |
XSRETURN_bool (ioctl (SvIV (SvRV (sv)), VIDIOCSTUNER, old_struct (sv, "Video::Capture::V4l::Tuner")) == 0); |
468 |
|
469 |
MODULE = Video::Capture::V4l PACKAGE = Video::Capture::V4l::Audio |
470 |
|
471 |
void |
472 |
get(sv) |
473 |
SV * sv |
474 |
CODE: |
475 |
XSRETURN_bool (ioctl (SvIV (SvRV (sv)), VIDIOCGAUDIO, old_struct (sv, "Video::Capture::V4l::Audio")) == 0); |
476 |
|
477 |
void |
478 |
set(sv) |
479 |
SV * sv |
480 |
CODE: |
481 |
XSRETURN_bool (ioctl (SvIV (SvRV (sv)), VIDIOCSAUDIO, old_struct (sv, "Video::Capture::V4l::Audio")) == 0); |
482 |
|
483 |
MODULE = Video::Capture::V4l PACKAGE = Video::Capture::V4l::Picture |
484 |
|
485 |
void |
486 |
get(sv) |
487 |
SV * sv |
488 |
CODE: |
489 |
XSRETURN_bool (ioctl (SvIV (SvRV (sv)), VIDIOCGPICT, old_struct (sv, "Video::Capture::V4l::Picture")) == 0); |
490 |
|
491 |
void |
492 |
set(sv) |
493 |
SV * sv |
494 |
CODE: |
495 |
XSRETURN_bool (ioctl (SvIV (SvRV (sv)), VIDIOCSPICT, old_struct (sv, "Video::Capture::V4l::Picture")) == 0); |
496 |
|
497 |
# accessors/mutators |
498 |
INCLUDE: ./genacc | |
499 |
|
500 |
MODULE = Video::Capture::V4l PACKAGE = Video::Capture::V4l |
501 |
|
502 |
PROTOTYPES: ENABLE |
503 |
|
504 |
BOOT: |
505 |
{ |
506 |
HV *stash = gv_stashpvn("Video::Capture::V4l", 19, TRUE); |
507 |
|
508 |
newCONSTSUB(stash,"AUDIO_BASS", newSViv(VIDEO_AUDIO_BASS)); |
509 |
newCONSTSUB(stash,"AUDIO_MUTABLE", newSViv(VIDEO_AUDIO_MUTABLE)); |
510 |
newCONSTSUB(stash,"AUDIO_MUTE", newSViv(VIDEO_AUDIO_MUTE)); |
511 |
newCONSTSUB(stash,"AUDIO_TREBLE", newSViv(VIDEO_AUDIO_TREBLE)); |
512 |
newCONSTSUB(stash,"AUDIO_VOLUME", newSViv(VIDEO_AUDIO_VOLUME)); |
513 |
newCONSTSUB(stash,"CAPTURE_EVEN", newSViv(VIDEO_CAPTURE_EVEN)); |
514 |
newCONSTSUB(stash,"CAPTURE_ODD", newSViv(VIDEO_CAPTURE_ODD)); |
515 |
newCONSTSUB(stash,"MAX_FRAME", newSViv(VIDEO_MAX_FRAME)); |
516 |
newCONSTSUB(stash,"MODE_AUTO", newSViv(VIDEO_MODE_AUTO)); |
517 |
newCONSTSUB(stash,"MODE_NTSC", newSViv(VIDEO_MODE_NTSC)); |
518 |
newCONSTSUB(stash,"MODE_PAL", newSViv(VIDEO_MODE_PAL)); |
519 |
newCONSTSUB(stash,"MODE_SECAM", newSViv(VIDEO_MODE_SECAM)); |
520 |
newCONSTSUB(stash,"PALETTE_COMPONENT", newSViv(VIDEO_PALETTE_COMPONENT)); |
521 |
newCONSTSUB(stash,"PALETTE_GREY", newSViv(VIDEO_PALETTE_GREY)); |
522 |
newCONSTSUB(stash,"PALETTE_HI240", newSViv(VIDEO_PALETTE_HI240)); |
523 |
newCONSTSUB(stash,"PALETTE_PLANAR", newSViv(VIDEO_PALETTE_PLANAR)); |
524 |
newCONSTSUB(stash,"PALETTE_RAW", newSViv(VIDEO_PALETTE_RAW)); |
525 |
newCONSTSUB(stash,"PALETTE_RGB24", newSViv(VIDEO_PALETTE_RGB24)); |
526 |
newCONSTSUB(stash,"PALETTE_RGB32", newSViv(VIDEO_PALETTE_RGB32)); |
527 |
newCONSTSUB(stash,"PALETTE_RGB555", newSViv(VIDEO_PALETTE_RGB555)); |
528 |
newCONSTSUB(stash,"PALETTE_RGB565", newSViv(VIDEO_PALETTE_RGB565)); |
529 |
newCONSTSUB(stash,"PALETTE_UYVY", newSViv(VIDEO_PALETTE_UYVY)); |
530 |
newCONSTSUB(stash,"PALETTE_YUV410P", newSViv(VIDEO_PALETTE_YUV410P)); |
531 |
newCONSTSUB(stash,"PALETTE_YUV411", newSViv(VIDEO_PALETTE_YUV411)); |
532 |
newCONSTSUB(stash,"PALETTE_YUV411P", newSViv(VIDEO_PALETTE_YUV411P)); |
533 |
newCONSTSUB(stash,"PALETTE_YUV420", newSViv(VIDEO_PALETTE_YUV420)); |
534 |
newCONSTSUB(stash,"PALETTE_YUV420P", newSViv(VIDEO_PALETTE_YUV420P)); |
535 |
newCONSTSUB(stash,"PALETTE_YUV422", newSViv(VIDEO_PALETTE_YUV422)); |
536 |
newCONSTSUB(stash,"PALETTE_YUV422P", newSViv(VIDEO_PALETTE_YUV422P)); |
537 |
newCONSTSUB(stash,"PALETTE_YUYV", newSViv(VIDEO_PALETTE_YUYV)); |
538 |
newCONSTSUB(stash,"SOUND_LANG1", newSViv(VIDEO_SOUND_LANG1)); |
539 |
newCONSTSUB(stash,"SOUND_LANG2", newSViv(VIDEO_SOUND_LANG2)); |
540 |
newCONSTSUB(stash,"SOUND_MONO", newSViv(VIDEO_SOUND_MONO)); |
541 |
newCONSTSUB(stash,"SOUND_STEREO", newSViv(VIDEO_SOUND_STEREO)); |
542 |
newCONSTSUB(stash,"TUNER_LOW", newSViv(VIDEO_TUNER_LOW)); |
543 |
newCONSTSUB(stash,"TUNER_MBS_ON", newSViv(VIDEO_TUNER_MBS_ON)); |
544 |
newCONSTSUB(stash,"TUNER_NORM", newSViv(VIDEO_TUNER_NORM)); |
545 |
newCONSTSUB(stash,"TUNER_NTSC", newSViv(VIDEO_TUNER_NTSC)); |
546 |
newCONSTSUB(stash,"TUNER_PAL", newSViv(VIDEO_TUNER_PAL)); |
547 |
newCONSTSUB(stash,"TUNER_RDS_ON", newSViv(VIDEO_TUNER_RDS_ON)); |
548 |
newCONSTSUB(stash,"TUNER_SECAM", newSViv(VIDEO_TUNER_SECAM)); |
549 |
newCONSTSUB(stash,"TUNER_STEREO_ON", newSViv(VIDEO_TUNER_STEREO_ON)); |
550 |
newCONSTSUB(stash,"TYPE_CAMERA", newSViv(VIDEO_TYPE_CAMERA)); |
551 |
newCONSTSUB(stash,"TYPE_TV", newSViv(VIDEO_TYPE_TV)); |
552 |
newCONSTSUB(stash,"VC_AUDIO", newSViv(VIDEO_VC_AUDIO)); |
553 |
newCONSTSUB(stash,"VC_TUNER", newSViv(VIDEO_VC_TUNER)); |
554 |
newCONSTSUB(stash,"TYPE_CAPTURE", newSViv(VID_TYPE_CAPTURE)); |
555 |
newCONSTSUB(stash,"TYPE_CHROMAKEY", newSViv(VID_TYPE_CHROMAKEY)); |
556 |
newCONSTSUB(stash,"TYPE_CLIPPING", newSViv(VID_TYPE_CLIPPING)); |
557 |
newCONSTSUB(stash,"TYPE_FRAMERAM", newSViv(VID_TYPE_FRAMERAM)); |
558 |
newCONSTSUB(stash,"TYPE_MONOCHROME", newSViv(VID_TYPE_MONOCHROME)); |
559 |
newCONSTSUB(stash,"TYPE_OVERLAY", newSViv(VID_TYPE_OVERLAY)); |
560 |
newCONSTSUB(stash,"TYPE_SCALES", newSViv(VID_TYPE_SCALES)); |
561 |
newCONSTSUB(stash,"TYPE_SUBCAPTURE", newSViv(VID_TYPE_SUBCAPTURE)); |
562 |
newCONSTSUB(stash,"TYPE_TELETEXT", newSViv(VID_TYPE_TELETEXT)); |
563 |
newCONSTSUB(stash,"TYPE_TUNER", newSViv(VID_TYPE_TUNER)); |
564 |
} |
565 |
|
566 |
void |
567 |
bgr2rgb(fr) |
568 |
SV * fr |
569 |
CODE: |
570 |
{ |
571 |
u8 *data, *end; |
572 |
|
573 |
end = SvEND (fr); |
574 |
|
575 |
for (data = SvPV_nolen (fr); data < end; data += 3) |
576 |
{ |
577 |
data[0] ^= data[2]; |
578 |
data[2] ^= data[0]; |
579 |
data[0] ^= data[2]; |
580 |
} |
581 |
} |
582 |
OUTPUT: |
583 |
fr |
584 |
|
585 |
SV * |
586 |
reduce2(fr,w) |
587 |
SV * fr |
588 |
UI w |
589 |
CODE: |
590 |
{ |
591 |
u8 *src, *dst, *end; |
592 |
|
593 |
src = SvPV_nolen (fr); |
594 |
dst = SvPV_nolen (fr); |
595 |
|
596 |
w *= 3; |
597 |
|
598 |
do |
599 |
{ |
600 |
end = src + w; |
601 |
do |
602 |
{ |
603 |
dst[1] = ((UI)src[0] + (UI)src[3]) >> 1; src++; |
604 |
dst[2] = ((UI)src[0] + (UI)src[3]) >> 1; src++; |
605 |
dst[0] = ((UI)src[0] + (UI)src[3]) >> 1; src++; |
606 |
src += 3; |
607 |
dst += 3; |
608 |
} |
609 |
while (src < end); |
610 |
src = end + w; |
611 |
} |
612 |
while (src < (u8*)SvEND (fr)); |
613 |
|
614 |
SvCUR_set (fr, dst - (u8*)SvPV_nolen (fr)); |
615 |
} |
616 |
OUTPUT: |
617 |
fr |
618 |
|
619 |
void |
620 |
normalize(fr) |
621 |
SV * fr |
622 |
CODE: |
623 |
{ |
624 |
u8 mfr = 255, max = 0; |
625 |
u8 *src, *dst, *end; |
626 |
|
627 |
end = SvEND (fr); |
628 |
dst = SvPV_nolen (fr); |
629 |
|
630 |
for (src = SvPV_nolen (fr); src < end; src++) |
631 |
{ |
632 |
if (*src > max) max = *src; |
633 |
if (*src < mfr) mfr = *src; |
634 |
} |
635 |
|
636 |
if (max != mfr) |
637 |
for (src = SvPV_nolen (fr); src < end; ) |
638 |
*dst++ = ((UI)*src++ - mfr) * 255 / (max-mfr); |
639 |
} |
640 |
OUTPUT: |
641 |
fr |
642 |
|
643 |
void |
644 |
findmin(db,fr,start=0,count=0) |
645 |
SV * db |
646 |
SV * fr |
647 |
UI start |
648 |
UI count |
649 |
PPCODE: |
650 |
{ |
651 |
UI diff, min = -1; |
652 |
int mindata, data; |
653 |
u8 *src, *dst, *end, *efr; |
654 |
UI datasize = SvCUR (fr); |
655 |
UI framesize = datasize + sizeof(int); |
656 |
|
657 |
src = SvPV_nolen (db) + start * framesize; |
658 |
if (src < (u8*)SvPV_nolen (db) || src > (u8*)SvEND (db)) |
659 |
src = SvPV_nolen (db); |
660 |
|
661 |
end = src + count * framesize; |
662 |
if (end <= src || end > (u8*)SvEND (db)) |
663 |
end = SvEND (db); |
664 |
|
665 |
do |
666 |
{ |
667 |
data = *((int *)src); src += sizeof (int); |
668 |
|
669 |
dst = SvPV_nolen (fr); |
670 |
efr = src + datasize; |
671 |
diff = 0; |
672 |
|
673 |
do |
674 |
{ |
675 |
int dif = (int)*src++ - (int)*dst++; |
676 |
diff += dif*dif; |
677 |
} |
678 |
while (src < efr); |
679 |
|
680 |
if (min > diff) |
681 |
{ |
682 |
min = diff; |
683 |
mindata = data; |
684 |
} |
685 |
} |
686 |
while (src < end); |
687 |
|
688 |
EXTEND (sp, 2); |
689 |
PUSHs (sv_2mortal (newSViv (mindata))); |
690 |
PUSHs (sv_2mortal (newSViv ((min << 8) / SvCUR (fr)))); |
691 |
} |
692 |
|
693 |
void |
694 |
linreg(array) |
695 |
SV * array |
696 |
PPCODE: |
697 |
{ |
698 |
AV *xy = (AV*) SvRV (array); |
699 |
I32 i; |
700 |
I32 n = (av_len (xy)+1)>>1; |
701 |
double x_ = 0, y_ = 0; |
702 |
double sxy = 0, sxx = 0, syy = 0; |
703 |
|
704 |
for (i=0; i<n; i++) |
705 |
{ |
706 |
x_ += SvNV(*av_fetch(xy, i*2 ,1)); |
707 |
y_ += SvNV(*av_fetch(xy, i*2+1,1)); |
708 |
} |
709 |
|
710 |
x_ /= n; |
711 |
y_ /= n; |
712 |
|
713 |
for (i=0; i<n; i++) |
714 |
{ |
715 |
double x = SvNV(*av_fetch(xy, i*2 ,1)); |
716 |
double y = SvNV(*av_fetch(xy, i*2+1,1)); |
717 |
|
718 |
sxy += (x-x_)*(y-y_); |
719 |
sxx += (x-x_)*(x-x_); |
720 |
syy += (y-y_)*(y-y_); |
721 |
} |
722 |
|
723 |
{ |
724 |
double rxy2 = sxy*sxy / (sxx*syy); |
725 |
double b = sxy / sxx; |
726 |
double a = y_ - b*x_; |
727 |
double r2 = (n-1)/(n-2)*syy*(1-rxy2); |
728 |
|
729 |
EXTEND (sp, 3); |
730 |
PUSHs (sv_2mortal (newSVnv (a ))); |
731 |
PUSHs (sv_2mortal (newSVnv (b ))); |
732 |
PUSHs (sv_2mortal (newSVnv (r2))); |
733 |
} |
734 |
} |
735 |
|
736 |
void |
737 |
linreg1(array) |
738 |
SV * array |
739 |
PPCODE: |
740 |
{ |
741 |
AV *xy = (AV*) SvRV (array); |
742 |
I32 i; |
743 |
I32 n = (av_len (xy)+1)>>1; |
744 |
double c = 0; |
745 |
double c2 = 0; |
746 |
|
747 |
for (i=0; i<n; i++) |
748 |
{ |
749 |
double d = SvNV(*av_fetch(xy, i*2-1,1)) - SvNV(*av_fetch(xy, i*2,1)); |
750 |
c += d; |
751 |
} |
752 |
|
753 |
c /= n; |
754 |
|
755 |
for (i=0; i<n; i++) |
756 |
{ |
757 |
double d = c + SvNV(*av_fetch(xy, i*2,1)) - SvNV(*av_fetch(xy, i*2-1,1)); |
758 |
c2 += d*d; |
759 |
} |
760 |
|
761 |
c2 /= n; |
762 |
|
763 |
EXTEND (sp, 3); |
764 |
PUSHs (sv_2mortal (newSVnv (c))); |
765 |
PUSHs (sv_2mortal (newSVnv (1))); |
766 |
PUSHs (sv_2mortal (newSVnv (c2))); |
767 |
} |
768 |
|