change copy packed clip to unpacked in move_edits
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / ydiff.C
1 #include <stdio.h>
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <math.h>
5 #include <pthread.h>
6 #include <signal.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <sys/stat.h>
10 #include <sys/time.h>
11 #include <sys/ipc.h>
12 #include <sys/shm.h>
13
14 #include <X11/Xatom.h>
15 #include <X11/Xlib.h>
16 #include <X11/Xutil.h>
17 #include <X11/extensions/XShm.h>
18
19 extern "C" {
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"
32 }
33
34 int done = 0;
35 int64_t tm = 0, tn = 0;
36
37 void sigint(int n)
38 {
39   done = 1;
40 }
41
42 class gg_window
43 {
44 public:
45   gg_window(Display *display, int x, int y, int w, int h);
46   ~gg_window();
47   Display *display;
48   Window win;
49   GC gc;
50   int x, y, w, h;
51   void show();
52   void hide();
53 };
54
55 gg_window::gg_window(Display *display, int x, int y, int w, int h)
56 {
57   this->display = display;
58   this->x = x;  this->y = y;
59   this->w = w;  this->h = h;
60
61   Window root = DefaultRootWindow(display);
62   Screen *screen = DefaultScreenOfDisplay(display);
63   Visual *visual = DefaultVisualOfScreen(screen);
64   int depth = DefaultDepthOfScreen(screen);
65   int border = 0;
66   unsigned long gcmask = GCGraphicsExposures;
67   XGCValues gcvalues;
68   gcvalues.graphics_exposures = 0;
69   gc = XCreateGC(display, root, gcmask, &gcvalues);
70
71   XSetWindowAttributes attributes;
72   attributes.background_pixel = BlackPixel(display, DefaultScreen(display));
73   attributes.border_pixel = WhitePixel(display, DefaultScreen(display));
74   attributes.event_mask =
75     EnterWindowMask | LeaveWindowMask |
76     ButtonPressMask | ButtonReleaseMask |
77     PointerMotionMask | FocusChangeMask;
78   int valueMask = CWBackPixel | CWBorderPixel | CWEventMask;
79   this->win = XCreateWindow(display, root, x, y, w, h, border, depth,
80       InputOutput, visual, valueMask, &attributes);
81 }
82
83 gg_window::~gg_window()
84 {
85   XFreeGC(display, gc);
86   XDestroyWindow(display, win);
87 }
88
89 void gg_window::show()
90 {
91   XMapWindow(display,win);
92   XFlush(display);
93 }
94 void gg_window::hide()
95 {
96   XUnmapWindow(display,win);
97   XFlush(display);
98 }
99
100 class gg_ximage
101 {
102 public:
103   Display *display;
104   XShmSegmentInfo shm_info;
105   XImage *ximage;
106   int w, h;
107   unsigned char *data;
108   int shm, sz;
109   uint32_t lsb[3];
110   gg_ximage(Display *display, int w, int h, int shm);
111   ~gg_ximage();
112
113   void put_image(gg_window &gw);
114 };
115
116 gg_ximage::gg_ximage(Display *display, int w, int h, int shm)
117 {
118   this->display = display;
119   this->w = w;  this->h = h;
120   this->shm = shm;
121
122   ximage = 0;  sz = 0;  data = 0;
123   Screen *screen = DefaultScreenOfDisplay(display);
124   Visual *visual = DefaultVisualOfScreen(screen);
125   int depth = DefaultDepthOfScreen(screen);
126
127   if( shm ) {
128     ximage = XShmCreateImage(display, visual, depth, ZPixmap, (char*)NULL, &shm_info, w, h);
129 // Create shared memory
130     sz = h * ximage->bytes_per_line;
131     shm_info.shmid = shmget(IPC_PRIVATE, sz + 8, IPC_CREAT | 0777);
132     if(shm_info.shmid < 0) perror("shmget");
133     data = (unsigned char *)shmat(shm_info.shmid, NULL, 0);
134 // This causes it to automatically delete when the program exits.
135     shmctl(shm_info.shmid, IPC_RMID, 0);
136     ximage->data = shm_info.shmaddr = (char*)data;
137     shm_info.readOnly = 0;
138 // Get the real parameters
139     if(!XShmAttach(display, &shm_info)) perror("XShmAttach");
140   }
141   else {
142       ximage = XCreateImage(display, visual, depth, ZPixmap, 0, (char*)data, w, h, 8, 0);
143       sz = h * ximage->bytes_per_line;
144       data = new unsigned char[sz+8];
145   }
146   memset(data, 0, sz);
147   ximage->data = (char*) data;
148   lsb[0] = ximage->red_mask & ~(ximage->red_mask<<1);
149   lsb[1] = ximage->green_mask & ~(ximage->green_mask<<1);
150   lsb[2] = ximage->blue_mask & ~(ximage->blue_mask<<1);
151 }
152
153 gg_ximage::~gg_ximage()
154 {
155   if( shm ) {
156     data = 0;
157     ximage->data = 0;
158     XDestroyImage(ximage);
159     XShmDetach(display, &shm_info);
160     XFlush(display);
161     shmdt(shm_info.shmaddr);
162   }
163   else {
164     delete [] data;
165     data = 0;
166     ximage->data = 0;
167     XDestroyImage(ximage);
168   }
169 }
170
171 void gg_ximage::put_image(gg_window &gw)
172 {
173   Display *display = gw.display;
174   Window win = gw.win;
175   GC gc = gw.gc;
176   if( shm )
177     XShmPutImage(display, win, gc, ximage, 0,0, 0,0,w,h, 0);
178   else
179     XPutImage(display, win, gc, ximage, 0,0, 0,0,w,h); 
180   XFlush(display);
181 }
182
183 class gg_thread
184 {
185 public:
186   pthread_t tid;
187   gg_window &gw;
188   gg_ximage *imgs[2], *img;
189   int active, done;
190   gg_thread(gg_window &gw, int shm) ;
191   ~gg_thread();
192
193   static void *entry(void *t);
194   void start();
195   void *run();
196   void join();
197   void post(gg_ximage *ip);
198   gg_ximage *next_img();
199
200   pthread_mutex_t draw;
201   void draw_lock() { pthread_mutex_lock(&draw); }
202   void draw_unlock() { pthread_mutex_unlock(&draw); }
203 };
204
205 gg_thread::gg_thread(gg_window &gw, int shm)
206  : gw(gw)
207 {
208   imgs[0] = new gg_ximage(gw.display, gw.w, gw.h, shm);
209   imgs[1] = new gg_ximage(gw.display, gw.w, gw.h, shm);
210   done = -1;
211   img = 0;  active = 0;
212   pthread_mutex_init(&draw, 0);
213 }
214
215 gg_thread::~gg_thread()
216 {
217   delete imgs[0];
218   delete imgs[1];
219   pthread_mutex_destroy(&draw);
220 }
221
222 void *gg_thread::entry(void *t)
223 {
224   return ((gg_thread*)t)->run();
225 }
226
227 void gg_thread::start()
228 {
229   pthread_attr_t attr;
230   pthread_attr_init(&attr);
231   pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
232   done = 0;
233   pthread_create(&tid, &attr, &entry, this);
234   pthread_attr_destroy(&attr);
235 }
236 void gg_thread::join()
237 {
238   done = 1;
239   pthread_join(tid, 0);
240 }
241
242 void *gg_thread::run()
243 {
244   while( !done ) {
245     if( XPending(gw.display) ) {
246       XEvent xev;
247       XNextEvent(gw.display, &xev);
248       switch( xev.type ) {
249       case KeyPress:
250       case KeyRelease:
251       case ButtonPress:
252       case Expose:
253         break;
254       }
255       continue;
256     }
257
258     if( !img ) { usleep(10000);  continue; }
259     img->put_image(gw);
260     img = 0;
261     draw_unlock();
262   }
263   return (void*)0;
264 }
265
266 gg_ximage *gg_thread::next_img()
267 {
268   gg_ximage *ip = imgs[active];
269   active ^= 1;
270   return ip;
271 }
272
273 void gg_thread::post(gg_ximage *ip)
274 {
275   this->img = ip;
276 }
277
278
279 class ffcmpr {
280 public:
281   ffcmpr();
282   ~ffcmpr();
283   AVPacket ipkt;
284   AVFormatContext *fmt_ctx;
285   AVFrame *ipic;
286   AVStream *st;
287   AVCodecContext *ctx;
288   AVPixelFormat pix_fmt;
289   double frame_rate;
290   int width, height;
291   int need_packet, eof;
292   int open_decoder(const char *filename, int vid_no);
293   void close_decoder();
294   AVFrame *read_frame();
295 };
296
297 ffcmpr::ffcmpr()
298 {
299   av_init_packet(&this->ipkt);
300   this->fmt_ctx = 0;
301   this->ipic = 0;
302   this->st = 0;
303   this->ctx = 0 ;
304   this->frame_rate = 0;
305   this->need_packet = 0;
306   this->eof = 0;
307   this->pix_fmt = AV_PIX_FMT_NONE;
308   width = height = 0;
309 }
310
311 void ffcmpr::close_decoder()
312 {
313   av_packet_unref(&ipkt);
314   if( !fmt_ctx ) return;
315   if( ctx ) avcodec_free_context(&ctx);
316   avformat_close_input(&fmt_ctx);
317   av_frame_free(&ipic);
318 }
319
320 ffcmpr::~ffcmpr()
321 {
322   close_decoder();
323 }
324
325 int ffcmpr::open_decoder(const char *filename, int vid_no)
326 {
327   struct stat fst;
328   if( stat(filename, &fst) ) return 1;
329
330   av_log_set_level(AV_LOG_VERBOSE);
331   fmt_ctx = 0;
332   AVDictionary *fopts = 0;
333   av_register_all();
334   av_dict_set(&fopts, "formatprobesize", "5000000", 0);
335   av_dict_set(&fopts, "scan_all_pmts", "1", 0);
336   av_dict_set(&fopts, "threads", "auto", 0);
337   int ret = avformat_open_input(&fmt_ctx, filename, NULL, &fopts);
338   av_dict_free(&fopts);
339   if( ret < 0 ) {
340     fprintf(stderr,"file open failed: %s\n", filename);
341     return ret;
342   }
343   ret = avformat_find_stream_info(fmt_ctx, NULL);
344   if( ret < 0 ) {
345     fprintf(stderr,"file probe failed: %s\n", filename);
346     return ret;
347   }
348
349   this->st = 0;
350   for( int i=0; !this->st && ret>=0 && i<(int)fmt_ctx->nb_streams; ++i ) {
351     AVStream *fst = fmt_ctx->streams[i];
352     AVMediaType type = fst->codecpar->codec_type;
353     if( type != AVMEDIA_TYPE_VIDEO ) continue;
354     if( --vid_no < 0 ) this->st = fst;
355   }
356
357   AVCodecID codec_id = st->codecpar->codec_id;
358   AVDictionary *copts = 0;
359   //av_dict_copy(&copts, opts, 0);
360   AVCodec *decoder = avcodec_find_decoder(codec_id);
361   ctx = avcodec_alloc_context3(decoder);
362   avcodec_parameters_to_context(ctx, st->codecpar);
363   if( avcodec_open2(ctx, decoder, &copts) < 0 ) {
364     fprintf(stderr,"codec open failed: %s\n", filename);
365     return -1;
366   }
367   av_dict_free(&copts);
368   ipic = av_frame_alloc();
369   eof = 0;
370   need_packet = 1;
371
372   AVRational framerate = av_guess_frame_rate(fmt_ctx, st, 0);
373   this->frame_rate = !framerate.den ? 0 : (double)framerate.num / framerate.den;
374   this->pix_fmt = (AVPixelFormat)st->codecpar->format;
375   this->width  = st->codecpar->width;
376   this->height = st->codecpar->height;
377   return 0;
378 }
379
380 AVFrame *ffcmpr::read_frame()
381 {
382   av_frame_unref(ipic);
383
384   for( int retrys=1000; --retrys>=0; ) {
385     if( need_packet ) {
386       if( eof ) return 0;
387       AVPacket *pkt = &ipkt;
388       av_packet_unref(pkt);
389       int ret = av_read_frame(fmt_ctx, pkt);
390       if( ret < 0 ) {
391         if( ret != AVERROR_EOF ) return 0;
392         ret = 0;  eof = 1;  pkt = 0;
393       }
394       if( pkt ) {
395         if( pkt->stream_index != st->index ) continue;
396         if( !pkt->data || !pkt->size ) continue;
397       }
398       avcodec_send_packet(ctx, pkt);
399       need_packet = 0;
400     }
401     int ret = avcodec_receive_frame(ctx, ipic);
402     if( ret >= 0 ) return ipic;
403     if( ret != AVERROR(EAGAIN) ) {
404       eof = 1; need_packet = 0;
405       break;
406     }
407     need_packet = 1;
408   }
409   return 0;
410 }
411
412 static int diff_frame(AVFrame *afrm, AVFrame *bfrm, gg_ximage *ximg, int w, int h)
413 {
414   int n = 0, m = 0;
415   uint8_t *arow = afrm->data[0];
416   uint8_t *brow = bfrm->data[0];
417   uint8_t *frow = ximg->data;
418   int asz = afrm->linesize[0], bsz = afrm->linesize[0];
419   XImage *ximage = ximg->ximage;
420   int fsz = ximage->bytes_per_line;
421   int rsz = w, bpp = (ximage->bits_per_pixel+7)/8;
422   uint32_t *lsb = ximg->lsb;
423
424   for( int y=h; --y>=0; arow+=asz, brow+=bsz, frow+=fsz ) {
425     uint8_t *ap = arow, *bp = brow, *fp = frow;
426     for( int x=rsz; --x>=0; ) {
427       uint32_t rgb = 0;  uint8_t *rp = fp;
428       for( int i=0; i<3; ++i ) {
429         int d = *ap++ - *bp++;
430         int v = d + 128;
431         if( v > 255 ) v = 255;
432         else if( v < 0 ) v = 0;
433         rgb |= v * lsb[i];
434         m += d;
435         if( d < 0 ) d = -d;
436         n += d;
437       }
438       if( ximage->byte_order == MSBFirst )
439         for( int i=3; --i>=0; ) *rp++ = rgb>>(8*i);
440       else
441         for( int i=0; i<3; ++i ) *rp++ = rgb>>(8*i);
442       fp += bpp;
443     }
444   }
445   int sz = h*rsz;
446   printf("%d %d %d %f", sz, m, n, (double)n/sz);
447   tm += m;  tn += n;
448   return n;
449 }
450
451 int main(int ac, char **av)
452 {
453   int ret;
454   setbuf(stdout,NULL);
455   XInitThreads();
456   Display *display = XOpenDisplay(getenv("DISPLAY"));
457   if( !display ) {
458     fprintf(stderr,"Unable to open display\n");
459     exit(1);
460   }
461
462   ffcmpr a, b;
463   if( a.open_decoder(av[1],0) ) return 1;
464   if( b.open_decoder(av[2],0) ) return 1;
465
466   printf("file a:%s\n", av[1]);
467   printf("  id 0x%06x:", a.ctx->codec_id);
468   const AVCodecDescriptor *adesc = avcodec_descriptor_get(a.ctx->codec_id);
469   printf("  video %s\n", adesc ? adesc->name : " (unkn)");
470   printf(" %dx%d %5.2f", a.width, a.height, a.frame_rate);
471   const char *apix = av_get_pix_fmt_name(a.pix_fmt);
472   printf(" pix %s\n", apix ? apix : "(unkn)");
473
474   printf("file b:%s\n", av[2]);
475   printf("  id 0x%06x:", b.ctx->codec_id);
476   const AVCodecDescriptor *bdesc = avcodec_descriptor_get(b.ctx->codec_id);
477   printf("  video %s\n", bdesc ? bdesc->name : " (unkn)");
478   printf(" %dx%d %5.2f", b.width, b.height, b.frame_rate);
479   const char *bpix = av_get_pix_fmt_name(b.pix_fmt);
480   printf(" pix %s\n", bpix ? bpix : "(unkn)");
481
482 //  if( a.ctx->codec_id != b.ctx->codec_id ) { printf("codec mismatch\n"); return 1;}
483   if( a.width != b.width ) { printf("width mismatch\n"); return 1;}
484   if( a.height != b.height ) { printf("height mismatch\n"); return 1;}
485 //  if( a.frame_rate != b.frame_rate ) { printf("framerate mismatch\n"); return 1;}
486 //  if( a.pix_fmt != b.pix_fmt ) { printf("format mismatch\n"); return 1;}
487
488   signal(SIGINT,sigint);
489
490   struct SwsContext *a_cvt = sws_getCachedContext(0, a.width, a.height, a.pix_fmt,
491                 a.width, a.height, AV_PIX_FMT_RGB24, SWS_POINT, 0, 0, 0);
492   struct SwsContext *b_cvt = sws_getCachedContext(0, b.width, b.height, b.pix_fmt,
493                 b.width, b.height, AV_PIX_FMT_RGB24, SWS_POINT, 0, 0, 0);
494   if( !a_cvt || !b_cvt ) {
495     printf("sws_getCachedContext() failed\n");
496     return 1;
497   }
498
499   AVFrame *afrm = av_frame_alloc();
500   av_image_alloc(afrm->data, afrm->linesize,
501      a.width, a.height, AV_PIX_FMT_RGB24, 1);
502
503   AVFrame *bfrm = av_frame_alloc();
504   av_image_alloc(bfrm->data, bfrm->linesize,
505      b.width, b.height, AV_PIX_FMT_RGB24, 1);
506 { gg_window gw(display, 10,10, a.width,a.height);
507   gw.show();
508   gg_thread thr(gw, 1);
509   thr.start();
510
511   int64_t err = 0;
512   int frm_no = 0;
513
514   if( ac>3 && (ret=atoi(av[3])) ) {
515     while( ret > 0 ) { a.read_frame(); --ret; }
516     while( ret < 0 ) { b.read_frame(); ++ret; }
517   }
518
519   while( !done ) {
520     AVFrame *ap = a.read_frame();
521     if( !ap ) break;
522     AVFrame *bp = b.read_frame();
523     if( !bp ) break;
524     sws_scale(a_cvt, ap->data, ap->linesize, 0, ap->height,
525        afrm->data, afrm->linesize);
526     sws_scale(b_cvt, bp->data, bp->linesize, 0, bp->height,
527        bfrm->data, bfrm->linesize);
528     thr.draw_lock();
529     gg_ximage *fimg = thr.next_img();
530     ret = diff_frame(afrm, bfrm, fimg, ap->width, ap->height);
531     thr.post(fimg);
532     err += ret;  ++frm_no;
533     printf("  %d\n",frm_no);
534   }
535
536   av_freep(&afrm->data);
537   av_frame_free(&afrm);
538   av_freep(&bfrm->data);
539   av_frame_free(&bfrm);
540   
541   b.close_decoder();
542   a.close_decoder();
543
544   thr.join();
545   gw.hide(); }
546   XCloseDisplay(display);
547   printf("\n%jd %jd\n", tm, tn);
548   return 0;
549 }
550