initial commit
[goodguy/history.git] / cinelerra-5.0 / db / utils / xtv.C
1 #include <stdio.h>
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <time.h>
8 #include <math.h>
9
10 #include <X11/X.h>
11 #include <X11/Xlib.h>
12 #include <X11/extensions/Xvlib.h>
13
14 #include <alsa/asoundlib.h>
15 #include <sys/time.h>
16 #include <sys/ipc.h>
17 #include <sys/shm.h>
18
19 #include "libzmpeg3/libzmpeg3.h"
20 #include "thr.h"
21 #include "tdb.h"
22
23 /*
24 c++ -g -pthread -I../.. -I ../guicast xtv.C \
25   ../../cinelerra/x86_64/mediadb.o ../../cinelerra/x86_64/filexml.o \
26   ../../libzmpeg/x86_64/libzmpeg3.a ../../db/x86_64/db.a \
27   -lX11 -lXext -lasound -lm
28 */
29
30 using namespace std;
31 int building = getenv("BUILDING") ? 1 : 0;
32
33 zmpeg3_t *ysrc;
34
35 const double nudge = 0.0;
36 const double ahead = 0.0;
37 static int verbose = 0;
38
39 typedef int (*skim_fn)(void *vp, int track);
40
41
42 #define AUDIO
43 #define VIDEO
44 #define DB
45 //#define LAST8M
46
47
48 #ifdef AUDIO
49 snd_pcm_t *zpcm = 0;
50 snd_pcm_uframes_t zpcm_ufrm_size = 0;
51 snd_pcm_sframes_t zpcm_sbfr_size = 0;
52 snd_pcm_sframes_t zpcm_sper_size = 0;
53 snd_pcm_sframes_t zpcm_total_samples = 0;
54 double zpcm_play_time = 0.;
55 Mutex zpcm_lock;
56 unsigned int zpcm_rate = 0;
57 static const char *zpcm_device = "plughw:0,0";
58 static unsigned int zpcm_bfr_time_us = 500000; 
59 static unsigned int zpcm_per_time_us = 200000;
60 static snd_pcm_channel_area_t *zpcm_areas = 0;
61 static int zpcm_channels = 0;
62 static short **zpcm_buffers = 0;
63 static short *zpcm_samples = 0;
64 static short *zpcm_silence = 0;
65 static int alsa_mute = 0;
66
67 void alsa_close()
68 {
69   if( zpcm_buffers != 0 ) { delete [] zpcm_buffers; zpcm_buffers = 0; }
70   if( zpcm_areas != 0 ) { delete [] zpcm_areas; zpcm_areas = 0; }
71   if( zpcm_silence != 0 ) { delete [] zpcm_silence; zpcm_silence = 0; }
72   if( zpcm_samples != 0 ) { delete [] zpcm_samples; zpcm_samples = 0; }
73   if( zpcm != 0 ) { snd_pcm_close(zpcm);  zpcm = 0; }
74 }
75
76 void alsa_open(int chs,int rate)
77 {
78   int ich, bits, dir, ret;
79   zpcm_lock.reset();
80   snd_pcm_format_t fmt = SND_PCM_FORMAT_S16;
81   snd_pcm_hw_params_t *phw;
82   snd_pcm_sw_params_t *psw;
83   snd_pcm_hw_params_alloca(&phw);
84   snd_pcm_sw_params_alloca(&psw);
85
86   zpcm = 0;
87   zpcm_total_samples = 0;
88   zpcm_play_time = 0.;
89   ret = snd_pcm_open(&zpcm, zpcm_device,
90      SND_PCM_STREAM_PLAYBACK, 0 /* + SND_PCM_NONBLOCK */);
91   if( ret >= 0 )
92     ret = snd_pcm_hw_params_any(zpcm, phw);
93   if( ret >= 0 )
94     ret = snd_pcm_hw_params_set_rate_resample(zpcm, phw, 1);
95   if( ret >= 0 )
96     ret = snd_pcm_hw_params_set_access(zpcm, phw,
97       SND_PCM_ACCESS_RW_NONINTERLEAVED);
98   if( ret >= 0 )
99     ret = snd_pcm_hw_params_set_format(zpcm, phw, fmt);
100   if( ret >= 0 ) {
101     zpcm_channels = chs;
102     ret = snd_pcm_hw_params_set_channels(zpcm, phw, chs);
103   }
104   if( ret >= 0 ) {
105     zpcm_rate = rate;
106     ret = snd_pcm_hw_params_set_rate_near(zpcm, phw, &zpcm_rate, 0);
107     if( (int)zpcm_rate != rate )
108       printf("nearest audio_rate for %d is %u\n",rate,zpcm_rate);
109   }
110   if( ret >= 0 )
111     ret = snd_pcm_hw_params_set_buffer_time_near(zpcm, phw,
112       &zpcm_bfr_time_us, &dir);
113   if( ret >= 0 )
114     ret = snd_pcm_hw_params_get_buffer_size(phw, &zpcm_ufrm_size);
115   if( ret >= 0 ) {
116     zpcm_sbfr_size = zpcm_ufrm_size;
117     ret = snd_pcm_hw_params_set_period_time_near(zpcm, phw,
118       &zpcm_per_time_us, &dir);
119   }
120   if( ret >= 0 )
121     ret = snd_pcm_hw_params_get_period_size(phw, &zpcm_ufrm_size, &dir);
122   if( ret >= 0 ) {
123     zpcm_sper_size = zpcm_ufrm_size;
124     ret = snd_pcm_hw_params(zpcm, phw);
125   }
126   if( ret >= 0 )
127     ret = snd_pcm_sw_params_current(zpcm, psw);
128   if( ret >= 0 )
129     ret = snd_pcm_sw_params_set_start_threshold(zpcm, psw,
130       (zpcm_sbfr_size / zpcm_sper_size) * zpcm_sper_size);
131   if( ret >= 0 )
132     ret = snd_pcm_sw_params_set_avail_min(zpcm, psw, zpcm_sper_size);
133   if( ret >= 0 )
134     ret = snd_pcm_sw_params(zpcm, psw);
135   /* snd_pcm_dump(zpcm, stdout); */
136
137   if( ret >= 0 ) {
138      zpcm_areas = new snd_pcm_channel_area_t[chs];
139      bits = snd_pcm_format_physical_width(fmt);
140      zpcm_silence = new short[zpcm_sper_size];
141      memset(zpcm_silence,0,zpcm_sper_size*sizeof(zpcm_silence[0]));
142      zpcm_samples = new short[zpcm_sper_size * chs];
143      zpcm_buffers = new short *[chs];
144      if( zpcm_samples ) {
145        for( ich = 0; ich < chs; ++ich ) {
146          zpcm_areas[ich].addr = zpcm_samples;
147          zpcm_areas[ich].first = ich * zpcm_sper_size * bits;
148          zpcm_areas[ich].step = bits;
149          zpcm_buffers[ich] = zpcm_samples + ich*zpcm_sper_size;
150        }
151      }
152      else {
153        fprintf(stderr,"alsa sample buffer allocation failure.\n");
154        ret = -999;
155      }
156   }
157   if( ret < 0 ) {
158     if( ret > -999 )
159       printf("audio error: %s\n", snd_strerror(ret));
160     alsa_close();
161   }
162 }
163
164 short *alsa_bfr(int ch) { return zpcm_buffers[ch]; }
165 int alsa_bfrsz() { return zpcm_sper_size; }
166
167 int alsa_recovery(int ret)
168 {
169   printf("alsa recovery\n");
170   switch( ret ) {
171   case -ESTRPIPE:
172     /* wait until the suspend flag is released, then fall through */
173     while( (ret=snd_pcm_resume(zpcm)) == -EAGAIN ) usleep(100000);
174   case -EPIPE:
175     ret = snd_pcm_prepare(zpcm);
176     if( ret < 0 )
177       printf("underrun, prepare failed: %s\n", snd_strerror(ret));
178     break;
179   default:
180     printf("unhandled error: %s\n",snd_strerror(ret));
181     break;
182   }
183   return ret;
184 }
185
186 int alsa_write(int length)
187 {
188   struct timeval tv;
189   int i, ret, count, retry;
190   snd_pcm_sframes_t sample_delay;
191   double play_time, time;
192   short *bfrs[zpcm_channels];
193   retry = 3;
194   ret = count = 0;
195   for( i=0; i<zpcm_channels; ++i ) bfrs[i] = alsa_mute ? zpcm_silence : zpcm_buffers[i];
196
197   while( count < length ) {
198     ret = snd_pcm_writen(zpcm,(void **)bfrs, length-count);
199     if( ret == -EAGAIN ) continue;
200     if ( ret < 0 ) {
201       if( --retry < 0 ) return ret;
202       alsa_recovery(ret);
203       ret = 0;
204       continue;
205     }
206     for( i=0; i<zpcm_channels; ++i ) bfrs[i] += ret;
207     count += ret;
208   }
209   zpcm_lock.lock();
210   zpcm_total_samples += count;
211   snd_pcm_delay(zpcm,&sample_delay);
212   if( sample_delay > zpcm_total_samples )
213     sample_delay = zpcm_total_samples;
214   gettimeofday(&tv,NULL);
215   time = tv.tv_sec + tv.tv_usec/1000000.0;
216   play_time = (zpcm_total_samples - sample_delay) / (double)zpcm_rate;
217   zpcm_play_time = time - play_time;
218   zpcm_lock.unlock();
219   return ret < 0 ? ret : 0;
220 }
221
222 double alsa_time()
223 {
224   double time, play_time;
225   struct timeval tv;
226   zpcm_lock.lock();
227   gettimeofday(&tv,NULL);
228   time = tv.tv_sec + tv.tv_usec/1000000.0;
229   play_time = time - zpcm_play_time;
230   zpcm_lock.unlock();
231   return play_time;
232 }
233
234 #else
235
236 double alsa_time()
237 {
238   double time;
239   struct timeval tv;
240   gettimeofday(&tv,NULL);
241   time = tv.tv_sec + tv.tv_usec/1000000.0;
242   return time;
243 }
244
245 #endif
246
247
248 double tstart;
249
250 double the_time()
251 {
252   double time;
253   struct timeval tv;
254   gettimeofday(&tv,NULL);
255   time = tv.tv_sec + tv.tv_usec/1000000.0;
256   if( tstart < 0. ) tstart = time;
257   return time - tstart;
258 }
259
260
261 void mpeg3_stats(zmpeg3_t *zsrc)
262 {
263   int astream, vstream;
264   int has_audio, total_astreams, audio_channels, sample_rate;
265   int has_video, total_vstreams, width, height, colormodel;
266   long audio_samples, video_frames;
267   float frame_rate;
268
269   has_audio = mpeg3_has_audio(zsrc);
270   printf(" has_audio = %d\n", has_audio);
271   total_astreams = mpeg3_total_astreams(zsrc);
272   printf(" total_astreams = %d\n", total_astreams);
273   for( astream=0; astream<total_astreams; ++astream ) {
274     audio_channels = zsrc->audio_channels(astream);
275     printf("   audio_channels = %d\n", audio_channels);
276     sample_rate = zsrc->sample_rate(astream);
277     printf("   sample_rate = %d\n", sample_rate);
278     audio_samples = zsrc->audio_samples(astream);
279     printf("   audio_samples = %ld\n", audio_samples);
280   }
281   printf("\n");
282   has_video = mpeg3_has_video(zsrc);
283   printf(" has_video = %d\n", has_video);
284   total_vstreams = mpeg3_total_vstreams(zsrc);
285   printf(" total_vstreams = %d\n", total_vstreams);
286   for( vstream=0; vstream<total_vstreams; ++vstream ) {
287     width = zsrc->video_width(vstream);
288     printf("   video_width = %d\n", width);
289     height = zsrc->video_height(vstream);
290     printf("   video_height = %d\n", height);
291     frame_rate = zsrc->frame_rate(vstream);
292     printf("   frame_rate = %f\n", frame_rate);
293     video_frames = zsrc->video_frames(vstream);
294     printf("   video_frames = %ld\n", video_frames);
295     colormodel = zsrc->colormodel(vstream);
296     printf("   colormodel = %d\n", colormodel);
297   }
298 }
299
300 #ifdef DB
301
302 #include "guicast/linklist.h"
303 #include "guicast/arraylist.h"
304 #include "cinelerra/mediadb.h"
305
306 class SdbPacket;
307 class SdbPacketQueue;
308 class SdbSkimFrame;
309 class SkimDbThread;
310 class Media;
311
312
313 class SdbPacket : public ListItem<SdbPacket>
314 {
315 public:
316         enum sdb_packet_type { sdb_none, sdb_skim_frame, } type;
317         SkimDbThread *thread;
318         void start();
319         virtual void run() = 0;
320
321         SdbPacket(sdb_packet_type ty, SkimDbThread *tp) : type(ty), thread(tp) {}
322         ~SdbPacket() {}
323 };
324
325 class SdbPacketQueue : public List<SdbPacket>, public Mutex
326 {
327 public:
328         SdbPacket *get_packet();
329         void put_packet(SdbPacket *p);
330 };
331
332 void SdbPacketQueue::
333 put_packet(SdbPacket *p)
334 {
335         lock();
336         append(p);
337         unlock();
338 }
339
340 SdbPacket *SdbPacketQueue::
341 get_packet()
342 {
343         lock();
344         SdbPacket *p = first;
345         remove_pointer(p);
346         unlock();
347         return p;
348 }
349
350
351 class SdbSkimFrame : public SdbPacket
352 {
353 public:
354         int pid;
355         int64_t framenum;
356         double framerate;
357         uint8_t dat[SWIDTH*SHEIGHT];
358
359         void load(int pid, int64_t framenum, double position,
360                 uint8_t *idata,int mw,int mh,int iw,int ih);
361         void run();
362
363         SdbSkimFrame(SkimDbThread *t) : SdbPacket(sdb_skim_frame, t) {}
364         ~SdbSkimFrame() {}
365 };
366
367
368 class Media {
369 public:
370   int skimming_active;
371
372   int set_skimming(int track, int skim, skim_fn fn, void *vp);
373   int get_video_info(int track, int &pid,
374     double &framerate, int &width, int &height, char *title);
375   static int skimming(void *vp, int track);
376   int skimming(int track);
377   void start_skimming();
378   void stop_skimming();
379
380   Media() { skimming_active = 0; }
381   ~Media() {}
382 };
383
384 int Media::set_skimming(int track, int skim, skim_fn fn, void *vp)
385 {
386         return !fn ? ysrc->set_thumbnail_callback(track, 0, 0, 0, 0) :
387                 ysrc->set_thumbnail_callback(track, skim, 1, fn, vp);
388 }
389
390 int Media::get_video_info(int track, int &pid,
391     double &framerate, int &width, int &height, char *title)
392 {       
393   pid = ysrc->video_pid(track);
394   framerate = ysrc->frame_rate(track);
395   width = ysrc->video_width(track);
396   height = ysrc->video_height(track);
397   if( !title ) return 0;
398   *title = 0;
399   
400   int elements = ysrc->dvb.channel_count();
401   for( int n=0; n<elements; ++n ) {
402     int major, minor, total_vstreams, vstream, vidx;
403     if( ysrc->dvb.get_channel(n, major, minor) ||
404         ysrc->dvb.total_vstreams(n, total_vstreams) ) continue;
405     for( vidx=0; vidx<total_vstreams; ++vidx ) {
406       if( ysrc->dvb.vstream_number(n, vidx, vstream) ) continue;
407       if( vstream < 0 ) continue;
408       if( vstream == track ) {
409         sprintf(title, "%3d.%-3d", major, minor);
410         return 0;
411       }
412     }
413   }
414   return 0;
415 }
416
417 int Media::skimming(void *vp, int track)
418 {
419   return ((Media*)vp)->skimming(track);
420 }
421
422
423 class SkimDbThread : public Thread, public Media, public MediaDb
424 {
425         SdbPacketQueue active_packets;
426         Condition *input_lock;
427         friend class SdbSkimFrame;
428 public: 
429         int done;
430         int sfrm_sz;
431         double framerate_ratio;
432
433         SdbPacketQueue skim_frames;
434         Snips *snips;
435
436         void start();
437         void stop();
438         void run();
439         void put_packet(SdbPacket *p);
440         int skim(int pid,int64_t framenum,double framerate,
441                 uint8_t *idata,int mw,int mh,int iw,int ih);
442         int skim_frame(Snips *snips, uint8_t *tdat, double position);
443         double abs_err(Snip *snip, double *ap, int j, int len, double iframerate);
444         double frame_weight(uint8_t *tdat, int rowsz, int width, int height);
445         int verify_snip(Snip *snip, double weight, double position);
446
447         SkimDbThread();
448         ~SkimDbThread();
449 } *the_db;
450
451 int Media::skimming(int track)
452 {
453   int64_t framenum; uint8_t *tdat; int mw, mh;
454   if( ysrc->get_thumbnail(track, framenum, tdat, mw, mh) ) return 1;
455   int pid, width, height;  double framerate;
456   if( get_video_info(track, pid, framerate, width, height, 0) ) return 1;
457   if( !framerate ) return 1;
458 //printf("Media::skimming framenum %ld, framerate %f\n",framenum,framerate);
459   return the_db->skim(pid,framenum,framerate, tdat,mw,mh,width,height);
460 }
461
462 void Media::start_skimming()
463 {
464   if( !skimming_active ) {
465     skimming_active = 1;
466     the_db->start();
467     set_skimming(0, 0, skimming, this);
468   }
469 }
470
471 void Media::stop_skimming()
472 {
473   if( skimming_active ) {
474     skimming_active = 0;
475     set_skimming(0, 0, 0, 0);
476     the_db->stop();
477   }
478 }
479
480
481 SkimDbThread::
482 SkimDbThread()
483  : Thread()
484 {
485         input_lock = new Condition(0); // "SkimDbThread::input_lock");
486         sfrm_sz = SWIDTH*SHEIGHT;
487         for( int i=32; --i>=0; ) skim_frames.append(new SdbSkimFrame(this));
488         snips = new Snips();
489         done = 1;
490 }
491
492 SkimDbThread::
493 ~SkimDbThread()
494 {
495         stop();
496         delete snips;
497         delete input_lock;
498 }
499
500
501
502 void SkimDbThread::
503 start()
504 {
505         if( openDb() ) return;
506         if( detachDb() ) return;
507         done = 0;
508         Thread::start();
509 }
510
511 void SkimDbThread::
512 stop()
513 {
514         if( running() ) {
515                 done = 1;
516                 input_lock->unlock();
517                 cancel();
518                 join();
519                 closeDb();
520         }
521 }
522
523 int SkimDbThread::
524 skim(int pid,int64_t framenum,double framerate,
525         uint8_t *idata,int mw,int mh,int iw,int ih)
526 {
527         SdbSkimFrame *sf = (SdbSkimFrame *)skim_frames.get_packet();
528 if( !sf ) printf("SkimDbThread::skim no packet\n");
529         if( !sf ) return 1;
530         sf->load(pid,framenum,framerate, idata,mw,mh,iw,ih);
531         sf->start();
532         return 0;
533 }
534
535
536 void SdbPacket::start()
537 {
538         thread->put_packet(this);
539 }
540
541 void SkimDbThread::
542 put_packet(SdbPacket *p)
543 {
544         active_packets.put_packet(p);
545         input_lock->unlock();
546 }
547
548 void SkimDbThread::
549 run()
550 {
551         while( !done ) {
552                 input_lock->lock(); //"SkimDbThread::run");
553                 if( done ) break;
554                 SdbPacket *p = active_packets.get_packet();
555                 if( !p ) continue;
556                 attachDb();
557                 p->run();
558                 detachDb();
559         }
560 }
561
562
563 void SdbSkimFrame::
564 load(int pid,int64_t framenum, double framerate, uint8_t *idata,int mw,int mh,int iw,int ih)
565 {
566         int sw=SWIDTH, sh=SHEIGHT;
567         this->pid = pid;
568         this->framenum = framenum;
569         this->framerate = framerate;
570 //write_pbm(idata, mw, mh, "/tmp/data/f%05d.pbm",framenum);
571         Scale(idata,0,mw,mh,0,0,iw/8,ih/8).scale(dat,sw,sh,0,0,sw,sh);
572 //write_pbm(dat, SWIDTH, SHEIGHT, "/tmp/data/s%05d.pbm",framenum);
573 }
574
575 void SdbSkimFrame::
576 run()
577 {
578 //write_pbm(dat, SWIDTH, SHEIGHT, "/tmp/data/h%05d.pbm",framenum);
579         double position = framenum / framerate;
580         thread->skim_frame(thread->snips, dat, position);
581         thread->skim_frames.put_packet(this);
582 }
583
584
585 double SkimDbThread::
586 frame_weight(uint8_t *tdat, int rowsz, int width, int height)
587 {
588         int64_t weight = 0;
589         for( int y=height; --y>=0; tdat+=rowsz ) {
590                 uint8_t *bp = tdat;
591                 for( int x=width; --x>=0; ++bp ) weight += *bp;
592         }
593         return (double)weight / (width*height);
594 }
595
596 int SkimDbThread::
597 skim_frame(Snips *snips, uint8_t *tdat, double position)
598 {
599         int fid, result = get_frame_key(tdat, fid, snips, position, 1);
600         double weight = frame_weight(tdat, SWIDTH, SWIDTH, SHEIGHT);
601         for( Clip *next=0,*clip=snips->first; clip; clip=next ) {
602                 next = clip->next;
603                 result = verify_snip((Snip*)clip,weight,position);
604                 if( !result ) {
605                         if( clip->votes >= 2 || position-clip->start > 2 ) {
606                                 if( !(clip->groups & 4) ) {
607                                         clip->groups |= 4;
608                                         if( !alsa_mute ) {
609                                                 alsa_mute = 1;
610                                                 printf("*** MUTE AUDIO clip %d\n", clip->clip_id);
611                                         }
612                                 }
613                         }
614                 }
615                 else if( result < 0 || --clip->votes < 0 ) {
616 printf("delete clip %d, snips %d\n", clip->clip_id, snips->count-1);
617                         delete clip;  --snips->count;
618                         if( !snips->first && alsa_mute ) {
619                                 alsa_mute = 0;
620                                 printf("*** UNMUTE AUDIO clip %d\n", clip->clip_id);
621                         }
622                 }
623         }
624 if( snips->count > 0 ) { printf("snips %d = ", snips->count);
625   for( Clip *cp=snips->first; cp; cp=cp->next ) printf(" %d/%d",cp->clip_id,cp->votes);
626 printf("\n"); }
627         return 0;
628 }
629
630 double SkimDbThread::
631 abs_err(Snip *snip, double *ap, int j, int len, double iframerate)
632 {
633         double vv = 0;
634         int i, k, sz = snip->weights.size(), n = 0;
635         for( i=0; i<sz; ++i ) {
636                 k = j + (snip->positions[i] - snip->start) * iframerate + 0.5;
637                 if( k < 0 ) continue;
638                 if( k >= len ) break;
639                 double a = ap[k], b =  snip->weights[i];
640                 double dv = fabs(a - b);
641                 vv += dv;
642                 ++n;
643         }
644         return !n ? MEDIA_WEIGHT_ERRLMT : vv / n;
645 }
646
647 int SkimDbThread::
648 verify_snip(Snip *snip, double weight, double position)
649 {
650         int cid = snip->clip_id;
651         if( clip_id(cid) ) return -1;
652         double iframerate = clip_framerate();
653         int iframes = clip_frames();
654         double pos = position - snip->start;
655         int iframe_no = pos * iframerate + 0.5;
656         if( iframe_no >= iframes ) return -1;
657         snip->weights.append(weight);
658         snip->positions.append(position);
659 printf("%f %d ",weight,iframe_no);
660         double *iweights = clip_weights();
661         double errlmt = MEDIA_WEIGHT_ERRLMT;
662         double err = errlmt, kerr = err;
663         int k = 0;
664         int tmargin = TRANSITION_MARGIN * iframerate + 0.5;
665         for( int j=-tmargin; j<=tmargin; ++j ) {
666                 err = abs_err(snip, iweights, j, iframes, iframerate);
667 //printf("   err %f, j=%d\n", err, j);
668                 if( err < kerr ) { kerr = err;  k = j; }
669         }
670 printf("kerr %f, k=%d ", kerr, k);
671         if( kerr >= errlmt ) return 1;
672         if( iframe_no + k >= iframes ) return -1;
673         return 0;
674 }
675
676
677 #endif
678
679 #ifdef VIDEO
680
681 class Video : public Thread {
682   zmpeg3_t *zsrc;
683   double vstart;
684   double last_vtime;
685   double video_time(int stream);
686
687 public:
688   void run();
689
690   Video(zmpeg3_t *z) : zsrc(z) {}
691   ~Video() {}
692 } *the_video;
693
694
695 double Video::video_time(int stream)
696 {
697   double vtime = zsrc->get_video_time(stream);
698   double vnow = vtime;
699   if( vstart == 0. ) vstart = vnow;
700   if( vtime < last_vtime )
701     vstart -= last_vtime;
702   last_vtime = vtime;
703   return vnow - vstart + nudge;
704 }
705
706 void Video::run()
707 {
708   Window w, root;
709   Display *display;
710   GC gc;
711   XGCValues gcv;
712   XEvent xev;
713   XImage *image0, *image1, *image;
714   Visual *visual;
715   Screen *screen;
716   XShmSegmentInfo info0, info1;
717   int done;
718   double delay;
719   float frame_rate, frame_delay;
720   uint8_t **rows, **row0, **row1, *cap, *cap0, *cap1;
721   int ret, row, frame, dropped, more_data;
722   int width, height, depth, owidth, oheight;
723   const int frame_drop = 0;
724   const int shared_memory = 1;
725
726   display = XOpenDisplay(NULL);
727   if( display == NULL ) {
728     fprintf(stderr,"cant open display\n");
729     exit(1);
730   }
731
732   root = RootWindow(display,0);
733   screen = DefaultScreenOfDisplay(display);
734   depth = DefaultDepthOfScreen(screen);
735   visual = DefaultVisualOfScreen(screen);
736   if( visual->c_class != TrueColor ) {
737     printf("visual class not truecolor\n");
738     exit(1);
739   }
740   int image_bpp = 4;
741  
742   frame_rate = zsrc->frame_rate(0);
743   frame_delay = 2.0 / frame_rate;
744   height = zsrc->video_height(0);
745   width = zsrc->video_width(0);
746
747   //oheight = 2*height / 3;
748   //owidth = 2*width / 3;
749   //oheight = 1050-96;
750   //owidth = 1680-64;
751   //oheight = height;
752   //owidth = width;
753   oheight = 720;
754   owidth = 1280;
755
756   w = XCreateSimpleWindow(display, root, 0, 0, owidth, oheight,
757     0, 0, WhitePixelOfScreen(screen));
758   XSelectInput(display, w, ExposureMask|StructureNotifyMask|ButtonPressMask);
759
760   if( !shared_memory ) {
761     int sz0 = oheight*owidth*image_bpp + 4;
762     cap0 = new uint8_t[sz0];
763     image0 = XCreateImage(display, visual, depth, ZPixmap, 0,
764          (char*)cap0, owidth, oheight, 8, owidth*4);
765     int sz1 = oheight*owidth*image_bpp + 4;
766     cap1 = new uint8_t[sz1];
767     image1 = XCreateImage(display, visual, depth, ZPixmap, 0,
768          (char*)cap1, owidth, oheight, 8, owidth*4);
769   }
770   else {
771     image0 = XShmCreateImage(display, visual, depth, ZPixmap, 0,
772          &info0, owidth, oheight);
773     int sz0 = oheight*image0->bytes_per_line+image_bpp;
774     info0.shmid = shmget(IPC_PRIVATE, sz0, IPC_CREAT | 0777);
775     if( info0.shmid < 0) { perror("shmget"); exit(1); }
776     cap0 = (uint8_t *)shmat(info0.shmid, NULL, 0);
777     shmctl(info0.shmid, IPC_RMID, 0);
778     image0->data = info0.shmaddr = (char *)cap0;
779     info0.readOnly = 0;
780     XShmAttach(display,&info0);
781     image1 = XShmCreateImage(display, visual, depth, ZPixmap, 0,
782          &info1, owidth, oheight);
783     int sz1 = oheight*image1->bytes_per_line+image_bpp;
784     info1.shmid = shmget(IPC_PRIVATE, sz1, IPC_CREAT | 0777);
785     if( info1.shmid < 0) { perror("shmget"); exit(1); }
786     cap1 = (uint8_t *)shmat(info1.shmid, NULL, 0);
787     shmctl(info1.shmid, IPC_RMID, 0);
788     image1->data = info1.shmaddr = (char *)cap1;
789     info1.readOnly = 0;
790     XShmAttach(display,&info1);
791   }
792
793   row0 = new uint8_t *[oheight];
794   row1 = new uint8_t *[oheight];
795   for( row=0; row<oheight; ++row ) {
796     row0[row] = cap0 + row*owidth*image_bpp;
797     row1[row] = cap1 + row*owidth*image_bpp;
798   }
799
800   cap = cap0;
801   rows = row0;
802   image = image0;
803   if( image->bits_per_pixel != 32 ) {
804     printf("image bits_per_pixel=%d\n",image->bits_per_pixel);
805     exit(1);
806   }
807   int cmdl = -1;
808   unsigned int rmsk = image->red_mask;
809   unsigned int gmsk = image->green_mask;
810   unsigned int bmsk = image->blue_mask;
811   switch( image_bpp ) {
812   case 4:
813     if( bmsk==0xff0000 && gmsk==0x00ff00 && rmsk==0x0000ff )
814       cmdl = zmpeg3_t::cmdl_RGBA8888;
815     else if( bmsk==0x0000ff && gmsk==0x00ff00 && rmsk==0xff0000 )
816       cmdl = zmpeg3_t::cmdl_BGRA8888;
817     break;
818   case 3:
819     if( bmsk==0xff0000 && gmsk==0x00ff00 && rmsk==0x0000ff )
820       cmdl = zmpeg3_t::cmdl_RGB888;
821     else if( bmsk==0x0000ff && gmsk==0x00ff00 && rmsk==0xff0000 )
822       cmdl = zmpeg3_t::cmdl_BGR888;
823     break;
824   case 2:
825     if( bmsk==0x00f800 && gmsk==0x0007e0 && rmsk==0x00001f )
826       cmdl = zmpeg3_t::cmdl_RGB565;
827     break;
828   }
829   if( cmdl < 0 ) {
830     printf("unknown color model, bpp=%d, ",image_bpp);
831     printf(" rmsk=%08x,gmsk=%08x,bmsk=%08x\n", rmsk, gmsk, bmsk);
832     exit(1);
833   }
834
835   XMapWindow(display, w);
836   XFlush(display);
837   gcv.function = GXcopy;
838   gcv.foreground = BlackPixelOfScreen(DefaultScreenOfDisplay(display));
839   gcv.line_width = 1;
840   gcv.line_style = LineSolid;
841   int m = GCFunction|GCForeground|GCLineWidth|GCLineStyle;
842   gc = XCreateGC(display,w,m,&gcv);
843
844   frame = dropped = 0;
845   vstart = last_vtime = 0.;
846   more_data = 1;
847   done = 0;
848
849   while( !done && running() && more_data ) {
850     if( !XPending(display) ) {
851       more_data = 0;
852       delay = (frame+dropped)/frame_rate - alsa_time();
853 //      delay = the_time() - video_time(zsrc,0);
854       if( frame_drop ) {
855         if( -delay >= frame_delay ) {
856           int nframes = (long)ceil(-delay/frame_rate);
857           if( nframes > 2 ) nframes = 2;
858           zsrc->drop_frames(nframes,0);
859           dropped += nframes;
860         }
861       }
862       //printf("delay %f\n",delay*1000.);
863       if( delay > 0 )
864         usleep((int)(delay*1000000.0));
865       ret = zsrc->read_frame(rows, 0, 0, width, height,
866          owidth, oheight, cmdl, 0);
867       //printf("%d %ld\n",frame,zsrc->vtrack[0]->demuxer->titles[0]->fs->current_byte);
868       if( verbose )
869         printf("read_video(stream=%d, frame=%d) = %d, %f sec\n", 0, frame, ret, the_time());
870       ++frame;
871
872       if( !shared_memory )
873         XPutImage(display, w, gc, image, 0, 0, 0, 0, owidth, oheight);
874       else
875         XShmPutImage(display, w, gc, image, 0, 0, 0, 0, owidth, oheight, False);
876
877       cap = cap==cap0 ?
878   (rows=row1, image=image1, cap1) :
879   (rows=row0, image=image0, cap0) ;
880       //printf(" %d/%d  %f  %f  %f\n",frame,dropped,(frame+dropped)/frame_rate,
881       //  video_time(zsrc,0),zsrc->get_video_time(0));
882       more_data |= !zsrc->end_of_video(0);
883     }
884     else {
885       XNextEvent(display,&xev);
886       /* printf("xev.type = %d/%08x\n",xev.type,xev.xany.window); */
887       switch( xev.type ) {
888       case ButtonPress:
889         if( xev.xbutton.window != w ) continue;
890         if( xev.xbutton.button != Button1 )
891           done = 1;
892         break;
893       case Expose:
894         break;
895       case DestroyNotify:
896         done = 1;
897         break;
898       }
899     }
900   }
901
902   XCloseDisplay(display);
903   delete [] row0; row0 = 0;
904   delete [] row1; row1 = 0;
905 }
906
907 #endif
908
909 #ifdef AUDIO
910
911 class Audio : public Thread {
912   zmpeg3_t *zsrc;
913   double astart;
914   double audio_time(int stream);
915
916 public:
917   void run();
918
919   Audio(zmpeg3_t *z) : zsrc(z) {}
920   ~Audio() {}
921 } *the_audio;
922
923
924 double Audio::audio_time(int stream)
925 {
926   double anow = zsrc->get_audio_time(stream);
927   if( astart < 0. ) astart = anow;
928   return anow - astart;
929 }
930
931 void Audio::run()
932 {
933   double delay;
934   int audio_channels, sample_rate, more_data;
935   int stream = 0;
936
937   audio_channels = zsrc->audio_channels(stream);
938   sample_rate = zsrc->sample_rate(stream);
939   alsa_open(audio_channels,sample_rate);
940   astart = -1.;
941   more_data = 1;
942
943   while( running() && more_data ) {
944     more_data = 0;
945     int ich;
946     int n = alsa_bfrsz();
947     for( ich=0; ich<audio_channels; ++ich) {
948       short *bfr = alsa_bfr(ich);
949       int ret = ich == 0 ?
950         zsrc->read_audio(bfr, ich, n, stream) :
951         zsrc->reread_audio(bfr, ich, n, stream);
952       if( verbose )
953         printf("read_audio(stream=%d,channel=%d) = %d\n", stream, ich, ret);
954     }
955
956     alsa_write(n);
957     delay = audio_time(stream) - the_time();
958     if( (delay-=ahead) > 0. ) {
959       usleep((int)(delay*1000000.0/2.));
960     }
961     more_data |= !zsrc->end_of_audio(0);
962   }
963
964   alsa_close();
965 }
966
967 #endif
968
969 void sigint(int n)
970 {
971 #ifdef VIDEO
972   the_video->cancel();
973 #endif
974 #ifdef AUDIO
975   the_audio->cancel();
976 #endif
977 #ifdef DB
978   the_db->stop_skimming();
979 #endif
980 }
981
982
983 int main(int ac, char **av)
984 {
985   int ret;
986   long pos;
987 #ifdef LAST8M
988   struct stat st_buf;
989 #endif
990   setbuf(stdout,NULL);
991   tstart = -1.;
992
993 #ifdef VIDEO
994   XInitThreads();
995 #endif
996
997   zmpeg3_t* zsrc = new zmpeg3_t(av[1],ret);
998   ysrc = zsrc;
999   printf(" ret = %d\n",ret);
1000   if( ret != 0 ) exit(1);
1001   mpeg3_stats(zsrc);
1002
1003   zsrc->set_cpus(8);
1004   pos = 0;
1005 #ifdef LAST8M
1006   if( stat(zsrc->fs->path, &st_buf) >= 0 ) {
1007     if( (pos = (st_buf.st_size & ~0x7ffl) - 0x800000l) < 0 )
1008       pos = 0;
1009   }
1010 #endif
1011   zsrc->seek_byte(pos);
1012 //  zsrc->show_subtitle(0);
1013   signal(SIGINT,sigint);
1014
1015 #ifdef DB
1016   the_db = new SkimDbThread();
1017 #endif
1018 #ifdef AUDIO
1019   the_audio = new Audio(zsrc);
1020 #endif
1021 #ifdef VIDEO
1022   the_video = new Video(zsrc);
1023 #endif
1024 #ifdef DB
1025   the_db->start_skimming();
1026 #endif
1027 #ifdef AUDIO
1028   the_audio->start();
1029 #endif
1030 #ifdef VIDEO
1031   the_video->start();
1032 #endif
1033 #ifdef VIDEO
1034   the_video->join();
1035 #endif
1036 #ifdef AUDIO
1037   the_audio->join();
1038 #endif
1039 #ifdef DB
1040   the_db->stop_skimming();
1041 #endif
1042
1043   delete zsrc;
1044   return 0;
1045 }
1046