14 #include <X11/Xatom.h>
16 #include <X11/Xutil.h>
17 #include <X11/extensions/XShm.h>
20 #include "libavfilter/buffersrc.h"
21 #include "libavfilter/buffersink.h"
22 #include "libavformat/avformat.h"
23 #include "libavformat/avio.h"
24 #include "libavcodec/avcodec.h"
25 #include "libavfilter/avfilter.h"
26 #include "libavutil/avutil.h"
27 #include "libavutil/imgutils.h"
28 #include "libavutil/opt.h"
29 #include "libavutil/pixdesc.h"
30 #include "libswresample/swresample.h"
31 #include "libswscale/swscale.h"
37 #define av_register_all(s)
38 #define avfilter_register_all(s)
42 int64_t tm = 0, tn = 0;
52 gg_window(Display *display, int x, int y, int w, int h);
62 gg_window::gg_window(Display *display, int x, int y, int w, int h)
64 this->display = display;
65 this->x = x; this->y = y;
66 this->w = w; this->h = h;
68 Window root = DefaultRootWindow(display);
69 Screen *screen = DefaultScreenOfDisplay(display);
70 Visual *visual = DefaultVisualOfScreen(screen);
71 int depth = DefaultDepthOfScreen(screen);
73 unsigned long gcmask = GCGraphicsExposures;
75 gcvalues.graphics_exposures = 0;
76 gc = XCreateGC(display, root, gcmask, &gcvalues);
78 XSetWindowAttributes attributes;
79 attributes.background_pixel = BlackPixel(display, DefaultScreen(display));
80 attributes.border_pixel = WhitePixel(display, DefaultScreen(display));
81 attributes.event_mask =
82 EnterWindowMask | LeaveWindowMask |
83 ButtonPressMask | ButtonReleaseMask |
84 PointerMotionMask | FocusChangeMask;
85 int valueMask = CWBackPixel | CWBorderPixel | CWEventMask;
86 this->win = XCreateWindow(display, root, x, y, w, h, border, depth,
87 InputOutput, visual, valueMask, &attributes);
90 gg_window::~gg_window()
93 XDestroyWindow(display, win);
96 void gg_window::show()
98 XMapWindow(display,win);
101 void gg_window::hide()
103 XUnmapWindow(display,win);
111 XShmSegmentInfo shm_info;
117 gg_ximage(Display *display, int w, int h, int shm);
120 void put_image(gg_window &gw);
123 gg_ximage::gg_ximage(Display *display, int w, int h, int shm)
125 this->display = display;
126 this->w = w; this->h = h;
129 ximage = 0; sz = 0; data = 0;
130 Screen *screen = DefaultScreenOfDisplay(display);
131 Visual *visual = DefaultVisualOfScreen(screen);
132 int depth = DefaultDepthOfScreen(screen);
135 ximage = XShmCreateImage(display, visual, depth, ZPixmap, (char*)NULL, &shm_info, w, h);
136 // Create shared memory
137 sz = h * ximage->bytes_per_line;
138 shm_info.shmid = shmget(IPC_PRIVATE, sz + 8, IPC_CREAT | 0777);
139 if(shm_info.shmid < 0) perror("shmget");
140 data = (unsigned char *)shmat(shm_info.shmid, NULL, 0);
141 // This causes it to automatically delete when the program exits.
142 shmctl(shm_info.shmid, IPC_RMID, 0);
143 ximage->data = shm_info.shmaddr = (char*)data;
144 shm_info.readOnly = 0;
145 // Get the real parameters
146 if(!XShmAttach(display, &shm_info)) perror("XShmAttach");
149 ximage = XCreateImage(display, visual, depth, ZPixmap, 0, (char*)data, w, h, 8, 0);
150 sz = h * ximage->bytes_per_line;
151 data = new unsigned char[sz+8];
154 ximage->data = (char*) data;
155 lsb[0] = ximage->red_mask & ~(ximage->red_mask<<1);
156 lsb[1] = ximage->green_mask & ~(ximage->green_mask<<1);
157 lsb[2] = ximage->blue_mask & ~(ximage->blue_mask<<1);
160 gg_ximage::~gg_ximage()
165 XDestroyImage(ximage);
166 XShmDetach(display, &shm_info);
168 shmdt(shm_info.shmaddr);
174 XDestroyImage(ximage);
178 void gg_ximage::put_image(gg_window &gw)
180 Display *display = gw.display;
184 XShmPutImage(display, win, gc, ximage, 0,0, 0,0,w,h, 0);
186 XPutImage(display, win, gc, ximage, 0,0, 0,0,w,h);
195 gg_ximage *imgs[2], *img;
197 gg_thread(gg_window &gw, int shm) ;
200 static void *entry(void *t);
204 void post(gg_ximage *ip);
205 gg_ximage *next_img();
207 pthread_mutex_t draw;
208 void draw_lock() { pthread_mutex_lock(&draw); }
209 void draw_unlock() { pthread_mutex_unlock(&draw); }
212 gg_thread::gg_thread(gg_window &gw, int shm)
215 imgs[0] = new gg_ximage(gw.display, gw.w, gw.h, shm);
216 imgs[1] = new gg_ximage(gw.display, gw.w, gw.h, shm);
219 pthread_mutex_init(&draw, 0);
222 gg_thread::~gg_thread()
226 pthread_mutex_destroy(&draw);
229 void *gg_thread::entry(void *t)
231 return ((gg_thread*)t)->run();
234 void gg_thread::start()
237 pthread_attr_init(&attr);
238 pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
240 pthread_create(&tid, &attr, &entry, this);
241 pthread_attr_destroy(&attr);
243 void gg_thread::join()
246 pthread_join(tid, 0);
249 void *gg_thread::run()
252 if( XPending(gw.display) ) {
254 XNextEvent(gw.display, &xev);
265 if( !img ) { usleep(10000); continue; }
273 gg_ximage *gg_thread::next_img()
275 gg_ximage *ip = imgs[active];
280 void gg_thread::post(gg_ximage *ip)
291 AVFormatContext *fmt_ctx;
295 AVPixelFormat pix_fmt;
298 int need_packet, eof;
299 int open_decoder(const char *filename, int vid_no);
300 void close_decoder();
301 AVFrame *read_frame();
306 av_init_packet(&this->ipkt);
311 this->frame_rate = 0;
312 this->need_packet = 0;
314 this->pix_fmt = AV_PIX_FMT_NONE;
318 void ffcmpr::close_decoder()
320 av_packet_unref(&ipkt);
321 if( !fmt_ctx ) return;
322 if( ctx ) avcodec_free_context(&ctx);
323 avformat_close_input(&fmt_ctx);
324 av_frame_free(&ipic);
332 int ffcmpr::open_decoder(const char *filename, int vid_no)
335 if( stat(filename, &fst) ) return 1;
337 av_log_set_level(AV_LOG_VERBOSE);
339 AVDictionary *fopts = 0;
341 av_dict_set(&fopts, "formatprobesize", "5000000", 0);
342 av_dict_set(&fopts, "scan_all_pmts", "1", 0);
343 av_dict_set(&fopts, "threads", "auto", 0);
344 int ret = avformat_open_input(&fmt_ctx, filename, NULL, &fopts);
345 av_dict_free(&fopts);
347 fprintf(stderr,"file open failed: %s\n", filename);
350 ret = avformat_find_stream_info(fmt_ctx, NULL);
352 fprintf(stderr,"file probe failed: %s\n", filename);
357 for( int i=0; !this->st && ret>=0 && i<(int)fmt_ctx->nb_streams; ++i ) {
358 AVStream *fst = fmt_ctx->streams[i];
359 AVMediaType type = fst->codecpar->codec_type;
360 if( type != AVMEDIA_TYPE_VIDEO ) continue;
361 if( --vid_no < 0 ) this->st = fst;
364 AVCodecID codec_id = st->codecpar->codec_id;
365 AVDictionary *copts = 0;
366 //av_dict_copy(&copts, opts, 0);
367 AVCodec *decoder = avcodec_find_decoder(codec_id);
368 ctx = avcodec_alloc_context3(decoder);
369 avcodec_parameters_to_context(ctx, st->codecpar);
370 if( avcodec_open2(ctx, decoder, &copts) < 0 ) {
371 fprintf(stderr,"codec open failed: %s\n", filename);
374 av_dict_free(&copts);
375 ipic = av_frame_alloc();
379 AVRational framerate = av_guess_frame_rate(fmt_ctx, st, 0);
380 this->frame_rate = !framerate.den ? 0 : (double)framerate.num / framerate.den;
381 this->pix_fmt = (AVPixelFormat)st->codecpar->format;
382 this->width = st->codecpar->width;
383 this->height = st->codecpar->height;
387 AVFrame *ffcmpr::read_frame()
389 av_frame_unref(ipic);
391 for( int retrys=1000; --retrys>=0; ) {
394 AVPacket *pkt = &ipkt;
395 av_packet_unref(pkt);
396 int ret = av_read_frame(fmt_ctx, pkt);
398 if( ret != AVERROR_EOF ) return 0;
399 ret = 0; eof = 1; pkt = 0;
402 if( pkt->stream_index != st->index ) continue;
403 if( !pkt->data || !pkt->size ) continue;
405 avcodec_send_packet(ctx, pkt);
408 int ret = avcodec_receive_frame(ctx, ipic);
409 if( ret >= 0 ) return ipic;
410 if( ret != AVERROR(EAGAIN) ) {
411 eof = 1; need_packet = 0;
419 static int diff_frame(AVFrame *afrm, AVFrame *bfrm, gg_ximage *ximg, int w, int h)
422 uint8_t *arow = afrm->data[0];
423 uint8_t *brow = bfrm->data[0];
424 uint8_t *frow = ximg->data;
425 int asz = afrm->linesize[0], bsz = afrm->linesize[0];
426 XImage *ximage = ximg->ximage;
427 int fsz = ximage->bytes_per_line;
428 int rsz = w, bpp = (ximage->bits_per_pixel+7)/8;
429 uint32_t *lsb = ximg->lsb;
431 for( int y=h; --y>=0; arow+=asz, brow+=bsz, frow+=fsz ) {
432 uint8_t *ap = arow, *bp = brow, *fp = frow;
433 for( int x=rsz; --x>=0; ) {
434 uint32_t rgb = 0; uint8_t *rp = fp;
435 for( int i=0; i<3; ++i ) {
436 int d = *ap++ - *bp++;
438 if( v > 255 ) v = 255;
439 else if( v < 0 ) v = 0;
445 if( ximage->byte_order == MSBFirst )
446 for( int i=3; --i>=0; ) *rp++ = rgb>>(8*i);
448 for( int i=0; i<3; ++i ) *rp++ = rgb>>(8*i);
453 printf("%d %d %d %f", sz, m, n, (double)n/sz);
458 int main(int ac, char **av)
463 Display *display = XOpenDisplay(getenv("DISPLAY"));
465 fprintf(stderr,"Unable to open display\n");
470 if( a.open_decoder(av[1],0) ) return 1;
471 if( b.open_decoder(av[2],0) ) return 1;
473 printf("file a:%s\n", av[1]);
474 printf(" id 0x%06x:", a.ctx->codec_id);
475 const AVCodecDescriptor *adesc = avcodec_descriptor_get(a.ctx->codec_id);
476 printf(" video %s\n", adesc ? adesc->name : " (unkn)");
477 printf(" %dx%d %5.2f", a.width, a.height, a.frame_rate);
478 const char *apix = av_get_pix_fmt_name(a.pix_fmt);
479 printf(" pix %s\n", apix ? apix : "(unkn)");
481 printf("file b:%s\n", av[2]);
482 printf(" id 0x%06x:", b.ctx->codec_id);
483 const AVCodecDescriptor *bdesc = avcodec_descriptor_get(b.ctx->codec_id);
484 printf(" video %s\n", bdesc ? bdesc->name : " (unkn)");
485 printf(" %dx%d %5.2f", b.width, b.height, b.frame_rate);
486 const char *bpix = av_get_pix_fmt_name(b.pix_fmt);
487 printf(" pix %s\n", bpix ? bpix : "(unkn)");
489 // if( a.ctx->codec_id != b.ctx->codec_id ) { printf("codec mismatch\n"); return 1;}
490 if( a.width != b.width ) { printf("width mismatch\n"); return 1;}
491 if( a.height != b.height ) { printf("height mismatch\n"); return 1;}
492 // if( a.frame_rate != b.frame_rate ) { printf("framerate mismatch\n"); return 1;}
493 // if( a.pix_fmt != b.pix_fmt ) { printf("format mismatch\n"); return 1;}
495 signal(SIGINT,sigint);
497 struct SwsContext *a_cvt = sws_getCachedContext(0, a.width, a.height, a.pix_fmt,
498 a.width, a.height, AV_PIX_FMT_RGB24, SWS_POINT, 0, 0, 0);
499 struct SwsContext *b_cvt = sws_getCachedContext(0, b.width, b.height, b.pix_fmt,
500 b.width, b.height, AV_PIX_FMT_RGB24, SWS_POINT, 0, 0, 0);
501 if( !a_cvt || !b_cvt ) {
502 printf("sws_getCachedContext() failed\n");
506 AVFrame *afrm = av_frame_alloc();
507 av_image_alloc(afrm->data, afrm->linesize,
508 a.width, a.height, AV_PIX_FMT_RGB24, 1);
510 AVFrame *bfrm = av_frame_alloc();
511 av_image_alloc(bfrm->data, bfrm->linesize,
512 b.width, b.height, AV_PIX_FMT_RGB24, 1);
513 { gg_window gw(display, 10,10, a.width,a.height);
515 gg_thread thr(gw, 1);
521 if( ac>3 && (ret=atoi(av[3])) ) {
522 while( ret > 0 ) { a.read_frame(); --ret; }
523 while( ret < 0 ) { b.read_frame(); ++ret; }
527 AVFrame *ap = a.read_frame();
529 AVFrame *bp = b.read_frame();
531 sws_scale(a_cvt, ap->data, ap->linesize, 0, ap->height,
532 afrm->data, afrm->linesize);
533 sws_scale(b_cvt, bp->data, bp->linesize, 0, bp->height,
534 bfrm->data, bfrm->linesize);
536 gg_ximage *fimg = thr.next_img();
537 ret = diff_frame(afrm, bfrm, fimg, ap->width, ap->height);
539 err += ret; ++frm_no;
540 printf(" %d\n",frm_no);
543 av_freep(&afrm->data);
544 av_frame_free(&afrm);
545 av_freep(&bfrm->data);
546 av_frame_free(&bfrm);
553 XCloseDisplay(display);
554 printf("\n%jd %jd\n", tm, tn);