version update
[goodguy/cinelerra.git] / cinelerra-5.1 / libzmpeg3 / y.C
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <alsa/asoundlib.h>
5 #include <sys/time.h>
6 #include <gtk/gtk.h>
7 #include <gdk/gdk.h>
8
9 const double nudge = 3.5;
10 const double ahead = .5;
11
12 #ifdef __cplusplus
13 #include "libzmpeg3.h"
14 #else
15 #include "../libmpeg3/libmpeg3.h"
16 #define zmpeg3_t mpeg3_t
17 #endif
18
19 zmpeg3_t *ysrc;
20
21 /* alpha currently is broken in gdk */
22 /*   would render faster if alpha worked correctly */
23 #define RGBA FALSE
24
25 #if RGBA
26 #define BPP 4
27 #ifdef __cplusplus
28 #define COLOR_MODEL zmpeg3_t::cmdl_RGBA8888
29 #else
30 #define COLOR_MODEL MPEG3_RGBA8888
31 #endif
32
33 #else
34 #define BPP 3
35 #ifdef __cplusplus
36 #define COLOR_MODEL zmpeg3_t::cmdl_RGB888
37 #else
38 #define COLOR_MODEL MPEG3_RGB888
39 #endif
40 #endif
41
42
43 //#define AUDIO
44 #define VIDEO
45 //#define LAST8M
46 static int verbose = 0;
47
48 /* c++ `pkg-config --cflags --libs gtk+-2.0` y.C ./x86_64/libzmpeg3.a -lpthread -lasound -lm -lX11 */
49
50 #ifdef AUDIO
51 snd_pcm_t *zpcm = 0;
52 snd_pcm_uframes_t zpcm_ufrm_size = 0;
53 snd_pcm_sframes_t zpcm_sbfr_size = 0;
54 snd_pcm_sframes_t zpcm_sper_size = 0;
55 snd_pcm_sframes_t zpcm_total_samples = 0;
56 double zpcm_play_time = 0.;
57 pthread_mutex_t zpcm_lock;
58 unsigned int zpcm_rate = 0;
59 static const char *zpcm_device = "plughw:0,0";
60 static unsigned int zpcm_bfr_time_us = 500000; 
61 static unsigned int zpcm_per_time_us = 200000;
62 static snd_pcm_channel_area_t *zpcm_areas = 0;
63 static int zpcm_channels = 0;
64 static short **zpcm_buffers = 0;
65 static short *zpcm_samples = 0;
66
67 void alsa_close()
68 {
69 #ifdef __cplusplus
70   if( zpcm_buffers != 0 ) { delete [] zpcm_buffers; zpcm_buffers = 0; }
71   if( zpcm_areas != 0 ) { delete [] zpcm_areas; zpcm_areas = 0; }
72   if( zpcm_samples != 0 ) { delete [] zpcm_samples; zpcm_samples = 0; }
73 #else
74   if( zpcm_buffers != 0 ) { free(zpcm_buffers); zpcm_buffers = 0; }
75   if( zpcm_areas != 0 ) { free(zpcm_areas); zpcm_areas = 0; }
76   if( zpcm_samples != 0 ) { free(zpcm_samples); zpcm_samples = 0; }
77 #endif
78   if( zpcm != 0 ) { snd_pcm_close(zpcm);  zpcm = 0; }
79   pthread_mutex_destroy(&zpcm_lock);
80 }
81
82 void alsa_open(int chs,int rate)
83 {
84   int ich, bits, byts, dir, ret;
85   pthread_mutex_init(&zpcm_lock,0);
86   snd_pcm_format_t fmt = SND_PCM_FORMAT_S16;
87   snd_pcm_hw_params_t *phw;
88   snd_pcm_sw_params_t *psw;
89   snd_pcm_hw_params_alloca(&phw);
90   snd_pcm_sw_params_alloca(&psw);
91
92   zpcm = 0;
93   zpcm_total_samples = 0;
94   zpcm_play_time = 0.;
95   ret = snd_pcm_open(&zpcm, zpcm_device,
96      SND_PCM_STREAM_PLAYBACK, 0 /* + SND_PCM_NONBLOCK */);
97   if( ret >= 0 )
98     ret = snd_pcm_hw_params_any(zpcm, phw);
99   if( ret >= 0 )
100     ret = snd_pcm_hw_params_set_rate_resample(zpcm, phw, 1);
101   if( ret >= 0 )
102     ret = snd_pcm_hw_params_set_access(zpcm, phw,
103       SND_PCM_ACCESS_RW_NONINTERLEAVED);
104   if( ret >= 0 )
105     ret = snd_pcm_hw_params_set_format(zpcm, phw, fmt);
106   if( ret >= 0 ) {
107     zpcm_channels = chs;
108     ret = snd_pcm_hw_params_set_channels(zpcm, phw, chs);
109   }
110   if( ret >= 0 ) {
111     zpcm_rate = rate;
112     ret = snd_pcm_hw_params_set_rate_near(zpcm, phw, &zpcm_rate, 0);
113     if( (int)zpcm_rate != rate )
114       printf("nearest audio_rate for %d is %u\n",rate,zpcm_rate);
115   }
116   if( ret >= 0 )
117     ret = snd_pcm_hw_params_set_buffer_time_near(zpcm, phw,
118       &zpcm_bfr_time_us, &dir);
119   if( ret >= 0 )
120     ret = snd_pcm_hw_params_get_buffer_size(phw, &zpcm_ufrm_size);
121   if( ret >= 0 ) {
122     zpcm_sbfr_size = zpcm_ufrm_size;
123     ret = snd_pcm_hw_params_set_period_time_near(zpcm, phw,
124       &zpcm_per_time_us, &dir);
125   }
126   if( ret >= 0 )
127     ret = snd_pcm_hw_params_get_period_size(phw, &zpcm_ufrm_size, &dir);
128   if( ret >= 0 ) {
129     zpcm_sper_size = zpcm_ufrm_size;
130     ret = snd_pcm_hw_params(zpcm, phw);
131   }
132   if( ret >= 0 )
133     ret = snd_pcm_sw_params_current(zpcm, psw);
134   if( ret >= 0 )
135     ret = snd_pcm_sw_params_set_start_threshold(zpcm, psw,
136       (zpcm_sbfr_size / zpcm_sper_size) * zpcm_sper_size);
137   if( ret >= 0 )
138     ret = snd_pcm_sw_params_set_avail_min(zpcm, psw, zpcm_sper_size);
139   if( ret >= 0 )
140     ret = snd_pcm_sw_params(zpcm, psw);
141   /* snd_pcm_dump(zpcm, stdout); */
142
143   if( ret >= 0 ) {
144 #ifdef __cplusplus
145      zpcm_areas = new snd_pcm_channel_area_t[chs];
146      byts = snd_pcm_format_physical_width(fmt) / 8;
147      zpcm_samples = new short[zpcm_sper_size * chs * byts/2];
148      zpcm_buffers = new short *[chs];
149 #else
150      zpcm_areas = calloc(chs, sizeof(snd_pcm_channel_area_t));
151      bits = snd_pcm_format_physical_width(fmt);
152      byts = bits / 8;
153      zpcm_samples = malloc(zpcm_sper_size * chs * byts);
154      zpcm_buffers = malloc(chs * sizeof(short*));
155 #endif
156      if( zpcm_samples ) {
157        for( ich = 0; ich < chs; ++ich ) {
158          zpcm_areas[ich].addr = zpcm_samples;
159          zpcm_areas[ich].first = ich * zpcm_sper_size * bits;
160          zpcm_areas[ich].step = bits;
161          zpcm_buffers[ich] = zpcm_samples + ich*zpcm_sper_size;
162        }
163      }
164      else {
165        fprintf(stderr,"alsa sample buffer allocation failure.\n");
166        ret = -999;
167      }
168   }
169   if( ret < 0 ) {
170     if( ret > -999 )
171       printf("audio error: %s\n", snd_strerror(ret));
172     alsa_close();
173   }
174 }
175
176 short *alsa_bfr(int ch)
177 {
178   return zpcm_buffers[ch];
179 }
180
181 int alsa_bfrsz()
182 {
183   return zpcm_sper_size;
184 }
185
186 int alsa_recovery(int ret)
187 {
188   printf("alsa recovery\n");
189   switch( ret ) {
190   case -ESTRPIPE:
191     /* wait until the suspend flag is released, then fall through */
192     while( (ret=snd_pcm_resume(zpcm)) == -EAGAIN ) usleep(100000);
193   case -EPIPE:
194     ret = snd_pcm_prepare(zpcm);
195     if( ret < 0 )
196       printf("underrun, prepare failed: %s\n", snd_strerror(ret));
197     break;
198   default:
199     printf("unhandled error: %s\n",snd_strerror(ret));
200     break;
201   }
202   return ret;
203 }
204
205 int alsa_write(int length)
206 {
207   struct timeval tv;
208   int i, ret, count, retry;
209   snd_pcm_sframes_t sample_delay;
210   double play_time, time;
211 #ifdef __cplusplus
212   short *bfrs[zpcm_channels];
213 #else
214   short **bfrs = alloca(zpcm_channels*sizeof(short *));
215 #endif
216   retry = 3;
217   ret = count = 0;
218   for( i=0; i<zpcm_channels; ++i ) bfrs[i] = zpcm_buffers[i];
219
220   while( count < length ) {
221     ret = snd_pcm_writen(zpcm,(void **)bfrs, length-count);
222     if( ret == -EAGAIN ) continue;
223     if ( ret < 0 ) {
224       if( --retry < 0 ) return ret;
225       alsa_recovery(ret);
226       ret = 0;
227       continue;
228     }
229     for( i=0; i<zpcm_channels; ++i ) bfrs[i] += ret;
230     count += ret;
231   }
232   pthread_mutex_lock(&zpcm_lock);
233   zpcm_total_samples += count;
234   snd_pcm_delay(zpcm,&sample_delay);
235   if( sample_delay > zpcm_total_samples )
236     sample_delay = zpcm_total_samples;
237   gettimeofday(&tv,NULL);
238   time = tv.tv_sec + tv.tv_usec/1000000.0;
239   play_time = (zpcm_total_samples - sample_delay) / (double)zpcm_rate;
240   zpcm_play_time = time - play_time;
241   pthread_mutex_unlock(&zpcm_lock);
242   return ret < 0 ? ret : 0;
243 }
244
245 double alsa_time()
246 {
247   double time, play_time;
248   struct timeval tv;
249   pthread_mutex_lock(&zpcm_lock);
250   gettimeofday(&tv,NULL);
251   time = tv.tv_sec + tv.tv_usec/1000000.0;
252   play_time = time - zpcm_play_time;
253   pthread_mutex_unlock(&zpcm_lock);
254   return play_time;
255 }
256
257 #else
258
259 double alsa_time()
260 {
261   double time, play_time;
262   struct timeval tv;
263   gettimeofday(&tv,NULL);
264   time = tv.tv_sec + tv.tv_usec/1000000.0;
265   return time;
266 }
267
268 #endif
269
270 int done = 0;
271
272 void sigint(int n)
273 {
274   done = 1;
275 }
276
277
278 void dst_exit(GtkWidget *widget, gpointer data)
279 {
280    exit(0);
281 }
282
283 double tstart;
284
285 double the_time()
286 {
287   double time;
288   struct timeval tv;
289   gettimeofday(&tv,NULL);
290   time = tv.tv_sec + tv.tv_usec/1000000.0;
291   if( tstart < 0. ) tstart = time;
292   return time - tstart;
293 }
294
295
296 void mpeg3_stats(zmpeg3_t *zsrc)
297 {
298   int astream, vstream;
299   int has_audio, total_astreams, audio_channels, sample_rate;
300   int has_video, total_vstreams, width, height, colormodel;
301   long audio_samples, video_frames;
302   float frame_rate;
303
304   has_audio = mpeg3_has_audio(zsrc);
305   printf(" has_audio = %d\n", has_audio);
306   total_astreams = mpeg3_total_astreams(zsrc);
307   printf(" total_astreams = %d\n", total_astreams);
308   for( astream=0; astream<total_astreams; ++astream ) {
309     audio_channels = mpeg3_audio_channels(zsrc, astream);
310     printf("   audio_channels = %d\n", audio_channels);
311     sample_rate = mpeg3_sample_rate(zsrc, astream);
312     printf("   sample_rate = %d\n", sample_rate);
313     audio_samples = mpeg3_audio_samples(zsrc, astream);
314     printf("   audio_samples = %ld\n", audio_samples);
315   }
316   printf("\n");
317   has_video = mpeg3_has_video(zsrc);
318   printf(" has_video = %d\n", has_video);
319   total_vstreams = mpeg3_total_vstreams(zsrc);
320   printf(" total_vstreams = %d\n", total_vstreams);
321   for( vstream=0; vstream<total_vstreams; ++vstream ) {
322     width = mpeg3_video_width(zsrc, vstream);
323     printf("   video_width = %d\n", width);
324     height = mpeg3_video_height(zsrc, vstream);
325     printf("   video_height = %d\n", height);
326     frame_rate = mpeg3_frame_rate(zsrc, vstream);
327     printf("   frame_rate = %f\n", frame_rate);
328     video_frames = mpeg3_video_frames(zsrc, vstream);
329     printf("   video_frames = %ld\n", video_frames);
330     colormodel = mpeg3_colormodel(zsrc, vstream);
331     printf("   colormodel = %d\n", colormodel);
332   }
333 }
334
335 #ifdef VIDEO
336 double vstart;
337 double last_vtime;
338
339 double video_time(zmpeg3_t *zsrc,int stream)
340 {
341 #ifdef __cplusplus
342   double vtime = mpeg3_get_video_time(zsrc,stream);
343 #else
344   double vtime = mpeg3_get_time(zsrc);
345 #endif
346   double vnow = vtime;
347   if( vstart == 0. ) vstart = vnow;
348   if( vtime < last_vtime )
349     vstart -= last_vtime;
350   last_vtime = vtime;
351   return vnow - vstart + nudge;
352 }
353
354 pthread_t the_video;
355
356 void *video_thread(void *the_zsrc)
357 {
358   GtkWidget *window;
359   GtkWidget *panel_hbox;
360   GtkWidget *image;
361   GdkPixbuf *pbuf0, *pbuf1;
362   GdkImage  *img0, *img1;
363   GdkVisual *visual;
364   double delay;
365   float frame_rate, frame_delay;
366   unsigned char **rows, **row0, **row1, *cap, *cap0, *cap1;
367   int ret, row, frame, dropped, more_data;
368   int width, height, owidth, oheight;
369   const int frame_drop = 0;
370   zmpeg3_t *zsrc = (zmpeg3_t *)the_zsrc;
371
372   frame_rate = mpeg3_frame_rate(zsrc, 0);
373   frame_delay = 2.0 / frame_rate;
374   height = mpeg3_video_height(zsrc, 0);
375   width = mpeg3_video_width(zsrc, 0);
376   //oheight = 2*height / 3;
377   //owidth = 2*width / 3;
378   //oheight = 1050-96;
379   //owidth = 1680-64;
380   //oheight = height;
381   //owidth = width;
382   oheight = 540;
383   owidth = 960;
384
385   visual = gdk_visual_get_system();
386   /* toplevel window */
387   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
388   gtk_signal_connect(GTK_OBJECT(window),"destroy",
389      GTK_SIGNAL_FUNC(dst_exit),NULL);
390   gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_NONE);
391   /* try for shared image bfr, only seems to work with gtk_rgb */
392   img0 = gdk_image_new(GDK_IMAGE_SHARED, visual, owidth, oheight);
393   pbuf0 = gdk_pixbuf_new_from_data((const guchar *)img0->mem,
394              GDK_COLORSPACE_RGB,RGBA,8,owidth,oheight,owidth*BPP,NULL,NULL);
395   cap0 = gdk_pixbuf_get_pixels(pbuf0);
396   image = gtk_image_new_from_pixbuf(pbuf0);
397   /* double buffered */
398   img1 = gdk_image_new(GDK_IMAGE_SHARED, visual, owidth, oheight);
399   pbuf1 = gdk_pixbuf_new_from_data((const guchar *)img1->mem,
400              GDK_COLORSPACE_RGB,RGBA,8,owidth,oheight,owidth*BPP,NULL,NULL);
401   cap1 = gdk_pixbuf_get_pixels(pbuf1);
402
403   panel_hbox = gtk_hbox_new(FALSE,0);
404   gtk_container_add(GTK_CONTAINER(window), panel_hbox);
405   /* pack image into panel */
406   gtk_box_pack_start(GTK_BOX(panel_hbox), image, TRUE, TRUE, 0);
407
408
409 #ifdef __cplusplus
410   row0 = new unsigned char *[oheight];
411   row1 = new unsigned char *[oheight];
412   int cmdl = COLOR_MODEL;
413 #else
414   row0 = malloc(oheight*sizeof(unsigned char *));
415   row1 = malloc(oheight*sizeof(unsigned char *));
416   int cmdl = COLOR_MODEL;
417 #endif
418   for( row=0; row<oheight; ++row ) {
419     row0[row] = cap0 + row*owidth*BPP;
420     row1[row] = cap1 + row*owidth*BPP;
421   }
422
423   gtk_widget_show_all(window);
424   cap = cap0;
425   rows = row0;
426   frame = dropped = 0;
427   vstart = last_vtime = 0.;
428   more_data = 1;
429
430   while( done == 0 && more_data ) {
431     if( !gtk_events_pending() ) {
432       more_data = 0;
433       delay = (frame+dropped)/frame_rate - alsa_time();
434 //      delay = the_time() - video_time(zsrc,0);
435       if( frame_drop ) {
436         if( -delay >= frame_delay ) {
437           int nframes = (long)ceil(-delay/frame_rate);
438           if( nframes > 2 ) nframes = 2;
439           mpeg3_drop_frames(zsrc,nframes,0);
440           dropped += nframes;
441         }
442       }
443       if( delay > 0 )
444         usleep((int)(delay*1000000.0));
445       ret = mpeg3_read_frame(zsrc, rows, 0, 0, width, height,
446                owidth, oheight, cmdl, 0);
447       //printf("%d %ld\n",frame,zsrc->vtrack[0]->demuxer->titles[0]->fs->current_byte);
448       if( verbose )
449         printf("read_video(stream=%d, frame=%d) = %d\n", 0, frame, ret);
450       //if( frame > 500 && frame < 800 ) {
451       //  mpeg3_previous_frame(zsrc,0);
452       //  mpeg3_previous_frame(zsrc,0);
453       //}
454       GdkGC *blk = image->style->black_gc;
455       gdk_draw_rgb_image(image->window,blk, 0,0,owidth,oheight,
456          GDK_RGB_DITHER_NONE,cap,owidth*BPP);
457       gdk_flush();
458 #if 0
459 #if 0
460       { static FILE *fp = 0;
461         int sz = owidth*oheight*BPP;
462         if( fp == 0 ) fp = fopen("/tmp/dat.raw","w");
463         fwrite(cap,1,sz,fp);
464       }
465 #else
466       { static FILE *fp = 0;
467         int z, sz = owidth*oheight*BPP;
468         uint8_t zbfr[sz];
469         if( fp == 0 ) fp = fopen("/tmp/dat.raw","r");
470         fread(zbfr,1,sz,fp);
471         for( z=0; z<sz && abs(zbfr[z]-cap[z])<=1; ++z );
472         //for( z=0; z<sz && zbfr[z]==cap[z]; ++z );
473         if( z < sz )
474           printf("bug %d\n",z);
475       }
476 #endif
477       if( frame > 250 ) exit(0);
478 #endif
479       *(unsigned long *)&cap ^= ((unsigned long)cap0 ^ (unsigned long)cap1);
480       *(unsigned long *)&rows ^= ((unsigned long)row0 ^ (unsigned long)row1);
481       //printf(" %d/%d  %f  %f  %f\n",frame,dropped,(frame+dropped)/frame_rate,
482       //  video_time(zsrc,0),mpeg3_get_video_time(zsrc,0));
483       ++frame;
484       more_data |= !mpeg3_end_of_video(zsrc,0);
485     }
486     else
487       gtk_main_iteration();
488   }
489
490   return (void*)0;
491 }
492
493 #endif
494
495 #ifdef AUDIO
496 double astart;
497
498 double audio_time(zmpeg3_t *zsrc, int stream)
499 {
500 #ifdef __cplusplus
501   double anow = mpeg3_get_audio_time(zsrc,stream);
502 #else
503   double anow = mpeg3_get_time(zsrc);
504 #endif
505   if( astart < 0. ) astart = anow;
506   return anow - astart;
507 }
508
509 pthread_t the_audio;
510
511 void *audio_thread(void *the_zsrc)
512 {
513   double delay;
514   int audio_channels, sample_rate, more_data;
515   zmpeg3_t *zsrc = (zmpeg3_t *)the_zsrc;
516   int stream = 0;
517
518   audio_channels = mpeg3_audio_channels(zsrc, stream);
519   sample_rate = mpeg3_sample_rate(zsrc, stream);
520   alsa_open(audio_channels,sample_rate);
521   astart = -1.;
522   more_data = 1;
523
524   while( done == 0 && more_data ) {
525     more_data = 0;
526     int ich;
527     int n = alsa_bfrsz();
528     for( ich=0; ich<audio_channels; ++ich) {
529       short *bfr = alsa_bfr(ich);
530       int ret = ich == 0 ?
531         mpeg3_read_audio(zsrc, 0, bfr, ich, n, stream) :
532         mpeg3_reread_audio(zsrc, 0, bfr, ich, n, stream);
533       if( verbose )
534         printf("read_audio(stream=%d,channel=%d) = %d\n", stream, ich, ret);
535     }
536
537     alsa_write(n);
538     delay = audio_time(zsrc,stream) - the_time();
539     if( (delay-=ahead) > 0. ) {
540       usleep((int)(delay*1000000.0/2.));
541     }
542     more_data |= !mpeg3_end_of_audio(zsrc,0);
543   }
544
545   alsa_close();
546   return (void*)0;
547 }
548
549 #endif
550
551
552 int main(int ac, char **av)
553 {
554   int ret;
555   struct stat st_buf;
556   long pos;
557   setbuf(stdout,NULL);
558   tstart = -1.;
559
560 #ifdef VIDEO
561   gtk_set_locale();
562   gtk_init(&ac, &av);
563 #endif
564
565   //zmpeg3_t* zsrc = mpeg3_open("/tmp/dat",&ret);
566   //zmpeg3_t* zsrc = mpeg3_zopen(0,"/tmp/dat",&ret,ZIO_UNBUFFERED+ZIO_SINGLE_ACCESS+ZIO_SEQUENTIAL);
567   //zmpeg3_t* zsrc = mpeg3_open("/tmp/dat.toc",&ret);
568   //zmpeg3_t* zsrc = mpeg3_open("/root/LimeWire/Shared/Britney Spears - Pepsi Commercial with Bob Dole.mpeg",&ret);
569   //zmpeg3_t* zsrc = mpeg3_open("/tmp/dat.mp3",&ret);
570   //zmpeg3_t* zsrc = mpeg3_open("/dvd/VIDEO_TS/VTS_01_0.IFO",&ret);
571   //zmpeg3_t* zsrc = mpeg3_open("/home/dat2.vts",&ret);
572   //zmpeg3_t* zsrc = mpeg3_open("/dvd/BDMV/STREAM/00000.m2ts",&ret);
573   //zmpeg3_t* zsrc = mpeg3_open("/root/xcolorbars1.mpg",&ret);
574   zmpeg3_t* zsrc = mpeg3_open(av[1],&ret);
575   ysrc = zsrc;
576   printf(" ret = %d\n",ret);
577   if( ret != 0 ) exit(1);
578   mpeg3_stats(zsrc);
579
580 #ifdef AUDIO
581 #ifdef __cplusplus
582   zmpeg3_t* zsrc1 = zsrc;
583 #else
584   zmpeg3_t* zsrc1 = mpeg3_open_copy(zsrc->fs->path, zsrc, &ret);
585 #endif
586 #endif
587
588   mpeg3_set_cpus(zsrc,3);
589   pos = 0;
590 #ifdef LAST8M
591   if( stat(zsrc->fs->path, &st_buf) >= 0 ) {
592     if( (pos = (st_buf.st_size & ~0x7ffl) - 0x800000l) < 0 )
593       pos = 0;
594   }
595 #endif
596   mpeg3_seek_byte(zsrc, pos);
597   mpeg3_show_subtitle(zsrc, 0, 0);
598   signal(SIGINT,sigint);
599
600 #ifdef AUDIO
601   pthread_create(&the_audio, NULL, audio_thread, (void*)zsrc1);
602 #endif
603 #ifdef VIDEO
604   pthread_create(&the_video, NULL, video_thread, (void*)zsrc);
605 #endif
606 #ifdef VIDEO
607   pthread_join(the_video,NULL);
608 #endif
609 #ifdef AUDIO
610   pthread_join(the_audio,NULL);
611 #endif
612
613   mpeg3_close(zsrc);
614   return 0;
615 }
616