minor changes; mostly for new Context Help feature
[goodguy/cinelerra.git] / cinelerra-5.1 / db / utils / clip_dups.C
1 #include<stdio.h>
2 #include<stdarg.h>
3 #include<time.h>
4 #include<math.h>
5 #include "tdb.h"
6 #include "s.C"
7
8 #include "../cinelerra/mediadb.inc"
9
10 char *diff_path = 0;
11 int curr_id = -1;
12 int next_id = -1;
13
14 static inline int clip(int v, int mn, int mx)
15 {
16   return v<mn ? mn : v>mx ? mx : v;
17 }
18
19 void write_pbm(uint8_t *tp, int w, int h, const char *fmt, ...)
20 {
21   va_list ap;    va_start(ap, fmt);
22   char fn[256];  vsnprintf(fn, sizeof(fn), fmt, ap);
23   va_end(ap);
24   FILE *fp = !strcmp(fn,"-") ? stdout : fopen(fn,"w");
25   if( fp ) {
26     fprintf(fp,"P5\n%d %d\n255\n",w,h);
27     fwrite(tp,w,h,fp);
28     fclose(fp);
29   }
30 }
31
32 void pbm_diff(uint8_t *ap, uint8_t *bp, int w, int h, const char *fmt, ...)
33 {
34   va_list va;    va_start(va, fmt);
35   char fn[256];  vsnprintf(fn, sizeof(fn), fmt, va);
36   va_end(va);
37   FILE *fp = !strcmp(fn,"-") ? stdout : fopen(fn,"w");
38   if( fp ) {
39     int sz = w*h;
40     uint8_t dat[sz], *dp = dat;
41     for( int i=sz; --i>=0; ++dp,++ap,++bp ) *dp = clip(*ap-*bp+128, 0 ,255);
42     fprintf(fp,"P5\n%d %d\n255\n",w,h);
43     fwrite(dat,w,h,fp);
44     fclose(fp);
45   }
46 }
47
48 int verify_clips(uint8_t *ap,int asz, double afr,
49                  uint8_t *bp, int bsz, double bfr,
50                  double &ofs, double &error)
51 {
52   double atm = asz/afr, btm = bsz/bfr, lmt = fmin(atm, btm) / 2;
53   if( lmt > 8 ) lmt = 8;
54   if( fabs(atm - btm) > lmt ) return 0;
55   double arate, brate, fr; int margin;
56   if( afr < bfr ) {
57     arate = 1;  brate = bfr / afr;
58     fr = afr;   margin = afr + 0.5;
59   }
60   else {
61     arate = afr / bfr;  brate = 1;
62     fr = bfr;   margin = bfr + 0.5;
63   }
64   double *awt = (double *)ap, *bwt = (double *)bp;
65   double best = 1e100;  int ii = -1;
66   for( int i=-margin; i<=margin; ++i ) {
67     int aofs = i < 0 ? 0 : i*arate + 0.5;
68     int bofs = i < 0 ? -i*brate + 0.5 : 0;
69     double err = 0;  int n = 0;
70     for( ;; ++n ) {
71       int ai = aofs + n*arate + 0.5;
72       if( ai >= asz ) break;
73       int bi = bofs + n*brate + 0.5;
74       if( bi >= bsz ) break;
75       err += fabs(awt[ai] - bwt[bi]);
76     }
77     err /= n;
78     if( err < best ) { best = err;  ii = i; }
79   }
80   ofs = ii / fr;  error = best;
81   if( best >= MEDIA_WEIGHT_ERRLMT ) return 0;
82   return 1;
83 }
84
85 int *load_frames(theDb &db, int clip_id, int no, int len)
86 {
87   int *fp = new int[len], first = no;
88   for( int i=len; --i>=0; ) fp[i] = -1;
89   int ret = TimelineLoc::ikey_Sequences(db.timeline,clip_id,no).Locate();
90   if( !ret ) {
91     for( int i=0; i<len; ++i,++no ) {
92       if( (int)db.timeline.Clip_id() != clip_id ) break;
93       int next = db.timeline.Sequence_no();
94       if( next != no ) {
95 //        printf("seqno skipped in clip %d from %d to %d\n",clip_id,no,next);
96         no = next;
97       }
98       int k = no-first;
99       if( k >= 0 && k < len ) fp[k] = db.timeline.Frame_id();
100       if( TimelineLoc::rkey_Sequences(db.timeline).Next() ) break;
101     }
102   }
103   else
104     printf("cant access timeline of clip %d\n",clip_id);
105   return fp;
106 }
107
108 int64_t media_weight(uint8_t *dat, int sz)
109 {
110   int64_t wt = 0;
111   for( int i=sz; --i>=0; ++dat ) wt += *dat;
112   return wt;
113 }
114
115 int64_t media_cmpr(uint8_t *ap, uint8_t *bp, int sz)
116 {
117   int64_t v = 0;
118   for( int i=sz; --i>=0; ++ap,++bp ) v += fabs(*ap-*bp);
119   return v;
120 }
121
122 int fix_err(theDb &db, double &err, double ofs,
123             int aid, int *afp, int asz, double afr,
124             int bid, int *bfp, int bsz, double bfr)
125 {
126   double arate, brate;
127   if( afr < bfr ) {
128     arate = 1;  brate = bfr / afr;
129   }
130   else {
131     arate = afr / bfr;  brate = 1;
132   }
133   int aofs = ofs < 0 ? 0 : ofs*afr + 0.5;
134   int bofs = ofs < 0 ? -ofs*bfr + 0.5 : 0;
135   int n = 0, fno = 0;  int votes = 0;
136   for( ;; ++fno ) {
137     int ai = aofs + fno*arate + 0.5;
138     if( ai >= asz ) break;
139     int bi = bofs + fno*brate + 0.5;
140     if( bi >= bsz ) break;
141     Video_frameLoc aframe(db.video_frame);
142     Video_frameLoc bframe(db.video_frame);
143     if( afp[ai] < 0 || bfp[bi] < 0 ) {
144 //      printf("missing prefix frame %d %d/%d in clip %d/%d\n",
145 //        n, afp[ai], bfp[bi], aid, bid);
146       continue;
147     }
148     if( aframe.FindId(afp[ai]) ) {
149       printf("cant access prefix frame %d (id %d) in clip %d\n",
150         ai, afp[ai], aid);  continue;
151     }
152     if( bframe.FindId(bfp[bi]) ) {
153       printf("cant access prefix frame %d (id %d) in clip %d\n",
154         bi, bfp[bi], bid);  continue;
155     }
156     int w=80, h=45, sfrm_sz = w*h;
157     int64_t v = media_cmpr(aframe._Frame_data(), bframe._Frame_data(), sfrm_sz);
158     if( v/3600 < MEDIA_SEARCH_ERRLMT ) ++votes;
159 //printf("%d %ld(%d,%d)\n", n, v, afp[ai], bfp[bi]);
160     if( diff_path ) {
161       uint8_t *adat = aframe._Frame_data(), *bdat = bframe._Frame_data();
162       write_pbm(adat,w,h,"%s/a%05d.pbm",diff_path,afp[ai]);
163       write_pbm(bdat,w,h,"%s/b%05d.pbm",diff_path,bfp[bi]);
164       pbm_diff(adat,bdat,80,45,"%s/da%05d-b%05d.pbm",diff_path,afp[ai],bfp[bi]);
165     }
166     err += v;  ++n;
167   }
168   printf("v%d",votes);
169   return n;
170 }
171
172 int verify_frames(theDb &db, int aid, int bid, double ofs)
173 {
174   int *apfx=0, *asfx=0, *bpfx=0, *bsfx=0;
175   int apsz=0, assz=0, bpsz=0, bssz=0, alen=0, blen=0, afrm=0, bfrm=0;
176   double afr = 1, bfr = 1;
177   if( !db.clip_set.FindId(aid) ) {
178     apsz = db.clip_set.Prefix_size();
179     assz = db.clip_set.Suffix_size();
180     alen = db.clip_set.Frames();
181     apfx = load_frames(db, aid, 0, apsz);
182     asfx = load_frames(db, aid, alen-assz, assz);
183     afr = db.clip_set.Framerate();
184     afrm = db.clip_set.Frames();
185   }
186   else {
187     printf("cant open -a- clip %d\n",aid);
188     return 0;
189   }
190   if( !db.clip_set.FindId(bid) ) {
191     bpsz = db.clip_set.Prefix_size();
192     bssz = db.clip_set.Suffix_size();
193     blen = db.clip_set.Frames();
194     bpfx = load_frames(db, bid, 0, bpsz);
195     bsfx = load_frames(db, bid, blen-bssz, bssz);
196     bfr = db.clip_set.Framerate();
197     bfrm = db.clip_set.Frames();
198   }
199   else {
200     printf("cant open -b- clip %d\n",bid);
201     return 0;
202   }
203 //printf("afrm %d, bfrm %d ",afrm, bfrm);
204   double perr = 0;
205   int pn = fix_err(db, perr, ofs, aid, apfx, apsz, afr, bid, bpfx, bpsz, bfr);
206   double serr = 0;
207   printf("/");
208   ofs -= afrm/afr-bfrm/bfr;
209   int sn = fix_err(db, serr, ofs, aid, asfx, assz, afr, bid, bsfx, bssz, bfr);
210   printf(" err %0.3f/%0.3f #%d+%d=%d\n", perr/(pn*80*45),serr/(sn*80*45), pn,sn,pn+sn);
211
212   delete [] bsfx;
213   delete [] bpfx;
214   delete [] asfx;
215   delete [] apfx;
216   return 0;
217 }
218
219 double avg_wt(uint8_t *wp, int sz)
220 {
221   double *wt = (double *)wp;
222   double v = 0;
223   if( sz > 0 ) {
224     for( int i=sz; --i>=0; ++wt ) v += *wt;
225      v /= sz;
226   }
227   return v;
228 }
229
230 int main(int ac, char **av)
231 {
232   int ret;  setbuf(stdout,0);
233   theDb db;
234   db.open(av[1]);
235   //db.access(av[1], 34543, 0);
236   if( !db.opened() || db.error() ) exit(1);
237   if( ac > 2 ) curr_id = atoi(av[2]);
238   if( ac > 3 ) next_id = atoi(av[3]);
239   if( ac > 4 ) diff_path = av[4];
240   if( curr_id >= 0 && next_id >= 0 ) {
241     Clip_setLoc curr(db.clip_set);
242     if( (ret=curr.FindId(curr_id)) ) {
243       printf("cant access clip %d\n", curr_id);
244       exit(1);
245     }
246     Clip_setLoc next(db.clip_set);
247     if( (ret=next.FindId(next_id)) ) {
248       printf("cant access clip %d\n", next_id);
249       exit(1);
250     }
251     double awt = curr.Average_weight();
252     double bwt = next.Average_weight();
253     double error=-1, ofs=-1;
254     int bias = awt - bwt;
255     verify_clips(curr._Weights(),curr.Frames(),curr.Framerate(),
256       next._Weights(), next.Frames(), next.Framerate(), ofs, error);
257     printf("dupl a=%4d(%0.2f)-%0.2f b=%4d(%0.2f)-%0.2f %s%d err=%0.3f ofs=%0.5f ",
258       curr.id(), curr.Framerate(),curr.Frames()/curr.Framerate(),
259       next.id(), next.Framerate(), next.Frames()/next.Framerate(),
260       bias >= 0 ? "+" : "", bias, error, ofs);
261     verify_frames(db, curr.id(), next.id(), ofs);
262     exit(0);
263   }
264   Clip_setLoc curr(db.clip_set);
265   Db::pgRef curr_loc;
266   if( !(ret=curr.FirstId(curr_loc)) ) {
267     do {
268       Clip_setLoc next(curr);
269       Db::pgRef next_loc = curr_loc;
270       double awt = curr.Average_weight();
271       while( !next.NextId(next_loc) ) {
272         double error=-1, ofs =-1;
273         double bwt = next.Average_weight();
274         int bias = awt - bwt;
275         if( fabs(bias) > 10 ) continue;
276         if( verify_clips(curr._Weights(),curr.Frames(),curr.Framerate(),
277               next._Weights(), next.Frames(), next.Framerate(), ofs, error) ) {
278           printf("dupl a=%4d(%0.2f)-%0.2f b=%4d(%0.2f)-%0.2f %s%d err=%0.3f ofs=%0.3f ",
279             curr.id(), curr.Framerate(),curr.Frames()/curr.Framerate(),
280             next.id(), next.Framerate(), next.Frames()/next.Framerate(),
281             bias >= 0 ? "+" : "", bias, error, ofs);
282           verify_frames(db, curr.id(), next.id(), ofs);
283           break;
284         }
285       }
286     } while( !(ret=curr.NextId(curr_loc)) );
287   }
288   db.close();
289   return 0;
290 }
291