Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / vicon.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
22 #include "vicon.h"
23
24 #include "bctimer.h"
25 #include "bcwindow.h"
26 #include "bccolors.h"
27 #include "keys.h"
28 #include "mutex.h"
29 #include "condition.h"
30
31 VIcon::
32 VIcon(int w, int h, double rate)
33 {
34         this->w = w;
35         this->h = h;
36         this->frame_rate = rate;
37
38         cycle_start = 0;
39         age = 0;
40         seq_no = 0;
41         in_use = 1;
42         hidden = 0;
43         audio_data = 0;
44         audio_size = 0;
45         playing_audio = 0;
46 }
47
48 VIcon::
49 ~VIcon()
50 {
51         clear_images();
52         delete [] audio_data;
53 }
54
55 void VIcon::
56 add_image(VFrame *frm, int ww, int hh, int vcmdl)
57 {
58         VIFrame *vifrm = new VIFrame(ww, hh, vcmdl);
59         vifrm->vfrm->transfer_from(frm);
60         images.append(vifrm);
61 }
62
63 void VIcon::
64 draw_vframe(VIconThread *vt, BC_WindowBase *wdw, int x, int y)
65 {
66         VFrame *vfrm = frame();
67         if( !vfrm ) return;
68         int sx0 = 0, sx1 = sx0 + vt->vw;
69         int sy0 = 0, sy1 = sy0 + vt->vh;
70         int dx0 = x, dx1 = dx0 + w;
71         int dy0 = y, dy1 = dy0 + h;
72         if( (x=vt->draw_x0-dx0) > 0 ) { sx0 += (x*vt->vw)/w;  dx0 = vt->draw_x0; }
73         if( (x=dx1-vt->draw_x1) > 0 ) { sx1 -= (x*vt->vw)/w;  dx1 = vt->draw_x1; }
74         if( (y=vt->draw_y0-dy0) > 0 ) { sy0 += (y*vt->vh)/h;  dy0 = vt->draw_y0; }
75         if( (y=dy1-vt->draw_y1) > 0 ) { sy1 -= (y*vt->vh)/h;  dy1 = vt->draw_y1; }
76         int sw = sx1 - sx0, sh = sy1 - sy0;
77         int dw = dx1 - dx0, dh = dy1 - dy0;
78         if( dw > 0 && dh > 0 && sw > 0 && sh > 0 )
79                 wdw->draw_vframe(vfrm, dx0,dy0, dw,dh, sx0,sy0, sw,sh);
80 }
81
82
83 int VIconThread::cursor_inside(int x, int y)
84 {
85         if( !viewing ) return 0;
86         int vx = viewing->get_vx();
87         if( x < vx || x >= vx+vw ) return 0;
88         int vy = viewing->get_vy();
89         if( y < vy || y >= vy+vh ) return 0;
90         return 1;
91 }
92
93 void VIconThread::
94 set_drawing_area(int x0, int y0, int x1, int y1)
95 {
96         draw_x0 = x0;  draw_y0 = y0;
97         draw_x1 = x1;  draw_y1 = y1;
98 }
99
100 VIcon *VIconThread::low_vicon()
101 {
102         if( !t_heap.size() ) return 0;
103         VIcon *vip = t_heap[0];
104         remove_vicon(0);
105         return vip;
106 }
107
108 void VIconThread::remove_vicon(int i)
109 {
110         if( t_heap[i] == solo ) solo = 0;
111         int sz = t_heap.size();
112         for( int k; (k=2*(i+1)) < sz; i=k ) {
113                 if( t_heap[k]->age > t_heap[k-1]->age ) --k;
114                 t_heap[i] = t_heap[k];
115         }
116         VIcon *last = t_heap[--sz];
117         t_heap.remove_number(sz);
118         double age = last->age;
119         for( int k; i>0 && age<t_heap[k=(i-1)/2]->age; i=k )
120                 t_heap[i] = t_heap[k];
121         t_heap[i] = last;
122 }
123
124
125 VIconThread::
126 VIconThread(BC_WindowBase *wdw, int vw, int vh, int view_w, int view_h)
127  : Thread(1, 0, 0)
128 {
129         this->wdw = wdw;
130         this->vw = vw;         this->vh = vh;
131         this->view_w = view_w; this->view_h = view_h;
132         this->view_win = 0;    this->vicon = 0;
133         this->viewing = 0;     this->solo = 0;
134         this->draw_x0 = 0;     this->draw_x1 = wdw->get_w();
135         this->draw_y0 = 0;     this->draw_y1 = wdw->get_h();
136         draw_lock = new Condition(0, "VIconThread::draw_lock", 1);
137         timer = new Timer();
138         this->refresh_rate = VICON_RATE;
139         this->draw_flash = 0;
140         this->seq_no = 0;
141         this->now = 0;
142         done = 0;
143         interrupted = -1;
144         stop_age = 0;
145 }
146
147 VIconThread::
148 ~VIconThread()
149 {
150         stop_drawing();
151         done = 1;
152         draw_lock->unlock();
153         if( Thread::running() ) {
154                 Thread::cancel();
155         }
156         Thread::join();
157         t_heap.remove_all_objects();
158         delete timer;
159         delete draw_lock;
160 }
161
162 void VIconThread::
163 start_drawing()
164 {
165         wdw->lock_window("VIconThread::start_drawing");
166         if( view_win )
167                 wdw->set_active_subwindow(view_win);
168         if( interrupted < 0 )
169                 draw_lock->unlock();
170         timer->update();
171         timer->subtract(-stop_age);
172         interrupted = 0;
173         wdw->unlock_window();
174 }
175
176 void VIconThread::
177 stop_drawing()
178 {
179         wdw->lock_window("VIconThread::stop_drawing");
180         close_view_popup();
181         if( !interrupted )
182                 interrupted = 1;
183         stop_age = timer->get_difference();
184         wdw->unlock_window();
185 }
186
187 void VIconThread::
188 stop_viewing()
189 {
190         if( viewing ) {
191                 viewing->stop_audio();
192                 viewing = 0;
193         }
194 }
195
196 int VIconThread::keypress_event(int key)
197 {
198         if( key != ESC ) return 0;
199         close_view_popup();
200         return 1;
201 }
202
203 bool VIconThread::
204 visible(VIcon *vicon, int x, int y)
205 {
206         if( vicon->hidden ) return false;
207         if( y+vicon->h <= draw_y0 ) return false;
208         if( y >= draw_y1 ) return false;
209         if( x+vicon->w <= draw_x0 ) return false;
210         if( x >= draw_x1 ) return false;
211         return true;
212 }
213
214 int ViewPopup::keypress_event()
215 {
216         int key = get_keypress();
217         return vt->keypress_event(key);
218 }
219
220
221 ViewPopup::ViewPopup(VIconThread *vt, int x, int y, int w, int h)
222  : BC_Popup(vt->wdw, x, y, w, h, BLACK)
223 {
224         this->vt = vt;
225 }
226
227 ViewPopup::~ViewPopup()
228 {
229         vt->wdw->set_active_subwindow(0);
230 }
231
232 ViewPopup *VIconThread::new_view_window(ViewPopup *vpopup)
233 {
234         BC_WindowBase *parent = wdw->get_parent();
235         XineramaScreenInfo *info = parent->get_xinerama_info(-1);
236         int cx = info ? info->x_org + info->width/2 : parent->get_root_w(0)/2;
237         int cy = info ? info->y_org + info->height/2 : parent->get_root_h(0)/2;
238         int vx = viewing->get_vx(), rx = 0;
239         int vy = viewing->get_vy(), ry = 0;
240         wdw->get_root_coordinates(vx, vy, &rx, &ry);
241         rx += (rx >= cx ? -view_w+viewing->w/4 : viewing->w-viewing->w/4);
242         ry += (ry >= cy ? -view_h+viewing->h/4 : viewing->h-viewing->h/4);
243         if( vpopup )
244                 vpopup->reposition_window(rx, ry, view_w, view_h);
245         else
246                 vpopup = new ViewPopup(this, rx, ry, view_w, view_h);
247         wdw->set_active_subwindow(vpopup);
248         return vpopup;
249 }
250
251
252 void VIconThread::
253 reset_images()
254 {
255         for( int i=t_heap.size(); --i>=0; ) t_heap[i]->reset();
256         timer->update();
257         img_dirty = win_dirty = 0;
258 }
259
260 void VIconThread::add_vicon(VIcon *vip)
261 {
262         double age = vip->age;
263         int i = t_heap.size();  t_heap.append(vip);
264         for( int k; i>0 && age<t_heap[(k=(i-1)/2)]->age; i=k )
265                 t_heap[i] = t_heap[k];
266         t_heap[i] = vip;
267 }
268
269 int VIconThread::del_vicon(VIcon *vicon)
270 {
271         int i = t_heap.size();
272         while( --i >= 0 && t_heap[i] != vicon );
273         if( i < 0 ) return 0;
274         remove_vicon(i);
275         return 1;
276 }
277
278 void ViewPopup::draw_vframe(VFrame *frame)
279 {
280         if( !frame ) return;
281         BC_WindowBase::draw_vframe(frame, 0,0, get_w(),get_h());
282 }
283
284 void VIconThread::set_view_popup(VIcon *vicon)
285 {
286         if( viewing == vicon && !this->vicon ) return;
287         this->vicon = vicon;
288         if( interrupted ) update_view(vicon ? 1 : 0);
289
290 }
291
292 void VIconThread::close_view_popup()
293 {
294         set_view_popup(0);
295 }
296
297 int VIconThread::
298 update_view(int do_audio)
299 {
300         if( viewing ) viewing->stop_audio();
301         delete view_win;  view_win = 0;
302         VFrame *vfrm;
303         if( (viewing=vicon) != 0 && (vfrm=viewing->frame()) != 0 ) {
304                 view_win = new_view_window(0);
305                 view_win->draw_vframe(vfrm);
306                 view_win->flash(0);
307                 view_win->show_window();
308                 if( do_audio ) vicon->start_audio();
309         }
310         wdw->set_active_subwindow(view_win);
311         return 1;
312 }
313
314 int VIconThread::zoom_scale(int dir)
315 {
316         VFrame *vfrm;
317         if( !viewing || !view_win || !(vfrm=viewing->frame()) ) return 0;
318         int view_h = this->view_h;
319         view_h += dir*view_h/10 + dir;
320         bclamp(view_h, 16,512);
321         this->view_h = view_h;
322         this->view_w = view_h * vw/vh;
323         new_view_window(view_win);
324         view_win->draw_vframe(vfrm);
325         view_win->flash(1);
326         return 1;
327 }
328
329
330 void VIconThread::
331 draw_images()
332 {
333         for( int i=0; i<t_heap.size(); ++i )
334                 draw(t_heap[i]);
335 }
336
337 void VIconThread::
338 flash()
339 {
340         if( !img_dirty && !win_dirty ) return;
341         if( img_dirty ) wdw->flash();
342         if( win_dirty && view_win ) view_win->flash();
343         win_dirty = img_dirty = 0;
344 }
345
346 int VIconThread::
347 draw(VIcon *vicon)
348 {
349         int x = vicon->get_vx(), y = vicon->get_vy();
350         int draw_img = visible(vicon, x, y);
351         int draw_win = view_win && viewing == vicon ? 1 : 0;
352         if( !draw_img && !draw_win ) return 0;
353         VFrame *vfrm = vicon->frame();
354         if( !vfrm ) return 0;
355         if( draw_img ) {
356                 vicon->draw_vframe(this, wdw, x, y);
357                 img_dirty = 1;
358         }
359         if( draw_win ) {
360                 view_win->draw_vframe(vfrm);
361                 win_dirty = 1;
362         }
363         return 1;
364 }
365
366 void VIconThread::hide_vicons(int v)
367 {
368         for( int i=0; i<t_heap.size(); ++i ) {
369                 t_heap[i]->hidden = v;
370                 t_heap[i]->age = 0;
371         }
372 }
373
374 int VIconThread::show_vicon(VIcon *next)
375 {
376         now = timer->get_difference();
377         if( now >= draw_flash ) return 1;
378         draw(next);
379         if( !next->seq_no ) {
380                 next->cycle_start = now;
381                 if( next->playing_audio > 0 )
382                         next->start_audio();
383         }
384         int64_t ref_no = (now - next->cycle_start) / 1000. * refresh_rate;
385         int count = ref_no - next->seq_no;
386         if( count < 1 ) count = 1;
387         ref_no = next->seq_no + count;
388         next->age =  next->cycle_start + 1000. * ref_no / refresh_rate;
389         if( !next->set_seq_no(ref_no) )
390                 next->age = now + 1000.;
391         return 0;
392 }
393
394 void VIconThread::
395 run()
396 {
397         while(!done) {
398                 draw_lock->lock("VIconThread::run 0");
399                 if( done ) break;
400                 wdw->lock_window("BC_WindowBase::run 1");
401                 drawing_started();
402                 reset_images();
403                 draw_flash = 1000 / refresh_rate;
404                 seq_no = 0;  now = 0;
405                 while( !interrupted ) {
406                         if( viewing != vicon )
407                                 update_view(1);
408                         if( !solo ) {
409                                 VIcon *next = low_vicon();
410                                 while( !interrupted && next && next->age < draw_flash ) {
411                                         if( show_vicon(next) ) break;
412                                         add_vicon(next);
413                                         next = low_vicon();
414                                 }
415                                 if( !next ) break;
416                                 add_vicon(next);
417                                 if( draw_flash < now+1 )
418                                         draw_flash = now+1;
419                         }
420                         else
421                                 show_vicon(solo);
422                         wdw->unlock_window();
423                         while( !interrupted ) {
424                                 now = timer->get_difference();
425                                 int64_t ms = draw_flash - now;
426                                 if( ms <= 0 ) break;
427                                 if( ms > 100 ) ms = 100;
428                                 Timer::delay(ms);
429                         }
430                         wdw->lock_window("BC_WindowBase::run 2");
431                         now = timer->get_difference();
432                         int64_t late = now - draw_flash;
433                         if( late < 1000 ) flash();
434                         int64_t ref_no = now / 1000. * refresh_rate;
435                         int64_t count = ref_no - seq_no;
436                         if( count < 1 ) count = 1;
437                         seq_no += count;
438                         draw_flash = seq_no * 1000. / refresh_rate;
439                 }
440                 if( viewing != vicon )
441                         update_view(1);
442                 drawing_stopped();
443                 interrupted = -1;
444                 wdw->unlock_window();
445         }
446 }
447
448
449 void VIcon::init_audio(int audio_size)
450 {
451         this->audio_size = audio_size;
452         audio_data = new uint8_t[audio_size];
453         memset(audio_data, 0, audio_size);
454 }
455
456 void VIcon::dump(const char *dir)
457 {
458         mkdir(dir,0777);
459         for( int i=0; i<images.size(); ++i ) {
460                 VFrame *vfrm = images[i]->vfrm;
461                 if( !vfrm ) continue;
462                 char fn[1024];  sprintf(fn,"%s/img%05d.png",dir,i);
463                 printf("\r%s",fn);
464                 vfrm->write_png(fn);
465         }
466         printf("\n");
467 }
468