1 /*
2 * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org>
3 *
4 */
5 #include "config.h"
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <math.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <signal.h>
16 #include <pthread.h>
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <sys/time.h>
20 #include <sys/ioctl.h>
21 #include <sys/stat.h>
22 #include <sys/mman.h>
23 #include <sys/shm.h>
24 #include <sys/ipc.h>
25 #include <sys/wait.h>
26
27 #include "grab-ng.h"
28 #include "writefile.h"
29 #include "channel.h"
30 #include "sound.h"
31 #include "capture.h"
32 #include "commands.h"
33
34 /* ---------------------------------------------------------------------- */
35
36 static int bufcount = 16;
37 static int parallel = 1;
38 static char* tvnorm = NULL;
39 static char* input = NULL;
40 static char* moviename = NULL;
41 static char* audioname = NULL;
42 static char* vfmt_name;
43 static char* afmt_name;
44
45 static const struct ng_writer *writer;
46 static const void *video_priv;
47 static const void *audio_priv;
48 static struct ng_video_fmt video = {
49 width: 320,
50 height: 240,
51 };
52 static struct ng_audio_fmt audio = {
53 rate: 44100,
54 };
55 static void *movie_state;
56
57 static int absframes = 1;
58 static int quiet = 0, fps = 10000;
59
60 static int signaled = 0, wait_seconds = 0;
61
62 int debug = 0, have_dga = 0;
63
64 /* ---------------------------------------------------------------------- */
65
66 static void
67 list_formats(FILE *out)
68 {
69 struct list_head *item;
70 const struct ng_writer *wr;
71 int j;
72
73 fprintf(out,"\nmovie writers:\n");
74 list_for_each(item,&ng_writers) {
75 wr = list_entry(item, struct ng_writer, list);
76 fprintf(out," %s - %s\n",wr->name,
77 wr->desc ? wr->desc : "-");
78 if (NULL != wr->video) {
79 fprintf(out," video formats:\n");
80 for (j = 0; NULL != wr->video[j].name; j++) {
81 fprintf(out," %-7s %-28s [%s]\n",wr->video[j].name,
82 wr->video[j].desc ? wr->video[j].desc :
83 ng_vfmt_to_desc[wr->video[j].fmtid],
84 wr->video[j].ext);
85 }
86 }
87 if (NULL != wr->audio) {
88 fprintf(out," audio formats:\n");
89 for (j = 0; NULL != wr->audio[j].name; j++) {
90 fprintf(out," %-7s %-28s [%s]\n",wr->audio[j].name,
91 wr->audio[j].desc ? wr->audio[j].desc :
92 ng_afmt_to_desc[wr->audio[j].fmtid],
93 wr->audio[j].ext ? wr->audio[j].ext : "-");
94 }
95 }
96 fprintf(out,"\n");
97 }
98 }
99
100 static void
101 usage(FILE *out)
102 {
103 fprintf(out,
104 "streamer grabs image(s), records movies and sound\n"
105 "\n"
106 "usage: streamer [ options ]\n"
107 "\n"
108 "general options:\n"
109 " -h print this help text\n"
110 " -q quiet operation\n"
111 " -d enable debug output\n"
112 " -p n use n compression threads [%d]\n"
113 " -w seconds wait before grabbing [%d]\n"
114 "\n"
115 "video options:\n"
116 " -o file video/movie file name\n"
117 " -f format specify video format\n"
118 " -c device specify video4linux device [%s]\n"
119 " -r fps frame rate [%d.%03d]\n"
120 " -s size specify size [%dx%d]\n"
121 "\n"
122 " -t times number of frames or hh:mm:ss [%d]\n"
123 " -b buffers specify # of buffers [%d]\n"
124 " -j quality quality for mjpeg or jpeg [%d]\n"
125 " -n tvnorm set pal/ntsc/secam\n"
126 " -i input set video source\n"
127 " -a don't unmute/mute v4l device.\n"
128 "\n"
129 "audio options:\n"
130 " -O file wav file name\n"
131 " -F format specify audio format\n"
132 " -C device specify dsp device [%s]\n"
133 " -R rate sample rate [%d]\n"
134 "\n",
135
136 parallel,wait_seconds,
137 ng_dev.video, fps/1000, fps%1000,
138 video.width, video.height,
139 absframes, bufcount, ng_jpeg_quality,
140 ng_dev.dsp, audio.rate
141 );
142
143 list_formats(out);
144 fprintf(out,
145 "If you want to capture to multiple image files you should include some\n"
146 "digits into the movie filename (foo0000.jpeg for example), streamer will\n"
147 "use the digit block to enumerate the image files.\n"
148 "\n"
149 "For file formats which can hold *both* audio and video (like AVI and\n"
150 "QuickTime) the -O option has no effect.\n"
151 "\n"
152 "streamer will use the file extention of the output file name to figure\n"
153 "which format to use. You need the -f/-F options only if the extention\n"
154 "allows more than one format.\n"
155 "\n"
156 "Examples:\n"
157 " capture a single frame:\n"
158 " streamer -o foobar.ppm\n"
159 "\n"
160 " capture ten frames, two per second:\n"
161 " streamer -t 10 -r 2 -o foobar00.jpeg\n"
162 "\n"
163 " record 30 seconds stereo sound:\n"
164 " streamer -t 0:30 -O soundtrack.wav -F stereo\n"
165 "\n"
166 " record a quicktime movie with sound:\n"
167 " streamer -t 0:30 -o movie.mov -f jpeg -F mono16\n"
168 "\n"
169 " build mpeg movies using mjpegtools + compressed avi file:\n"
170 " streamer -t 0:30 -s 352x240 -r 24 -o movie.avi -f mjpeg -F stereo\n"
171 " lav2wav +p movie.avi | mp2enc -o audio.mp2\n"
172 " lav2yuv +p movie.avi | mpeg2enc -o video.m1v\n"
173 " mplex audio.mp2 video.m1v -o movie.mpg\n"
174 "\n"
175 " build mpeg movies using mjpegtools + raw, uncompressed video:\n"
176 " streamer -t 0:30 -s 352x240 -r 24 -o video.yuv -O audio.wav -F stereo\n"
177 " mp2enc -o audio.mp2 < audio.wav\n"
178 " mpeg2enc -o video.m1v < video.yuv\n"
179 " mplex audio.mp2 video.m1v -o movie.mpg\n"
180 "\n"
181 "-- \n"
182 "(c) 1998-2001 Gerd Knorr <kraxel@bytesex.org>\n");
183 }
184
185 /* ---------------------------------------------------------------------- */
186
187 static void
188 find_formats(void)
189 {
190 struct list_head *item;
191 const struct ng_writer *wr = NULL;
192 char *mext = NULL;
193 char *aext = NULL;
194 int v=-1,a=-1;
195
196 if (moviename) {
197 mext = strrchr(moviename,'.');
198 if (mext)
199 mext++;
200 }
201 if (audioname) {
202 aext = strrchr(audioname,'.');
203 if (aext)
204 aext++;
205 }
206 list_for_each(item,&ng_writers) {
207 wr = list_entry(item, struct ng_writer, list);
208 if (debug)
209 fprintf(stderr,"checking writer %s [%s] ...\n",wr->name,wr->desc);
210 if ((/*!wr->combined && */mext) || NULL != vfmt_name) {
211 if (NULL == wr->video) {
212 if (debug)
213 fprintf(stderr," no video, skipping\n");
214 continue;
215 }
216 for (v = 0; NULL != wr->video[v].name; v++) {
217 if (debug)
218 fprintf(stderr," video name=%s ext=%s: ",
219 wr->video[v].name,wr->video[v].ext);
220 if (mext && 0 != strcasecmp(wr->video[v].ext,mext)) {
221 if (debug)
222 fprintf(stderr,"ext mismatch [need %s]\n",mext);
223 continue;
224 }
225 if (vfmt_name && 0 != strcasecmp(wr->video[v].name,vfmt_name)) {
226 if (debug)
227 fprintf(stderr,"name mismatch [need %s]\n",vfmt_name);
228 continue;
229 }
230 if (debug)
231 fprintf(stderr,"OK\n");
232 break;
233 }
234 if (NULL == wr->video[v].name)
235 continue;
236 }
237 if ((!wr->combined && aext) || NULL != afmt_name) {
238 if (NULL == wr->audio) {
239 if (debug)
240 fprintf(stderr," no audio, skipping\n");
241 continue;
242 }
243 for (a = 0; NULL != wr->audio[a].name; a++) {
244 if (debug)
245 fprintf(stderr," audio name=%s ext=%s: ",
246 wr->audio[a].name,wr->audio[a].ext);
247 if (!wr->combined &&
248 aext && 0 != strcasecmp(wr->audio[a].ext,aext)) {
249 if (debug)
250 fprintf(stderr,"ext mismatch [need %s]\n",aext);
251 continue;
252 }
253 if (wr->combined &&
254 mext && 0 != strcasecmp(wr->audio[a].ext,mext)) {
255 if (debug)
256 fprintf(stderr,"ext mismatch [need %s]\n",mext);
257 continue;
258 }
259 if (afmt_name && 0 != strcasecmp(wr->audio[a].name,afmt_name)) {
260 if (debug)
261 fprintf(stderr,"name mismatch [need %s]\n",afmt_name);
262 continue;
263 }
264 if (debug)
265 fprintf(stderr,"OK\n");
266 break;
267 }
268 if (NULL == wr->audio[a].name)
269 continue;
270 }
271 break;
272 }
273 if (item != &ng_writers) {
274 writer = wr;
275 if (-1 != v) {
276 video.fmtid = wr->video[v].fmtid;
277 video_priv = wr->video[v].priv;
278 }
279 if (-1 != a) {
280 audio.fmtid = wr->audio[a].fmtid;
281 audio_priv = wr->audio[a].priv;
282 }
283 } else {
284 if (debug)
285 fprintf(stderr,"no match found\n");
286 }
287 }
288
289 static int
290 parse_time(char *time)
291 {
292 int hours, minutes, seconds, total=0;
293
294 if (3 == sscanf(time,"%d:%d:%d",&hours,&minutes,&seconds))
295 total = hours * 60*60 + minutes * 60 + seconds;
296 else if (2 == sscanf(time,"%d:%d",&minutes,&seconds))
297 total = minutes * 60 + seconds;
298
299 if (0 != total) {
300 /* hh:mm:ss => framecount */
301 return total * fps / 1000;
302 }
303
304 return atoi(time);
305 }
306
307
308 /* ---------------------------------------------------------------------- */
309
310 static void
311 do_rec_status(char *message)
312 {
313 if (!quiet)
314 fprintf(stderr,"%s \r",message);
315 }
316
317 static void
318 ctrlc(int signal)
319 {
320 static char text[] = "^C - one moment please\n";
321 if (!quiet)
322 write(2,text,strlen(text));
323 signaled=1;
324 }
325
326 int
327 main(int argc, char **argv)
328 {
329 int c,queued=0,noaudio=0;
330 char *raw_length=NULL;
331
332 /* parse options */
333 ng_init();
334 for (;;) {
335 if (-1 == (c = getopt(argc, argv, "haqdp:w:"
336 "o:c:f:r:s:t:n:i:b:j:" "O:C:F:R:")))
337 break;
338 switch (c) {
339 /* general options */
340 case 'q':
341 quiet = 1;
342 break;
343 case 'a':
344 noaudio = 1;
345 break;
346 case 'd':
347 debug++;
348 ng_debug++;
349 break;
350 case 'w':
351 wait_seconds = atoi(optarg);
352 break;
353 case 'p':
354 parallel = atoi(optarg);
355 break;
356
357 /* video options */
358 case 'o':
359 moviename = optarg;
360 break;
361 case 'f':
362 vfmt_name = optarg;
363 break;
364 case 'c':
365 ng_dev.video = optarg;
366 break;
367 case 'r':
368 fps = (int)(atof(optarg) * 1000 + 0.5);
369 break;
370 case 's':
371 if (2 != sscanf(optarg,"%dx%d",&video.width,&video.height))
372 video.width = video.height = 0;
373 break;
374
375 case 't':
376 raw_length = optarg;
377 break;
378 case 'b':
379 bufcount = atoi(optarg);
380 break;
381 case 'j':
382 ng_jpeg_quality = atoi(optarg);
383 break;
384 case 'n':
385 tvnorm = optarg;
386 break;
387 case 'i':
388 input = optarg;
389 break;
390
391 /* audio options */
392 case 'O':
393 audioname = optarg;
394 break;
395 case 'F':
396 afmt_name = optarg;
397 break;
398 case 'C':
399 ng_dev.dsp = optarg;
400 break;
401 case 'R':
402 audio.rate = atoi(optarg);
403 break;
404
405 /* errors / help */
406 case 'h':
407 usage(stdout);
408 exit(0);
409 default:
410 usage(stderr);
411 exit(1);
412 }
413 }
414 if (raw_length)
415 absframes = parse_time(raw_length);
416 find_formats();
417
418 /* sanity checks */
419 if (video.fmtid == VIDEO_NONE && audio.fmtid == AUDIO_NONE) {
420 fprintf(stderr,"neither audio nor video format specified/found\n");
421 exit(1);
422 }
423 if (NULL == writer) {
424 fprintf(stderr,"no output driver found\n");
425 exit(1);
426 }
427 if (audio.fmtid != AUDIO_NONE && !writer->combined && NULL == audioname) {
428 fprintf(stderr,"no audio file name specified\n");
429 exit(1);
430 }
431
432 /* set hooks */
433 rec_status = do_rec_status;
434
435 /* open */
436 if (writer && !quiet)
437 fprintf(stderr,"%s / video: %s / audio: %s\n",writer->name,
438 ng_vfmt_to_desc[video.fmtid],ng_afmt_to_desc[audio.fmtid]);
439
440 if (video.fmtid != VIDEO_NONE) {
441 drv = ng_vid_open(ng_dev.video,NULL,NULL,0,&h_drv);
442 if (NULL == drv) {
443 fprintf(stderr,"no grabber device available\n");
444 exit(1);
445 }
446 f_drv = drv->capabilities(h_drv);
447 add_attrs(drv->list_attrs(h_drv));
448 if (!(f_drv & CAN_CAPTURE)) {
449 fprintf(stderr,"%s: capture not supported\n",drv->name);
450 exit(1);
451 }
452 if (!noaudio)
453 audio_on();
454 audio_init();
455
456 /* modify settings */
457 if (input != NULL)
458 do_va_cmd(2,"setinput",input);
459 if (tvnorm != NULL)
460 do_va_cmd(2,"setnorm",tvnorm);
461 }
462
463 /* init movie writer */
464 ng_ratio_x = video.width;
465 ng_ratio_y = video.height;
466 movie_state = movie_writer_init
467 (moviename, audioname, writer,
468 &video, video_priv, fps,
469 &audio, audio_priv, ng_dev.dsp,
470 bufcount, parallel);
471 if (NULL == movie_state) {
472 fprintf(stderr,"movie writer initialisation failed\n");
473 if (video.fmtid != VIDEO_NONE) {
474 audio_off();
475 drv->close(h_drv);
476 }
477 exit(1);
478 }
479
480 /* catch ^C */
481 signal(SIGINT,ctrlc);
482
483 /* wait for some cameras to wake up and adjust light and all that */
484 if (wait_seconds)
485 sleep(wait_seconds);
486
487 /* main loop */
488 movie_writer_start(movie_state);
489 for (;queued < absframes && !signaled;) {
490 if (video.fmtid != VIDEO_NONE) {
491 /* video */
492 queued = movie_grab_put_video(movie_state,NULL);
493 } else {
494 sleep(1);
495 queued += fps / 1000;
496 }
497 }
498 movie_writer_stop(movie_state);
499
500 /* done */
501 if (video.fmtid != VIDEO_NONE) {
502 if (!noaudio)
503 audio_off();
504 drv->close(h_drv);
505 }
506 return 0;
507 }
508
|
This page was automatically generated by the
LXR engine.
|