4 * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
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.
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.
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
24 #include "bcsignals.h"
28 #include "condition.h"
31 #include "edlsession.h"
33 #include "framecache.h"
36 #include "mwindowgui.h"
37 #include "renderengine.h"
38 #include "resourcethread.h"
39 #include "resourcepixmap.h"
41 #include "timelinepane.h"
42 #include "trackcanvas.h"
43 #include "transportque.h"
46 #include "wavecache.h"
51 ResourceThreadItem::ResourceThreadItem(ResourcePixmap *pixmap,
57 this->pane_number = pane_number;
58 this->data_type = data_type;
59 this->pixmap = pixmap;
60 this->indexable = indexable;
62 // Assets are garbage collected so they don't need to be replicated.
63 this->operation_count = operation_count;
64 indexable->Garbage::add_user();
68 ResourceThreadItem::~ResourceThreadItem()
70 indexable->Garbage::remove_user();
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)
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;
90 VResourceThreadItem::~VResourceThreadItem()
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)
102 this->channel = channel;
107 AResourceThreadItem::~AResourceThreadItem()
111 ResourceAudioThread::ResourceAudioThread(ResourceThread *resource_thread)
112 : ResourceThreadBase(resource_thread)
114 this->resource_thread = resource_thread;
118 for(int i = 0; i < MAXCHANNELS; i++)
126 ResourceAudioThread::~ResourceAudioThread()
129 for(int i = 0; i < MAXCHANNELS; i++)
130 delete temp_buffer[i];
132 if( audio_asset ) audio_asset->remove_user();
135 void ResourceAudioThread::start_draw()
140 ResourceThreadItem *item = items.last;
141 // Tag last audio item to cause refresh.
142 if( item ) item->last = 1;
144 ResourceThreadBase::start_draw();
147 File *ResourceAudioThread::get_audio_source(Asset *asset)
149 MWindow *mwindow = resource_thread->mwindow;
150 if( interrupted ) asset = 0;
152 if( audio_asset && audio_asset != asset && (!asset ||
153 strcmp(audio_asset->path, asset->path)) ) {
154 mwindow->audio_cache->check_in(audio_asset);
156 audio_asset->remove_user();
160 if( !audio_asset && asset ) {
162 audio_asset->add_user();
163 audio_source = mwindow->audio_cache->check_out(asset, mwindow->edl);
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)
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));
180 ResourceVideoThread::ResourceVideoThread(ResourceThread *resource_thread)
181 : ResourceThreadBase(resource_thread)
183 this->resource_thread = resource_thread;
189 ResourceVideoThread::~ResourceVideoThread()
193 if( video_asset ) video_asset->remove_user();
196 File *ResourceVideoThread::get_video_source(Asset *asset)
198 MWindow *mwindow = resource_thread->mwindow;
199 if( interrupted ) asset = 0;
201 if( video_asset && video_asset != asset && (!asset ||
202 strcmp(video_asset->path, asset->path)) ) {
203 mwindow->video_cache->check_in(video_asset);
205 video_asset->remove_user();
209 if( !video_asset && asset ) {
211 video_asset->add_user();
212 video_source = mwindow->video_cache->check_out(asset, mwindow->edl);
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)
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));
232 ResourceThreadBase::ResourceThreadBase(ResourceThread *resource_thread)
235 this->resource_thread = resource_thread;
238 draw_lock = new Condition(0, "ResourceThreadBase::draw_lock", 0);
239 item_lock = new Mutex("ResourceThreadBase::item_lock");
241 render_engine_id = -1;
245 ResourceThreadBase::~ResourceThreadBase()
250 delete render_engine;
253 void ResourceThreadBase::create_objects()
259 void ResourceThreadBase::reset(int pane_number)
261 item_lock->lock("ResourceThreadBase::reset");
262 ResourceThreadItem *item = items.first;
264 ResourceThreadItem *next_item = item->next;
265 if( item->pane_number == pane_number ) delete item;
271 void ResourceThreadBase::stop_draw(int reset)
275 item_lock->lock("ResourceThreadBase::stop_draw");
276 //printf("ResourceThreadBase::stop_draw %d %d\n", __LINE__, reset);
277 //BC_Signals::dump_stack();
279 ResourceThreadItem *item;
280 while( (item=items.last) != 0 ) delete item;
286 void ResourceThreadBase::start_draw()
292 void ResourceThreadBase::run()
294 MWindow *mwindow = resource_thread->mwindow;
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);
306 mwindow->age_caches();
310 void ResourceThreadBase::stop()
321 void ResourceThreadBase::open_render_engine(EDL *nested_edl,
322 int do_audio, int do_video)
324 if( render_engine && render_engine_id != nested_edl->id ) {
325 delete render_engine; render_engine = 0;
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);
343 void ResourceThreadBase::close_render_engine()
345 delete render_engine; render_engine = 0;
346 render_engine_id = -1;
349 void ResourceVideoThread::draw_item(ResourceThreadItem *item)
351 do_video((VResourceThreadItem *)item);
354 void ResourceVideoThread::do_video(VResourceThreadItem *item)
359 int source_cmodel = -1;
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;
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;
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);
378 // Search frame cache again.
380 VFrame *picon_frame = 0;
381 int need_conversion = 0;
384 MWindow *mwindow = resource_thread->mwindow;
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);
393 temp_picon2->copy_from(picon_frame);
394 // Unlock the get_frame_ptr command
395 mwindow->frame_cache->unlock();
397 else if( !item->indexable->is_asset ) {
398 nested_edl = (EDL*)item->indexable;
399 open_render_engine(nested_edl, 0, 1);
401 int64_t source_position = (int64_t)(item->position *
402 nested_edl->session->frame_rate /
404 if(render_engine->vrender)
405 render_engine->vrender->process_buffer(
413 asset = (Asset*)item->indexable;
414 File *source = get_video_source(asset);
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);
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);
435 if(interrupted) return;
437 MWindowGUI *gui = mwindow->gui;
439 gui->lock_window("ResourceThreadBase::do_video");
440 // It was interrupted while waiting.
442 gui->unlock_window();
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 );
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);
460 gui->unlock_window();
464 #define BUFFERSIZE 65536
465 void ResourceAudioThread::draw_item(ResourceThreadItem *item)
467 do_audio((AResourceThreadItem *)item);
470 void ResourceAudioThread::do_audio(AResourceThreadItem *item)
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);
479 high = wave_item->high;
480 low = wave_item->low;
481 mwindow->wave_cache->unlock();
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();
491 for( int64_t sample=start; sample<end; ++sample ) {
492 // Get value from previous 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
500 audio_buffer = new Samples(BUFFERSIZE);
501 buffer_samples = audio_buffer->get_data();
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;
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);
517 else if( i >= source_channels && temp_buffer[i] ) {
518 delete temp_buffer[i]; temp_buffer[i] = 0;
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));
528 if(fragment > 0) bzero(buffer_samples, sizeof(double) * fragment);
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);
540 audio_asset_id = item->indexable->id;
541 audio_channel = item->channel;
542 audio_start = sample;
543 audio_samples = fragment;
545 double value = buffer_samples[sample - audio_start];
551 if( value > high ) high = value;
552 else if( value < low ) low = value;
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);
562 if(interrupted) return;
564 MWindowGUI *gui = mwindow->gui;
566 gui->lock_window("ResourceThreadBase::do_audio");
568 gui->unlock_window();
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 );
578 if( prev_x == item->x-1 ) {
579 high = MAX(high, prev_l);
580 low = MIN(low, prev_h);
585 if( gui->pane[item->pane_number] )
586 item->pixmap->draw_wave(
587 gui->pane[item->pane_number]->canvas,
589 if( timer->get_difference() > 250 || item->last ) {
590 gui->update(0, IGNORE_THREAD, 0, 0, 0, 0, 0);
596 gui->unlock_window();
599 ResourceThread::ResourceThread(MWindow *mwindow)
601 this->mwindow = mwindow;
608 ResourceThread::~ResourceThread()
614 void ResourceThread::create_objects()
616 audio_thread = new ResourceAudioThread(this);
617 audio_thread->create_objects();
618 video_thread = new ResourceVideoThread(this);
619 video_thread->create_objects();
622 void ResourceThread::stop_draw(int reset)
626 audio_thread->stop_draw(reset);
627 video_thread->stop_draw(reset);
632 void ResourceThread::start_draw()
636 audio_thread->start_draw();
637 video_thread->start_draw();
641 // Be sure to stop_draw before changing the asset table,
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)
648 video_thread->add_picon(pixmap, pane_number,
649 picon_x, picon_y, picon_w, picon_h,
650 frame_rate, position, layer, indexable);
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)
658 audio_thread->add_wave(pixmap, pane_number,
659 indexable, x, channel, source_start, source_end);
662 void ResourceThread::run()
668 void ResourceThread::stop()
670 audio_thread->stop();
671 video_thread->stop();
674 void ResourceThread::reset(int pane_number, int indexes_only)
676 audio_thread->reset(pane_number);
678 video_thread->reset(pane_number);
681 void ResourceThread::close_indexable(Indexable *idxbl)
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();