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