4 * Copyright (C) 2008 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
23 #include "bcsignals.h"
26 #include "condition.h"
28 #include "edlsession.h"
29 #include "localsession.h"
33 #include "mwindowgui.h"
36 #include "playbackengine.h"
37 #include "playtransport.h"
38 #include "preferences.h"
39 #include "renderengine.h"
40 #include "mainsession.h"
41 #include "trackcanvas.h"
42 #include "transportque.h"
46 PlaybackEngine::PlaybackEngine(MWindow *mwindow, Canvas *output)
49 this->mwindow = mwindow;
50 this->output = output;
52 tracking_position = 0;
56 command = new TransportCommand();
57 command->command = STOP;
58 next_command = new TransportCommand();
59 next_command->change_type = CHANGE_ALL;
60 stop_command = new TransportCommand();
61 stop_command->command = STOP;
62 stop_command->realtime = 1;
63 sent_command = new TransportCommand();
64 sent_command->command = -1;
66 tracking_lock = new Mutex("PlaybackEngine::tracking_lock");
67 renderengine_lock = new Mutex("PlaybackEngine::renderengine_lock");
68 tracking_done = new Condition(1, "PlaybackEngine::tracking_done");
69 pause_lock = new Condition(0, "PlaybackEngine::pause_lock");
70 start_lock = new Condition(0, "PlaybackEngine::start_lock");
71 input_lock = new Condition(1, "PlaybackEngine::input_lock");
72 output_lock = new Condition(0, "PlaybackEngine::output_lock", 1);
78 PlaybackEngine::~PlaybackEngine()
81 output_lock->unlock();
84 delete_render_engine();
91 delete renderengine_lock;
100 void PlaybackEngine::create_objects()
102 preferences = new Preferences;
103 preferences->copy_from(mwindow->preferences);
107 start_lock->lock("PlaybackEngine::create_objects");
110 ChannelDB* PlaybackEngine::get_channeldb()
112 PlaybackConfig *config = command->get_edl()->session->playback_config;
113 switch(config->vconfig->driver)
115 case VIDEO4LINUX2JPEG:
116 return mwindow->channeldb_v4l2jpeg;
121 int PlaybackEngine::create_render_engine()
123 // Fix playback configurations
124 delete_render_engine();
125 render_engine = new RenderEngine(this, preferences, output, 0);
126 //printf("PlaybackEngine::create_render_engine %d\n", __LINE__);
130 void PlaybackEngine::delete_render_engine()
132 renderengine_lock->lock("PlaybackEngine::delete_render_engine");
133 if( render_engine ) {
134 render_engine->interrupt_playback();
135 render_engine->wait_done();
136 delete render_engine; render_engine = 0;
138 renderengine_lock->unlock();
141 void PlaybackEngine::arm_render_engine()
144 render_engine->arm_command(command);
147 void PlaybackEngine::start_render_engine()
150 render_engine->start_command();
153 void PlaybackEngine::wait_render_engine()
155 if( command->realtime && render_engine ) {
156 render_engine->join();
160 void PlaybackEngine::create_cache()
162 if(audio_cache) { delete audio_cache; audio_cache = 0; }
163 if(video_cache) { delete video_cache; video_cache = 0; }
164 if(!audio_cache) audio_cache = new CICache(preferences);
165 if(!video_cache) video_cache = new CICache(preferences);
169 void PlaybackEngine::perform_change()
171 switch( command->change_type ) {
175 create_render_engine();
178 render_engine->get_edl()->synchronize_params(command->get_edl());
184 void PlaybackEngine::sync_parameters(EDL *edl)
186 // TODO: lock out render engine from keyframe deletions
187 command->get_edl()->synchronize_params(edl);
189 render_engine->get_edl()->synchronize_params(edl);
192 void PlaybackEngine::interrupt_playback(int wait_tracking)
194 renderengine_lock->lock("PlaybackEngine::interrupt_playback");
196 render_engine->interrupt_playback();
197 renderengine_lock->unlock();
200 pause_lock->unlock();
202 // Wait for tracking to finish if it is running
203 if( wait_tracking ) {
204 tracking_done->lock("PlaybackEngine::interrupt_playback");
205 tracking_done->unlock();
209 // Return 1 if levels exist
210 int PlaybackEngine::get_output_levels(double *levels, long position)
213 if( render_engine && render_engine->do_audio ) {
214 render_engine->get_output_levels(levels, position);
221 int PlaybackEngine::get_module_levels(ArrayList<double> *module_levels, long position)
224 if( render_engine && render_engine->do_audio ) {
225 render_engine->get_module_levels(module_levels, position);
231 int PlaybackEngine::brender_available(long position)
236 void PlaybackEngine::init_cursor(int active)
240 void PlaybackEngine::init_meters()
244 void PlaybackEngine::stop_cursor()
249 void PlaybackEngine::init_tracking()
251 tracking_active = !command->single_frame() ? 1 : 0;
252 tracking_position = command->playbackstart;
253 tracking_done->lock("PlaybackEngine::init_tracking");
254 init_cursor(tracking_active);
258 void PlaybackEngine::stop_tracking()
262 tracking_done->unlock();
265 void PlaybackEngine::update_tracking(double position)
267 tracking_lock->lock("PlaybackEngine::update_tracking");
268 tracking_position = position;
269 // Signal that the timer is accurate.
270 if(tracking_active) tracking_active = 2;
271 tracking_timer.update();
272 tracking_lock->unlock();
275 double PlaybackEngine::get_tracking_position()
279 tracking_lock->lock("PlaybackEngine::get_tracking_position");
282 // Adjust for elapsed time since last update_tracking.
283 // But tracking timer isn't accurate until the first update_tracking
285 if(tracking_active == 2)
287 //printf("PlaybackEngine::get_tracking_position %d %d %d\n", command->get_direction(), tracking_position, tracking_timer.get_scaled_difference(command->get_edl()->session->sample_rate));
290 // Don't interpolate when every frame is played.
291 if( command->get_edl()->session->video_every_frame &&
292 render_engine && render_engine->do_video ) {
293 result = tracking_position;
298 double loop_start, loop_end;
299 int play_loop = command->loop_play ? 1 : 0;
300 EDL *edl = command->get_edl();
301 int loop_playback = edl->local_session->loop_playback ? 1 : 0;
302 if( play_loop || !loop_playback ) {
303 loop_start = command->start_position;
304 loop_end = command->end_position;
307 loop_start = edl->local_session->loop_start;
308 loop_end = edl->local_session->loop_end;
311 double loop_size = loop_end - loop_start;
313 if( command->get_direction() == PLAY_FORWARD ) {
315 result = tracking_position +
316 command->get_speed() *
317 tracking_timer.get_difference() /
320 // Compensate for loop
321 //printf("PlaybackEngine::get_tracking_position 1 %d\n", command->get_edl()->local_session->loop_playback);
322 if( play_loop && loop_size > 0 ) {
323 while( result > loop_end ) result -= loop_size;
328 result = tracking_position -
329 command->get_speed() *
330 tracking_timer.get_difference() /
333 // Compensate for loop
334 if( play_loop && loop_size > 0 ) {
335 while( result < loop_start ) result += loop_size;
342 result = tracking_position;
344 tracking_lock->unlock();
345 //printf("PlaybackEngine::get_tracking_position %f %f %d\n", result, tracking_position, tracking_active);
352 void PlaybackEngine::update_transport(int command, int paused)
354 // mwindow->gui->lock_window();
355 // mwindow->gui->mbuttons->transport->update_gui_state(command, paused);
356 // mwindow->gui->unlock_window();
359 void PlaybackEngine::run()
361 start_lock->unlock();
364 // Wait for current command to finish
365 output_lock->lock("PlaybackEngine::run");
367 // Read the new command
368 input_lock->lock("PlaybackEngine::run");
369 command->copy_from(sent_command);
370 //printf("sent command=%d\n", sent_command->command);
371 int active = this->send_active;
372 this->send_active = 0;
373 input_lock->unlock();
374 if( !active ) continue;
376 interrupt_playback(0);
377 wait_render_engine();
379 switch( command->command ) {
380 // Parameter change only
387 pause_lock->lock("PlaybackEngine::run");
399 // Dispatch the command
400 start_render_engine();
403 case SINGLE_FRAME_FWD:
404 case SINGLE_FRAME_REWIND:
411 // Start tracking after arming so the tracking position doesn't change.
412 // The tracking for a single frame command occurs during PAUSE
414 if( !command->single_frame() )
416 // Dispatch the command
417 start_render_engine();
420 //printf("PlaybackEngine::run 100\n");
424 void PlaybackEngine::clear_output()
426 BC_WindowBase *cwdw = output->get_canvas();
428 cwdw->lock_window("PlaybackEngine::clear_output");
430 cwdw->unlock_window();
433 void PlaybackEngine::stop_playback(int wait_tracking)
435 transport_stop(wait_tracking);
436 renderengine_lock->lock("PlaybackEngine::stop_playback");
437 if( render_engine ) {
438 render_engine->interrupt_playback();
439 render_engine->wait_done();
441 renderengine_lock->unlock();
445 void PlaybackEngine::send_command(int command, EDL *edl, int wait_tracking, int use_inout)
447 //printf("PlaybackEngine::send_command 1 %d\n", command);
448 // Stop requires transferring the output buffer to a refresh buffer.
450 int curr_command = is_playing_back ? this->command->command : STOP;
451 int curr_single_frame = TransportCommand::single_frame(curr_command);
452 int curr_audio = this->command->toggle_audio ?
453 !curr_single_frame : curr_single_frame;
454 int single_frame = TransportCommand::single_frame(command);
455 int next_audio = next_command->toggle_audio ? !single_frame : single_frame;
456 float next_speed = next_command->speed;
460 case FAST_REWIND: // Commands that play back
463 case SINGLE_FRAME_REWIND:
464 case SINGLE_FRAME_FWD:
470 // run shuttle as no prev command
471 if( next_speed ) curr_command = COMMAND_NONE;
472 // Same direction pressed twice, not shuttle, and no change in audio state, Stop
473 if( curr_command == command && !curr_single_frame &&
474 curr_audio == next_audio ) { do_stop = 1; break; }
476 // Resume or change direction
477 switch( curr_command ) {
480 next_command->resume = 1;
484 case SINGLE_FRAME_FWD:
485 case SINGLE_FRAME_REWIND:
488 next_command->realtime = 1;
489 // Start from scratch
490 transport_command(command, CHANGE_NONE, edl, use_inout);
495 // Commands that stop
504 transport_stop(wait_tracking);
508 int PlaybackEngine::put_command(TransportCommand *command, int reset)
510 input_lock->lock("PlaybackEngine::put_command");
511 int prev_change_type = sent_command->change_type;
512 sent_command->copy_from(command);
513 // run only last command, sum change type
515 sent_command->change_type |= prev_change_type;
517 if( reset ) command->reset();
518 output_lock->unlock();
519 input_lock->unlock();
523 int PlaybackEngine::transport_stop(int wait_tracking)
525 put_command(stop_command, 0);
526 if( wait_tracking ) {
527 tracking_done->lock("PlaybackEngine::transport_stop");
528 tracking_done->unlock();
530 //printf("send: %d (STOP) 0\n", STOP);
534 int PlaybackEngine::transport_command(int command, int change_type, EDL *new_edl, int use_inout)
536 next_command->command = command;
537 next_command->change_type |= change_type;
539 // Just change the EDL if the change requires it because renderengine
540 // structures won't point to the new EDL otherwise and because copying the
541 // EDL for every cursor movement is slow.
542 if( change_type == CHANGE_EDL || change_type == CHANGE_ALL )
543 next_command->get_edl()->copy_all(new_edl);
544 else if( change_type == CHANGE_PARAMS )
545 next_command->get_edl()->synchronize_params(new_edl);
546 next_command->set_playback_range(new_edl, use_inout,
547 preferences->forward_render_displacement);
549 put_command(next_command, 1);
550 //static const char *types[] = { "NONE",
551 // "FRAME_FWD", "NORMAL_FWD", "FAST_FWD", "FRAME_REV", "NORMAL_REV", "FAST_REV",
552 // "STOP", "PAUSE", "SLOW_FWD", "SLOW_REV", "REWIND", "GOTO_END", "CURRENT_FRAME",
554 //printf("send= %d (%s) %d\n", sent_command->command,
555 // types[sent_command->command], sent_command->locked);
559 void PlaybackEngine::refresh_frame(int change_type, EDL *edl, int dir)
561 int command = dir >= 0 ? CURRENT_FRAME : LAST_FRAME;
562 next_command->realtime = 1;
563 transport_command(command, change_type, edl);