12 #include <X11/extensions/Xvlib.h>
14 #include <alsa/asoundlib.h>
19 #include "libzmpeg3/libzmpeg3.h"
22 //c++ -pthread -I.. -I../db x.C ./x86_64/libzmpeg3.a ../db/x86_64/db.a -lX11 -lasound -lm
23 //c++ -g -pthread -I.. -I../db x.C ./x86_64/libzmpeg3.a ../db/x86_64/db.a -lX11 -lXext -lasound -lm
26 int building = getenv("BUILDING") ? 1 : 0;
30 const double nudge = 0.0;
31 const double ahead = 0.0;
32 static int verbose = 0;
47 snd_pcm_uframes_t zpcm_ufrm_size = 0;
48 snd_pcm_sframes_t zpcm_sbfr_size = 0;
49 snd_pcm_sframes_t zpcm_sper_size = 0;
50 snd_pcm_sframes_t zpcm_total_samples = 0;
51 double zpcm_play_time = 0.;
53 unsigned int zpcm_rate = 0;
54 static const char *zpcm_device = "plughw:0,0";
55 static unsigned int zpcm_bfr_time_us = 500000;
56 static unsigned int zpcm_per_time_us = 200000;
57 static snd_pcm_channel_area_t *zpcm_areas = 0;
58 static int zpcm_channels = 0;
59 static short **zpcm_buffers = 0;
60 static short *zpcm_samples = 0;
64 if( zpcm_buffers != 0 ) { delete [] zpcm_buffers; zpcm_buffers = 0; }
65 if( zpcm_areas != 0 ) { delete [] zpcm_areas; zpcm_areas = 0; }
66 if( zpcm_samples != 0 ) { delete [] zpcm_samples; zpcm_samples = 0; }
67 if( zpcm != 0 ) { snd_pcm_close(zpcm); zpcm = 0; }
70 void alsa_open(int chs,int rate)
72 int ich, bits, byts, dir, ret;
74 snd_pcm_format_t fmt = SND_PCM_FORMAT_S16;
75 snd_pcm_hw_params_t *phw;
76 snd_pcm_sw_params_t *psw;
77 snd_pcm_hw_params_alloca(&phw);
78 snd_pcm_sw_params_alloca(&psw);
81 zpcm_total_samples = 0;
83 ret = snd_pcm_open(&zpcm, zpcm_device,
84 SND_PCM_STREAM_PLAYBACK, 0 /* + SND_PCM_NONBLOCK */);
86 ret = snd_pcm_hw_params_any(zpcm, phw);
88 ret = snd_pcm_hw_params_set_rate_resample(zpcm, phw, 1);
90 ret = snd_pcm_hw_params_set_access(zpcm, phw,
91 SND_PCM_ACCESS_RW_NONINTERLEAVED);
93 ret = snd_pcm_hw_params_set_format(zpcm, phw, fmt);
96 ret = snd_pcm_hw_params_set_channels(zpcm, phw, chs);
100 ret = snd_pcm_hw_params_set_rate_near(zpcm, phw, &zpcm_rate, 0);
101 if( (int)zpcm_rate != rate )
102 printf("nearest audio_rate for %d is %u\n",rate,zpcm_rate);
105 ret = snd_pcm_hw_params_set_buffer_time_near(zpcm, phw,
106 &zpcm_bfr_time_us, &dir);
108 ret = snd_pcm_hw_params_get_buffer_size(phw, &zpcm_ufrm_size);
110 zpcm_sbfr_size = zpcm_ufrm_size;
111 ret = snd_pcm_hw_params_set_period_time_near(zpcm, phw,
112 &zpcm_per_time_us, &dir);
115 ret = snd_pcm_hw_params_get_period_size(phw, &zpcm_ufrm_size, &dir);
117 zpcm_sper_size = zpcm_ufrm_size;
118 ret = snd_pcm_hw_params(zpcm, phw);
121 ret = snd_pcm_sw_params_current(zpcm, psw);
123 ret = snd_pcm_sw_params_set_start_threshold(zpcm, psw,
124 (zpcm_sbfr_size / zpcm_sper_size) * zpcm_sper_size);
126 ret = snd_pcm_sw_params_set_avail_min(zpcm, psw, zpcm_sper_size);
128 ret = snd_pcm_sw_params(zpcm, psw);
129 /* snd_pcm_dump(zpcm, stdout); */
132 zpcm_areas = new snd_pcm_channel_area_t[chs];
133 bits = snd_pcm_format_physical_width(fmt);
135 zpcm_samples = new short[zpcm_sper_size * chs * (byts+1)/2];
136 zpcm_buffers = new short *[chs];
138 for( ich = 0; ich < chs; ++ich ) {
139 zpcm_areas[ich].addr = zpcm_samples;
140 zpcm_areas[ich].first = ich * zpcm_sper_size * bits;
141 zpcm_areas[ich].step = bits;
142 zpcm_buffers[ich] = zpcm_samples + ich*zpcm_sper_size;
146 fprintf(stderr,"alsa sample buffer allocation failure.\n");
152 printf("audio error: %s\n", snd_strerror(ret));
157 short *alsa_bfr(int ch) { return zpcm_buffers[ch]; }
158 int alsa_bfrsz() { return zpcm_sper_size; }
160 int alsa_recovery(int ret)
162 printf("alsa recovery\n");
165 /* wait until the suspend flag is released, then fall through */
166 while( (ret=snd_pcm_resume(zpcm)) == -EAGAIN ) usleep(100000);
168 ret = snd_pcm_prepare(zpcm);
170 printf("underrun, prepare failed: %s\n", snd_strerror(ret));
173 printf("unhandled error: %s\n",snd_strerror(ret));
179 int alsa_write(int length)
182 int i, ret, count, retry;
183 snd_pcm_sframes_t sample_delay;
184 double play_time, time;
185 short *bfrs[zpcm_channels];
188 for( i=0; i<zpcm_channels; ++i ) bfrs[i] = zpcm_buffers[i];
190 while( count < length ) {
191 ret = snd_pcm_writen(zpcm,(void **)bfrs, length-count);
192 if( ret == -EAGAIN ) continue;
194 if( --retry < 0 ) return ret;
199 for( i=0; i<zpcm_channels; ++i ) bfrs[i] += ret;
203 zpcm_total_samples += count;
204 snd_pcm_delay(zpcm,&sample_delay);
205 if( sample_delay > zpcm_total_samples )
206 sample_delay = zpcm_total_samples;
207 gettimeofday(&tv,NULL);
208 time = tv.tv_sec + tv.tv_usec/1000000.0;
209 play_time = (zpcm_total_samples - sample_delay) / (double)zpcm_rate;
210 zpcm_play_time = time - play_time;
212 return ret < 0 ? ret : 0;
217 double time, play_time;
220 gettimeofday(&tv,NULL);
221 time = tv.tv_sec + tv.tv_usec/1000000.0;
222 play_time = time - zpcm_play_time;
231 double time, play_time;
233 gettimeofday(&tv,NULL);
234 time = tv.tv_sec + tv.tv_usec/1000000.0;
247 gettimeofday(&tv,NULL);
248 time = tv.tv_sec + tv.tv_usec/1000000.0;
249 if( tstart < 0. ) tstart = time;
250 return time - tstart;
254 void mpeg3_stats(zmpeg3_t *zsrc)
256 int astream, vstream;
257 int has_audio, total_astreams, audio_channels, sample_rate;
258 int has_video, total_vstreams, width, height, colormodel;
259 long audio_samples, video_frames;
262 has_audio = mpeg3_has_audio(zsrc);
263 printf(" has_audio = %d\n", has_audio);
264 total_astreams = mpeg3_total_astreams(zsrc);
265 printf(" total_astreams = %d\n", total_astreams);
266 for( astream=0; astream<total_astreams; ++astream ) {
267 audio_channels = zsrc->audio_channels(astream);
268 printf(" audio_channels = %d\n", audio_channels);
269 sample_rate = zsrc->sample_rate(astream);
270 printf(" sample_rate = %d\n", sample_rate);
271 audio_samples = zsrc->audio_samples(astream);
272 printf(" audio_samples = %ld\n", audio_samples);
275 has_video = mpeg3_has_video(zsrc);
276 printf(" has_video = %d\n", has_video);
277 total_vstreams = mpeg3_total_vstreams(zsrc);
278 printf(" total_vstreams = %d\n", total_vstreams);
279 for( vstream=0; vstream<total_vstreams; ++vstream ) {
280 width = zsrc->video_width(vstream);
281 printf(" video_width = %d\n", width);
282 height = zsrc->video_height(vstream);
283 printf(" video_height = %d\n", height);
284 frame_rate = zsrc->frame_rate(vstream);
285 printf(" frame_rate = %f\n", frame_rate);
286 video_frames = zsrc->video_frames(vstream);
287 printf(" video_frames = %ld\n", video_frames);
288 colormodel = zsrc->colormodel(vstream);
289 printf(" colormodel = %d\n", colormodel);
295 class mdb_q : public Condition {
309 uint8_t *dat() { return bfr; }
310 pkt_t(int sz) { bfr = new uint8_t[sz]; }
311 ~pkt_t() { delete [] bfr; }
314 class pkt_q : Mutex {
322 ~pkt_q() { for( pkt_t *p=list; p!=0; ) { list=p->next; delete p; } }
329 void init(int qsize, int bsize);
330 pkt *avl_get() { return avail.get(); }
331 void avl_put(pkt *p) { avail.put(p); }
332 pkt *pkt_get() { return packets.get(); }
333 void pkt_put(pkt *p) { packets.put(p); }
339 typedef mdb_q::pkt mdb_pkt;
340 typedef mdb_q::pktq mdb_pktq;
343 mdb_pkt *mdb_pktq::get()
349 if( p == list ) list = 0;
350 else list->next = p->next;
356 void mdb_pktq::put(mdb_pkt *p)
359 if( !list ) list = p;
360 else p->next = list->next;
366 void mdb_q::init(int qsize, int bsize)
368 pkt_t **qp = &avail.list;
369 while( --qsize >= 0 ) {
370 pkt_t *p = new pkt_t(bsize);
371 *qp = p; qp = &p->next;
377 class Media : public Thread {
383 void newDb(const char *dfn);
384 void openDb(const char *dfn);
386 void add_key(uint8_t *dat, int sz, int id, double tm);
387 void get_key(uint8_t *dat, int sz, int &id, double &tm);
389 Media() { q.init(32,9600); }
393 void Media::newDb(const char *dfn)
395 if( db.create(dfn) ) {
396 fprintf(stderr,"Media::db.create failed\n");
401 void Media::openDb(const char *dfn)
404 fprintf(stderr,"Media::db.open failed\n");
409 void Media::closeDb()
414 void Media::add_key(uint8_t *dat, int sz, int id, double tm)
416 printf("add %d\n",id);
417 db.prefix.Allocate();
418 db.prefix.Clip_id(id);
419 db.prefix.Time_offset(tm);
420 int ksz = Db::mediaKey::byte_size(sz, 8);
421 uint8_t *key = db.prefix._Key_data(ksz);
422 Db::mediaKey::build(key, dat, 8, sz);
423 db.prefix.Construct();
427 void Media::get_key(uint8_t *dat, int sz, int &id, double &tm)
429 int ksz = Db::mediaKey::byte_size(sz, 8);
431 Db::mediaKey::build(key, dat, 8, sz);
432 int ret = PrefixLoc::ikey_Prefix_key_data(db.prefix,
433 PrefixObj::t_Key_data(key, ksz)).Find();
435 printf(" not found, ret = %d\n",ret);
439 id = db.prefix.Clip_id();
440 tm = db.prefix.Time_offset();
441 printf("found id = %d\n",id);
447 if( building ) newDb("/tmp/dat/media.db");
448 openDb("/tmp/dat/media.db");
451 while( !q.lock() && running() ) {
453 while( (p=q.pkt_get()) != 0 ) {
455 double tm = 0.0; int id = p->framenum;
456 add_key(p->dat(), p->len, id, tm);
459 double tm = 0.0; int id = -1;
460 get_key(p->dat(), p->len, id, tm);
472 class Video : public Thread {
476 double video_time(int stream);
481 Video(zmpeg3_t *z) : zsrc(z) {}
486 double Video::video_time(int stream)
488 double vtime = zsrc->get_video_time(stream);
490 if( vstart == 0. ) vstart = vnow;
491 if( vtime < last_vtime )
492 vstart -= last_vtime;
494 return vnow - vstart + nudge;
504 XImage *image0, *image1, *image;
507 XShmSegmentInfo info0, info1;
510 float frame_rate, frame_delay;
511 uint8_t **rows, **row0, **row1, *cap, *cap0, *cap1;
512 int ret, row, frame, dropped, more_data;
513 int width, height, depth, owidth, oheight;
514 const int frame_drop = 0;
515 const int shared_memory = 1;
517 display = XOpenDisplay(NULL);
518 if( display == NULL ) {
519 fprintf(stderr,"cant open display\n");
523 root = RootWindow(display,0);
524 screen = DefaultScreenOfDisplay(display);
525 depth = DefaultDepthOfScreen(screen);
526 visual = DefaultVisualOfScreen(screen);
527 if( visual->c_class != TrueColor ) {
528 printf("visual class not truecolor\n");
533 frame_rate = zsrc->frame_rate(0);
534 frame_delay = 2.0 / frame_rate;
535 height = zsrc->video_height(0);
536 width = zsrc->video_width(0);
538 //oheight = 2*height / 3;
539 //owidth = 2*width / 3;
547 w = XCreateSimpleWindow(display, root, 0, 0, owidth, oheight,
548 0, 0, WhitePixelOfScreen(screen));
549 XSelectInput(display, w, ExposureMask|StructureNotifyMask|ButtonPressMask);
551 if( !shared_memory ) {
552 int sz0 = oheight*owidth*image_bpp + 4;
553 cap0 = new uint8_t[sz0];
554 image0 = XCreateImage(display, visual, depth, ZPixmap, 0,
555 (char*)cap0, owidth, oheight, 8, owidth*4);
556 int sz1 = oheight*owidth*image_bpp + 4;
557 cap1 = new uint8_t[sz1];
558 image1 = XCreateImage(display, visual, depth, ZPixmap, 0,
559 (char*)cap1, owidth, oheight, 8, owidth*4);
562 image0 = XShmCreateImage(display, visual, depth, ZPixmap, 0,
563 &info0, owidth, oheight);
564 int sz0 = oheight*image0->bytes_per_line+image_bpp;
565 info0.shmid = shmget(IPC_PRIVATE, sz0, IPC_CREAT | 0777);
566 if( info0.shmid < 0) { perror("shmget"); exit(1); }
567 cap0 = (uint8_t *)shmat(info0.shmid, NULL, 0);
568 shmctl(info0.shmid, IPC_RMID, 0);
569 image0->data = info0.shmaddr = (char *)cap0;
571 XShmAttach(display,&info0);
572 image1 = XShmCreateImage(display, visual, depth, ZPixmap, 0,
573 &info1, owidth, oheight);
574 int sz1 = oheight*image1->bytes_per_line+image_bpp;
575 info1.shmid = shmget(IPC_PRIVATE, sz1, IPC_CREAT | 0777);
576 if( info1.shmid < 0) { perror("shmget"); exit(1); }
577 cap1 = (uint8_t *)shmat(info1.shmid, NULL, 0);
578 shmctl(info1.shmid, IPC_RMID, 0);
579 image1->data = info1.shmaddr = (char *)cap1;
581 XShmAttach(display,&info1);
584 row0 = new uint8_t *[oheight];
585 row1 = new uint8_t *[oheight];
586 for( row=0; row<oheight; ++row ) {
587 row0[row] = cap0 + row*owidth*image_bpp;
588 row1[row] = cap1 + row*owidth*image_bpp;
594 if( image->bits_per_pixel != 32 ) {
595 printf("image bits_per_pixel=%d\n",image->bits_per_pixel);
599 unsigned int rmsk = image->red_mask;
600 unsigned int gmsk = image->green_mask;
601 unsigned int bmsk = image->blue_mask;
602 switch( image_bpp ) {
604 if( bmsk==0xff0000 && gmsk==0x00ff00 && rmsk==0x0000ff )
605 cmdl = zmpeg3_t::cmdl_RGBA8888;
606 else if( bmsk==0x0000ff && gmsk==0x00ff00 && rmsk==0xff0000 )
607 cmdl = zmpeg3_t::cmdl_BGRA8888;
610 if( bmsk==0xff0000 && gmsk==0x00ff00 && rmsk==0x0000ff )
611 cmdl = zmpeg3_t::cmdl_RGB888;
612 else if( bmsk==0x0000ff && gmsk==0x00ff00 && rmsk==0xff0000 )
613 cmdl = zmpeg3_t::cmdl_BGR888;
616 if( bmsk==0x00f800 && gmsk==0x0007e0 && rmsk==0x00001f )
617 cmdl = zmpeg3_t::cmdl_RGB565;
621 printf("unknown color model, bpp=%d, ",image_bpp);
622 printf(" rmsk=%08x,gmsk=%08x,bmsk=%08x\n", rmsk, gmsk, bmsk);
626 XMapWindow(display, w);
628 gcv.function = GXcopy;
629 gcv.foreground = BlackPixelOfScreen(DefaultScreenOfDisplay(display));
631 gcv.line_style = LineSolid;
632 int m = GCFunction|GCForeground|GCLineWidth|GCLineStyle;
633 gc = XCreateGC(display,w,m,&gcv);
636 vstart = last_vtime = 0.;
640 while( !done && running() && more_data ) {
641 if( !XPending(display) ) {
643 delay = (frame+dropped)/frame_rate - alsa_time();
644 // delay = the_time() - video_time(zsrc,0);
646 if( -delay >= frame_delay ) {
647 int nframes = (long)ceil(-delay/frame_rate);
648 if( nframes > 2 ) nframes = 2;
649 zsrc->drop_frames(nframes,0);
653 //printf("delay %f\n",delay*1000.);
655 usleep((int)(delay*1000000.0));
656 ret = zsrc->read_frame(rows, 0, 0, width, height,
657 owidth, oheight, cmdl, 0);
658 //printf("%d %ld\n",frame,zsrc->vtrack[0]->demuxer->titles[0]->fs->current_byte);
660 printf("read_video(stream=%d, frame=%d) = %d, %f sec\n", 0, frame, ret, the_time());
664 XPutImage(display, w, gc, image, 0, 0, 0, 0, owidth, oheight);
666 XShmPutImage(display, w, gc, image, 0, 0, 0, 0, owidth, oheight, False);
669 mdb_pkt *p = the_db->q.avl_get();
671 uint8_t *bp; int w, h, frn;
672 zsrc->get_thumbnail(0,frn,bp,w,h);
674 printf("put %d\n",frame);
675 uint8_t *bfr = p->dat();
676 memcpy(bfr,bp,p->len=w*h);
677 the_db->q.pkt_put(p);
683 (rows=row1, image=image1, cap1) :
684 (rows=row0, image=image0, cap0) ;
685 //printf(" %d/%d %f %f %f\n",frame,dropped,(frame+dropped)/frame_rate,
686 // video_time(zsrc,0),zsrc->get_video_time(0));
687 more_data |= !zsrc->end_of_video(0);
690 XNextEvent(display,&xev);
691 /* printf("xev.type = %d/%08x\n",xev.type,xev.xany.window); */
694 if( xev.xbutton.window != w ) continue;
695 if( xev.xbutton.button != Button1 )
707 XCloseDisplay(display);
708 delete [] row0; row0 = 0;
709 delete [] row1; row1 = 0;
716 class Audio : public Thread {
719 double audio_time(int stream);
724 Audio(zmpeg3_t *z) : zsrc(z) {}
729 double Audio::audio_time(int stream)
731 double anow = zsrc->get_audio_time(stream);
732 if( astart < 0. ) astart = anow;
733 return anow - astart;
739 int audio_channels, sample_rate, more_data;
742 audio_channels = zsrc->audio_channels(stream);
743 sample_rate = zsrc->sample_rate(stream);
744 alsa_open(audio_channels,sample_rate);
748 while( running() && more_data ) {
751 int n = alsa_bfrsz();
752 for( ich=0; ich<audio_channels; ++ich) {
753 short *bfr = alsa_bfr(ich);
755 zsrc->read_audio(bfr, ich, n, stream) :
756 zsrc->reread_audio(bfr, ich, n, stream);
758 printf("read_audio(stream=%d,channel=%d) = %d\n", stream, ich, ret);
762 delay = audio_time(stream) - the_time();
763 if( (delay-=ahead) > 0. ) {
764 usleep((int)(delay*1000000.0/2.));
766 more_data |= !zsrc->end_of_audio(0);
788 int main(int ac, char **av)
802 zmpeg3_t* zsrc = new zmpeg3_t(av[1],ret);
804 printf(" ret = %d\n",ret);
805 if( ret != 0 ) exit(1);
811 if( stat(zsrc->fs->path, &st_buf) >= 0 ) {
812 if( (pos = (st_buf.st_size & ~0x7ffl) - 0x800000l) < 0 )
816 zsrc->seek_byte(pos);
817 // zsrc->show_subtitle(0);
818 signal(SIGINT,sigint);
821 the_db = new Media();
824 the_audio = new Audio(zsrc);
827 the_video = new Video(zsrc);