Credit Andrew R for finding the direct copy mods for exr and ppm sequences
[goodguy/cinelerra.git] / cinelerra-5.1 / libzmpeg3 / dmux.C
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <unistd.h>
5 #include <signal.h>
6 #include <string.h>
7 #include <fcntl.h>
8 #include <math.h>
9 #include <errno.h>
10 #include <sys/time.h>
11 #include <gtk/gtk.h>
12 #include <gdk/gdk.h>
13 #include <gdk/gdkkeysyms.h>
14 #include <alsa/asoundlib.h>
15 #include <linux/dvb/dmx.h>
16 #include <linux/dvb/frontend.h>
17
18 /* c++ `pkg-config --cflags --libs gtk+-2.0` dmux.C thread.C ./x86_64/libzmpeg3.a -lpthread -lasound -lm */
19
20 #include "libzmpeg3.h"
21 #include "thread.h"
22
23 double the_time();
24 void reset();
25 int open_tuner(int dev_no,int chan,int vid_pid,int aud_pid);
26 void close_tuner();
27 double audio_time(int stream);
28 void alsa_close();
29 void alsa_open(int chs,int rate);
30 short *alsa_bfr(int ch);
31 int alsa_bfrsz();
32 int alsa_recovery(int ret);
33 int alsa_write(int len);
34 void dst_exit(GtkWidget *widget, gpointer data);
35 gint key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data);
36 double video_time(int stream);
37 void stats();
38 void sig_int(int n);
39 int main(int ac,char **av);
40
41 /* alpha currently is broken in gdk */
42 /*   would render faster if alpha worked correctly */
43 #define RGBA FALSE
44
45 #if RGBA
46 #define BPP 4
47 #define COLOR_MODEL zmpeg3_t::cmdl_RGBA8888
48 #else
49 #define BPP 3
50 #define COLOR_MODEL zmpeg3_t::cmdl_RGB888
51 #endif
52
53 int verbose = 0;
54 double nudge = .5; // 3.5;
55 const double ahead = .5;
56
57 //#define DEBUG
58 #ifndef DEBUG
59 #define dprintf(s...) do{}while(0)
60 #else
61 #define dprintf(s...) printf(s)
62 #endif
63
64 #define NETTUNE_AIR 1
65 #define NETTUNE_CABLE 2
66
67 static unsigned long ntsc_dvb[ ] = {
68   0,  0,  57,  63,  69,  79,  85, 177, 183, 189 ,
69   195, 201, 207, 213, 473, 479, 485, 491, 497, 503 ,
70   509, 515, 521, 527, 533, 539, 545, 551, 557, 563 ,
71   569, 575, 581, 587, 593, 599, 605, 611, 617, 623 ,
72   629, 635, 641, 647, 653, 659, 665, 671, 677, 683 ,
73   689, 695, 701, 707, 713, 719, 725, 731, 737, 743 ,
74   749, 755, 761, 767, 773, 779, 785, 791, 797, 803 ,
75   809, 815, 821, 827, 833, 839, 845, 851, 857, 863 ,
76   869, 875, 881, 887, 893, 899, 905, 911, 917, 923 ,
77   929, 935, 941, 947, 953, 959, 965, 971, 977, 983 ,
78   989, 995, 1001, 1007, 1013, 1019, 1025, 1031, 1037, 1043
79 };
80
81 static unsigned long catv_dvb[] = {
82   0, 0, 57, 63, 69, 79, 85, 177, 183, 189,
83   195, 201, 207, 213, 123, 129, 135, 141, 147, 153,
84   159, 165, 171, 219, 225, 231, 237, 243, 249, 255,
85   261, 267, 273, 279, 285, 291, 297, 303, 309, 315,
86   321, 327, 333, 339, 345, 351, 357, 363, 369, 375,
87   381, 387, 393, 399, 405, 411, 417, 423, 429, 435,
88   441, 447, 453, 459, 465, 471, 477, 483, 489, 495,
89   501, 507, 513, 519, 525, 531, 537, 543, 549, 555,
90   561, 567, 573, 579, 585, 591, 597, 603, 609, 615,
91   621, 627, 633, 639, 645,  93,  99, 105, 111, 117,
92   651, 657, 663, 669, 675, 681, 687, 693, 699, 705,
93   711, 717, 723, 729, 735, 741, 747, 753, 759, 765,
94   771, 777, 783, 789, 795, 781, 807, 813, 819, 825,
95   831, 837, 843, 849, 855, 861, 867, 873, 879, 885,
96   891, 897, 903, 909, 915, 921, 927, 933, 939, 945,
97   951, 957, 963, 969, 975, 981, 987, 993, 999
98 };
99
100 int frontend_fd;
101 int audio_fd;
102 int video_fd;
103 int dvr_fd;
104 int prev_lock;
105 int has_lock;
106 int done;
107 zmpeg3_t *zsrc;
108 int aud, vid;
109 int subchan;
110 int audio_done;
111 int video_done;
112
113 class vid_thread : public Thread
114 {
115   void Proc();
116 } *the_vid;
117
118 class aud_thread : public Thread
119 {
120   void Proc();
121 } *the_aud;
122
123 class lck_thread : public Thread
124 {
125   void Proc();
126 } *the_lck;
127
128 int do_status()
129 {
130   fe_status_t status;
131   uint16_t snr, signal;
132   uint32_t ber, uncorrected_blocks;
133
134   bzero(&status, sizeof(status));
135   ioctl(frontend_fd, FE_READ_STATUS, &status);
136   if( verbose ) {
137     ioctl(frontend_fd, FE_READ_SIGNAL_STRENGTH, &signal);
138     ioctl(frontend_fd, FE_READ_SNR, &snr);
139     ioctl(frontend_fd, FE_READ_BER, &ber);
140     ioctl(frontend_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks);
141     printf("lck:: %02x | signal %04x | snr %04x | ber %08x | unc %08x | ",
142            status, signal, snr, ber, uncorrected_blocks);
143   }
144   has_lock =  status & FE_HAS_LOCK ? 1 : 0;
145   if( verbose )
146     printf( has_lock ? "lock\n" : "lost\n" );
147   if( prev_lock != has_lock ) {
148     printf(" %s\n",has_lock ? "signal locked" : "signal lost");
149     prev_lock = has_lock;
150   }
151   return has_lock;
152 }
153
154 void lck_thread::
155 Proc()
156 {
157   dprintf("lck_thread::Proc()\n");
158   while( Running() ) {
159     do_status();
160     sleep(1);
161   }
162 }
163
164 double tstart;
165
166 double the_time()
167 {
168   double time;
169   struct timeval tv;
170   gettimeofday(&tv,NULL);
171   time = tv.tv_sec + tv.tv_usec/1000000.0;
172   if( tstart < 0. ) tstart = time;
173   return time - tstart;
174 }
175
176 void reset()
177 {
178   frontend_fd = -1;
179   audio_fd = -1;
180   video_fd = -1;
181   dvr_fd = -1;
182   the_vid = 0;
183   the_lck = 0;
184   prev_lock = 0;
185   has_lock = 0;
186   tstart = -1.;
187   aud = 0;
188   vid = 0;
189   subchan = -1;
190 }
191
192 int open_tuner(int dev_no,int chan,int vid_pid,int aud_pid)
193 {
194   char frontend_path[512];
195   char demux_path[512];
196   char dvr_path[512];
197   //dvr_fd = open("/tmp/dat7",O_RDONLY);
198   //return dvr_fd >= 0 ? 0 : 1;
199
200   sprintf(frontend_path, "/dev/dvb/adapter%d/frontend%d", dev_no, 0);
201   sprintf(demux_path, "/dev/dvb/adapter%d/demux%d", dev_no, 0);
202   sprintf(dvr_path, "/dev/dvb/adapter%d/dvr%d", dev_no, 0);
203   if( (frontend_fd=::open(frontend_path, O_RDWR)) < 0 ) {
204     fprintf(stderr, "open_tuner %s: %s\n", frontend_path, strerror(errno));
205     return 1;
206   }
207
208    struct dvb_frontend_parameters frontend_param;
209    bzero(&frontend_param, sizeof(frontend_param));
210
211 // Set frequency
212    int index = chan;
213    int table = NETTUNE_AIR;
214
215    switch(table) {
216    case NETTUNE_AIR:
217      frontend_param.frequency = ntsc_dvb[index] * 1000000;
218      frontend_param.u.vsb.modulation = VSB_8;
219      break;
220    case NETTUNE_CABLE:
221      frontend_param.frequency = catv_dvb[index] * 1000000;
222      frontend_param.u.vsb.modulation = QAM_AUTO;
223      break;
224    }
225
226    if( ioctl(frontend_fd, FE_SET_FRONTEND, &frontend_param) < 0 ) {
227      fprintf(stderr, "open_tuner FE_SET_FRONTEND frequency=%d: %s",
228        frontend_param.frequency, strerror(errno));
229      return 1;
230    }
231
232    int retry = 5;
233    while( !do_status() && --retry>=0 ) sleep(1);
234
235    if( retry < 0 ) {
236      fprintf(stderr, "open_tuner: no signal on channel %d\n",chan);
237      return 1;
238    }
239
240    if( (video_fd=::open(demux_path, O_RDWR)) < 0 ) {
241      fprintf(stderr, "open_tuner %s for video: %s\n",
242        demux_path, strerror(errno));
243      return 1;
244    }
245
246 // Setting exactly one PES filter to 0x2000 dumps the entire
247 // transport stream.
248    struct dmx_pes_filter_params pesfilter;
249    if( !vid_pid && !aud_pid ) {
250      pesfilter.pid = 0x2000;
251      pesfilter.input = DMX_IN_FRONTEND;
252      pesfilter.output = DMX_OUT_TS_TAP;
253      pesfilter.pes_type = DMX_PES_OTHER;
254      pesfilter.flags = DMX_IMMEDIATE_START;
255      if( ioctl(video_fd, DMX_SET_PES_FILTER, &pesfilter) < 0 ) {
256        fprintf(stderr, "open_tuner DMX_SET_PES_FILTER for raw: %s\n",
257          strerror(errno));
258        return 1;
259      }
260    }
261
262
263    if( vid_pid ) {
264      pesfilter.pid = vid_pid;
265      pesfilter.input = DMX_IN_FRONTEND;
266      pesfilter.output = DMX_OUT_TS_TAP;
267      pesfilter.pes_type = DMX_PES_VIDEO;
268      pesfilter.flags = DMX_IMMEDIATE_START;
269      if( ioctl(video_fd, DMX_SET_PES_FILTER, &pesfilter) < 0 ) {
270        fprintf(stderr, "open_tuner DMX_SET_PES_FILTER for video: %s\n",
271          strerror(errno));
272        return 1;
273      }
274    }
275
276    if( aud_pid ) {
277      if( (audio_fd=::open(demux_path,O_RDWR)) < 0 ) {
278        fprintf(stderr, "open_tuner %s for audio: %s\n",
279          demux_path, strerror(errno));
280        return 1;
281      }
282      pesfilter.pid = aud_pid;
283      pesfilter.input = DMX_IN_FRONTEND;
284      pesfilter.output = DMX_OUT_TS_TAP;
285      pesfilter.pes_type = DMX_PES_AUDIO;
286      pesfilter.flags = DMX_IMMEDIATE_START;
287      if( ioctl(audio_fd, DMX_SET_PES_FILTER, &pesfilter) < 0 ) {
288        fprintf(stderr, "open_tuner DMX_SET_PES_FILTER for audio: %s\n",
289          strerror(errno));
290        return 1;
291      }
292    }
293
294 // Open transport stream for reading
295    if( (dvr_fd=::open(dvr_path, O_RDONLY)) < 0 ) {
296      fprintf(stderr, "open_tuner %s: %s\n", dvr_path, strerror(errno));
297      return 1;
298    }
299
300    return 0;
301 }
302
303 void close_tuner()
304 {
305   if( frontend_fd >= 0 ) close(frontend_fd);
306   if( audio_fd >= 0 )    close(audio_fd);
307   if( video_fd >= 0 )    close(video_fd);
308   if( dvr_fd >= 0 )      close(dvr_fd);
309 }
310
311 int open_stream()
312 {
313   if( zsrc == 0 && dvr_fd >= 0 ) {
314     int ret;
315     zsrc = new zmpeg3_t(dvr_fd, ret, ZIO_THREADED);
316     //zsrc = new zmpeg3_t(dvr_fd, ret);
317     zsrc->set_cpus(2);
318     if( ret ) {
319       fprintf(stderr,"mpeg3 open failed (%d)\n", ret);
320       return 1;
321     }
322     if( subchan >= 0 ) {
323       int nch = zsrc->dvb.channel_count();
324       while( --nch >= 0 ) {
325         int mjr, mnr;
326         if( zsrc->dvb.get_channel(nch, mjr, mnr) ) continue;
327         if( mnr == subchan ) break;
328       }
329       if( nch >= 0 ) {
330         zsrc->dvb.vstream_number(nch, 0, vid);
331         zsrc->dvb.astream_number(nch, 0, aud, 0);
332       }
333     }
334   }
335   return 0;
336 }
337
338 void close_stream()
339 {
340   if( zsrc ) { delete zsrc;  zsrc = 0; }
341 }
342
343 int start_stream()
344 {
345   if( !the_lck && frontend_fd >= 0 ) {
346     the_lck = new lck_thread();
347     the_lck->Run();
348   }
349   if( !the_vid && dvr_fd >= 0 ) {
350     the_vid = new vid_thread();
351     the_vid->Run();
352   }
353   if( !the_aud && dvr_fd >= 0 ) {
354     the_aud = new aud_thread();
355     the_aud->Run();
356   }
357   return 0;
358 }
359
360 void stop_stream()
361 {
362   if( the_lck ) { the_lck->Kill();  the_lck = 0; }
363   if( the_vid ) { the_vid->Kill();  the_vid = 0; }
364   if( the_aud ) { the_aud->Kill();  the_aud = 0; }
365 }
366
367 double astart;
368
369 double audio_time(int stream)
370 {
371   double anow = zsrc->get_audio_time(stream);
372   if( astart < 0. ) astart = anow;
373   return anow - astart;
374 }
375
376 void aud_thread::
377 Proc()
378 {
379   int audio_channels, sample_rate, more_data;
380
381   audio_channels = zsrc->audio_channels(aud);
382   sample_rate = zsrc->sample_rate(aud);
383   alsa_open(audio_channels,sample_rate);
384   astart = -1.;
385   more_data = 1;
386
387   while( done == 0 && more_data ) {
388     more_data = 0;
389     int ich;
390     int n = alsa_bfrsz();
391     for( ich=0; ich<audio_channels; ++ich ) {
392       short *bfr = alsa_bfr(ich);
393       int ret = ich == 0 ?
394         zsrc->read_audio(bfr, ich, n, aud) :
395         zsrc->reread_audio(bfr, ich, n, aud);
396       if( verbose )
397         printf("read_audio(stream=%d,channel=%d) = %d\n", aud, ich, ret);
398     }
399     alsa_write(n);
400     //double atime = audio_time(aud);
401     //double ttime = the_time();
402     //doule delay = atime - ttime;
403     //printf("delay = %f  (%f-%f)\n",delay,atime,ttime);
404     //if( (delay-ahead) > 0. )
405     //  usleep((int)(delay*1000000.0/2.));
406     more_data |= !zsrc->end_of_audio(aud);
407   }
408
409   alsa_close();
410   audio_done = 1;
411 }
412
413 snd_pcm_t *zpcm;
414 snd_pcm_uframes_t zpcm_ufrm_size;
415 snd_pcm_sframes_t zpcm_sbfr_size;
416 snd_pcm_sframes_t zpcm_sper_size;
417 unsigned int zpcm_rate;
418 static const char *zpcm_device = "plughw:0,0";
419 static unsigned int zpcm_bfr_time_us = 500000; 
420 static unsigned int zpcm_per_time_us = 200000;
421 static snd_pcm_channel_area_t *zpcm_areas = 0;
422 static int zpcm_channels = 0;
423 static short **zpcm_buffers = 0;
424 static short *zpcm_samples = 0;
425
426 void alsa_close()
427 {
428   if( zpcm_buffers != 0 ) { delete [] zpcm_buffers; zpcm_buffers = 0; }
429   if( zpcm_areas != 0 ) { delete [] zpcm_areas; zpcm_areas = 0; }
430   if( zpcm_samples != 0 ) { delete [] zpcm_samples; zpcm_samples = 0; }
431   if( zpcm != 0 ) { snd_pcm_close(zpcm);  zpcm = 0; }
432 }
433
434 void alsa_open(int chs,int rate)
435 {
436   int ich, bits, byts, dir, ret;
437   snd_pcm_format_t fmt = SND_PCM_FORMAT_S16;
438   snd_pcm_hw_params_t *phw;
439   snd_pcm_sw_params_t *psw;
440   snd_pcm_hw_params_alloca(&phw);
441   snd_pcm_sw_params_alloca(&psw);
442
443   zpcm = 0;
444   ret = snd_pcm_open(&zpcm, zpcm_device,
445      SND_PCM_STREAM_PLAYBACK, 0 /* SND_PCM_NONBLOCK */);
446   if( ret >= 0 )
447     ret = snd_pcm_hw_params_any(zpcm, phw);
448   if( ret >= 0 )
449     ret = snd_pcm_hw_params_set_rate_resample(zpcm, phw, 1);
450   if( ret >= 0 )
451     ret = snd_pcm_hw_params_set_access(zpcm, phw,
452       SND_PCM_ACCESS_RW_NONINTERLEAVED);
453   if( ret >= 0 )
454     ret = snd_pcm_hw_params_set_format(zpcm, phw, fmt);
455   if( ret >= 0 ) {
456     zpcm_channels = chs;
457     ret = snd_pcm_hw_params_set_channels(zpcm, phw, chs);
458   }
459   if( ret >= 0 ) {
460     zpcm_rate = rate;
461     ret = snd_pcm_hw_params_set_rate_near(zpcm, phw, &zpcm_rate, 0);
462     if( (int)zpcm_rate != rate )
463       printf("nearest audio_rate for %d is %u\n",rate,zpcm_rate);
464   }
465   if( ret >= 0 )
466     ret = snd_pcm_hw_params_set_buffer_time_near(zpcm, phw,
467       &zpcm_bfr_time_us, &dir);
468   if( ret >= 0 )
469     ret = snd_pcm_hw_params_get_buffer_size(phw, &zpcm_ufrm_size);
470   if( ret >= 0 ) {
471     zpcm_sbfr_size = zpcm_ufrm_size;
472     ret = snd_pcm_hw_params_set_period_time_near(zpcm, phw,
473       &zpcm_per_time_us, &dir);
474   }
475   if( ret >= 0 )
476     ret = snd_pcm_hw_params_get_period_size(phw, &zpcm_ufrm_size, &dir);
477   if( ret >= 0 ) {
478     zpcm_sper_size = zpcm_ufrm_size;
479     ret = snd_pcm_hw_params(zpcm, phw);
480   }
481   if( ret >= 0 )
482     ret = snd_pcm_sw_params_current(zpcm, psw);
483   if( ret >= 0 )
484     ret = snd_pcm_sw_params_set_start_threshold(zpcm, psw,
485       (zpcm_sbfr_size / zpcm_sper_size) * zpcm_sper_size);
486   if( ret >= 0 )
487     ret = snd_pcm_sw_params_set_avail_min(zpcm, psw, zpcm_sper_size);
488   if( ret >= 0 )
489     ret = snd_pcm_sw_params(zpcm, psw);
490   /* snd_pcm_dump(zpcm, stdout); */
491
492   if( ret >= 0 ) {
493      zpcm_areas = new snd_pcm_channel_area_t[chs];
494      byts = snd_pcm_format_physical_width(fmt) / 8;
495      zpcm_samples = new short[zpcm_sper_size * chs * byts/2];
496      zpcm_buffers = new short *[chs];
497      if( zpcm_samples ) {
498        for( ich = 0; ich < chs; ++ich ) {
499          zpcm_areas[ich].addr = zpcm_samples;
500          zpcm_areas[ich].first = ich * zpcm_sper_size * bits;
501          zpcm_areas[ich].step = bits;
502          zpcm_buffers[ich] = zpcm_samples + ich*zpcm_sper_size;
503        }
504      }
505      else {
506        fprintf(stderr,"alsa sample buffer allocation failure.\n");
507        ret = -999;
508      }
509   }
510   if( ret < 0 ) {
511     if( ret > -999 )
512       printf("audio error: %s\n", snd_strerror(ret));
513     alsa_close();
514   }
515 }
516
517 short *alsa_bfr(int ch)
518 {
519   return zpcm_buffers[ch];
520 }
521
522 int alsa_bfrsz()
523 {
524   return zpcm_sper_size;
525 }
526
527 int alsa_recovery(int ret)
528 {
529   printf("alsa recovery\n");
530   switch( ret ) {
531   case -ESTRPIPE:
532     /* wait until the suspend flag is released, then fall through */
533     while( (ret=snd_pcm_resume(zpcm)) == -EAGAIN ) usleep(100000);
534   case -EPIPE:
535     ret = snd_pcm_prepare(zpcm);
536     if( ret < 0 )
537       printf("underrun, prepare failed: %s\n", snd_strerror(ret));
538     break;
539   default:
540     printf("unhandled error: %s\n",snd_strerror(ret));
541     break;
542   }
543   return ret;
544 }
545
546 int alsa_write(int len)
547 {
548   int i, ret;
549   short *bfrs[zpcm_channels];
550   ret = 0;
551   for( i=0; i<zpcm_channels; ++i ) bfrs[i] = zpcm_buffers[i];
552
553   while( len > 0 ) {
554     ret = snd_pcm_writen(zpcm,(void **)bfrs, len);
555     if( ret == -EAGAIN ) continue;
556     if ( ret < 0 ) {
557       alsa_recovery(ret);
558       break;
559     }
560     for( i=0; i<zpcm_channels; ++i ) bfrs[i] += ret;
561     len -= ret;
562   }
563   return ret < 0 ? ret : 0;
564 }
565
566 void dst_exit(GtkWidget *widget, gpointer data)
567 {
568    exit(0);
569 }
570
571 gint 
572 key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data)
573 {
574 #if 0
575    if (event->length > 0)
576       printf("The key event's string is `%s'\n", event->string);
577
578    printf("The name of this keysym is `%s'\n", 
579            gdk_keyval_name(event->keyval));
580
581    switch (event->keyval) {
582    case GDK_Home:
583       printf("The Home key was pressed.\n");
584       break;
585    case GDK_Up:
586       printf("The Up arrow key was pressed.\n");
587       break;
588    default:
589       break;
590    }
591
592    if( gdk_keyval_is_lower(event->keyval) ) {
593       printf("A non-uppercase key was pressed.\n");
594    }
595    else if( gdk_keyval_is_upper(event->keyval) ) {
596       printf("An uppercase letter was pressed.\n");
597    }
598 #endif
599    switch (event->keyval) {
600    case GDK_plus:
601    case GDK_equal:
602       nudge += 0.1;
603       printf("+nudge = %f\n",nudge);
604       break;
605    case GDK_minus:
606    case GDK_underscore:
607       nudge -= 0.1;
608       printf("-nudge = %f\n",nudge);
609       break;
610    case GDK_q:
611       printf("exit\n");
612       dst_exit(widget,data);
613       break;
614    }
615    return TRUE;
616 }
617
618
619 double vstart;
620
621 double video_time(int stream)
622 {
623   double vnow = zsrc->get_video_time(stream);
624   if( vstart < 0. && vnow > 0. ) vstart = vnow;
625   vnow += nudge;
626   return vnow - vstart;
627 }
628
629 void vid_thread::
630 Proc()
631 {
632   GtkWidget *window;
633   GtkWidget *panel_hbox;
634   GtkWidget *image;
635   GdkPixbuf *pbuf0, *pbuf1;
636   GdkImage  *img0, *img1;
637   GdkVisual *visual;
638   float frame_rate, frame_delay;
639   unsigned char **rows, **row0, **row1, *cap, *cap0, *cap1;
640   int ret, row, frame, more_data;
641   int width, height, owidth, oheight;
642   const int frame_drop = 1;
643   int rheight = 1050;
644   int rwidth = 1680;
645
646   frame_rate = zsrc->frame_rate(vid);
647   frame_delay = 2.0 / frame_rate;
648   height = zsrc->video_height(vid);
649   width = zsrc->video_width(vid);
650   //oheight = rheight-96;
651   //owidth = rwidth-64;
652   oheight = height;
653   owidth = width;
654   int fheight = rheight - 96;
655   int fwidth = rwidth - 64;
656   if( oheight > fheight || owidth > fwidth ) {
657     int mheight = (owidth * fheight) / fwidth;
658     int mwidth = (oheight * fwidth) / fheight;
659     if( mheight > fheight ) mheight = fheight;
660     if( mwidth > fwidth ) mwidth = fwidth;
661     oheight = mheight;
662     owidth = mwidth;
663   }
664   else if( oheight < 1050/2 && owidth < 1650/2 ) {
665     oheight = oheight * 2;
666     owidth  = owidth * 2;
667   }
668
669   visual = gdk_visual_get_system();
670   /* toplevel window */
671   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
672   gtk_signal_connect(GTK_OBJECT(window),"destroy",
673      GTK_SIGNAL_FUNC(dst_exit),NULL);
674   gtk_signal_connect(GTK_OBJECT(window),"key_press_event",
675      GTK_SIGNAL_FUNC(key_press_cb),NULL);
676   gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_NONE);
677   /* try for shared image bfr, only seems to work with gtk_rgb */
678   img0 = gdk_image_new(GDK_IMAGE_SHARED, visual, owidth, oheight);
679   pbuf0 = gdk_pixbuf_new_from_data((const guchar *)img0->mem,
680              GDK_COLORSPACE_RGB,RGBA,8,owidth,oheight,owidth*BPP,NULL,NULL);
681   cap0 = gdk_pixbuf_get_pixels(pbuf0);
682   image = gtk_image_new_from_pixbuf(pbuf0);
683   /* double buffered */
684   img1 = gdk_image_new(GDK_IMAGE_SHARED, visual, owidth, oheight);
685   pbuf1 = gdk_pixbuf_new_from_data((const guchar *)img1->mem,
686              GDK_COLORSPACE_RGB,RGBA,8,owidth,oheight,owidth*BPP,NULL,NULL);
687   cap1 = gdk_pixbuf_get_pixels(pbuf1);
688
689   panel_hbox = gtk_hbox_new(FALSE,0);
690   gtk_container_add(GTK_CONTAINER(window), panel_hbox);
691   /* pack image into panel */
692   gtk_box_pack_start(GTK_BOX(panel_hbox), image, TRUE, TRUE, 0);
693
694
695   row0 = new unsigned char *[oheight];
696   row1 = new unsigned char *[oheight];
697   int cmdl = COLOR_MODEL;
698   for( row=0; row<oheight; ++row ) {
699     row0[row] = cap0 + row*owidth*BPP;
700     row1[row] = cap1 + row*owidth*BPP;
701   }
702
703   gtk_widget_show_all(window);
704   cap = cap0;
705   rows = row0;
706   frame = 0;
707   vstart = -1.;
708   more_data = 1;
709
710   while( done == 0 && more_data ) {
711     if( !gtk_events_pending() ) {
712       more_data = 0;
713       double delay =  the_time() - video_time(vid);
714       if( frame_drop ) {
715         if( delay >= frame_delay ) {
716           int nframes = (long)ceil(delay/frame_rate);
717           if( nframes > 2 ) nframes = 2;
718           dprintf(" d%d",nframes);
719           zsrc->drop_frames(nframes,vid);
720         }
721       }
722       delay = -delay;
723       if( delay > 0 ) {
724         dprintf(" D%5.3f",delay*1000.0);
725         int us = (int)(delay*1000000.0);
726         usleep(us);
727       }
728       ret = zsrc->read_frame(rows, 0, 0, width, height,
729                owidth, oheight, cmdl, vid);
730       //printf("%d %ld\n",frame,zsrc->vtrack[0]->demuxer->titles[0]->fs->current_byte);
731       //printf("%d %ld %ld %ld\n",frame,zsrc->vtrack[0]->demuxer->titles[0]->fs->current_byte,
732       //    zsrc->vtrack[0]->demuxer->titles[0]->fs->buffer->file_pos,
733       //    zsrc->vtrack[0]->demuxer->titles[0]->fs->buffer->file_pos -
734       //    zsrc->vtrack[0]->demuxer->titles[0]->fs->current_byte);
735       if( verbose )
736         printf("read_video(stream=%d, frame=%d) = %d\n", vid, frame, ret);
737       GdkGC *blk = image->style->black_gc;
738       gdk_draw_rgb_image(image->window,blk, 0,0,owidth,oheight,
739          GDK_RGB_DITHER_NONE,cap,owidth*BPP);
740 #if 0
741 #if 0
742       { static FILE *fp = 0;
743         int sz = owidth*oheight*BPP;
744         if( fp == 0 ) fp = fopen("/tmp/dat.raw","w");
745         fwrite(cap,1,sz,fp);
746       }
747 #else
748       if( frame < 150 ) { static FILE *fp = 0;
749         int z, sz = owidth*oheight*BPP;
750         uint8_t zbfr[sz];
751         if( fp == 0 ) fp = fopen("/tmp/dat.raw","r");
752         fread(zbfr,1,sz,fp);
753         for( z=0; z<sz && abs(zbfr[z]-cap[z])<=1; ++z );
754         //for( z=0; z<sz && zbfr[z]==cap[z]; ++z );
755         if( z < sz )
756           printf("bug %d\n",z);
757       }
758 #endif
759 #endif
760       *(unsigned long *)&cap ^= ((unsigned long)cap0 ^ (unsigned long)cap1);
761       *(unsigned long *)&rows ^= ((unsigned long)row0 ^ (unsigned long)row1);
762       more_data |= !zsrc->end_of_video(vid);
763       ++frame;
764     }
765     else
766       gtk_main_iteration();
767   }
768   video_done = 1;
769 }
770
771 void stats()
772 {
773   int has_audio = zsrc->has_audio();
774   printf(" has_audio = %d\n", has_audio);
775   int total_astreams = zsrc->total_astreams();
776   printf(" total_astreams = %d\n", total_astreams);
777   for( int astream=0; astream<total_astreams; ++astream ) {
778     int audio_channels = zsrc->audio_channels(astream);
779     printf("   audio_channels = %d\n", audio_channels);
780     int sample_rate = zsrc->sample_rate(astream);
781     printf("   sample_rate = %d\n", sample_rate);
782     long audio_samples = zsrc->audio_samples(astream);
783     printf("   audio_samples = %ld\n", audio_samples);
784   }
785   printf("\n");
786   int has_video = zsrc->has_video();
787   printf(" has_video = %d\n", has_video);
788   int total_vstreams = zsrc->total_vstreams();
789   printf(" total_vstreams = %d\n", total_vstreams);
790   for( int vstream=0; vstream<total_vstreams; ++vstream ) {
791     int width = zsrc->video_width(vstream);
792     printf("   video_width = %d\n", width);
793     int height = zsrc->video_height(vstream);
794     printf("   video_height = %d\n", height);
795     float frame_rate = zsrc->frame_rate(vstream);
796     printf("   frame_rate = %f\n", frame_rate);
797     long video_frames = zsrc->video_frames(vstream);
798     printf("   video_frames = %ld\n", video_frames);
799     int colormodel = zsrc->colormodel(vstream);
800     printf("   colormodel = %d\n", colormodel);
801   }
802   int channel_count = zsrc->dvb.channel_count();
803   printf("\ndvb data: %d channels\n",channel_count);
804   for( int channel=0; channel<channel_count; ++channel ) {
805     int major, minor, stream;  char name[8], enc[4];
806     zsrc->dvb.get_channel(channel, major, minor);
807     zsrc->dvb.get_station_id(channel, &name[0]);
808     zsrc->dvb.total_astreams(channel, total_astreams);
809     zsrc->dvb.total_vstreams(channel, total_vstreams);
810     printf("  dvb channel %d.%d (%s) %d vstreams, %d astreams\n",
811       major, minor, name, total_vstreams, total_astreams);
812     for( int vstream=0; vstream<total_vstreams; ++vstream ) {
813       zsrc->dvb.vstream_number(channel, vstream, stream);
814       printf("    video%-2d = %d\n",vstream,stream);
815     }
816     for(int astream=0; astream<total_astreams; ++astream ) {
817       zsrc->dvb.astream_number(channel, astream, stream, &enc[0]);
818       printf("    audio%-2d = %d (%s)\n",astream,stream,&enc[0]);
819     }
820   }
821   printf("\n");
822 }
823
824 void sig_int(int n)
825 {
826   done = 1;
827 }
828
829 int ich[] = { /* June 14, 2009 */
830    7, //  7.1  (KMGH-DT) 1 vstreams, 1 astreams (eng)
831       //  7.27 (KZCO-SD) 1 vstreams, 1 astreams (spa)
832    9, //  9.1  (KUSA-DT) 1 vstreams, 1 astreams (eng)
833       //  9.2  (WX-Plus) 1 vstreams, 1 astreams (eng)
834       //  9.3  (NBC Uni) 1 vstreams, 1 astreams (eng)
835   13, // 12.1  (KBDI-DT) 1 vstreams, 1 astreams (eng)
836       // 12.2  (KBDI-DC) 1 vstreams, 1 astreams (eng)
837       // 12.3  (KBDI-WV) 1 vstreams, 1 astreams (eng)
838   15, // 14.1  (KTFD-DT) 1 vstreams, 1 astreams (spa)
839   19, // 20.1  (KTVD-DT) 1 vstreams, 1 astreams (eng)
840   21, // 22.1  (KDVR DT) 1 vstreams, 2 astreams (eng) (spa)
841   29, // 25.1  (KDEN-DT) 1 vstreams, 1 astreams (eng)
842   32, // 31.1  (KDVR DT) 1 vstreams, 2 astreams (eng) (spa)
843   34, //  2.1  (KWGN-DT) 1 vstreams, 2 astreams (eng) (eng)
844   35, //  4.1  (KCNC-DT) 1 vstreams, 2 astreams (eng) (spa)
845   38, // 38.1  (KPJR-1_) 1 vstreams, 1 astreams (eng)
846       // 38.2  (KPJR-2_) 1 vstreams, 1 astreams (eng)
847       // 38.3  (KPJR-3_) 1 vstreams, 1 astreams (eng)
848       // 38.4  (KPJR-4_) 1 vstreams, 1 astreams (eng)
849       // 38.5  (KPJR-5_) 1 vstreams, 1 astreams (eng)
850   40, // 41.1  (KRMT 41) 1 vstreams, 1 astreams (eng)
851   43, // 59.1  (ION____) 1 vstreams, 1 astreams (eng)
852       // 59.2  (qubo___) 1 vstreams, 2 astreams (eng) (eng)
853       // 59.3  (IONLife) 1 vstreams, 1 astreams (eng)
854       // 59.4  (Worship) 1 vstreams, 1 astreams (eng)
855   46, // 53.1  (KWHD-DT) 1 vstreams, 1 astreams (eng)
856   51, // 50.1  (Univisi) 1 vstreams, 1 astreams (spa)
857 };
858
859 int main(int ac, char **av)
860 {
861   setbuf(stdout,NULL);
862   setbuf(stderr,NULL);
863   if( ac < 2 ) {
864     printf("channel ordinal required\n");
865     return 1;
866   }
867   int ch_ord = atoi(av[1]);
868   if( ch_ord >= 0 ) {
869     gtk_set_locale();
870     gtk_init(&ac, &av);
871     audio_done = 0;
872     video_done = 0;
873     reset();
874     if( ac == 3 ) {
875       subchan = atoi(av[2]);
876     }
877     else if( ac == 4 ) {
878       vid = atoi(av[2]);
879       aud = atoi(av[3]);
880     }
881     if( !open_tuner(0,ch_ord,0,0) ) {
882       if( !open_stream() ) {
883         stats();
884         zsrc->seek_byte(0);
885         // zsrc->show_subtitle(0);
886         start_stream();
887         while( !done ) {
888           usleep(100000);
889           if( audio_done && video_done) break;
890         }
891         stop_stream();
892         close_stream();
893       }
894     }
895     close_tuner();
896   }
897   else {
898     for( int ch=-ch_ord>2? -ch_ord : 2; ch<78; ++ch ) {
899       printf("\r  %2d?\r",ch);
900       reset();
901       /* extra open/close clears previous bfr data */
902       if( !open_tuner(0,ch,0,0) ) close_tuner();
903       usleep(100000);
904       if( !open_tuner(0,ch,0,0) && !open_stream() ) {
905         int nch = zsrc->dvb.channel_count();
906         if( nch > 0 ) {
907           for( int ich=0, n=0; ich<nch; ++ich ) {
908             int mjr, mnr;
909             zsrc->dvb.get_channel(ich, mjr, mnr);
910             int astrs, vstrs, stream;  char name[8], enc[4];
911             zsrc->dvb.get_station_id(ich, &name[0]);
912             zsrc->dvb.total_astreams(ich, astrs);
913             zsrc->dvb.total_vstreams(ich, vstrs);
914             if( n++ ) printf("      "); else printf("  %2d: ", ch);
915             printf("   %2d.%-2d (%s) %d vstreams, %d astreams",
916               mjr, mnr, name, vstrs, astrs);
917             for( int j=0; j<astrs; ++j ) {
918               zsrc->dvb.astream_number(ich, j, stream, &enc[0]);
919               printf(" (%s)",&enc[0]);
920             }
921             printf("\n");
922           }
923         }
924         close_stream();
925       }
926       close_tuner();
927     }
928   }
929   return 0;
930 }
931