1 /*
2 * simple movie player
3 *
4 * (c) 2002 Gerd Knorr <kraxel@bytesex.org>
5 *
6 */
7
8 #include "config.h"
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <strings.h>
14 #include <errno.h>
15 #include <fcntl.h>
16
17 #include <sys/types.h>
18 #include <sys/time.h>
19 #include <sys/ipc.h>
20 #include <sys/shm.h>
21 #include <sys/ioctl.h>
22 #include <sys/soundcard.h>
23
24 #include <X11/Xlib.h>
25 #include <X11/Intrinsic.h>
26 #include <X11/StringDefs.h>
27 #include <X11/Shell.h>
28 #include <X11/Xaw/Simple.h>
29 #include <X11/extensions/XShm.h>
30 #ifdef HAVE_LIBXV
31 # include <X11/extensions/Xv.h>
32 # include <X11/extensions/Xvlib.h>
33 #endif
34
35 #include "grab-ng.h"
36 #include "blit.h"
37
38 /* ------------------------------------------------------------------------ */
39
40 int debug = 0;
41
42 /* X11 */
43 static XtAppContext app_context;
44 static Widget app_shell;
45 static Display *dpy;
46 static Colormap colormap;
47 static Visual *visual;
48 static XVisualInfo vinfo,*vinfo_list;
49
50 static Widget simple;
51 static Dimension swidth,sheight;
52
53 static struct blit_state *blit;
54
55 /* libng */
56 static const struct ng_reader *reader;
57 static void *rhandle;
58 static const struct ng_dsp_driver *snd;
59 static void *shandle;
60 static int snd_fd;
61
62 static struct ng_video_fmt cfmt;
63 static struct ng_video_conv *conv;
64 static void *chandle;
65
66 static struct ng_video_fmt *vfmt;
67 static struct ng_audio_fmt *afmt;
68 static struct ng_video_buf *vbuf;
69 static struct ng_audio_buf *abuf;
70
71 /* ------------------------------------------------------------------------ */
72
73 struct ARGS {
74 char *dsp;
75 int slow;
76 int help;
77 int verbose;
78 int debug;
79 int xv;
80 int gl;
81 int max;
82 int audio;
83 int video;
84 } args;
85
86 XtResource args_desc[] = {
87 /* name, class, type, size, offset, default_type, default_addr */
88 {
89 /* Strings */
90 "dsp",
91 XtCString, XtRString, sizeof(char*),
92 XtOffset(struct ARGS*,dsp),
93 XtRString, NULL,
94 },{
95 /* Integer */
96 "verbose",
97 XtCValue, XtRInt, sizeof(int),
98 XtOffset(struct ARGS*,verbose),
99 XtRString, ""
100 },{
101 "debug",
102 XtCValue, XtRInt, sizeof(int),
103 XtOffset(struct ARGS*,debug),
104 XtRString, ""
105 },{
106 "help",
107 XtCValue, XtRInt, sizeof(int),
108 XtOffset(struct ARGS*,help),
109 XtRString, ""
110 },{
111 "slow",
112 XtCValue, XtRInt, sizeof(int),
113 XtOffset(struct ARGS*,slow),
114 XtRString, "1"
115 },{
116 "xv",
117 XtCValue, XtRInt, sizeof(int),
118 XtOffset(struct ARGS*,xv),
119 XtRString, "1"
120 },{
121 "gl",
122 XtCValue, XtRInt, sizeof(int),
123 XtOffset(struct ARGS*,gl),
124 XtRString, "1"
125 },{
126 "max",
127 XtCValue, XtRInt, sizeof(int),
128 XtOffset(struct ARGS*,max),
129 XtRString, ""
130 },{
131 "audio",
132 XtCValue, XtRInt, sizeof(int),
133 XtOffset(struct ARGS*,audio),
134 XtRString, "1"
135 },{
136 "video",
137 XtCValue, XtRInt, sizeof(int),
138 XtOffset(struct ARGS*,video),
139 XtRString, "1"
140 }
141 };
142 const int args_count = XtNumber(args_desc);
143
144 XrmOptionDescRec opt_desc[] = {
145 { "-dsp", "dsp", XrmoptionSepArg, NULL },
146 { "-slow", "slow", XrmoptionSepArg, NULL },
147
148 { "-h", "help", XrmoptionNoArg, "1" },
149 { "-help", "help", XrmoptionNoArg, "1" },
150 { "--help", "help", XrmoptionNoArg, "1" },
151
152 { "-v", "verbose", XrmoptionNoArg, "1" },
153 { "-verbose", "verbose", XrmoptionNoArg, "1" },
154 { "-debug", "debug", XrmoptionNoArg, "1" },
155 { "-max", "max", XrmoptionNoArg, "1" },
156
157 { "-xv", "xv", XrmoptionNoArg, "1" },
158 { "-noxv", "xv", XrmoptionNoArg, "" },
159 { "-gl", "gl", XrmoptionNoArg, "1" },
160 { "-nogl", "gl", XrmoptionNoArg, "" },
161 { "-audio", "audio", XrmoptionNoArg, "1" },
162 { "-noaudio", "audio", XrmoptionNoArg, "" },
163 { "-video", "video", XrmoptionNoArg, "1" },
164 { "-novideo", "video", XrmoptionNoArg, "" },
165 };
166 const int opt_count = (sizeof(opt_desc)/sizeof(XrmOptionDescRec));
167
168 /* ------------------------------------------------------------------------ */
169
170 static void quit_ac(Widget widget, XEvent *event,
171 String *params, Cardinal *num_params)
172 {
173 exit(0);
174 }
175
176 static void resize_ev(Widget widget, XtPointer client_data,
177 XEvent *event, Boolean *d)
178 {
179 static int first = 1;
180
181 switch(event->type) {
182 case MapNotify:
183 case ConfigureNotify:
184 if (first) {
185 blit = blit_init(simple,&vinfo,args.gl);
186 first = 0;
187 }
188 XtVaGetValues(widget,XtNheight,&sheight,XtNwidth,&swidth,NULL);
189 if (vfmt)
190 blit_resize(blit,swidth,sheight);
191 break;
192 }
193 }
194
195 static XtActionsRec action_table[] = {
196 { "Quit", quit_ac },
197 };
198
199 static String res[] = {
200 "pia.winGravity: Static",
201 "pia.playback.translations: #override \\n"
202 " <Key>Q: Quit() \\n"
203 " <Key>Escape: Quit()",
204 "pia.playback.background: black",
205 NULL
206 };
207
208 static void usage(FILE *out, char *prog)
209 {
210 char *h;
211
212 if (NULL != (h = strrchr(prog,'/')))
213 prog = h+1;
214 fprintf(out,
215 "%s is simple movie player\n"
216 "\n"
217 "usage: %s [ options ] movie\n"
218 "options:\n"
219 " -h, -help this text\n"
220 " -v, -verbose be verbose\n"
221 " -debug enable debug messages\n"
222 " -dsp <dev> use sound device <dev>\n"
223 #ifdef HAVE_LIBXV
224 " -noxv disable Xvideo extention\n"
225 #endif
226 #ifdef HAVE_GL
227 " -nogl disable OpenGL\n"
228 #endif
229 "\n",
230 prog,prog);
231 }
232
233 static void sync_info(long long drift, int drops, int frames)
234 {
235 int total = drops + frames;
236
237 fprintf(stderr,"a/v sync: audio drift is %4d ms / "
238 "%d of %d frame(s) [%3.1f%%] dropped \r",
239 (int)((drift)/1000000),drops,total,
240 total ? (float)drops * 100 / total : 0);
241 }
242
243 int main(int argc, char *argv[])
244 {
245 long long start, now, delay, latency = 0, drift = 0;
246 struct timeval wait;
247 int n, drop, droptotal, framecount, ww, wh;
248 unsigned int fmtids[2*VIDEO_FMT_COUNT], i;
249 XEvent event;
250
251 app_shell = XtVaAppInitialize(&app_context, "pia",
252 opt_desc, opt_count,
253 &argc, argv,
254 res, NULL);
255 XtGetApplicationResources(app_shell,&args,
256 args_desc,args_count,
257 NULL,0);
258 if (args.help) {
259 usage(stdout,argv[0]);
260 exit(1);
261 }
262 if (args.debug) {
263 debug = args.debug;
264 ng_debug = args.debug;
265 }
266
267 if (argc < 2) {
268 usage(stderr,argv[0]);
269 exit(1);
270 }
271 ng_init();
272
273 /* open file */
274 reader = ng_find_reader(argv[1]);
275 if (NULL == reader) {
276 fprintf(stderr,"can't handle %s\n",argv[1]);
277 exit(1);
278 }
279 rhandle = reader->rd_open(argv[1]);
280 if (NULL == rhandle) {
281 fprintf(stderr,"opening %s failed\n",argv[1]);
282 exit(1);
283 }
284 vfmt = reader->rd_vfmt(rhandle,NULL,0);
285 if (0 == vfmt->width || 0 == vfmt->height)
286 vfmt = NULL;
287 afmt = reader->rd_afmt(rhandle);
288
289 if (0 == args.video)
290 vfmt = NULL;
291 if (0 == args.audio)
292 afmt = NULL;
293 if (1 != args.slow)
294 /* no audio for slow motion, will not sync anyway ... */
295 afmt = NULL;
296
297 /* init x11 stuff */
298 dpy = XtDisplay(app_shell);
299 visual = x11_find_visual(XtDisplay(app_shell));
300 vinfo.visualid = XVisualIDFromVisual(visual);
301 vinfo_list = XGetVisualInfo(dpy, VisualIDMask, &vinfo, &n);
302 vinfo = vinfo_list[0];
303 XFree(vinfo_list);
304 if (visual != DefaultVisualOfScreen(XtScreen(app_shell))) {
305 fprintf(stderr,"switching visual (0x%lx)\n",vinfo.visualid);
306 colormap = XCreateColormap(dpy,RootWindowOfScreen(XtScreen(app_shell)),
307 vinfo.visual,AllocNone);
308 XtDestroyWidget(app_shell);
309 app_shell = XtVaAppCreateShell("pia","pia",
310 applicationShellWidgetClass, dpy,
311 XtNvisual,vinfo.visual,
312 XtNcolormap,colormap,
313 XtNdepth, vinfo.depth,
314 NULL);
315 } else {
316 colormap = DefaultColormapOfScreen(XtScreen(app_shell));
317 }
318 x11_init_visual(XtDisplay(app_shell),&vinfo);
319 #if HAVE_LIBXV
320 if (args.xv)
321 xv_image_init(dpy);
322 #endif
323 XtAppAddActions(app_context,action_table,
324 sizeof(action_table)/sizeof(XtActionsRec));
325 XtVaSetValues(app_shell, XtNtitle,argv[1],NULL);
326
327 /* show window */
328 ww = 320;
329 wh = 32;
330 if (vfmt) {
331 ww = vfmt->width;
332 wh = vfmt->height;
333 if (args.max) {
334 int sw = XtScreen(app_shell)->width;
335 int sh = XtScreen(app_shell)->height;
336 if (sw * wh > sh * ww) {
337 ww = ww * sh / wh;
338 wh = sh;
339 } else {
340 wh = wh * sw / ww;
341 ww =sw;
342 }
343 }
344 }
345 simple = XtVaCreateManagedWidget("playback",simpleWidgetClass,app_shell,
346 XtNwidth,ww, XtNheight,wh, NULL);
347 XtAddEventHandler(simple, StructureNotifyMask, True, resize_ev, NULL);
348 XtRealizeWidget(app_shell);
349 while (NULL == blit) {
350 XFlush(dpy);
351 while (True == XCheckMaskEvent(dpy, ~0, &event))
352 XtDispatchEvent(&event);
353 }
354
355 /* video setup */
356 if (vfmt) {
357 blit_get_formats(blit,fmtids,sizeof(fmtids)/sizeof(int));
358 vfmt = reader->rd_vfmt(rhandle,fmtids,sizeof(fmtids)/sizeof(int));
359 if (0 == vfmt->width || 0 == vfmt->height || VIDEO_NONE == vfmt->fmtid)
360 vfmt = NULL;
361 }
362 if (vfmt) {
363 for (i = 0; i < sizeof(fmtids)/sizeof(int); i++)
364 if (fmtids[i] == vfmt->fmtid)
365 break;
366 if (i == sizeof(fmtids)/sizeof(int)) {
367 /* blit can't display directly -- have to convert somehow */
368 for (i = 0; i < sizeof(fmtids)/sizeof(int); i++)
369 if (NULL != (conv = ng_conv_find_match(vfmt->fmtid,fmtids[i])))
370 break;
371 if (conv) {
372 cfmt = *vfmt;
373 cfmt.fmtid = conv->fmtid_out;
374 cfmt.bytesperline = 0;
375 chandle = ng_convert_alloc(conv,vfmt,&cfmt);
376 ng_convert_init(chandle);
377 if (debug)
378 fprintf(stderr,"pia: conv [%s] => [%s]\n",
379 ng_vfmt_to_desc[vfmt->fmtid],
380 ng_vfmt_to_desc[cfmt.fmtid]);
381 }
382 }
383 }
384
385 /* audio setup */
386 if (afmt) {
387 struct ng_audio_fmt f = *afmt;
388 snd = ng_dsp_open(args.dsp ? args.dsp : "/dev/dsp", &f,0,&shandle);
389 if (NULL == snd)
390 afmt = NULL;
391 else {
392 snd_fd = snd->fd(shandle);
393 latency = snd->latency(shandle);
394 if (debug)
395 fprintf(stderr,"a/v sync: audio latency is %lld ms\n",
396 latency/1000000);
397 }
398 }
399
400 /* enter main loop
401 *
402 * can't use XtAppMainLoop + Input + Timeout handlers here because
403 * that doesn't give us usable event scheduling, thus we have our
404 * own main loop here ...
405 */
406 drop = 0;
407 droptotal = 0;
408 framecount = 0;
409 start = 0;
410 drift = 0;
411 if (afmt) {
412 /* fill sound buffer */
413 fd_set wr;
414 int rc;
415
416 for (;;) {
417 FD_ZERO(&wr);
418 FD_SET(snd_fd,&wr);
419 wait.tv_sec = 0;
420 wait.tv_usec = 0;
421 rc = select(snd_fd+1,NULL,&wr,NULL,&wait);
422 if (1 != rc)
423 break;
424 abuf = reader->rd_adata(rhandle);
425 if (NULL == abuf)
426 break;
427 if (0 == start)
428 start = ng_get_timestamp();
429 drift = abuf->info.ts - (ng_get_timestamp() - start);
430 abuf = snd->write(shandle,abuf);
431 if (NULL != abuf)
432 break;
433 }
434 }
435 if (0 == start)
436 start = ng_get_timestamp();
437
438 if (debug)
439 fprintf(stderr,"a/v sync: audio buffer filled\n");
440 for (; vfmt || afmt;) {
441 int rc,max;
442 fd_set rd,wr;
443
444 /* handle X11 events */
445 if (True == XCheckMaskEvent(dpy, ~0, &event)) {
446 XtDispatchEvent(&event);
447 continue;
448 }
449
450 /* read media data */
451 if (afmt && NULL == abuf) {
452 abuf = reader->rd_adata(rhandle);
453 if (NULL == abuf)
454 afmt = NULL; /* EOF */
455 else
456 drift = abuf->info.ts - (ng_get_timestamp() - start);
457 }
458 if (vfmt && NULL == vbuf) {
459 droptotal += drop;
460 vbuf = reader->rd_vdata(rhandle,drop);
461 if (conv)
462 vbuf = ng_convert_frame(chandle,NULL,vbuf);
463 if (NULL != vbuf && 1 != args.slow)
464 /* ts fixup for slow motion */
465 vbuf->info.ts *= args.slow;
466 if (NULL == vbuf)
467 vfmt = NULL; /* EOF */
468 }
469
470 /* wait for events */
471 XFlush(dpy);
472 FD_ZERO(&rd);
473 FD_ZERO(&wr);
474 FD_SET(ConnectionNumber(dpy),&rd);
475 max = ConnectionNumber(dpy);
476 if (afmt) {
477 FD_SET(snd_fd,&wr);
478 if (snd_fd > max)
479 max = snd_fd;
480 }
481 if (vfmt) {
482 /* check how long we have to wait until the next frame
483 * should be blitted to the screen */
484 now = ng_get_timestamp() - start;
485 if (afmt && latency) {
486 /* sync video with audio track */
487 now += drift;
488 now -= latency;
489 if (args.verbose)
490 sync_info(drift-latency,droptotal,framecount);
491 }
492 delay = vbuf->info.ts - now;
493 if (delay < 0) {
494 drop = -delay / reader->frame_time(rhandle);
495 if (drop) {
496 if (args.verbose)
497 sync_info(drift-latency,droptotal,framecount);
498 }
499 wait.tv_sec = 0;
500 wait.tv_usec = 0;
501 } else {
502 drop = 0;
503 wait.tv_sec = delay / 1000000000;
504 wait.tv_usec = (delay / 1000) % 1000000;
505 }
506 } else {
507 wait.tv_sec = 1;
508 wait.tv_usec = 0;
509 }
510 rc = select(max+1,&rd,&wr,NULL,&wait);
511
512 if (afmt && FD_ISSET(snd_fd,&wr)) {
513 /* write audio data */
514 abuf = snd->write(shandle,abuf);
515 }
516 if (vfmt && 0 == rc) {
517 /* blit video frame */
518 blit_putframe(blit,vbuf);
519 framecount++;
520 vbuf = NULL;
521 }
522 }
523 return 0;
524 }
525
|
This page was automatically generated by the
LXR engine.
|