olaf neophyte and de.po updates, valgrind tweaks, delete green lady, inkscape dpi=96
[goodguy/history.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
178         audio_asset = 0;
179         audio_source = 0;
180         video_asset = 0;
181         video_source = 0;
182 }
183
184 ResourceThread::~ResourceThread()
185 {
186         stop();
187         delete draw_lock;
188         delete item_lock;
189         delete temp_picon;
190         delete temp_picon2;
191         delete audio_buffer;
192         for(int i = 0; i < MAXCHANNELS; i++)
193                 delete temp_buffer[i];
194         delete timer;
195         delete render_engine;
196         if( audio_asset ) audio_asset->remove_user();
197         if( video_asset ) video_asset->remove_user();
198 }
199
200 void ResourceThread::create_objects()
201 {
202         done = 0;
203         Thread::start();
204 }
205
206 void ResourceThread::add_picon(ResourcePixmap *pixmap,
207         int pane_number,
208         int picon_x,
209         int picon_y,
210         int picon_w,
211         int picon_h,
212         double frame_rate,
213         int64_t position,
214         int layer,
215         Indexable *indexable)
216 {
217         item_lock->lock("ResourceThread::item_lock");
218
219         items.append(new VResourceThreadItem(pixmap,
220                 pane_number,
221                 picon_x,
222                 picon_y,
223                 picon_w,
224                 picon_h,
225                 frame_rate,
226                 position,
227                 layer,
228                 indexable,
229                 operation_count));
230         item_lock->unlock();
231 }
232
233 void ResourceThread::add_wave(ResourcePixmap *pixmap,
234         int pane_number,
235         Indexable *indexable,
236         int x,
237         int channel,
238         int64_t source_start,
239         int64_t source_end)
240 {
241         item_lock->lock("ResourceThread::item_lock");
242
243         items.append(new AResourceThreadItem(pixmap,
244                 pane_number,
245                 indexable,
246                 x,
247                 channel,
248                 source_start,
249                 source_end,
250                 operation_count));
251         item_lock->unlock();
252 }
253
254
255
256
257
258
259
260
261
262
263
264 void ResourceThread::stop_draw(int reset)
265 {
266         if(!interrupted)
267         {
268                 interrupted = 1;
269                 item_lock->lock("ResourceThread::stop_draw");
270
271 //printf("ResourceThread::stop_draw %d %d\n", __LINE__, reset);
272 //BC_Signals::dump_stack();
273                 if(reset) items.remove_all_objects();
274                 operation_count++;
275                 item_lock->unlock();
276                 prev_x = -1;
277                 prev_h = 0;
278                 prev_l = 0;
279         }
280 }
281
282 void ResourceThread::start_draw()
283 {
284         interrupted = 0;
285 // Tag last audio item to cause refresh.
286         int i = items.total;
287         while( --i>=0 && items[i]->data_type!=TRACK_AUDIO );
288         if( i >= 0 ) items[i]->last = 1;
289         timer->update();
290         draw_lock->unlock();
291 }
292
293 void ResourceThread::run()
294 {
295         while(!done)
296         {
297
298                 draw_lock->lock("ResourceThread::run");
299                 while(!interrupted)
300                 {
301 // Pull off item
302                         item_lock->lock("ResourceThread::run");
303                         int total_items = items.size();
304                         ResourceThreadItem *item = 0;
305                         if(items.size())
306                         {
307                                 item = items[0];
308                                 items.remove_number(0);
309                         }
310                         item_lock->unlock();
311 //printf("ResourceThread::run %d %d\n", __LINE__, items.size());
312                         if(!total_items) break;
313
314                         switch( item->data_type ) {
315                         case TRACK_VIDEO:
316                                 do_video((VResourceThreadItem*)item);
317                                 break;
318                         case TRACK_AUDIO:
319                                 do_audio((AResourceThreadItem*)item);
320                                 break;
321                         }
322
323                         delete item;
324                 }
325
326                 get_audio_source(0);
327                 get_video_source(0);
328                 mwindow->age_caches();
329         }
330 }
331
332 void ResourceThread::stop()
333 {
334         if( !done ) {
335                 done = 1;
336                 interrupted = 1;
337                 draw_lock->unlock();
338         }
339         join();
340 }
341
342
343 void ResourceThread::open_render_engine(EDL *nested_edl,
344         int do_audio,
345         int do_video)
346 {
347         if(render_engine && render_engine_id != nested_edl->id)
348         {
349                 delete render_engine;
350                 render_engine = 0;
351         }
352
353         if(!render_engine)
354         {
355                 TransportCommand command;
356                 if(do_audio)
357                         command.command = NORMAL_FWD;
358                 else
359                         command.command = CURRENT_FRAME;
360                 command.get_edl()->copy_all(nested_edl);
361                 command.change_type = CHANGE_ALL;
362                 command.realtime = 0;
363                 render_engine = new RenderEngine(0,
364                         mwindow->preferences, 0, 0);
365                 render_engine_id = nested_edl->id;
366                 render_engine->set_vcache(mwindow->video_cache);
367                 render_engine->set_acache(mwindow->audio_cache);
368                 render_engine->arm_command(&command);
369         }
370 }
371
372 File *ResourceThread::get_audio_source(Asset *asset)
373 {
374         if( interrupted ) asset = 0;
375
376         if( audio_asset && audio_asset != asset && (!asset ||
377                 strcmp(audio_asset->path, asset->path)) )
378         {
379                 mwindow->audio_cache->check_in(audio_asset);
380                 audio_source = 0;
381                 audio_asset->remove_user();
382                 audio_asset = 0;
383
384         }
385         if( !audio_asset && asset )
386         {
387                 audio_asset = asset;
388                 audio_asset->add_user();
389                 audio_source = mwindow->audio_cache->check_out(asset, mwindow->edl);
390         }
391         return audio_source;
392 }
393
394 File *ResourceThread::get_video_source(Asset *asset)
395 {
396         if( interrupted ) asset = 0;
397
398         if( video_asset && video_asset != asset && (!asset ||
399                 strcmp(video_asset->path, asset->path)) )
400         {
401                 mwindow->video_cache->check_in(video_asset);
402                 video_source = 0;
403                 video_asset->remove_user();
404                 video_asset = 0;
405
406         }
407         if( !video_asset && asset )
408         {
409                 video_asset = asset;
410                 video_asset->add_user();
411                 video_source = mwindow->video_cache->check_out(asset, mwindow->edl);
412         }
413         return video_source;
414 }
415
416 void ResourceThread::do_video(VResourceThreadItem *item)
417 {
418         int source_w = 0;
419         int source_h = 0;
420         int source_id = -1;
421         int source_cmodel = -1;
422
423         if(item->indexable->is_asset)
424         {
425                 Asset *asset = (Asset*)item->indexable;
426                 source_w = asset->width;
427                 source_h = asset->height;
428                 source_id = asset->id;
429                 source_cmodel = BC_RGB888;
430         }
431         else
432         {
433                 EDL *nested_edl = (EDL*)item->indexable;
434                 source_w = nested_edl->session->output_w;
435                 source_h = nested_edl->session->output_h;
436                 source_id = nested_edl->id;
437                 source_cmodel = nested_edl->session->color_model;
438         }
439
440         if(temp_picon &&
441                 (temp_picon->get_w() != source_w ||
442                 temp_picon->get_h() != source_h ||
443                 temp_picon->get_color_model() != source_cmodel))
444         {
445                 delete temp_picon;
446                 temp_picon = 0;
447         }
448
449         if(!temp_picon)
450         {
451                 temp_picon = new VFrame(0, -1, source_w, source_h, source_cmodel, -1);
452         }
453
454 // Get temporary to copy cached frame to
455         if(temp_picon2 &&
456                 (temp_picon2->get_w() != item->picon_w ||
457                 temp_picon2->get_h() != item->picon_h))
458         {
459                 delete temp_picon2;
460                 temp_picon2 = 0;
461         }
462
463         if(!temp_picon2)
464         {
465                 temp_picon2 = new VFrame( item->picon_w, item->picon_h, BC_RGB888, 0);
466         }
467
468
469
470 // Search frame cache again.
471
472         VFrame *picon_frame = 0;
473         int need_conversion = 0;
474         EDL *nested_edl = 0;
475         Asset *asset = 0;
476
477         picon_frame = mwindow->frame_cache->get_frame_ptr(item->position,
478                 item->layer, item->frame_rate, BC_RGB888,
479                 item->picon_w, item->picon_h, source_id);
480 //printf("search cache %ld,%d,%f,%dx%d,%d = %p\n",
481 //  item->position, item->layer, item->frame_rate,
482 //  item->picon_w, item->picon_h, source_id, picon_frame);
483         if( picon_frame ) {
484                 temp_picon2->copy_from(picon_frame);
485 // Unlock the get_frame_ptr command
486                 mwindow->frame_cache->unlock();
487         }
488         else
489         if(!item->indexable->is_asset)
490         {
491                 nested_edl = (EDL*)item->indexable;
492                 open_render_engine(nested_edl, 0, 1);
493
494                 int64_t source_position = (int64_t)(item->position *
495                         nested_edl->session->frame_rate /
496                         item->frame_rate);
497                 if(render_engine->vrender)
498                         render_engine->vrender->process_buffer(
499                                 temp_picon,
500                                 source_position,
501                                 0);
502
503                 need_conversion = 1;
504         }
505         else
506         {
507                 asset = (Asset*)item->indexable;
508                 File *source = get_video_source(asset);
509                 if(!source)
510                         return;
511
512                 source->set_layer(item->layer);
513                 int64_t normalized_position = (int64_t)(item->position *
514                         asset->frame_rate /
515                         item->frame_rate);
516                 source->set_video_position(normalized_position,
517                         0);
518
519                 source->read_frame(temp_picon);
520
521                 need_conversion = 1;
522         }
523
524         if(need_conversion)
525         {
526                 picon_frame = new VFrame(item->picon_w, item->picon_h, BC_RGB888, 0);
527                 BC_CModels::transfer(picon_frame->get_rows(), temp_picon->get_rows(),
528                         0, 0, 0,  0, 0, 0,
529                         0, 0, temp_picon->get_w(), temp_picon->get_h(),
530                         0, 0, picon_frame->get_w(), picon_frame->get_h(),
531                         source_cmodel, BC_RGB888, 0,
532                         temp_picon->get_bytes_per_line(),
533                         picon_frame->get_bytes_per_line());
534                 temp_picon2->copy_from(picon_frame);
535                 mwindow->frame_cache->put_frame(picon_frame, item->position, item->layer,
536                         mwindow->edl->session->frame_rate, 0, item->indexable);
537         }
538
539 // Allow escape here
540         if(interrupted)
541                 return;
542
543 // Draw the picon
544         mwindow->gui->lock_window("ResourceThread::do_video");
545
546 // It was interrupted while waiting.
547         if(interrupted)
548         {
549                 mwindow->gui->unlock_window();
550                 return;
551         }
552
553
554
555 // Test for pixmap existence first
556         if(item->operation_count == operation_count)
557         {
558                 ArrayList<ResourcePixmap*> &resource_pixmaps = gui->resource_pixmaps;
559                 int i = resource_pixmaps.total;
560                 while( --i >= 0 && resource_pixmaps[i] != item->pixmap );
561                 if( i >= 0 ) {
562                         item->pixmap->draw_vframe(temp_picon2,
563                                 item->picon_x,
564                                 item->picon_y,
565                                 item->picon_w,
566                                 item->picon_h,
567                                 0,
568                                 0);
569
570                         mwindow->gui->update(0, IGNORE_THREAD, 0, 0, 0, 0, 0);
571                 }
572         }
573
574         mwindow->gui->unlock_window();
575 }
576
577
578 #define BUFFERSIZE 65536
579 void ResourceThread::do_audio(AResourceThreadItem *item)
580 {
581 // Search again
582         WaveCacheItem *wave_item;
583         double high = 0;
584         double low = 0;
585         const int debug = 0;
586         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
587         if((wave_item = mwindow->wave_cache->get_wave(item->indexable->id,
588                 item->channel, item->start, item->end)))
589         {
590                 high = wave_item->high;
591                 low = wave_item->low;
592                 mwindow->wave_cache->unlock();
593         }
594         else
595         {
596                 int first_sample = 1;
597                 int64_t start = item->start;
598                 int64_t end = item->end;
599                 if(start == end) end = start + 1;
600                 double *buffer_samples = !audio_buffer ? 0 :
601                         audio_buffer->get_data();
602
603                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
604                 for(int64_t sample = start; sample < end; sample++)
605                 {
606                         double value;
607 // Get value from previous buffer
608                         if(audio_buffer &&
609                                 item->channel == audio_channel &&
610                                 item->indexable->id == audio_asset_id &&
611                                 sample >= audio_start &&
612                                 sample < audio_start + audio_samples)
613                         {
614                                 ;
615                         }
616                         else
617 // Load new buffer
618                         {
619                                 if(!audio_buffer) {
620                                         audio_buffer = new Samples(BUFFERSIZE);
621                                         buffer_samples = audio_buffer->get_data();
622                                 }
623
624                                 int64_t total_samples = item->indexable->get_audio_samples();
625                                 int fragment = BUFFERSIZE;
626                                 if(fragment + sample > total_samples)
627                                         fragment = total_samples - sample;
628
629                                 if(!item->indexable->is_asset)
630                                 {
631                                         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
632                                         open_render_engine((EDL*)item->indexable, 1, 0);
633                                         if(debug) printf("ResourceThread::do_audio %d %p\n", __LINE__, render_engine);
634                                         if(render_engine->arender)
635                                         {
636                                                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
637                                                 int source_channels = item->indexable->get_audio_channels();
638                                                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
639                                                 for(int i = 0; i < MAXCHANNELS; i++)
640                                                 {
641                                                         if(i < source_channels &&
642                                                                 !temp_buffer[i])
643                                                         {
644                                                                 temp_buffer[i] = new Samples(BUFFERSIZE);
645                                                         }
646                                                         else
647                                                         if(i >= source_channels &&
648                                                                 temp_buffer[i])
649                                                         {
650                                                                 delete temp_buffer[i];
651                                                                 temp_buffer[i] = 0;
652                                                         }
653                                                 }
654
655
656                                                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
657                                                 render_engine->arender->process_buffer(
658                                                         temp_buffer,
659                                                         fragment,
660                                                         sample);
661                                                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
662                                                 memcpy(buffer_samples,
663                                                         temp_buffer[item->channel]->get_data(),
664                                                         fragment * sizeof(double));
665                                         }
666                                         else
667                                         {
668                                                 if(debug) printf("ResourceThread::do_audio %d %d\n", __LINE__, fragment);
669                                                 if(fragment > 0) bzero(buffer_samples, sizeof(double) * fragment);
670                                                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
671
672                                         }
673                                 }
674                                 else
675                                 {
676                                         Asset *asset = (Asset*)item->indexable;
677                                         File *source = get_audio_source(asset);
678                                         if(!source)
679                                                 return;
680
681                                         source->set_channel(item->channel);
682                                         source->set_audio_position(sample);
683                                         source->read_samples(audio_buffer, fragment);
684                                 }
685
686                                 audio_asset_id = item->indexable->id;
687                                 audio_channel = item->channel;
688                                 audio_start = sample;
689                                 audio_samples = fragment;
690                         }
691
692
693                         value = buffer_samples[sample - audio_start];
694                         if(first_sample)
695                         {
696                                 high = low = value;
697                                 first_sample = 0;
698                         }
699                         else
700                         {
701                                 if(value > high)
702                                         high = value;
703                                 else
704                                 if(value < low)
705                                         low = value;
706                         }
707                 }
708                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
709
710 // If it's a nested EDL, store all the channels
711                 mwindow->wave_cache->put_wave(item->indexable,
712                         item->channel,
713                         item->start,
714                         item->end,
715                         high,
716                         low);
717                 if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
718         }
719         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
720
721 // Allow escape here
722         if(interrupted)
723                 return;
724
725         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
726 // Draw the column
727         mwindow->gui->lock_window("ResourceThread::do_audio");
728         if(interrupted)
729         {
730                 mwindow->gui->unlock_window();
731                 return;
732         }
733
734         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
735         if(item->operation_count == operation_count)
736         {
737
738 // Test for pixmap existence first
739                 ArrayList<ResourcePixmap*> &resource_pixmaps = gui->resource_pixmaps;
740                 int i = resource_pixmaps.total;
741                 while( --i >= 0 && resource_pixmaps[i] != item->pixmap );
742                 if( i >= 0 )
743                 {
744                         if(prev_x == item->x - 1)
745                         {
746                                 high = MAX(high, prev_l);
747                                 low = MIN(low, prev_h);
748                         }
749                         prev_x = item->x;
750                         prev_h = high;
751                         prev_l = low;
752                         if(gui->pane[item->pane_number])
753                                 item->pixmap->draw_wave(
754                                         gui->pane[item->pane_number]->canvas,
755                                         item->x,
756                                         high,
757                                         low);
758                         if(timer->get_difference() > 250 || item->last)
759                         {
760                                 mwindow->gui->update(0, 3, 0, 0, 0, 0, 0);
761                                 timer->update();
762                         }
763                 }
764         }
765         if(debug) printf("ResourceThread::do_audio %d\n", __LINE__);
766
767         mwindow->gui->unlock_window();
768
769 }
770
771
772
773