Reported by Fedora team for gcc-13 and Andrew created patch here
[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 VResourceThreadItem::VResourceThreadItem(ResourcePixmap *pixmap, int pane_number,
75                 int picon_x, int picon_y, int picon_w, int picon_h,
76                 double frame_rate, int64_t position, int layer,
77                 Indexable *indexable, int operation_count)
78  : ResourceThreadItem(pixmap, pane_number,
79                 indexable, TRACK_VIDEO, operation_count)
80 {
81         this->picon_x = picon_x;
82         this->picon_y = picon_y;
83         this->picon_w = picon_w;
84         this->picon_h = picon_h;
85         this->frame_rate = frame_rate;
86         this->position = position;
87         this->layer = layer;
88 }
89
90 VResourceThreadItem::~VResourceThreadItem()
91 {
92 }
93
94
95 AResourceThreadItem::AResourceThreadItem(ResourcePixmap *pixmap, int pane_number,
96                 Indexable *indexable, int x, int channel,
97                 int64_t start, int64_t end, int operation_count)
98  : ResourceThreadItem(pixmap, pane_number,
99                 indexable, TRACK_AUDIO, operation_count)
100 {
101         this->x = x;
102         this->channel = channel;
103         this->start = start;
104         this->end = end;
105 }
106
107 AResourceThreadItem::~AResourceThreadItem()
108 {
109 }
110
111 ResourceAudioThread::ResourceAudioThread(ResourceThread *resource_thread)
112  : ResourceThreadBase(resource_thread)
113 {
114         this->resource_thread = resource_thread;
115         audio_buffer = 0;
116         audio_asset = 0;
117         audio_source = 0;
118         for(int i = 0; i < MAXCHANNELS; i++)
119                 temp_buffer[i] = 0;
120         timer = new Timer;
121         prev_x = -1;
122         prev_h = 0;
123         prev_l = 0;
124 }
125
126 ResourceAudioThread::~ResourceAudioThread()
127 {
128         delete audio_buffer;
129         for(int i = 0; i < MAXCHANNELS; i++)
130                 delete temp_buffer[i];
131         delete timer;
132         if( audio_asset ) audio_asset->remove_user();
133 }
134
135 void ResourceAudioThread::start_draw()
136 {
137         prev_x = -1;
138         prev_h = 0;
139         prev_l = 0;
140         ResourceThreadItem *item = items.last;
141 // Tag last audio item to cause refresh.
142         if( item ) item->last = 1;
143         timer->update();
144         ResourceThreadBase::start_draw();
145 }
146
147 File *ResourceAudioThread::get_audio_source(Asset *asset)
148 {
149         MWindow *mwindow = resource_thread->mwindow;
150         if( interrupted ) asset = 0;
151
152         if( audio_asset && audio_asset != asset && (!asset ||
153                 strcmp(audio_asset->path, asset->path)) ) {
154                 mwindow->audio_cache->check_in(audio_asset);
155                 audio_source = 0;
156                 audio_asset->remove_user();
157                 audio_asset = 0;
158
159         }
160         if( !audio_asset && asset ) {
161                 audio_asset = asset;
162                 audio_asset->add_user();
163                 audio_source = mwindow->audio_cache->check_out(asset, mwindow->edl);
164         }
165         return audio_source;
166 }
167
168 void ResourceAudioThread::add_wave(ResourcePixmap *pixmap, int pane_number,
169                 Indexable *indexable, int x, int channel,
170                 int64_t source_start, int64_t source_end)
171 {
172         item_lock->lock("ResourceThreadBase::item_lock");
173         items.append(new AResourceThreadItem(pixmap, pane_number,
174                         indexable, x, channel, source_start, source_end,
175                         resource_thread->operation_count));
176         item_lock->unlock();
177 }
178
179
180 ResourceVideoThread::ResourceVideoThread(ResourceThread *resource_thread)
181  : ResourceThreadBase(resource_thread)
182 {
183         this->resource_thread = resource_thread;
184         video_asset = 0;
185         video_source = 0;
186         temp_picon = 0;
187         temp_picon2 = 0;
188 }
189 ResourceVideoThread::~ResourceVideoThread()
190 {
191         delete temp_picon;
192         delete temp_picon2;
193         if( video_asset ) video_asset->remove_user();
194 }
195
196 File *ResourceVideoThread::get_video_source(Asset *asset)
197 {
198         MWindow *mwindow = resource_thread->mwindow;
199         if( interrupted ) asset = 0;
200
201         if( video_asset && video_asset != asset && (!asset ||
202                 strcmp(video_asset->path, asset->path)) ) {
203                 mwindow->video_cache->check_in(video_asset);
204                 video_source = 0;
205                 video_asset->remove_user();
206                 video_asset = 0;
207
208         }
209         if( !video_asset && asset ) {
210                 video_asset = asset;
211                 video_asset->add_user();
212                 video_source = mwindow->video_cache->check_out(asset, mwindow->edl);
213         }
214         return video_source;
215 }
216
217 void ResourceVideoThread::add_picon(ResourcePixmap *pixmap, int pane_number,
218                 int picon_x, int picon_y, int picon_w, int picon_h,
219                 double frame_rate, int64_t position, int layer,
220                 Indexable *indexable)
221 {
222         item_lock->lock("ResourceThreadBase::item_lock");
223         items.append(new VResourceThreadItem(pixmap, pane_number,
224                         picon_x, picon_y, picon_w, picon_h,
225                         frame_rate, position, layer, indexable,
226                         resource_thread->operation_count));
227         item_lock->unlock();
228 }
229
230
231
232 ResourceThreadBase::ResourceThreadBase(ResourceThread *resource_thread)
233  : Thread(1, 0, 0)
234 {
235         this->resource_thread = resource_thread;
236         interrupted = 1;
237         done = 1;
238         draw_lock = new Condition(0, "ResourceThreadBase::draw_lock", 0);
239         item_lock = new Mutex("ResourceThreadBase::item_lock");
240         render_engine = 0;
241         render_engine_id = -1;
242
243 }
244
245 ResourceThreadBase::~ResourceThreadBase()
246 {
247         stop();
248         delete draw_lock;
249         delete item_lock;
250         delete render_engine;
251 }
252
253 void ResourceThreadBase::create_objects()
254 {
255         done = 0;
256         Thread::start();
257 }
258
259 void ResourceThreadBase::reset(int pane_number)
260 {
261         item_lock->lock("ResourceThreadBase::reset");
262         ResourceThreadItem *item = items.first;
263         while( item ) {
264                 ResourceThreadItem *next_item = item->next;
265                 if( item->pane_number == pane_number ) delete item;
266                 item = next_item;
267         }
268         item_lock->unlock();
269 }
270
271 void ResourceThreadBase::stop_draw(int reset)
272 {
273         if( !interrupted ) {
274                 interrupted = 1;
275                 item_lock->lock("ResourceThreadBase::stop_draw");
276 //printf("ResourceThreadBase::stop_draw %d %d\n", __LINE__, reset);
277 //BC_Signals::dump_stack();
278                 if( reset ) {
279                         ResourceThreadItem *item;
280                         while( (item=items.last) != 0 ) delete item;
281                 }
282                 item_lock->unlock();
283         }
284 }
285
286 void ResourceThreadBase::start_draw()
287 {
288         interrupted = 0;
289         draw_lock->unlock();
290 }
291
292 void ResourceThreadBase::run()
293 {
294         MWindow *mwindow = resource_thread->mwindow;
295         while( !done ) {
296                 draw_lock->lock("ResourceThreadBase::run");
297                 while( !interrupted ) {
298                         item_lock->lock("ResourceThreadBase::run");
299                         ResourceThreadItem *item = items.first;
300                         items.remove_pointer(item);
301                         item_lock->unlock();
302                         if( !item ) break;
303                         draw_item(item);
304                         delete item;
305                 }
306                 mwindow->age_caches();
307         }
308 }
309
310 void ResourceThreadBase::stop()
311 {
312         if( !done ) {
313                 done = 1;
314                 interrupted = 1;
315                 draw_lock->unlock();
316         }
317         join();
318 }
319
320
321 void ResourceThreadBase::open_render_engine(EDL *nested_edl,
322                 int do_audio, int do_video)
323 {
324         if( render_engine && render_engine_id != nested_edl->id ) {
325                 delete render_engine;  render_engine = 0;
326         }
327
328         if( !render_engine ) {
329                 MWindow *mwindow = resource_thread->mwindow;
330                 TransportCommand command(mwindow->preferences);
331                 command.command = do_audio ? NORMAL_FWD : CURRENT_FRAME;
332                 command.get_edl()->copy_all(nested_edl);
333                 command.change_type = CHANGE_ALL;
334                 command.realtime = 0;
335                 render_engine = new RenderEngine(0, mwindow->preferences, 0, 0);
336                 render_engine_id = nested_edl->id;
337                 render_engine->set_vcache(mwindow->video_cache);
338                 render_engine->set_acache(mwindow->audio_cache);
339                 render_engine->arm_command(&command);
340         }
341 }
342
343 void ResourceThreadBase::close_render_engine()
344 {
345         delete render_engine;  render_engine = 0;
346         render_engine_id = -1;
347 }
348
349 void ResourceVideoThread::draw_item(ResourceThreadItem *item)
350 {
351         do_video((VResourceThreadItem *)item);
352 }
353
354 void ResourceVideoThread::do_video(VResourceThreadItem *item)
355 {
356         int source_w = 0;
357         int source_h = 0;
358         int source_id = -1;
359         int source_cmodel = -1;
360
361         if( item->indexable->is_asset ) {
362                 Asset *asset = (Asset*)item->indexable;
363                 source_w = asset->width;
364                 source_h = asset->height;
365                 source_id = asset->id;
366                 source_cmodel = BC_RGB888;
367         }
368         else {
369                 EDL *nested_edl = (EDL*)item->indexable;
370                 source_w = nested_edl->session->output_w;
371                 source_h = nested_edl->session->output_h;
372                 source_id = nested_edl->id;
373                 source_cmodel = nested_edl->session->color_model;
374         }
375         VFrame::get_temp(temp_picon, source_w, source_h, source_cmodel);
376         VFrame::get_temp(temp_picon2, item->picon_w, item->picon_h, BC_RGB888);
377
378 // Search frame cache again.
379
380         VFrame *picon_frame = 0;
381         int need_conversion = 0;
382         EDL *nested_edl = 0;
383         Asset *asset = 0;
384         MWindow *mwindow = resource_thread->mwindow;
385
386         picon_frame = mwindow->frame_cache->get_frame_ptr(item->position,
387                 item->layer, item->frame_rate, BC_RGB888,
388                 item->picon_w, item->picon_h, source_id);
389 //printf("search cache %ld,%d,%f,%dx%d,%d = %p\n",
390 //  item->position, item->layer, item->frame_rate,
391 //  item->picon_w, item->picon_h, source_id, picon_frame);
392         if( picon_frame ) {
393                 temp_picon2->copy_from(picon_frame);
394 // Unlock the get_frame_ptr command
395                 mwindow->frame_cache->unlock();
396         }
397         else if( !item->indexable->is_asset ) {
398                 nested_edl = (EDL*)item->indexable;
399                 open_render_engine(nested_edl, 0, 1);
400
401                 int64_t source_position = (int64_t)(item->position *
402                         nested_edl->session->frame_rate /
403                         item->frame_rate);
404                 if(render_engine->vrender)
405                         render_engine->vrender->process_buffer(
406                                 temp_picon,
407                                 source_position,
408                                 0);
409
410                 need_conversion = 1;
411         }
412         else {
413                 asset = (Asset*)item->indexable;
414                 File *source = get_video_source(asset);
415                 if(!source) return;
416
417                 source->set_layer(item->layer);
418                 int64_t normalized_position = (int64_t)(item->position *
419                                 asset->frame_rate / item->frame_rate);
420                 source->set_video_position(normalized_position, 0);
421                 source->read_frame(temp_picon);
422                 get_video_source(0);
423                 need_conversion = 1;
424         }
425
426         if( need_conversion ) {
427                 picon_frame = new VFrame(item->picon_w, item->picon_h, BC_RGB888, 0);
428                 picon_frame->transfer_from(temp_picon);
429                 temp_picon2->copy_from(picon_frame);
430                 mwindow->frame_cache->put_frame(picon_frame, item->position, item->layer,
431                         mwindow->edl->session->frame_rate, 0, item->indexable);
432         }
433
434 // Allow escape here
435         if(interrupted) return;
436
437         MWindowGUI *gui = mwindow->gui;
438 // Draw the picon
439         gui->lock_window("ResourceThreadBase::do_video");
440 // It was interrupted while waiting.
441         if( interrupted ) {
442                 gui->unlock_window();
443                 return;
444         }
445
446 // Test for pixmap existence first
447         if( item->operation_count == resource_thread->operation_count ) {
448                 ArrayList<ResourcePixmap*> &resource_pixmaps = gui->resource_pixmaps;
449                 int i = resource_pixmaps.total;
450                 while( --i >= 0 && resource_pixmaps[i] != item->pixmap );
451                 if( i >= 0 ) {
452                         item->pixmap->draw_vframe(temp_picon2,
453                                 item->picon_x, item->picon_y,
454                                 item->picon_w, item->picon_h, 0, 0);
455                         TimelinePane *pane = gui->pane[item->pane_number];
456                         if( pane ) pane->update(0, IGNORE_THREAD, 0, 0);
457                 }
458         }
459
460         gui->unlock_window();
461 }
462
463
464 #define BUFFERSIZE 65536
465 void ResourceAudioThread::draw_item(ResourceThreadItem *item)
466 {
467         do_audio((AResourceThreadItem *)item);
468 }
469
470 void ResourceAudioThread::do_audio(AResourceThreadItem *item)
471 {
472 // Search again
473         double high = 0, low = 0;
474         MWindow *mwindow = resource_thread->mwindow;
475         WaveCacheItem * wave_item =
476                 mwindow->wave_cache->get_wave(item->indexable->id,
477                         item->channel, item->start, item->end);
478         if( wave_item ) {
479                 high = wave_item->high;
480                 low = wave_item->low;
481                 mwindow->wave_cache->unlock();
482         }
483         else {
484                 int first_sample = 1;
485                 int64_t start = item->start;
486                 int64_t end = item->end;
487                 if(start == end) end = start + 1;
488                 double *buffer_samples = !audio_buffer ? 0 :
489                         audio_buffer->get_data();
490
491                 for( int64_t sample=start; sample<end; ++sample ) {
492 // Get value from previous buffer
493                         if(audio_buffer &&
494                                 item->channel == audio_channel &&
495                                 item->indexable->id == audio_asset_id &&
496                                 sample >= audio_start &&
497                                 sample < audio_start + audio_samples) {}
498                         else { // Load new buffer
499                                 if(!audio_buffer) {
500                                         audio_buffer = new Samples(BUFFERSIZE);
501                                         buffer_samples = audio_buffer->get_data();
502                                 }
503
504                                 int64_t total_samples = item->indexable->get_audio_samples();
505                                 int fragment = BUFFERSIZE;
506                                 if( fragment + sample > total_samples )
507                                         fragment = total_samples - sample;
508
509                                 if( !item->indexable->is_asset ) {
510                                         open_render_engine((EDL*)item->indexable, 1, 0);
511                                         if( render_engine->arender ) {
512                                                 int source_channels = item->indexable->get_audio_channels();
513                                                 for( int i=0; i<MAXCHANNELS; ++i ) {
514                                                         if( i<source_channels && !temp_buffer[i] ) {
515                                                                 temp_buffer[i] = new Samples(BUFFERSIZE);
516                                                         }
517                                                         else if( i >= source_channels && temp_buffer[i] ) {
518                                                                 delete temp_buffer[i];  temp_buffer[i] = 0;
519                                                         }
520                                                 }
521                                                 render_engine->arender->
522                                                         process_buffer( temp_buffer, fragment, sample);
523                                                 memcpy(buffer_samples,
524                                                         temp_buffer[item->channel]->get_data(),
525                                                         fragment * sizeof(double));
526                                         }
527                                         else {
528                                                 if(fragment > 0) bzero(buffer_samples, sizeof(double) * fragment);
529                                         }
530                                 }
531                                 else {
532                                         Asset *asset = (Asset*)item->indexable;
533                                         File *source = get_audio_source(asset);
534                                         if( !source ) return;
535                                         source->set_channel(item->channel);
536                                         source->set_audio_position(sample);
537                                         source->read_samples(audio_buffer, fragment);
538                                         get_audio_source(0);
539                                 }
540                                 audio_asset_id = item->indexable->id;
541                                 audio_channel = item->channel;
542                                 audio_start = sample;
543                                 audio_samples = fragment;
544                         }
545                         double value = buffer_samples[sample - audio_start];
546                         if( first_sample ) {
547                                 high = low = value;
548                                 first_sample = 0;
549                         }
550                         else {
551                                 if( value > high ) high = value;
552                                 else if( value < low ) low = value;
553                         }
554                 }
555
556 // If it's a nested EDL, store all the channels
557                 mwindow->wave_cache->put_wave(item->indexable, item->channel,
558                                 item->start, item->end, high, low);
559         }
560
561 // Allow escape here
562         if(interrupted) return;
563
564         MWindowGUI *gui = mwindow->gui;
565 // Draw the column
566         gui->lock_window("ResourceThreadBase::do_audio");
567         if( interrupted ) {
568                 gui->unlock_window();
569                 return;
570         }
571
572         if( item->operation_count == resource_thread->operation_count ) {
573 // Test for pixmap existence first
574                 ArrayList<ResourcePixmap*> &resource_pixmaps = gui->resource_pixmaps;
575                 int i = resource_pixmaps.total;
576                 while( --i >= 0 && resource_pixmaps[i] != item->pixmap );
577                 if( i >= 0 ) {
578                         if( prev_x == item->x-1 ) {
579                                 high = MAX(high, prev_l);
580                                 low = MIN(low, prev_h);
581                         }
582                         prev_x = item->x;
583                         prev_h = high;
584                         prev_l = low;
585                         if( gui->pane[item->pane_number] )
586                                 item->pixmap->draw_wave(
587                                         gui->pane[item->pane_number]->canvas,
588                                         item->x, high, low);
589                         if( timer->get_difference() > 250 || item->last ) {
590                                 gui->update(0, IGNORE_THREAD, 0, 0, 0, 0, 0);
591                                 timer->update();
592                         }
593                 }
594         }
595
596         gui->unlock_window();
597 }
598
599 ResourceThread::ResourceThread(MWindow *mwindow)
600 {
601         this->mwindow = mwindow;
602         audio_thread = 0;
603         video_thread = 0;
604         interrupted = 1;
605         operation_count = 0;
606 }
607
608 ResourceThread::~ResourceThread()
609 {
610         delete audio_thread;
611         delete video_thread;
612 }
613
614 void ResourceThread::create_objects()
615 {
616         audio_thread = new ResourceAudioThread(this);
617         audio_thread->create_objects();
618         video_thread = new ResourceVideoThread(this);
619         video_thread->create_objects();
620 }
621
622 void ResourceThread::stop_draw(int reset)
623 {
624         if( !interrupted ) {
625                 interrupted = 1;
626                 audio_thread->stop_draw(reset);
627                 video_thread->stop_draw(reset);
628                 ++operation_count;
629         }
630 }
631
632 void ResourceThread::start_draw()
633 {
634         if( interrupted ) {
635                 interrupted = 0;
636                 audio_thread->start_draw();
637                 video_thread->start_draw();
638         }
639 }
640
641 // Be sure to stop_draw before changing the asset table,
642 // closing files.
643 void ResourceThread::add_picon(ResourcePixmap *pixmap, int pane_number,
644                 int picon_x, int picon_y, int picon_w, int picon_h,
645                 double frame_rate, int64_t position, int layer,
646                 Indexable *indexable)
647 {
648         video_thread->add_picon(pixmap, pane_number,
649                 picon_x, picon_y, picon_w, picon_h,
650                 frame_rate, position, layer, indexable);
651 }
652
653 void ResourceThread::add_wave(ResourcePixmap *pixmap, int pane_number,
654                 Indexable *indexable, int x, int channel,
655 // samples relative to asset rate
656                 int64_t source_start, int64_t source_end)
657 {
658         audio_thread->add_wave(pixmap, pane_number,
659                 indexable, x, channel, source_start, source_end);
660 }
661
662 void ResourceThread::run()
663 {
664         audio_thread->run();
665         video_thread->run();
666 }
667
668 void ResourceThread::stop()
669 {
670         audio_thread->stop();
671         video_thread->stop();
672 }
673
674 void ResourceThread::reset(int pane_number, int indexes_only)
675 {
676         audio_thread->reset(pane_number);
677         if( !indexes_only )
678                 video_thread->reset(pane_number);
679 }
680
681 void ResourceThread::close_indexable(Indexable *idxbl)
682 {
683         if( audio_thread && audio_thread->render_engine_id == idxbl->id )
684                 audio_thread->close_render_engine();
685         if( video_thread && video_thread->render_engine_id == idxbl->id )
686                 video_thread->close_render_engine();
687 }
688