no longer need ffmpeg patch0 which was for Termux
[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 #ifdef FFMPEG3
35 #define url filename
36 #else
37 #define av_register_all(s)
38 #define avfilter_register_all(s)
39 #endif
40
41 int done = 0;
42 int64_t tm = 0, tn = 0;
43
44 void sigint(int n)
45 {
46   done = 1;
47 }
48
49 class gg_window
50 {
51 public:
52   gg_window(Display *display, int x, int y, int w, int h);
53   ~gg_window();
54   Display *display;
55   Window win;
56   GC gc;
57   int x, y, w, h;
58   void show();
59   void hide();
60 };
61
62 gg_window::gg_window(Display *display, int x, int y, int w, int h)
63 {
64   this->display = display;
65   this->x = x;  this->y = y;
66   this->w = w;  this->h = h;
67
68   Window root = DefaultRootWindow(display);
69   Screen *screen = DefaultScreenOfDisplay(display);
70   Visual *visual = DefaultVisualOfScreen(screen);
71   int depth = DefaultDepthOfScreen(screen);
72   int border = 0;
73   unsigned long gcmask = GCGraphicsExposures;
74   XGCValues gcvalues;
75   gcvalues.graphics_exposures = 0;
76   gc = XCreateGC(display, root, gcmask, &gcvalues);
77
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);
88 }
89
90 gg_window::~gg_window()
91 {
92   XFreeGC(display, gc);
93   XDestroyWindow(display, win);
94 }
95
96 void gg_window::show()
97 {
98   XMapWindow(display,win);
99   XFlush(display);
100 }
101 void gg_window::hide()
102 {
103   XUnmapWindow(display,win);
104   XFlush(display);
105 }
106
107 class gg_ximage
108 {
109 public:
110   Display *display;
111   XShmSegmentInfo shm_info;
112   XImage *ximage;
113   int w, h;
114   unsigned char *data;
115   int shm, sz;
116   uint32_t lsb[3];
117   gg_ximage(Display *display, int w, int h, int shm);
118   ~gg_ximage();
119
120   void put_image(gg_window &gw);
121 };
122
123 gg_ximage::gg_ximage(Display *display, int w, int h, int shm)
124 {
125   this->display = display;
126   this->w = w;  this->h = h;
127   this->shm = shm;
128
129   ximage = 0;  sz = 0;  data = 0;
130   Screen *screen = DefaultScreenOfDisplay(display);
131   Visual *visual = DefaultVisualOfScreen(screen);
132   int depth = DefaultDepthOfScreen(screen);
133
134   if( shm ) {
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");
147   }
148   else {
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];
152   }
153   memset(data, 0, sz);
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);
158 }
159
160 gg_ximage::~gg_ximage()
161 {
162   if( shm ) {
163     data = 0;
164     ximage->data = 0;
165     XDestroyImage(ximage);
166     XShmDetach(display, &shm_info);
167     XFlush(display);
168     shmdt(shm_info.shmaddr);
169   }
170   else {
171     delete [] data;
172     data = 0;
173     ximage->data = 0;
174     XDestroyImage(ximage);
175   }
176 }
177
178 void gg_ximage::put_image(gg_window &gw)
179 {
180   Display *display = gw.display;
181   Window win = gw.win;
182   GC gc = gw.gc;
183   if( shm )
184     XShmPutImage(display, win, gc, ximage, 0,0, 0,0,w,h, 0);
185   else
186     XPutImage(display, win, gc, ximage, 0,0, 0,0,w,h); 
187   XFlush(display);
188 }
189
190 class gg_thread
191 {
192 public:
193   pthread_t tid;
194   gg_window &gw;
195   gg_ximage *imgs[2], *img;
196   int active, done;
197   gg_thread(gg_window &gw, int shm) ;
198   ~gg_thread();
199
200   static void *entry(void *t);
201   void start();
202   void *run();
203   void join();
204   void post(gg_ximage *ip);
205   gg_ximage *next_img();
206
207   pthread_mutex_t draw;
208   void draw_lock() { pthread_mutex_lock(&draw); }
209   void draw_unlock() { pthread_mutex_unlock(&draw); }
210 };
211
212 gg_thread::gg_thread(gg_window &gw, int shm)
213  : gw(gw)
214 {
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);
217   done = -1;
218   img = 0;  active = 0;
219   pthread_mutex_init(&draw, 0);
220 }
221
222 gg_thread::~gg_thread()
223 {
224   delete imgs[0];
225   delete imgs[1];
226   pthread_mutex_destroy(&draw);
227 }
228
229 void *gg_thread::entry(void *t)
230 {
231   return ((gg_thread*)t)->run();
232 }
233
234 void gg_thread::start()
235 {
236   pthread_attr_t attr;
237   pthread_attr_init(&attr);
238   pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
239   done = 0;
240   pthread_create(&tid, &attr, &entry, this);
241   pthread_attr_destroy(&attr);
242 }
243 void gg_thread::join()
244 {
245   done = 1;
246   pthread_join(tid, 0);
247 }
248
249 void *gg_thread::run()
250 {
251   while( !done ) {
252     if( XPending(gw.display) ) {
253       XEvent xev;
254       XNextEvent(gw.display, &xev);
255       switch( xev.type ) {
256       case KeyPress:
257       case KeyRelease:
258       case ButtonPress:
259       case Expose:
260         break;
261       }
262       continue;
263     }
264
265     if( !img ) { usleep(10000);  continue; }
266     img->put_image(gw);
267     img = 0;
268     draw_unlock();
269   }
270   return (void*)0;
271 }
272
273 gg_ximage *gg_thread::next_img()
274 {
275   gg_ximage *ip = imgs[active];
276   active ^= 1;
277   return ip;
278 }
279
280 void gg_thread::post(gg_ximage *ip)
281 {
282   this->img = ip;
283 }
284
285
286 class ffcmpr {
287 public:
288   ffcmpr();
289   ~ffcmpr();
290   AVPacket ipkt;
291   AVFormatContext *fmt_ctx;
292   AVFrame *ipic;
293   AVStream *st;
294   AVCodecContext *ctx;
295   AVPixelFormat pix_fmt;
296   double frame_rate;
297   int width, height;
298   int need_packet, eof;
299   int open_decoder(const char *filename, int vid_no);
300   void close_decoder();
301   AVFrame *read_frame();
302 };
303
304 ffcmpr::ffcmpr()
305 {
306   av_init_packet(&this->ipkt);
307   this->fmt_ctx = 0;
308   this->ipic = 0;
309   this->st = 0;
310   this->ctx = 0 ;
311   this->frame_rate = 0;
312   this->need_packet = 0;
313   this->eof = 0;
314   this->pix_fmt = AV_PIX_FMT_NONE;
315   width = height = 0;
316 }
317
318 void ffcmpr::close_decoder()
319 {
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);
325 }
326
327 ffcmpr::~ffcmpr()
328 {
329   close_decoder();
330 }
331
332 int ffcmpr::open_decoder(const char *filename, int vid_no)
333 {
334   struct stat fst;
335   if( stat(filename, &fst) ) return 1;
336   fmt_ctx = 0;
337   AVDictionary *fopts = 0;
338   av_register_all();
339   av_dict_set(&fopts, "formatprobesize", "5000000", 0);
340   av_dict_set(&fopts, "scan_all_pmts", "1", 0);
341   av_dict_set(&fopts, "threads", "auto", 0);
342   int ret = avformat_open_input(&fmt_ctx, filename, NULL, &fopts);
343   av_dict_free(&fopts);
344   if( ret < 0 ) {
345     fprintf(stderr,"file open failed: %s\n", filename);
346     return ret;
347   }
348   ret = avformat_find_stream_info(fmt_ctx, NULL);
349   if( ret < 0 ) {
350     fprintf(stderr,"file probe failed: %s\n", filename);
351     return ret;
352   }
353
354   this->st = 0;
355   for( int i=0; !this->st && ret>=0 && i<(int)fmt_ctx->nb_streams; ++i ) {
356     AVStream *fst = fmt_ctx->streams[i];
357     AVMediaType type = fst->codecpar->codec_type;
358     if( type != AVMEDIA_TYPE_VIDEO ) continue;
359     if( --vid_no < 0 ) this->st = fst;
360   }
361
362   AVCodecID codec_id = st->codecpar->codec_id;
363   AVDictionary *copts = 0;
364   //av_dict_copy(&copts, opts, 0);
365   AVCodec *decoder = avcodec_find_decoder(codec_id);
366   ctx = avcodec_alloc_context3(decoder);
367   avcodec_parameters_to_context(ctx, st->codecpar);
368   if( avcodec_open2(ctx, decoder, &copts) < 0 ) {
369     fprintf(stderr,"codec open failed: %s\n", filename);
370     return -1;
371   }
372   av_dict_free(&copts);
373   ipic = av_frame_alloc();
374   eof = 0;
375   need_packet = 1;
376
377   AVRational framerate = av_guess_frame_rate(fmt_ctx, st, 0);
378   this->frame_rate = !framerate.den ? 0 : (double)framerate.num / framerate.den;
379   this->pix_fmt = (AVPixelFormat)st->codecpar->format;
380   this->width  = st->codecpar->width;
381   this->height = st->codecpar->height;
382   return 0;
383 }
384
385 AVFrame *ffcmpr::read_frame()
386 {
387   av_frame_unref(ipic);
388
389   for( int retrys=1000; --retrys>=0; ) {
390     if( need_packet ) {
391       if( eof ) return 0;
392       AVPacket *pkt = &ipkt;
393       av_packet_unref(pkt);
394       int ret = av_read_frame(fmt_ctx, pkt);
395       if( ret < 0 ) {
396         if( ret != AVERROR_EOF ) return 0;
397         ret = 0;  eof = 1;  pkt = 0;
398       }
399       if( pkt ) {
400         if( pkt->stream_index != st->index ) continue;
401         if( !pkt->data || !pkt->size ) continue;
402       }
403       avcodec_send_packet(ctx, pkt);
404       need_packet = 0;
405     }
406     int ret = avcodec_receive_frame(ctx, ipic);
407     if( ret >= 0 ) return ipic;
408     if( ret != AVERROR(EAGAIN) ) {
409       eof = 1; need_packet = 0;
410       break;
411     }
412     need_packet = 1;
413   }
414   return 0;
415 }
416
417 static inline int get_depth(AVPixelFormat pix_fmt)
418 {
419   int depth = 0;
420   const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
421   if( desc ) {
422     for( int i=desc->nb_components; --i>=0; ) {
423       int bits = desc->comp[i].depth;
424       if( depth < bits ) depth = bits;
425     }
426   }
427   return depth;
428 }
429
430 static int diff_frame(AVFrame *afrm, AVFrame *bfrm,
431     gg_ximage *ximg, int w, int h, int s, int a1)
432 {
433   int n = 0, m = 0;
434   uint8_t *arow = afrm->data[0];
435   uint8_t *brow = bfrm->data[0];
436   uint8_t *frow = ximg->data;
437   int asz = afrm->linesize[0], bsz = afrm->linesize[0];
438   XImage *ximage = ximg->ximage;
439   int fsz = ximage->bytes_per_line;
440   int rsz = w, bpp = (ximage->bits_per_pixel+7)/8;
441   uint32_t *lsb = ximg->lsb;
442
443   for( int y=h; --y>=0; arow+=asz, brow+=bsz, frow+=fsz ) {
444     uint16_t *ap = (uint16_t*)arow + a1;
445     uint16_t *bp = (uint16_t*)brow + a1;
446     uint8_t *fp = frow;
447     for( int x=rsz; --x>=0; ) {
448       uint32_t rgb = 0;  uint8_t *rp = fp;
449       for( int i=0; i<3; ++i ) {
450         int d = *ap++ - *bp++;
451         if( s > 0 ) d >>= s;
452         else if( s < 0 ) d <<= -s;
453         int v = d + 128;
454         if( v > 255 ) v = 255;
455         else if( v < 0 ) v = 0;
456         rgb |= v * lsb[i];
457         m += d;
458         if( d < 0 ) d = -d;
459         n += d;
460       }
461       if( ximage->byte_order == MSBFirst )
462         for( int i=3; --i>=0; ) *rp++ = rgb>>(8*i);
463       else
464         for( int i=0; i<3; ++i ) *rp++ = rgb>>(8*i);
465       ++ap;  ++bp;  fp += bpp;
466     }
467   }
468   int sz = h*rsz;
469   printf("%d %d %d %f", sz, m, n, (double)n/sz);
470   tm += m;  tn += n;
471   return n;
472 }
473
474 int main(int ac, char **av)
475 {
476   setbuf(stdout,NULL);
477   XInitThreads();
478   Display *display = XOpenDisplay(getenv("DISPLAY"));
479   if( !display ) {
480     fprintf(stderr,"Unable to open display\n");
481     exit(1);
482   }
483   if( ac < 3 ) {
484     printf("usage: %s a.fmt b.fmt <frm0> <s>\n"
485         "  a = src media, b = src media, frm0 = a/b skew\n"
486         "  s = shift <0:lt, =0:none(dft), >0:rt\n"
487         "  env var GG_LOG_LEVEL=q/f/e/v/d/<nbr>\n", av[0]);
488     exit(1);
489   }
490   const char *cp = getenv("GG_LOG_LEVEL");
491   if( cp ) {
492     int lvl = -1;
493     switch( *cp ) {
494     case 'q':  lvl = AV_LOG_QUIET;    break;
495     case 'f':  lvl = AV_LOG_FATAL;    break;
496     case 'e':  lvl = AV_LOG_ERROR;    break;
497     case 'v':  lvl = AV_LOG_VERBOSE;  break;
498     case 'd':  lvl = AV_LOG_DEBUG;    break;
499     case '0'...'9': lvl = atoi(cp);   break;
500     }
501     if( lvl >= 0 )
502       av_log_set_level(lvl);
503   }
504
505   ffcmpr a, b;
506   if( a.open_decoder(av[1],0) ) return 1;
507   if( b.open_decoder(av[2],0) ) return 1;
508
509   int64_t err = 0;
510   int frm_no = 0;
511   int frm0 = ac>3 ? atoi(av[3]) : 0;
512   int s = ac>4 ? atoi(av[4]) : 0;
513
514   printf("file a:%s\n", av[1]);
515   printf("  id 0x%06x:", a.ctx->codec_id);
516   const AVCodecDescriptor *adesc = avcodec_descriptor_get(a.ctx->codec_id);
517   printf("  video %s\n", adesc ? adesc->name : " (unkn)");
518   printf(" %dx%d %5.2f", a.width, a.height, a.frame_rate);
519   const char *apix = av_get_pix_fmt_name(a.pix_fmt);
520   int ad = get_depth(a.pix_fmt);
521   printf(" pix %s, depth=%d\n", apix ? apix : "(unkn)", ad);
522
523   printf("file b:%s\n", av[2]);
524   printf("  id 0x%06x:", b.ctx->codec_id);
525   const AVCodecDescriptor *bdesc = avcodec_descriptor_get(b.ctx->codec_id);
526   printf("  video %s\n", bdesc ? bdesc->name : " (unkn)");
527   printf(" %dx%d %5.2f", b.width, b.height, b.frame_rate);
528   const char *bpix = av_get_pix_fmt_name(b.pix_fmt);
529   int bd = get_depth(b.pix_fmt);
530   printf(" pix %s, depth=%d\n", bpix ? bpix : "(unkn)", bd);
531   int d = ad>bd ? ad : bd;
532   s = 16-d + s;
533   int lsb = s, msb = lsb + 7;
534   if( lsb < 0 ) lsb = 0;
535   if( msb > 15 ) msb = 15;
536   printf("shift: %d, msb..lsb: %d..%d of uint16\n", s, msb, lsb);
537
538 //  if( a.ctx->codec_id != b.ctx->codec_id ) { printf("codec mismatch\n"); return 1;}
539   if( a.width != b.width ) { printf("width mismatch\n"); return 1;}
540   if( a.height != b.height ) { printf("height mismatch\n"); return 1;}
541 //  if( a.frame_rate != b.frame_rate ) { printf("framerate mismatch\n"); return 1;}
542 //  if( a.pix_fmt != b.pix_fmt ) { printf("format mismatch\n"); return 1;}
543
544   signal(SIGINT,sigint);
545   const AVPixFmtDescriptor *afmt = av_pix_fmt_desc_get(a.pix_fmt);
546   AVPixelFormat a_pix_fmt = afmt->flags & AV_PIX_FMT_FLAG_RGB ?
547       AV_PIX_FMT_RGBA64LE : AV_PIX_FMT_AYUV64LE ;
548   const AVPixFmtDescriptor *bfmt = av_pix_fmt_desc_get(b.pix_fmt);
549   AVPixelFormat b_pix_fmt = bfmt->flags & AV_PIX_FMT_FLAG_RGB ?
550       AV_PIX_FMT_RGBA64LE : AV_PIX_FMT_AYUV64LE ;
551   if( a_pix_fmt != b_pix_fmt ) {
552     printf(" a/b yuv/rgb mismatched, using a = %s\n", apix);
553     b_pix_fmt = a_pix_fmt;
554   }
555   int a1 = a_pix_fmt == AV_PIX_FMT_AYUV64LE ? 1 : 0; // alpha 1st chan
556
557   struct SwsContext *a_cvt = sws_getCachedContext(0, a.width, a.height, a.pix_fmt,
558                 a.width, a.height, a_pix_fmt, SWS_POINT, 0, 0, 0);
559   struct SwsContext *b_cvt = sws_getCachedContext(0, b.width, b.height, b.pix_fmt,
560                 b.width, b.height, b_pix_fmt, SWS_POINT, 0, 0, 0);
561   if( !a_cvt || !b_cvt ) {
562     printf("sws_getCachedContext() failed\n");
563     exit(1);
564   }
565
566   AVFrame *afrm = av_frame_alloc();
567   av_image_alloc(afrm->data, afrm->linesize,
568      a.width, a.height, a_pix_fmt, 1);
569
570   AVFrame *bfrm = av_frame_alloc();
571   av_image_alloc(bfrm->data, bfrm->linesize,
572      b.width, b.height, b_pix_fmt, 1);
573 { gg_window gw(display, 10,10, a.width,a.height);
574   gw.show();
575   gg_thread thr(gw, 1);
576   thr.start();
577
578   while( frm0 > 0 ) { a.read_frame(); --frm0; }
579   while( frm0 < 0 ) { b.read_frame(); ++frm0; }
580
581   while( !done ) {
582     AVFrame *ap = a.read_frame();
583     if( !ap ) break;
584     AVFrame *bp = b.read_frame();
585     if( !bp ) break;
586     sws_scale(a_cvt, ap->data, ap->linesize, 0, ap->height,
587        afrm->data, afrm->linesize);
588     sws_scale(b_cvt, bp->data, bp->linesize, 0, bp->height,
589        bfrm->data, bfrm->linesize);
590     thr.draw_lock();
591     gg_ximage *fimg = thr.next_img();
592     int ret = diff_frame(afrm, bfrm, fimg, ap->width, ap->height, s, a1);
593     thr.post(fimg);
594     err += ret;  ++frm_no;
595     printf("  %d\n",frm_no);
596   }
597
598   av_freep(&afrm->data);
599   av_frame_free(&afrm);
600   av_freep(&bfrm->data);
601   av_frame_free(&bfrm);
602   
603   b.close_decoder();
604   a.close_decoder();
605
606   thr.join();
607   gw.hide(); }
608   XCloseDisplay(display);
609   printf("\n%jd %jd\n", tm, tn);
610   return 0;
611 }
612