3 * Copyright (C) 2016-2020 William Morrow
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.
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.
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
21 #include "../libzmpeg3/libzmpeg3.h"
22 #include "arraylist.h"
37 #include <sys/resource.h>
39 // c++ `cat x86_64/c_flags` -c -o x86_64/cutads.o cutads.C
40 // c++ -pthread -o x86_64/cutads x86_64/cutads.o x86_64/mediadb.o x86_64/filexml.o
41 // ../libzmpeg3/x86_64/libzmpeg3.a ../db/x86_64/db.a -lX11
44 #define fail(s) do { printf("fail %s%s:%d\n",__func__,#s,__LINE__); return 1; } while(0)
46 /* missing from system headers, no /usr/include <linux/ioprio.h>
47 * IOPRIO_WHO_PROCESS, IOPRIO_CLASS_SHIFT, IOPRIO_CLASS_IDLE */
48 enum { IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE, };
49 #define IO_CLASS(n) (((int)(n)) << 13)
50 #define IO_WHO_PROCESS 1
51 #include <sys/syscall.h>
52 #include <asm/unistd.h>
54 // commercial edge detection:
55 // must have audio < min_audio
56 // and within +- check_margin seconds of low audio
57 // must have video < min_video or delta video > min_dvideo
59 static double min_audio = 0.5e-3 * 32767; // (-63db quite quiet)
60 static double min_video = 0.1 * 255; // (pretty dark)
61 static double min_dvideo = 0.1 * 255; // (gittery)
62 static double lo_video = 0.075 * 255; // (dark)
63 static double hi_video = 0.925 * 255; // (light)
64 static double check_margin = 1.0; // t-1.0..t+1.0 secs
65 static double min_clip_time = 3; // ignore clips shorter than this
66 static int video_cutoff = 15; // video outside color space
78 static int ioprio_set(int which, int who, int ioprio)
80 return syscall(SYS_ioprio_set, which, who, ioprio);
83 static inline int clip(int v, int mn, int mx)
85 return v<mn ? mn : v>mx ? mx : v;
95 bool open() { return zsrc != 0; }
96 operator zmpeg3_t *() { return zsrc; }
97 zmpeg3_t *operator ->() { return zsrc; }
98 const char *asset_path() { return path; }
100 Src(const char *fn) {
102 int ret; zsrc = new zmpeg3_t(path, ret,
103 ZIO_UNBUFFERED+ZIO_SINGLE_ACCESS);
104 if( ret ) { delete zsrc; zsrc = 0; }
106 ~Src() { delete zsrc; }
114 ArrayList<double> positions;
115 ArrayList<double> values;
117 int prev() { return cur > 0 ? --cur : -1; }
118 int next() { return cur < positions.size()-1 ? ++cur : -1; }
119 double position() { return positions.get(cur); }
120 double value() { return values.get(cur); }
121 double next_value() { return values.get(cur++); }
122 int length() { return values.size() - cur; }
123 int find(double pos);
124 int locate(double pos);
125 double err(double start, double len, double irate,
126 double *iweights, int iframes);
127 void add(double p, double v) { positions.append(p); values.append(v); }
128 int minimum_forward(double start, double end, double &time);
129 int minimum_backward(double start, double end, double &time);
130 int video_forward(double start, double end, double &time);
131 int audio_forward(double start, double end, double &time);
132 int video_backward(double start, double end, double &time);
133 int audio_backward(double start, double end, double &time);
134 double get_weight(double pos);
135 double average(double pos, int len);
136 int save(double pos, double *wp, int len);
137 void write(const char *fn);
138 Weights() : cur(0) {}
142 int Weights::find(double pos)
145 int r = positions.size();
146 while( (r - l) > 1 ) {
148 double v = positions.get(i);
149 if( pos == v ) return i;
150 if( pos > v ) l = i; else r = i;
155 int Weights::locate(double pos)
158 if( ret < 0 ) ret = 0;
163 err(double pos, double len, double irate,
164 double *iweights, int iframes)
166 // trim leading / trailing dark video
168 if( position() > pos ) return 256.;
169 double end_pos = pos + len;
170 while( position() < end_pos ) {
171 if( value() >= min_video ) break;
172 if( next() < 0 ) return 256.;
175 double vv = 0, lvv = 0, dd = 0, ldd = 0, ldv = 0;
176 while( position() < end_pos ) {
177 int k = (position()-pos) * irate + 1./1001+0.5;
179 if( k >= iframes ) break;
180 double v = value(), dv = v - iweights[k];
182 dd += fabs(dv - ldv); ldv = dv;
183 if( v > min_video ) { lvv = vv; ldd = dd; l = n; }
186 if( next() < 0 ) break;
189 if( ldd < lvv && lvv < MEDIA_MEAN_ERRLMT*l ) lvv = ldd;
190 return !l ? MEDIA_WEIGHT_ERRLMT : lvv / l;
194 get_weight(double pos)
197 return position() > pos ? -1 : value();
201 average(double pos, int len)
205 if( len < n ) n = len;
207 for( int i=len; --i>=0; ) v += next_value();
208 return n > 0 ? v / n : 256;
212 save(double pos, double *wp, int len)
215 if( position() > pos ) fail();
216 if( length() < len ) fail();
217 for( int i=len; --i>=0; ++wp ) *wp = next_value();
222 write(const char *fn)
224 FILE *fp = fopen(fn,"w");
227 if( length() > 0 ) do {
228 fprintf(fp,"%f %f\n",position(),value());
229 } while( next() >= 0 );
241 int zpid, w, h, ww, hh, is_scaled;
243 double rate, vorigin;
245 uint8_t sbfr[SFRM_SZ];
251 uint8_t *get_y() { return (uint8_t*)yp; }
252 uint8_t *get_u() { return (uint8_t*)up; }
253 uint8_t *get_v() { return (uint8_t*)vp; }
254 int tracks() { return trks; }
255 int track() { return trk; }
256 int width() { return w; }
257 int height() { return h; }
258 int coded_width() { return ww; }
259 int coded_height() { return hh; }
260 int pid() { return zpid; }
261 bool eof() { return src->end_of_video(trk); }
262 double time() { return src->get_video_time(trk); }
263 void set_title(char *cp) { strcpy(name,cp); }
264 char *title() { return name; }
265 double framerate() { return rate; }
266 int64_t frame_count(double position) { return position*rate + 1./1001; }
267 double frame_position(int64_t count) { return count / rate; }
268 void set_origin(double t) { vorigin = t; }
269 double origin() { return vorigin; }
270 int64_t length() { return len; }
271 int64_t frame_no() { return pos; }
272 void disable_weights() { delete weights; weights = 0; }
273 void enable_weights() { weights = new Weights(); }
274 void write_weights(const char *fn) { weights->write(fn); }
275 void margin_frames(Margin *mp=0) { margin = mp; }
276 int forward(double start, double end, double &time) {
277 return weights->video_forward(start, end, time);
279 int backward(double start, double end, double &time) {
280 return weights->video_backward(start, end, time);
282 double err(double pos, double len, double irate,
283 double *iweights, int iframes) {
284 return weights->err(pos, len, irate, iweights, iframes);
286 double frame_weight(double pos) {
287 return weights->get_weight(pos);
289 double average_weight(double pos, double len) {
290 return weights->average(pos, len*rate);
292 int save_weights(double pos, double *wp, int len) {
293 return weights->save(pos, wp, len);
295 void init(zmpeg3_t *zsrc, int ztrk);
300 double weight(uint8_t *bp);
302 Video() { weights = 0; }
303 ~Video() { delete weights; }
307 init(zmpeg3_t *zsrc, int ztrk)
309 src = zsrc; trk = ztrk;
310 trks = src->total_vstreams();
311 zpid = src->video_pid(trk);
312 rate = src->frame_rate(trk);
313 w = src->video_width(trk);
314 h = src->video_height(trk);
315 ww = src->coded_width(trk);
316 hh = src->coded_height(trk);
321 len = src->video_frames(trk);
325 int Video::load_frame()
327 ++pos; is_scaled = 0;
328 return src->read_yuvframe_ptr(&yp, &up, &vp,trk);
335 uint8_t sbfr[SFRM_SZ];
337 uint8_t *frame() { return sbfr; }
338 MarginFrame(uint8_t *bp) { memcpy(sbfr,bp,sizeof(sbfr)); }
341 class Margin : public ArrayList<MarginFrame*>
347 int locate(double t);
348 void copy(Margin *that);
349 double position() { return start_time + video.frame_position(cur); }
350 int check(Clips *clips, double start, double end, int group_mask);
351 int length() { return size() - cur; }
352 uint8_t *get_frame() { return get(cur++)->frame(); }
353 void add(uint8_t *sbfr) { append(new MarginFrame(sbfr)); }
355 Margin(int64_t frame, double time) : cur(0) {
356 start_frame = frame; start_time = time;
358 ~Margin() { remove_all_objects(); }
359 } *prefix = 0, *suffix = 0;
365 int pos = video.frame_count(t-start_time);
366 if( pos < 0 || pos >= size() ) return -1;
373 this->start_frame = that->start_frame + cur;
374 this->start_time = that->position();
375 while( that->length() > 0 ) add(that->get_frame());
379 check(Clips *clips, double start, double end, int group_mask)
381 if( locate(start) < 0 ) fail();
383 while( length() > 0 && (pos=position()) < end ) {
384 uint8_t *sbfr = get_frame(); int fid;
385 // write_pbm(sbfr,SWIDTH,SHEIGHT,"/tmp/dat/f%07.3f.pbm",pos-video.origin());
386 db->get_frame_key(sbfr, fid, clips, pos, group_mask);
392 int Video::read_frame()
394 if( load_frame() ) fail();
395 // write_pbm(get_y(),width(),height(),"/tmp/dat/f%05ld.pbm",pos);
396 if( margin ) margin->add(scaled());
397 if( weights ) weights->add(time(), weight(scaled()));
401 uint8_t *Video::scaled()
405 uint8_t *yp = get_y(); int sh = hh;
406 // while( sh>h && *yp<video_cutoff ) { yp += ww; --sh; }
407 //static int fno = 0;
408 //write_pbm(yp,ww,hh,"/tmp/data/f%04d.pbm",fno);
409 Scale(yp,0,ww,sh, 0,0,w,h).
410 scale(sbfr,SWIDTH,SHEIGHT, 0,0,SWIDTH,SHEIGHT);
411 //write_pbm(sbfr,SWIDTH,SHEIGHT,"/tmp/data/s%04d.pbm",fno);
417 double Video::raw_weight()
419 uint8_t *yp = get_y();
421 for( int y=h; --y>=0; yp+=ww ) {
422 uint8_t *bp = yp; int n = 0;
423 for( int x=w; --x>=0; ++bp ) n += *bp;
426 return (double)wt / (w*h);
429 double Video::weight(uint8_t *bp)
432 for( int i=SFRM_SZ; --i>=0; ++bp ) wt += *bp;
433 return (double)wt / SFRM_SZ;
437 minimum_forward(double start, double end, double &time)
440 double t = position();
441 double min = value();
442 while( next() >= 0 && position() < end ) {
443 if( value() > min ) continue;
444 min = value(); t = position();
451 minimum_backward(double start, double end, double &time)
454 double t = position();
455 double min = value();
456 while( prev() >= 0 && position() >= start ) {
457 if( value() > min ) continue;
458 min = value(); t = position();
464 // find trailing edge scanning backward
465 // must have video < min_video or delta video > min_dvideo
467 video_backward(double start, double end, double &time)
470 double t = position(), v = value();
472 if( t < start ) return 1;
473 double vv = v; v = value();
474 if( v <= min_video ) break;
475 if( fabs(vv-v) >= min_dvideo ) break;
476 if( prev() < 0 ) fail();
479 while( prev() >= 0 ) {
480 if( position() < start ) break;
482 if( value() > min_video ) break;
488 // find leading edge scanning backward
489 // must have audio < min_audio
491 audio_backward(double start, double end, double &time)
494 double t = position();
496 if( t < start ) fail();
497 if( value() <= min_audio ) break;
498 if( prev() < 0 ) fail();
505 // find trailing edge scanning forward
506 // must have video < min_video or delta video > min_dvideo
508 video_forward(double start, double end, double &time)
511 double t = position(), v = value();
513 if( t >= end ) return 1;
514 double vv = v; v = value();
515 if( v <= min_video ) break;
516 if( fabs(vv-v) >= min_dvideo ) break;
517 if( next() < 0 ) fail();
520 while( next() >= 0 ) {
521 if( position() >= end ) break;
523 if( value() > min_video ) break;
529 // find leading edge scanning forward
530 // must have audio < min_audio
532 audio_forward(double start, double end, double &time)
535 double t = position();
537 if( t >= end ) fail();
538 if( value() <= min_audio ) break;
539 if( next() < 0 ) fail();
558 int tracks() { return trks; }
559 int track() { return trk; }
560 int channels() { return chans; }
561 int samplerate() { return rate; }
562 int64_t length() { return len; }
563 int64_t sample_no() { return pos; }
564 bool eof() { return src->end_of_audio(trk); }
565 double time() { return src->get_audio_time(trk); }
566 void set_buffer(int n) {
568 bfr = new short[bfr_sz = clip(n,256,32767)];
570 void disable_weights() { delete weights; weights = 0; }
571 void enable_weights() { weights = new Weights(); }
572 void write_weights(const char *fn) { weights->write(fn); }
573 int forward(double start, double end, double &time) {
574 //if( !weights->audio_forward(start, end, time) ) return 0;
575 return weights->minimum_forward(start, end, time);
577 int backward(double start, double end, double &time) {
578 //if( !weights->audio_backward(start, end, time) ) return 0;
579 return weights->minimum_backward(start, end, time);
582 void init(zmpeg3_t *zsrc, int ztrk);
583 int load_samples(int ch);
587 Audio() { weights = 0; bfr = 0; }
588 ~Audio() { delete weights; delete [] bfr; }
591 void Audio::init(zmpeg3_t *zsrc, int ztrk)
593 src = zsrc; trk = ztrk;
594 trks = src->total_astreams();
595 chans = src->audio_channels(trk);
596 rate = src->sample_rate(trk);
597 len = src->audio_samples(trk);
602 int Audio::load_samples(int ch)
604 if( ch ) return src->reread_audio(bfr, ch, bfr_sz, trk);
605 if( src->read_audio(bfr, ch, bfr_sz, trk) ) fail();
610 int Audio::read_samples()
612 if( !weights ) return load_samples(0);
614 for( int ch=0; ch<chans; wt+=weight(), ++ch )
615 if( load_samples(ch) ) fail();
616 weights->add(time(), wt/chans);
620 double Audio::weight()
622 int64_t wt = 0; short *bp = bfr;
624 for( int i=bfr_sz; --i>0; v=*bp++ ) wt += abs(v-*bp);
625 return (double)wt / (bfr_sz-1);
631 int64_t creation_time, system_time;
632 int prefix_frames, suffix_frames, frames;
633 double prefix_length, suffix_length;
634 double reaction_time, margin;
635 double clip_start, clip_end, *clip_weights;
636 double start_time, end_time;
637 const char *asset_path;
638 static void set_priority(int pr, int io) {
639 setpriority(PRIO_PROCESS, 0, pr); // lowest cpu priority
640 ioprio_set(IO_WHO_PROCESS, 0, IO_CLASS(io));
642 class low_priority { public:
643 low_priority() { set_priority(19, IOPRIO_CLASS_IDLE); }
644 ~low_priority() { set_priority(0, IOPRIO_CLASS_BE); }
646 class high_priority { public:
647 high_priority() { set_priority(-4, IOPRIO_CLASS_BE); }
648 ~high_priority() { set_priority(0, IOPRIO_CLASS_BE); }
652 int scan(Src &src, Deletions *dels);
653 int cut_clip(Src &src, Dele *del, Dele *next);
655 int verify_clip(double position, double length);
656 int save_group(Margin *margin, double pos, int frame_no, int len, int group);
665 prefix_length = suffix_length = 2; // length of prefix/suffix in seconds
666 prefix_frames = video.frame_count(prefix_length);
667 suffix_frames = video.frame_count(suffix_length);
668 reaction_time = 1.0; // 1 sec reaction time for default start/end time
669 margin = 1.0; // 1 sec search window -1.0..+1.0 search margin
670 creation_time = system_time = 0;
671 clip_start = clip_end = 0;
672 clip_id = 0; clip_weights = 0;
686 verify_clip(double position, double length)
689 double start = position, end = position + length;
690 if( prefix->check(&clips, start, start+prefix_length, 1) ) fail();
691 if( !clips.count ) return 0;
692 if( suffix->check(&clips, end-suffix_length, end, 2) ) fail();
693 double avg_wt = video.average_weight(position, length);
694 for( Clip *clip=clips.first; clip; clip=clip->next ) {
695 int clip_id = clip->clip_id;
696 if( db->clip_id(clip_id) ) continue;
697 double bias = avg_wt - db->clip_average_weight();
698 if( fabs(bias) > 2*MEDIA_MEAN_ERRLMT ) continue;
699 double iframerate = db->clip_framerate();
700 if( iframerate <= 0. ) continue;
701 int mframes = 2*check_margin * iframerate;
702 int iframes = db->clip_frames();
703 if( iframes < mframes ) continue;
704 double ilength = iframes / iframerate;
705 if( fabs(ilength-length) > 2*check_margin ) continue;
706 double *iweights = db->clip_weights();
707 double pos = position-check_margin;
708 double kerr = MEDIA_WEIGHT_ERRLMT, kpos = -1;
709 for( double dt=1/iframerate; --mframes>=0; pos+=dt ) {
710 double err = video.err(pos, length, iframerate,
712 //printf(" clip %d, pos %f, err %f\n",clip_id,pos,err);
714 if( kpos >= 0 && err > kerr ) break;
715 kerr = err; kpos = pos;
718 //printf("vs clip %d, err %f, pos %f\n",clip_id,kerr,kpos);
720 printf(" dupl=%d, err=%f", clip_id, kerr);
728 save_group(Margin *margin, double pos, int frame_no, int len, int group)
730 if( margin->locate(pos) < 0 ) fail();
731 if( margin->length() < len ) fail();
732 while( --len >= 0 ) {
733 uint8_t *sbfr = margin->get_frame();
734 double offset = video.frame_position(frame_no++);
735 double wt = video.frame_weight(margin->position());
736 if( wt < lo_video || wt > hi_video ) continue;
737 db->new_frame(clip_id, sbfr, frame_no, group, offset);
738 //write_pbm(sbfr,SWIDTH,SHEIGHT,"/tmp/dat/c%03df%05d.pbm",clip_id,frame_no);
745 int read_to(double time)
748 while( !(ret=video.eof()) && video.time() < time ) video.read_frame();
749 while( !(ret=audio.eof()) && audio.time() < time ) audio.read_samples();
755 scan(Src &src, Deletions *dels)
757 Dele *next = dels->first;
758 if( !next || !next->next ) return 0;
759 if( next->action != DEL_START ) fail();
760 next->action = DEL_MARK;
761 if( next->next->action == DEL_OOPS )
762 next->next->action = DEL_SKIP;
764 time_t ct; time(&ct);
765 creation_time = (int64_t)ct;
770 switch( next->action ) {
772 if( !cut_clip(src, del, next) ) add_clip();
782 Dele *nxt = next->next;
783 while( nxt && nxt->next && nxt->next->action == DEL_OOPS ) {
784 while( (nxt=nxt->next) && nxt->action == DEL_OOPS );
787 } while( next && del && (next->time-del->time) < 3 );
788 delete prefix; prefix = suffix; suffix = 0;
791 delete prefix; prefix = 0;
797 cut_clip(Src &src, Dele *del, Dele *next)
800 double next_suffix_length = !del ? 0 : suffix_length;
801 double next_prefix_length = !next->next ? 0 : prefix_length;
802 double suffix_start_time =
803 next->time - prefix_length - next_suffix_length - margin;
804 if( read_to(suffix_start_time) ) fail();
805 // create new suffix, copy prefix if overlap
806 suffix = new Margin(video.frame_no(), video.time());
807 if( prefix && prefix->locate(suffix_start_time) >= 0 )
808 suffix->copy(prefix);
809 // read_to next mark, get system time
810 video.margin_frames(suffix);
811 if( read_to(next->time) ) fail();
812 system_time = src->dvb.get_system_time();
813 // read_to end of next prefix
814 double prefix_end_time = next->time + next_prefix_length + margin;
815 if( read_to(prefix_end_time) ) fail();
816 video.margin_frames();
817 // if no previous mark, return
818 if( !del ) return -1;
819 asset_path = src.asset_path();
820 double cut_start = del->time, cut_end = next->time;
821 if( cut_end-cut_start < min_clip_time ) return 1;
822 // default start/end clip endpoints
823 start_time = cut_start - reaction_time;
824 end_time = cut_end - reaction_time;
825 // find edges, if possible. default to reaction time
826 audio.backward(cut_start-prefix_length, cut_start, start_time);
827 video.forward(start_time-margin,start_time+margin, start_time);
828 audio.forward(cut_end-suffix_length, cut_end, end_time);
829 video.backward(end_time-margin,end_time+margin, end_time);
831 frames = video.frame_count(end_time - start_time);
832 if( frames < prefix_frames + suffix_frames ) fail();
834 clip_start = start_time - video.origin();
835 clip_end = end_time - video.origin();
836 printf(" %f-%f (%f), %f-%f %jd-%jd",
837 start_time, end_time, end_time-start_time, clip_start, clip_end,
838 video.frame_count(clip_start), video.frame_count(clip_end));
845 high_priority set_hi;
846 if( !db || !db->is_open() ) {
847 fprintf(stderr,"unable to open db: " MEDIA_DB "\n");
851 // check for duplicate
852 if( verify_clip(start_time, end_time-start_time) ) {
854 else if( !write_clip() ) {
855 printf(" new %d",clip_id);
863 printf("\n"); fflush(stdout);
870 if( db->new_clip_set(video.title(), asset_path, clip_start,
871 video.framerate(), frames, prefix_frames, suffix_frames,
872 creation_time, system_time) ) fail();
873 // save prefix video group
875 clip_id = db->clip_id();
876 if( save_group(prefix, start_time, frame_no, prefix_frames, 1) ) fail();
877 // save suffix video group
878 double start_suffix = end_time - suffix_length;
879 frame_no = frames - suffix_frames;
880 if( save_group(suffix, start_suffix, frame_no, suffix_frames, 2) ) fail();
881 // save video weights
882 if( video.save_weights(start_time, db->clip_weights(), frames) ) fail();
883 double avg_wt = video.average_weight(start_time, end_time-start_time);
884 db->clip_average_weight(avg_wt);
890 printf("\nsegv, pid=%d\n",getpid()); fflush(stdout);
898 int main(int ac, char **av)
900 printf("started %d\n",getpid());
901 signal(SIGSEGV,sig_segv);
902 signal(SIGHUP,sig_hup);
905 fprintf(stderr, "usage: %s <xml.del>\n", av[1]);
909 Deletions *deletions = Deletions::read_dels(av[1]);
911 fprintf(stderr, "read_dels: %s failed\n", av[1]);
915 // open mpeg3 dvb media
916 Src src(deletions->file_path());
918 fprintf(stderr, "open: %s failed\n", deletions->file_path());
922 src->set_pts_padding(-1);
925 int vtrk = src->total_vstreams();
926 while( --vtrk >= 0 && src->video_pid(vtrk) != deletions->pid );
928 fprintf(stderr, "pid %d failed\n", deletions->pid);
931 video.init(src, vtrk);
932 if( video.framerate() <= 0 ) {
933 fprintf(stderr, "framerate %d failed\n", vtrk);
937 // find first audio stream associate to video stream
939 int elements = src->dvb.channel_count();
940 for( n=0; atrk<0 && n<elements; ++n ) {
941 int atracks, vtracks;
942 if( src->dvb.total_vstreams(n,vtracks) ) continue;
943 for( int i=0; atrk<0 && i<vtracks; ++i ) {
944 if( src->dvb.vstream_number(n,i,vtrk) ) continue;
945 if( vtrk != video.track() ) continue;
946 if( src->dvb.total_astreams(n,atracks) ) continue;
947 if( !atracks ) continue;
948 if( src->dvb.astream_number(n,0,atrk,0) ) continue;
951 int major, minor; char title[64];
952 if( !src->dvb.get_channel(n, major, minor) ) {
953 sprintf(title, "%3d.%-3d", major, minor);
954 video.set_title(title);
958 fprintf(stderr, "audio %d failed\n", video.track());
961 audio.init(src, atrk);
962 audio.set_buffer(audio.samplerate()/video.framerate()+64);
964 // read to the first valid frame
965 if( read_to(0) ) fail();
966 double last_time = video.time(), origin = -1;
967 while( !video.eof() ) {
968 if( video.frame_position(video.frame_no()) >= 5 ) break;
969 if( video.raw_weight() >= video_cutoff ) { origin = last_time; break; }
970 last_time = video.time();
971 if( video.read_frame() ) break;
974 fprintf(stderr, "origin %d failed\n", video.track());
977 video.set_origin(origin);
979 double audio_time = audio.time(), video_time = video.time();
980 double start_time = audio_time > video_time ? audio_time : video_time;
981 if( read_to(start_time) ) fail();
983 audio.enable_weights();
984 video.enable_weights();
987 s.scan(src, deletions);
989 //audio.write_weights("/tmp/audio.wts");
990 //video.write_weights("/tmp/video.wts");
991 audio.disable_weights();
992 video.disable_weights();
994 // ::remove(deletions->path());
998 printf("completed %d\n",getpid());