resourcethread redraw speedup/fixes, replace vectorscope graticule IQ
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / resourcethread.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "arender.h"
23 #include "asset.h"
24 #include "bcsignals.h"
25 #include "bctimer.h"
26 #include "cache.h"
27 #include "clip.h"
28 #include "condition.h"
29 #include "datatype.h"
30 #include "edl.h"
31 #include "edlsession.h"
32 #include "file.h"
33 #include "framecache.h"
34 #include "mutex.h"
35 #include "mwindow.h"
36 #include "mwindowgui.h"
37 #include "renderengine.h"
38 #include "resourcethread.h"
39 #include "resourcepixmap.h"
40 #include "samples.h"
41 #include "timelinepane.h"
42 #include "trackcanvas.h"
43 #include "transportque.h"
44 #include "vframe.h"
45 #include "vrender.h"
46 #include "wavecache.h"
47
48
49 #include <unistd.h>
50
51 ResourceThreadItem::ResourceThreadItem(ResourcePixmap *pixmap,
52         int pane_number,
53         Indexable *indexable,
54         int data_type,
55         int operation_count)
56 {
57         this->pane_number = pane_number;
58         this->data_type = data_type;
59         this->pixmap = pixmap;
60         this->indexable = indexable;
61
62 // Assets are garbage collected so they don't need to be replicated.
63         this->operation_count = operation_count;
64         indexable->Garbage::add_user();
65         last = 0;
66 }
67
68 ResourceThreadItem::~ResourceThreadItem()
69 {
70         indexable->Garbage::remove_user();
71 }
72
73
74
75
76
77
78
79 VResourceThreadItem::VResourceThreadItem(ResourcePixmap *pixmap,
80         int pane_number,
81         int picon_x,
82         int picon_y,
83         int picon_w,
84         int picon_h,
85         double frame_rate,
86         int64_t position,
87         int layer,
88         Indexable *indexable,
89         int operation_count)
90  : ResourceThreadItem(pixmap,
91         pane_number,
92         indexable,
93         TRACK_VIDEO,
94         operation_count)
95 {
96         this->picon_x = picon_x;
97         this->picon_y = picon_y;
98         this->picon_w = picon_w;
99         this->picon_h = picon_h;
100         this->frame_rate = frame_rate;
101         this->position = position;
102         this->layer = layer;
103 }
104
105 VResourceThreadItem::~VResourceThreadItem()
106 {
107 }
108
109
110
111
112
113
114
115
116 AResourceThreadItem::AResourceThreadItem(ResourcePixmap *pixmap,
117         int pane_number,
118         Indexable *indexable,
119         int x,
120         int channel,
121         int64_t start,
122         int64_t end,
123         int operation_count)
124  : ResourceThreadItem(pixmap,
125         pane_number,
126         indexable,
127         TRACK_AUDIO,
128         operation_count)
129 {
130         this->x = x;
131         this->channel = channel;
132         this->start = start;
133         this->end = end;
134 }
135
136 AResourceThreadItem::~AResourceThreadItem()
137 {
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 ResourceThread::ResourceThread(MWindow *mwindow, MWindowGUI *gui)
157  : Thread(1, 0, 0)
158 {
159 //printf("ResourceThread::ResourceThread %d %p\n", __LINE__, this);
160         this->mwindow = mwindow;
161         this->gui = gui;
162         interrupted = 1;
163         done = 1;
164         temp_picon = 0;
165         temp_picon2 = 0;
166         draw_lock = new Condition(0, "ResourceThread::draw_lock", 0);
167         item_lock = new Mutex("ResourceThread::item_lock");
168         audio_buffer = 0;
169         for(int i = 0; i < MAXCHANNELS; i++)
170                 temp_buffer[i] = 0;
171         timer = new Timer;
172         prev_x = -1;
173         prev_h = 0;
174         prev_l = 0;
175         operation_count = 0;
176         render_engine = 0;
177         render_engine_id = -1;
178
179         audio_asset = 0;
180         audio_source = 0;
181         video_asset = 0;
182         video_source = 0;
183 }
184
185 ResourceThread::~ResourceThread()
186 {
187         stop();
188         delete draw_lock;
189         delete item_lock;
190         delete temp_picon;
191         delete temp_picon2;
192         delete audio_buffer;
193         for(int i = 0; i < MAXCHANNELS; i++)
194                 delete temp_buffer[i];
195         delete timer;
196         delete render_engine;
197         if( audio_asset ) audio_asset->remove_user();
198         if( video_asset ) video_asset->remove_user();
199 }
200
201 void ResourceThread::create_objects()
202 {
203         done = 0;
204         Thread::start();
205 }
206
207 void ResourceThread::add_picon(ResourcePixmap *pixmap,
208         int pane_number,
209         int picon_x,
210         int picon_y,
211         int picon_w,
212         int picon_h,
213         double frame_rate,
214         int64_t position,
215         int layer,
216         Indexable *indexable)
217 {
218         item_lock->lock("ResourceThread::item_lock");
219
220         items.append(new VResourceThreadItem(pixmap,
221                 pane_number,
222                 picon_x,
223                 picon_y,
224                 picon_w,
225                 picon_h,
226                 frame_rate,
227                 position,
228                 layer,
229                 indexable,
230                 operation_count));
231         item_lock->unlock();
232 }
233
234 void ResourceThread::add_wave(ResourcePixmap *pixmap,
235         int pane_number,
236         Indexable *indexable,
237         int x,
238         int channel,
239         int64_t source_start,
240         int64_t source_end)
241 {
242         item_lock->lock("ResourceThread::item_lock");
243
244         items.append(new AResourceThreadItem(pixmap,
245                 pane_number,
246                 indexable,
247                 x,
248                 channel,
249                 source_start,
250                 source_end,
251                 operation_count));
252         item_lock->unlock();
253 }
254
255 void ResourceThread::reset(int pane_number)
256 {
257         item_lock->lock("ResourceThread::reset");
258         ResourceThreadItem *item = items.first;
259         while( item ) {
260                 ResourceThreadItem *next_item = item->next;
261                 if( item->pane_number == pane_number ) delete item;
262                 item = next_item;
263         }
264         item_lock->unlock();
265 }
266
267
268
269
270
271
272
273
274
275
276 void ResourceThread::stop_draw(int reset)
277 {
278         if(!interrupted)
279         {
280                 interrupted = 1;
281                 item_lock->lock("ResourceThread::stop_draw");
282
283 //printf("ResourceThread::stop_draw %d %d\n", __LINE__, reset);
284 //BC_Signals::dump_stack();
285                 if( reset ) {
286                         ResourceThreadItem *item;
287                         while( (item=items.last) != 0 ) delete item;
288                         ++operation_count;
289                 }
290                 item_lock->unlock();
291                 prev_x = -1;
292                 prev_h = 0;
293                 prev_l = 0;
294         }
295 }
296
297 void ResourceThread::start_draw()
298 {
299         interrupted = 0;
300 // Tag last audio item to cause refresh.
301         ResourceThreadItem *item = items.last;
302         while( item && item->data_type!=TRACK_AUDIO ) item = item->previous;
303         if( item ) item->last = 1;
304         timer->update();
305         draw_lock->unlock();
306 }
307
308 void ResourceThread::run()
309 {
310         while(!done)
311         {
312
313                 draw_lock->lock("ResourceThread::run");
314                 while(!interrupted)
315                 {
316 // Pull off item
317                         item_lock->lock("ResourceThread::run");
318                         ResourceThreadItem *item = items.first;
319                         items.remove_pointer(item);
320                         item_lock->unlock();
321 //printf("ResourceThread::run %d %d\n", __LINE__, items.size());
322                         if(!item) break;
323
324                         switch( item->data_type ) {
325                         case TRACK_VIDEO:
326                                 do_video((VResourceThreadItem*)item);
327                                 break;
328                         case TRACK_AUDIO:
329                                 do_audio((AResourceThreadItem*)item);
330                                 break;
331                         }
332
333                         delete item;
334                 }
335
336                 get_audio_source(0);
337                 get_video_source(0);
338                 mwindow->age_caches();
339         }
340 }
341
342 void ResourceThread::stop()
343 {
344         if( !done ) {
345                 done = 1;
346                 interrupted = 1;
347                 draw_lock->unlock();
348         }
349         join();
350 }
351
352
353 void ResourceThread::open_render_engine(EDL *nested_edl,
354         int do_audio,
355         int do_video)
356 {
357         if(render_engine && render_engine_id != nested_edl->id)
358         {
359                 delete render_engine;
360                 render_engine = 0;
361         }
362
363         if(!render_engine)
364         {
365                 TransportCommand command;
366                 if(do_audio)
367                         command.command = NORMAL_FWD;
368                 else
369                         command.command = CURRENT_FRAME;
370                 command.get_edl()->copy_all(nested_edl);
371                 command.change_type = CHANGE_ALL;
372                 command.realtime = 0;
373                 render_engine = new RenderEngine(0,
374                         mwindow->preferences, 0, 0);
375                 render_engine_id = nested_edl->id;
376                 render_engine->set_vcache(mwindow->video_cache);
377                 render_engine->set_acache(mwindow->audio_cache);
378                 render_engine->arm_command(&command);
379         }
380 }
381
382 File *ResourceThread::get_audio_source(Asset *asset)
383 {
384         if( interrupted ) asset = 0;
385
386         if( audio_asset && audio_asset != asset && (!asset ||
387                 strcmp(audio_asset->path, asset->path)) )
388         {
389                 mwindow->audio_cache->check_in(audio_asset);
390                 audio_source = 0;
391                 audio_asset->remove_user();
392                 audio_asset = 0;
393
394         }
395         if( !audio_asset && asset )
396         {
397                 audio_asset = asset;
398                 audio_asset->add_user();
399                 audio_source = mwindow->audio_cache->check_out(asset, mwindow->edl);
400         }
401         return audio_source;
402 }
403
404 File *ResourceThread::get_video_source(Asset *asset)
405 {
406         if( interrupted ) asset = 0;
407
408         if( video_asset && video_asset != asset && (!asset ||
409                 strcmp(video_asset->path, asset->path)) )
410         {
411                 mwindow->video_cache->check_in(video_asset);
412                 video_source = 0;
413                 video_asset->remove_user();
414                 video_asset = 0;
415
416         }
417         if( !video_asset && asset )
418         {
419                 video_asset = asset;
420                 video_asset->add_user();
421                 video_source = mwindow->video_cache->check_out(asset, mwindow->edl);
422         }
423         return video_source;
424 }
425
426 void ResourceThread::do_video(VResourceThreadItem *item)
427 {
428         int source_w = 0;
429         int source_h = 0;
430         int source_id = -1;
431         int source_cmodel = -1;
432
433         if(item->indexable->is_asset)
434         {
435                 Asset *asset = (Asset*)item->indexable;
436                 source_w = asset->width;
437                 source_h = asset->height;
438                 source_id = asset->id;
439                 source_cmodel = BC_RGB888;
440         }
441         else
442         {
443                 EDL *nested_edl = (EDL*)item->indexable;
444                 source_w = nested_edl->session->output_w;
445                 source_h = nested_edl->session->output_h;
446                 source_id = nested_edl->id;
447                 source_cmodel = nested_edl->session->color_model;
448         }
449
450         if(temp_picon &&
451                 (temp_picon->get_w() != source_w ||
452                 temp_picon->get_h() != source_h ||
453                 temp_picon->get_color_model() != source_cmodel))
454         {
455                 delete temp_picon;
456                 temp_picon = 0;
457         }
458
459         if(!temp_picon)
460         {
461                 temp_picon = new VFrame(0, -1, source_w, source_h, source_cmodel, -1);
462         }
463
464 // Get temporary to copy cached frame to
465         if(temp_picon2 &&
466                 (temp_picon2->get_w() != item->picon_w ||
467                 temp_picon2->get_h() != item->picon_h))
468         {
469                 delete temp_picon2;
470                 temp_picon2 = 0;
471         }
472
473         if(!temp_picon2)
474         {
475                 temp_picon2 = new VFrame( item->picon_w, item->picon_h, BC_RGB888, 0);
476         }
477
478
479
480 // Search frame cache again.
481
482         VFrame *picon_frame = 0;
483         int need_conversion = 0;
484         EDL *nested_edl = 0;
485         Asset *asset = 0;
486
487         picon_frame = mwindow->frame_cache->get_frame_ptr(item->position,
488                 item->layer, item->frame_rate, BC_RGB888,
489                 item->picon_w, item->picon_h, source_id);
490 //printf("search cache %ld,%d,%f,%dx%d,%d = %p\n",
491 //  item->position, item->layer, item->frame_rate,
492 //  item->picon_w, item->picon_h, source_id, picon_frame);
493         if( picon_frame ) {
494                 temp_picon2->copy_from(picon_frame);
495 // Unlock the get_frame_ptr command
496                 mwindow->frame_cache->unlock();
497         }
498         else
499         if(!item->indexable->is_asset)
500         {
501                 nested_edl = (EDL*)item->indexable;
502                 open_render_engine(nested_edl, 0, 1);
503
504                 int64_t source_position = (int64_t)(item->position *
505                         nested_edl->session->frame_rate /
506                         item->frame_rate);
507                 if(render_engine->vrender)
508                         render_engine->vrender->process_buffer(
509                                 temp_picon,
510                                 source_position,
511                                 0);
512
513                 need_conversion = 1;
514         }
515         else
516         {
517                 asset = (Asset*)item->indexable;
518                 File *source = get_video_source(asset);
519                 if(!source)
520                         return;
521
522                 source->set_layer(item->layer);
523                 int64_t normalized_position = (int64_t)(item->position *
524                         asset->frame_rate /
525                         item->frame_rate);
526                 source->set_video_position(normalized_position,
527                         0);
528
529                 source->read_frame(temp_picon);
530
531                 need_conversion = 1;
532         }
533
534         if(need_conversion)
535         {
536                 picon_frame = new VFrame(item->picon_w, item->picon_h, BC_RGB888, 0);
537                 BC_CModels::transfer(picon_frame->get_rows(), temp_picon->get_rows(),
538                         0, 0, 0,  0, 0, 0,
539                         0, 0, temp_picon->get_w(), temp_picon->get_h(),
540                         0, 0, picon_frame->get_w(), picon_frame->get_h(),
541                         source_cmodel, BC_RGB888, 0,
542                         temp_picon->get_bytes_per_line(),
543                         picon_frame->get_bytes_per_line());
544                 temp_picon2->copy_from(picon_frame);
545                 mwindow->frame_cache->put_frame(picon_frame, item->position, item->layer,
546                         mwindow->edl->session->frame_rate, 0, item->indexable);
547         }
548
549 // Allow escape here
550         if(interrupted)
551                 return;
552
553 // Draw the picon
554         mwindow->gui->lock_window("ResourceThread::do_video");
555
556 // It was interrupted while waiting.
557         if(interrupted)
558         {
559                 mwindow->gui->unlock_window();
560                 return;
561         }
562
563
564
565 // Test for pixmap existence first
566         if(item->operation_count == operation_count)
567         {
568                 ArrayList<ResourcePixmap*> &resource_pixmaps = gui->resource_pixmaps;
569                 int i = resource_pixmaps.total;
570                 while( --i >= 0 && resource_pixmaps[i] != item->pixmap );
571                 if( i >= 0 ) {
572                         item->pixmap->draw_vframe(temp_picon2,
573                                 item->picon_x,
574                                 item->picon_y,
575                                 item->picon_w,
576                                 item->picon_h,
577                                 0,
578                                 0);
579
580                         mwindow->gui->update(0, IGNORE_THREAD, 0, 0, 0, 0, 0);
581                 }
582         }
583
584         mwindow->gui->unlock_window();
585 }
586
587
588 #define BUFFERSIZE 65536
589 void ResourceThread::do_audio(AResourceThreadItem *item)
590 {
591 // Search again
592         WaveCacheItem *wave_item;
593         double high = 0;
594         double low = 0;
595         const int debug = 0;
596         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
597         if((wave_item = mwindow->wave_cache->get_wave(item->indexable->id,
598                 item->channel, item->start, item->end)))
599         {
600                 high = wave_item->high;
601                 low = wave_item->low;
602                 mwindow->wave_cache->unlock();
603         }
604         else
605         {
606                 int first_sample = 1;
607                 int64_t start = item->start;
608                 int64_t end = item->end;
609                 if(start == end) end = start + 1;
610                 double *buffer_samples = !audio_buffer ? 0 :
611                         audio_buffer->get_data();
612
613                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
614                 for(int64_t sample = start; sample < end; sample++)
615                 {
616                         double value;
617 // Get value from previous buffer
618                         if(audio_buffer &&
619                                 item->channel == audio_channel &&
620                                 item->indexable->id == audio_asset_id &&
621                                 sample >= audio_start &&
622                                 sample < audio_start + audio_samples)
623                         {
624                                 ;
625                         }
626                         else
627 // Load new buffer
628                         {
629                                 if(!audio_buffer) {
630                                         audio_buffer = new Samples(BUFFERSIZE);
631                                         buffer_samples = audio_buffer->get_data();
632                                 }
633
634                                 int64_t total_samples = item->indexable->get_audio_samples();
635                                 int fragment = BUFFERSIZE;
636                                 if(fragment + sample > total_samples)
637                                         fragment = total_samples - sample;
638
639                                 if(!item->indexable->is_asset)
640                                 {
641                                         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
642                                         open_render_engine((EDL*)item->indexable, 1, 0);
643                                         if(debug) printf("ResourceThread::do_audio %d %p\n", __LINE__, render_engine);
644                                         if(render_engine->arender)
645                                         {
646                                                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
647                                                 int source_channels = item->indexable->get_audio_channels();
648                                                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
649                                                 for(int i = 0; i < MAXCHANNELS; i++)
650                                                 {
651                                                         if(i < source_channels &&
652                                                                 !temp_buffer[i])
653                                                         {
654                                                                 temp_buffer[i] = new Samples(BUFFERSIZE);
655                                                         }
656                                                         else
657                                                         if(i >= source_channels &&
658                                                                 temp_buffer[i])
659                                                         {
660                                                                 delete temp_buffer[i];
661                                                                 temp_buffer[i] = 0;
662                                                         }
663                                                 }
664
665
666                                                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
667                                                 render_engine->arender->process_buffer(
668                                                         temp_buffer,
669                                                         fragment,
670                                                         sample);
671                                                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
672                                                 memcpy(buffer_samples,
673                                                         temp_buffer[item->channel]->get_data(),
674                                                         fragment * sizeof(double));
675                                         }
676                                         else
677                                         {
678                                                 if(debug) printf("ResourceThread::do_audio %d %d\n", __LINE__, fragment);
679                                                 if(fragment > 0) bzero(buffer_samples, sizeof(double) * fragment);
680                                                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
681
682                                         }
683                                 }
684                                 else
685                                 {
686                                         Asset *asset = (Asset*)item->indexable;
687                                         File *source = get_audio_source(asset);
688                                         if(!source)
689                                                 return;
690
691                                         source->set_channel(item->channel);
692                                         source->set_audio_position(sample);
693                                         source->read_samples(audio_buffer, fragment);
694                                 }
695
696                                 audio_asset_id = item->indexable->id;
697                                 audio_channel = item->channel;
698                                 audio_start = sample;
699                                 audio_samples = fragment;
700                         }
701
702
703                         value = buffer_samples[sample - audio_start];
704                         if(first_sample)
705                         {
706                                 high = low = value;
707                                 first_sample = 0;
708                         }
709                         else
710                         {
711                                 if(value > high)
712                                         high = value;
713                                 else
714                                 if(value < low)
715                                         low = value;
716                         }
717                 }
718                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
719
720 // If it's a nested EDL, store all the channels
721                 mwindow->wave_cache->put_wave(item->indexable,
722                         item->channel,
723                         item->start,
724                         item->end,
725                         high,
726                         low);
727                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
728         }
729         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
730
731 // Allow escape here
732         if(interrupted)
733                 return;
734
735         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
736 // Draw the column
737         mwindow->gui->lock_window("ResourceThread::do_audio");
738         if(interrupted)
739         {
740                 mwindow->gui->unlock_window();
741                 return;
742         }
743
744         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
745         if(item->operation_count == operation_count)
746         {
747
748 // Test for pixmap existence first
749                 ArrayList<ResourcePixmap*> &resource_pixmaps = gui->resource_pixmaps;
750                 int i = resource_pixmaps.total;
751                 while( --i >= 0 && resource_pixmaps[i] != item->pixmap );
752                 if( i >= 0 )
753                 {
754                         if(prev_x == item->x - 1)
755                         {
756                                 high = MAX(high, prev_l);
757                                 low = MIN(low, prev_h);
758                         }
759                         prev_x = item->x;
760                         prev_h = high;
761                         prev_l = low;
762                         if(gui->pane[item->pane_number])
763                                 item->pixmap->draw_wave(
764                                         gui->pane[item->pane_number]->canvas,
765                                         item->x,
766                                         high,
767                                         low);
768                         if(timer->get_difference() > 250 || item->last)
769                         {
770                                 mwindow->gui->update(0, IGNORE_THREAD, 0, 0, 0, 0, 0);
771                                 timer->update();
772                         }
773                 }
774         }
775         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
776
777         mwindow->gui->unlock_window();
778
779 }
780
781
782
783