Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / commercials.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2016-2020 William Morrow
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published
7  * by the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20
21 #include "arraylist.h"
22 #include "asset.h"
23 #include "clip.h"
24 #include "commercials.h"
25 #include "cache.h"
26 #include "edit.h"
27 #include "edits.h"
28 #include "edl.h"
29 #include "edlsession.h"
30 #include "file.h"
31 #include "filexml.h"
32 #include "indexable.h"
33 #include "indexfile.h"
34 #include "linklist.h"
35 #include "mainmenu.h"
36 #include "mediadb.h"
37 #include "mwindow.h"
38 #include "mwindowgui.h"
39 #include "pluginset.h"
40 #include "preferences.h"
41 #include "record.h"
42 #include "track.h"
43 #include "tracks.h"
44 #include "vframe.h"
45
46 #include <stdio.h>
47 #include <stdarg.h>
48 #include <stdlib.h>
49 #include <stdint.h>
50 #include <unistd.h>
51 #include <sys/types.h>
52 #include <sys/wait.h>
53
54
55
56 Commercials::
57 Commercials(MWindow *mwindow)
58  : Garbage("Commercials")
59 {
60         this->mwindow = mwindow;
61         this->scan_status = 0;
62         mdb = new MediaDb();
63         scan_file = 0;
64         cancelled = 0;
65         muted = 0;
66         openDb();
67         detachDb();
68 }
69
70 Commercials::
71 ~Commercials()
72 {
73         closeDb();
74         delete mdb;
75         tracks.remove_all_objects();
76 }
77
78 int Commercials::
79 newDb()
80 {
81         return mdb->newDb();
82 }
83
84 void Commercials::
85 closeDb()
86 {
87         tracks.remove_all_objects();
88         mdb->closeDb();
89 }
90
91 int Commercials::
92 openDb()
93 {
94         if( !mwindow->has_commercials() )
95                 return -1;
96         if( !mdb->is_open() && mdb->openDb() ) {
97                 printf("Commercials::openDb failed\n");
98                 return -1;
99         }
100         return 0;
101 }
102
103 int Commercials::
104 resetDb()
105 {
106         mdb->closeDb();
107         if( !mwindow->has_commercials() )
108                 return -1;
109         return mdb->resetDb();
110 }
111
112 void Commercials::
113 commitDb()
114 {
115         mdb->commitDb();
116 }
117
118 void Commercials::
119 undoDb()
120 {
121         mdb->undoDb();
122 }
123
124 int Commercials::
125 attachDb(int rw)
126 {
127         return mdb->attachDb(rw);
128 }
129
130 int Commercials::
131 detachDb()
132 {
133         return mdb->detachDb();
134 }
135
136 int Commercials::
137 put_weight(VFrame *frame, int no)
138 {
139         int w = frame->get_w(), h = frame->get_h(), rsz = w;
140         uint8_t *tp = frame->get_y();
141         int64_t wt = 0;
142         for( int y=h; --y>=0; tp+=rsz ) {
143                 uint8_t *bp = tp;
144                 for( int x=w; --x>=0; ++bp ) wt += *bp;
145         }
146         clip_weights[no] = (double)wt / (w*h);
147         return 0;
148 }
149
150 int Commercials::
151 put_frame(VFrame *frame, int no, int group, double offset)
152 {
153         int iw = frame->get_w(), ih = frame->get_h();
154         int sw = SWIDTH, sh = SHEIGHT;
155         int slen = sw*sh;  uint8_t skey[slen];
156         Scale(frame->get_y(),0,iw,ih,0,0,iw,ih).scale(skey,sw,sh,0,0,sw,sh);
157         int ret = mdb->new_frame(clip_id, skey, no, group, offset);
158         if( ret < 0 ) ret = 0;  // ignore forbidden frames
159         return ret;
160 }
161
162 int Commercials::
163 put_clip(File *file, int track, double position, double length)
164 {
165         if( file->asset->format != FILE_MPEG ) return -1;
166         double framerate;  int pid, width, height;  char title[BCTEXTLEN];
167         if( file->get_video_info(track, pid, framerate,
168                                         width, height, title) ) return -1;
169         if( file->set_layer(track) ) return -1;
170         int64_t pos = position * framerate;
171         if( file->set_video_position(pos, 0) ) return 1;
172         time_t ct;  time(&ct);
173         int64_t creation_time = (int64_t)ct, system_time;
174         if( file->get_system_time(system_time) ) system_time = 0;
175         int frames = length * framerate;
176         int prefix_size = 2*framerate, length2 = frames/2;
177         if( prefix_size > length2 ) prefix_size = length2;
178         int suffix_size = prefix_size;
179
180         if( mdb->new_clip_set(title, file->asset->path, position,
181                 framerate, frames, prefix_size, suffix_size,
182                 creation_time, system_time) ) return 1;
183
184         clip_id = mdb->clip_id();
185         cancelled = 0;
186         scan_status = new ScanStatus(this, xS(30), yS(30), 1, 1,
187                 cancelled, _("Cutting Ads"));
188         scan_status->update_length(0, frames);
189         scan_status->update_position(0, 0);
190         update_cut_info(track+1, position);
191
192         clip_weights = mdb->clip_weights();
193         frame_period = 1. / framerate;
194         VFrame frame(width, height, BC_YUV420P);
195
196         int i = 0, n = 0, result = 0;
197         // first 2 secs of frame data and weights
198         while( i < prefix_size && !result ) {
199                 if( (result=file->read_frame(&frame)) != 0 ) break;
200                 if( (result=put_frame(&frame, n++, 1, i/framerate)) != 0 ) break;
201                 if( (result=put_weight(&frame, i)) != 0 ) break;
202                 result = scan_status->update_position(0, ++i);
203         }
204         int suffix_start = frames - suffix_size;
205         while( i < suffix_start && !result ) {
206                 if( (result=file->read_frame(&frame)) != 0 ) break;
207                 if( (result=put_weight(&frame, i)) != 0 ) break;
208                 result = scan_status->update_position(0, ++i);
209                 ++n;
210         }
211         // last 2 secs of frame data and weights
212         while( i < frames && !result ) {
213                 if( (result=file->read_frame(&frame)) != 0 ) break;
214                 if( (result=put_frame(&frame, n++, 2, i/framerate)) != 0 ) break;
215                 if( (result=put_weight(&frame, i)) != 0 ) break;
216                 result = scan_status->update_position(0, ++i);
217         }
218
219         double wt = 0;
220         for( i=0; i<frames; ++i ) wt += clip_weights[i];
221         mdb->clip_average_weight(wt/frames);
222
223         delete scan_status;
224         scan_status = 0;
225         return result;
226 }
227
228
229 Clips *Commercials::
230 find_clips(int pid)
231 {
232         Clips *clips = 0;
233         int trk = tracks.size();
234         while( --trk >= 0 && (clips=tracks.get(trk))->pid != pid );
235         return trk >= 0 ? clips : 0;
236 }
237
238 int Commercials::
239 get_frame(File *file, int pid, double position,
240         uint8_t *tp, int mw, int mh, int ww, int hh)
241 {
242         if( file->asset->format != FILE_MPEG ) return -1;
243         int sw = SWIDTH, sh = SHEIGHT;
244         int slen= sw*sh;   uint8_t skey[slen];
245         Scale(tp,0,mw,mh,0,0,ww/8,hh/8).scale(skey,sw,sh,0,0,sw,sh);
246         Clips *clips = find_clips(pid);
247         if( !clips ) tracks.append(clips = new Clips(pid));
248         int fid, result = mdb->get_frame_key(skey, fid, clips, position);
249         return result;
250 }
251
252 double Commercials::
253 frame_weight(uint8_t *tdat, int rowsz, int width, int height)
254 {
255         int64_t weight = 0;
256         for( int y=height; --y>=0; tdat+=rowsz ) {
257                 uint8_t *bp = tdat;
258                 for( int x=width; --x>=0; ++bp ) weight += *bp;
259         }
260         return (double)weight / (width*height);
261 }
262
263 int Commercials::
264 skim_frame(Snips *snips, uint8_t *dat, double position)
265 {
266         int fid, result = mdb->get_frame_key(dat, fid, snips, position, 1);
267         double weight = frame_weight(dat, SWIDTH, SWIDTH, SHEIGHT);
268         for( Clip *next=0,*clip=snips->first; clip; clip=next ) {
269                 next = clip->next;
270                 result = verify_snip((Snip*)clip, weight, position);
271                 if( !result ) {
272                         if( clip->votes > 2 )
273                                 mute_audio(clip);
274                 }
275                 else if( result < 0 || --clip->votes < 0 ) {
276                         --snips->count;
277                         delete clip;
278                         if( !snips->first )
279                                 unmute_audio();
280                 }
281         }
282         return 0;
283 }
284
285 double Commercials::
286 abs_err(Snip *snip, double *ap, int j, int len, double iframerate)
287 {
288         double vv = 0;
289         int i, k, sz = snip->weights.size(), n = 0;
290         for( i=0; i<sz; ++i ) {
291                 k = j + (snip->positions[i] - snip->start) * iframerate + 0.5;
292                 if( k < 0 ) continue;
293                 if( k >= len ) break;
294                 double a = ap[k], b =  snip->weights[i];
295                 double dv = fabs(a - b);
296                 vv += dv;
297                 ++n;
298         }
299         return !n ? MEDIA_WEIGHT_ERRLMT : vv / n;
300 }
301
302 int Commercials::
303 verify_snip(Snip *snip, double weight, double position)
304 {
305         int cid = snip->clip_id;
306         if( mdb->clip_id(cid) ) return -1;
307         double iframerate = mdb->clip_framerate();
308         int iframes = mdb->clip_frames();
309         double pos = position - snip->start;
310         int iframe_no = pos * iframerate + 0.5;
311         if( iframe_no >= iframes ) return -1;
312         snip->weights.append(weight);
313         snip->positions.append(position);
314         double *iweights = mdb->clip_weights();
315         double errlmt = MEDIA_WEIGHT_ERRLMT;
316         double err = errlmt, kerr = err;
317         int k = 0;
318         int tmargin = TRANSITION_MARGIN * iframerate + 0.5;
319         for( int j=-2*tmargin; j<=tmargin; ++j ) {
320                 err = abs_err(snip, iweights, j, iframes, iframerate);
321                 if( err < kerr ) { kerr = err;  k = j; }
322         }
323         if( kerr >= errlmt ) return 1;
324         if( iframe_no + k >= iframes ) return -1;
325         return 0;
326 }
327
328
329 int Commercials::
330 mute_audio(Clip *clip)
331 {
332         Record *record = mwindow->gui->record;
333         if( !clip->muted ) {
334                 clip->muted = 1;
335                 mdb->access_clip(clip->clip_id);
336                 mdb->commitDb();
337                 char clip_title[BCSTRLEN];
338                 sprintf(clip_title,"%d",clip_id);
339                 record->display_video_text(10, 10, clip_title,
340                         BIGFONT, DKGREEN, LTYELLOW, -1, 300., 1.);
341         }
342         if( !muted ) {
343                 muted = 1;
344                 record->set_mute_gain(0);
345                 printf(_("***MUTE***\n"));
346         }
347         return 0;
348 }
349
350 int Commercials::
351 unmute_audio()
352 {
353         Record *record = mwindow->gui->record;
354         if( muted ) {
355                 muted = 0;
356                 record->set_mute_gain(1);
357                 printf(_("***UNMUTE***\n"));
358         }
359         record->undisplay_vframe();
360         return 0;
361 }
362
363 int Commercials::skim_weight(int track)
364 {
365         if( cancelled ) return 1;
366         int64_t framenum; uint8_t *tdat; int mw, mh;
367         if( scan_file->get_thumbnail(track, framenum, tdat, mw, mh) ) return 1;
368         double framerate;  int pid, width, height;
369         if( scan_file->get_video_info(track, pid, framerate,
370                                         width, height, 0) ) return 1;
371         if( !framerate ) return 1;
372         width /= 8;  height /= 8;
373 //write_pbm(tdat,width,height,"/tmp/dat/r%05ld.pbm",framenum);
374         if( height > mh ) height = mh;
375         if( !width || !height ) return 1;
376         weights.append(frame_weight(tdat, mw, width, height));
377         int64_t frame_no = framenum - frame_start;
378         offsets.append(frame_no / framerate);
379         if( scan_status->update_position(1,frame_no) ) return 1;
380         // return < 0, continue.  = 0, complete.  > 0 error
381         return frame_no < frame_total ? -1 : 0;
382 }
383
384 int Commercials::skim_weight(void *vp, int track)
385 {
386         return ((Commercials *)vp)->skim_weight(track);
387 }
388
389
390 int Commercials::
391 skim_weights(int track, double position, double iframerate, int iframes)
392 {
393         double framerate;  int pid, width, height;  char title[BCTEXTLEN];
394         if( scan_file->get_video_info(track, pid, framerate,
395                                         width, height, title) ) return 1;
396         if( scan_file->set_layer(track) ) return 1;
397         frame_start = framerate * position;
398         if( scan_file->set_video_position(frame_start, 0) ) return 1;
399         // skimming limits
400         double length = iframes / iframerate;
401         frame_total = length * framerate;
402         scan_status->update_length(1,frame_total);
403         scan_status->update_position(1, 0);
404         weights.remove_all();
405         offsets.remove_all();
406         return scan_file->skim_video(track, this, skim_weight);
407 }
408
409 double Commercials::
410 abs_err(double *ap, int len, double iframerate)
411 {
412         double vv = 0;
413         int i, k, sz = weights.size(), n = 0;
414         for( i=0; i<sz; ++i ) {
415                 k = offsets[i] * iframerate + 0.5;
416                 if( k < 0 ) continue;
417                 if( k >= len ) break;
418                 double a = ap[k], b = weights[i];
419                 double dv = fabs(a - b);
420                 vv += dv;
421                 ++n;
422         }
423         return !n ? MEDIA_WEIGHT_ERRLMT : vv / n;
424 }
425
426
427 int Commercials::
428 verify_clip(int clip_id, int track, double position, double &pos)
429 {
430         if( mdb->clip_id(clip_id) ) return 1;
431         double iframerate = mdb->clip_framerate();
432         if( iframerate <= 0. ) return 1;
433         int iframes = mdb->clip_frames();
434         int tmargin = TRANSITION_MARGIN * iframerate;
435         int mframes = iframes - 2*tmargin;
436         if( mframes <= 0 ) return 1;
437         double tposition = position + TRANSITION_MARGIN;
438         if( skim_weights(track, tposition, iframerate, mframes) ) return 1;
439         double *iweights = mdb->clip_weights();
440         double err = abs_err(iweights, mframes, iframerate);
441         int k = 0;  double kerr = err;
442         int n = 2*tmargin;
443         for( int j=1; j<n; ++j ) {
444                 err = abs_err(iweights+j, mframes, iframerate);
445                 if( err < kerr ) { kerr = err;  k = j; }
446         }
447 //printf("** kerr %f, k=%d\n", kerr, k);
448         if( kerr >= MEDIA_WEIGHT_ERRLMT ) return -1;
449         pos = position + (k - tmargin) / iframerate;
450         return 0;
451 }
452
453 int Commercials::
454 write_ads(const char *filename)
455 {
456         char index_filename[BCTEXTLEN], source_filename[BCTEXTLEN];
457         IndexFile::get_index_filename(source_filename,
458                 mwindow->preferences->index_directory, index_filename,
459                 filename, ".ads");
460
461         FILE *fp = fopen(index_filename, "wb");
462         if( fp != 0 ) {
463                 FileXML xml;
464                 xml.tag.set_title("ADS");
465                 xml.append_tag();
466                 xml.append_newline();
467                 int trks = tracks.size();
468                 for( int trk=0; trk<trks; ++trk )
469                         tracks.get(trk)->save(xml);
470                 xml.tag.set_title("/ADS");
471                 xml.append_tag();
472                 xml.append_newline();
473                 xml.terminate_string();
474                 xml.write_to_file(fp);
475                 fclose(fp);
476         }
477         return 0;
478 }
479
480 int Commercials::
481 read_ads(const char *filename)
482 {
483         char index_filename[BCTEXTLEN], source_filename[BCTEXTLEN];
484         IndexFile::get_index_filename(source_filename,
485                 mwindow->preferences->index_directory, index_filename,
486                 filename, ".ads");
487         tracks.remove_all_objects();
488         FileXML xml;
489         if( xml.read_from_file(index_filename, 1) ) return 1;
490
491         do {
492                 if( xml.read_tag() ) return 1;
493         } while( !xml.tag.title_is("ADS") );
494
495         for(;;) {
496                 if( xml.read_tag() ) return 1;
497                 if( xml.tag.title_is("/ADS") ) break;
498                 if( xml.tag.title_is("CLIPS") ) {
499                         int pid = xml.tag.get_property("PID", (int)0);
500                         Clips *clips = new Clips(pid);
501                         tracks.append(clips);
502                         if( clips->load(xml) ) return 1;
503                         if( !xml.tag.title_is("/CLIPS") ) break;
504                 }
505         }
506
507         return 0;
508 }
509
510 void Commercials::
511 dump_ads()
512 {
513         for( int i=0; i<tracks.size(); ++i )
514         {
515                 printf("clip %i:\n", i);
516                 for( Clip *clip=tracks.get(i)->first; clip; clip=clip->next )
517                         printf("  clip_id %d: votes %d, index %d, groups %d, start %f, end %f\n",
518                                 clip->clip_id, clip->votes, clip->index, clip->groups,
519                                 clip->start, clip->end);
520         }
521 }
522
523 int Commercials::
524 verify_edit(Track *track, Edit *edit, double start, double end)
525 {
526         int64_t clip_start = track->to_units(start,0);
527         int64_t clip_end = track->to_units(end,0);
528         int64_t edit_start = edit->startsource;
529         if( edit_start >= clip_end ) return 1;
530         int64_t edit_end = edit_start + edit->length;
531         if( edit_end <= clip_start ) return 1;
532         return 0;
533 }
534
535 Edit *Commercials::
536 cut_edit(Track *track, Edit *edit, int64_t clip_start, int64_t clip_end)
537 {
538         int64_t edit_start = edit->startsource;
539         int64_t edit_end = edit_start + edit->length;
540         int64_t cut_start = clip_start < edit_start ? edit_start : clip_start;
541         int64_t cut_end = clip_end >= edit_end ? edit_end : clip_end;
542         int64_t edit_offset = edit->startproject - edit->startsource;
543         // shift from source timeline to project timeline
544         cut_start += edit_offset;  cut_end += edit_offset;
545         // cut autos and plugins
546         track->automation->clear(cut_start, cut_end, 0, 1);
547         if( mwindow->edl->session->plugins_follow_edits ) {
548                 int sz = track->plugin_set.size();
549                 for( int i=0; i<sz; ++i ) {
550                         PluginSet *plugin_set = track->plugin_set.values[i];
551                         plugin_set->clear(cut_start, cut_end, 0);
552                 }
553         }
554         // cut edit
555         Edit *next_edit = edit->edits->split_edit(cut_start);
556         int64_t cut_length = cut_end - cut_start;
557         next_edit->length -= cut_length;
558         next_edit->startsource += cut_length;
559         for( Edit *ep=next_edit->next; ep;  ep=ep->next )
560                 ep->startproject -= cut_length;
561         return next_edit;
562 }
563
564 int Commercials::
565 scan_audio(int vstream, double start, double end)
566 {
567         int64_t channel_mask = 0;
568         int astrm = scan_file->get_audio_for_video(vstream, -1, channel_mask);
569         if( astrm < 0 || !channel_mask ) return -1;
570         Tracks *tracks = mwindow->edl->tracks;
571         for(Track *atrk=tracks->first; !cancelled && atrk; atrk=atrk->next) {
572                 if( atrk->data_type != TRACK_AUDIO ) continue;
573                 if( !atrk->is_armed() ) continue;
574                 Edits *edits = atrk->edits;  Edit *next = 0;
575                 for( Edit *edit=edits->first; !cancelled && edit; edit=next ) {
576                         next = edit->next;
577                         if( ((channel_mask>>edit->channel) & 1) == 0 ) continue;
578                         Indexable *indexable = edit->get_source();
579                         if( !indexable || !indexable->is_asset ) continue;
580                         Asset *asset = (Asset *)indexable;
581                         if( !scan_file->asset->equivalent(*asset,0,0,mwindow->edl) ) continue;
582                         if( verify_edit(atrk, edit, start, end) ) continue;
583                         next = cut_edit(atrk, edit,
584                                 atrk->to_units(start,0),
585                                 atrk->to_units(end,0));
586                 }
587                 edits->optimize();
588         }
589         return 0;
590 }
591
592 int Commercials::
593 scan_media()
594 {
595         cancelled = 0;
596         scan_status = new ScanStatus(this, xS(30), yS(30), 2, 2,
597                 cancelled, _("Cutting Ads"));
598         if( !openDb() ) {
599                 scan_video();
600                 commitDb();
601                 closeDb();
602         }
603         delete scan_status;
604         scan_status = 0;
605         return 0;
606 }
607
608 int Commercials::
609 scan_video()
610 {
611         Tracks *tracks = mwindow->edl->tracks;
612         for( Track *vtrk=tracks->first; !cancelled && vtrk; vtrk=vtrk->next) {
613                 if( vtrk->data_type != TRACK_VIDEO ) continue;
614                 if( !vtrk->is_armed() ) continue;
615                 Edits *edits = vtrk->edits;  Edit *next = 0;
616                 for( Edit *edit=edits->first; !cancelled && edit; edit=next ) {
617                         next = edit->next;
618                         Indexable *indexable = edit->get_source();
619                         if( !indexable || !indexable->is_asset ) continue;
620                         Asset *asset = (Asset *)indexable;
621                         if( update_caption(edit->channel+1,
622                                 edits->number_of(edit), asset->path) ) break;
623                         if( read_ads(asset->path) ) continue;
624                         if( scan_asset(asset, vtrk, edit) ) continue;
625                         next = edits->first;
626                 }
627                 edits->optimize();
628         }
629         return 0;
630 }
631
632 int Commercials::
633 scan_asset(Asset *asset, Track *vtrk, Edit *edit)
634 {
635         int result = 1;
636         scan_file = mwindow->video_cache->check_out(asset, mwindow->edl);
637         if( scan_file && scan_file->asset->format == FILE_MPEG ) {
638                 result = scan_clips(vtrk, edit);
639                 mwindow->video_cache->check_in(asset);
640                 scan_file = 0;
641         }
642         return result;
643 }
644
645 int Commercials::
646 scan_clips(Track *vtrk, Edit *edit)
647 {
648         int pid = scan_file->get_video_pid(edit->channel);
649         if( pid < 0 ) return 1;
650         Clips *clips = find_clips(pid);
651         if( !clips ) return 1;
652         double last_end = -CLIP_MARGIN;
653         scan_status->update_length(0, clips->total());
654         scan_status->update_position(0, 0);
655         for( Clip *clip=clips->first; !cancelled && clip; clip=clip->next ) {
656                 if( verify_edit(vtrk, edit, clip->start, clip->end) ) continue;
657                 if( update_status(clips->number_of(clip),
658                         clip->start, clip->end) ) break;
659                 scan_status->update_position(0, clips->number_of(clip));
660                 double start = clip->start;
661                 if( verify_clip(clip->clip_id, edit->channel,
662                                 clip->start, start) ) continue;
663                 double length = clip->end - clip->start;
664                 if( start < 0 ) {
665                         if( (length+=start) <= 0 ) continue;
666                         start = 0;
667                 }
668                 double end = start + length;
669 printf(_("cut clip %d in edit @%f %f-%f, clip @%f-%f\n"), clip->clip_id,
670   vtrk->from_units(edit->startsource), vtrk->from_units(edit->startproject),
671   vtrk->from_units(edit->startproject+edit->length),start,end);
672                 if( last_end+CLIP_MARGIN > start ) start = last_end;
673                 edit = cut_edit(vtrk, edit,
674                                 vtrk->to_units(start,0),
675                                 vtrk->to_units(end,0));
676                 scan_audio(edit->channel, start, end);
677                 mdb->access_clip(clip->clip_id);
678                 last_end = end;
679         }
680         return 1;
681 }
682
683
684 int Commercials::
685 update_cut_info(int trk, double position)
686 {
687         if( cancelled ) return 1;
688         char string[BCTEXTLEN];  EDLSession *session = mwindow->edl->session;
689         Units::totext(string, position, session->time_format, session->sample_rate,
690                 session->frame_rate, session->frames_per_foot);
691         char text[BCTEXTLEN];  sprintf(text,_("ad: trk %d@%s  "),trk,string);
692         scan_status->update_text(0, text);
693         return 0;
694 }
695
696 int Commercials::
697 update_caption(int trk, int edt, const char *path)
698 {
699         if( cancelled ) return 1;
700         char text[BCTEXTLEN];
701         snprintf(text,sizeof(text),_("trk%d edt%d asset %s"), trk, edt, path);
702         scan_status->update_text(0, text);
703         return 0;
704 }
705
706 int Commercials::
707 update_status(int clip, double start, double end)
708 {
709         if( cancelled ) return 1;
710         char text[BCTEXTLEN];
711         snprintf(text,sizeof(text),_("scan: clip%d %f-%f"), clip, start, end);
712         scan_status->update_text(1, text);
713         return 0;
714 }
715
716
717 ScanStatusGUI::
718 ScanStatusGUI(ScanStatus *sswindow, int x, int y, int nlines, int nbars)
719  : BC_Window(_("Scanning"), x, y, xS(340),
720         yS(40) + BC_CancelButton::calculate_h() +
721                 (BC_Title::calculate_h((BC_WindowBase*) sswindow->
722                         commercials->mwindow->gui, _("My")) + yS(5)) * nlines +
723                 (BC_ProgressBar::calculate_h() + yS(5)) * nbars, 0, 0, 0)
724 {
725         this->sswindow = sswindow;
726         this->nlines = nlines;
727         this->nbars = nbars;
728         this->texts = new BC_Title *[nlines];
729         this->bars = new ScanStatusBar *[nbars];
730 }
731
732 ScanStatusGUI::
733 ~ScanStatusGUI()
734 {
735         delete [] texts;
736         delete [] bars;
737 }
738
739 void ScanStatusGUI::
740 create_objects(const char *text)
741 {
742         int xs10 = xS(10), xs20 = xS(20);
743         int ys5 = yS(5), ys10 = yS(10);
744         lock_window("ScanStatusGUI::create_objects");
745         int x = xs10, y = ys10;
746         int dy = BC_Title::calculate_h((BC_WindowBase*) sswindow->
747                 commercials->mwindow->gui, "My") + ys5;
748         for( int i=0; i<nlines; ++i, y+=dy, text="" )
749                 add_tool(texts[i] = new BC_Title(x, y, text));
750         y += ys10;
751         dy = BC_ProgressBar::calculate_h() + ys5;
752         for( int i=0; i<nbars; ++i, y+=dy )
753                 add_tool(bars[i] = new ScanStatusBar(x, y, get_w() - xs20, yS(100)));
754
755         add_tool(new BC_CancelButton(this));
756         unlock_window();
757 }
758
759 ScanStatus::
760 ScanStatus(Commercials *commercials, int x, int y,
761         int nlines, int nbars, int &status, const char *text)
762  : Thread(1, 0, 0),
763    status(status)
764 {
765         this->commercials = commercials;
766         gui = new ScanStatusGUI(this, x, y, nlines, nbars);
767         gui->create_objects(text);
768         start();
769         gui->init_wait();
770 };
771
772 ScanStatus::
773 ~ScanStatus()
774 {
775         stop();
776         delete gui;
777 }
778
779
780 int ScanStatus::
781 update_length(int i, int64_t length)
782 {
783         if( status ) return 1;
784         gui->bars[i]->update_length(length);
785         return 0;
786 }
787
788 int ScanStatus::
789 update_position(int i, int64_t position)
790 {
791         if( status ) return 1;
792         gui->bars[i]->update(position);
793         return 0;
794 }
795
796 int ScanStatus::
797 update_text(int i, const char *text)
798 {
799         if( status ) return 1;
800         gui->texts[i]->update(text);
801         return 0;
802 }
803
804 void ScanStatus::
805 stop()
806 {
807         status = 1;
808         if( running() ) {
809                 if( gui ) gui->set_done(1);
810                 cancel();
811         }
812         join();
813 }
814
815 void ScanStatus::
816 run()
817 {
818         gui->create_objects(_("Cutting Ads"));
819         int result = gui->run_window();
820         if( result ) status = 1;
821 }
822
823
824
825 void SdbPacketQueue::
826 put_packet(SdbPacket *p)
827 {
828         lock("SdbPacketQueue::put_packet");
829         append(p);
830         unlock();
831 }
832
833 SdbPacket *SdbPacketQueue::
834 get_packet()
835 {
836         lock("SdbPacketQueue::get_packet");
837         SdbPacket *p = first;
838         remove_pointer(p);
839         unlock();
840         return p;
841 }
842
843
844 SkimDbThread::
845 SkimDbThread()
846  : Thread(1, 0, 0)
847 {
848         input_lock = new Condition(0, "SkimDbThread::input_lock");
849         for( int i=32; --i>=0; ) skim_frames.append(new SdbSkimFrame(this));
850         snips = new Snips();
851         commercials = 0;
852         done = 1;
853 }
854
855 SkimDbThread::
856 ~SkimDbThread()
857 {
858         stop();
859         delete snips;
860         delete input_lock;
861 }
862
863
864 void SkimDbThread::
865 start(Commercials *commercials)
866 {
867         commercials->add_user();
868         this->commercials = commercials;
869         if( commercials->openDb() ) return;
870         if( commercials->detachDb() ) return;
871         done = 0;
872         Thread::start();
873 }
874
875 void SkimDbThread::
876 stop()
877 {
878         if( running() ) {
879                 done = 1;
880                 input_lock->unlock();
881                 cancel();
882         }
883         join();
884         if( commercials && !commercials->remove_user() )
885                 commercials = 0;
886 }
887
888 int SkimDbThread::
889 skim(int pid,int64_t framenum,double framerate,
890         uint8_t *idata,int mw,int mh,int iw,int ih)
891 {
892         SdbSkimFrame *sf = (SdbSkimFrame *)skim_frames.get_packet();
893         if( !sf ) { printf("SkimDbThread::skim no packet\n");  return 1; }
894         sf->load(pid,framenum,framerate, idata,mw,mh,iw,ih);
895         sf->start();
896         return 0;
897 }
898
899
900 void SdbPacket::start()
901 {
902         thread->put_packet(this);
903 }
904
905 void SkimDbThread::
906 put_packet(SdbPacket *p)
907 {
908         active_packets.put_packet(p);
909         input_lock->unlock();
910 }
911
912 void SkimDbThread::
913 run()
914 {
915         while( !done ) {
916                 input_lock->lock("SkimDbThread::run");
917                 if( done ) break;
918                 SdbPacket *p = active_packets.get_packet();
919                 if( !p ) continue;
920                 commercials->attachDb();
921                 p->run();
922                 commercials->detachDb();
923         }
924 }
925
926
927 void SdbSkimFrame::
928 load(int pid,int64_t framenum,double framerate,
929         uint8_t *idata,int mw,int mh,int iw,int ih)
930 {
931         int sw=SWIDTH, sh=SHEIGHT;
932         this->pid = pid;
933         this->framenum = framenum;
934         this->framerate = framerate;
935         Scale(idata,0,mw,mh,0,0,iw/8,ih/8).scale(dat,sw,sh,0,0,sw,sh);
936 }
937
938 void SdbSkimFrame::
939 run()
940 {
941         double position = framenum / framerate;
942         thread->commercials->skim_frame(thread->snips, dat, position);
943         thread->skim_frames.put_packet(this);
944 }
945
946
947 void run_that_puppy(const char *fn)
948 {
949         FILE *fp = fopen(fn,"r");  double position, length;
950         if( !fp ) { perror("fopen"); return; }
951         MWindow::commercials->resetDb();
952         MWindow *mwindow = MWindow::commercials->mwindow;
953         File *file = mwindow->video_cache->first->file;
954         int result = 0;
955         while( !result && fscanf(fp,"%lf %lf\n",&position, &length) == 2 ) {
956                 int result = MWindow::commercials->put_clip(file, 0, position, length);
957                 printf(_("cut %f/%f = %d\n"),position,length, result);
958         }
959         MWindow::commercials->commitDb();
960         MWindow::commercials->closeDb();
961         fclose(fp);
962 }
963