Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / playbackengine.C
index 16f6bd7a73c65bda3ffd6065186063eefe206f58..815e506fa9dd684eb2e13427b2baf3fb3098739a 100644 (file)
@@ -22,6 +22,7 @@
 #include "bchash.h"
 #include "bcsignals.h"
 #include "cache.h"
+#include "canvas.h"
 #include "condition.h"
 #include "edl.h"
 #include "edlsession.h"
@@ -32,6 +33,7 @@
 #include "mwindowgui.h"
 #include "patchbay.h"
 #include "tracking.h"
+#include "tracks.h"
 #include "playbackengine.h"
 #include "playtransport.h"
 #include "preferences.h"
@@ -39,6 +41,8 @@
 #include "mainsession.h"
 #include "trackcanvas.h"
 #include "transportque.h"
+#include "videodevice.h"
+#include "vdevicex11.h"
 #include "vrender.h"
 
 
@@ -52,20 +56,22 @@ PlaybackEngine::PlaybackEngine(MWindow *mwindow, Canvas *output)
        tracking_active = 0;
        audio_cache = 0;
        video_cache = 0;
-       command = new TransportCommand();
+       command = new TransportCommand(mwindow->preferences);
        command->command = STOP;
-       next_command = new TransportCommand();
+       next_command = new TransportCommand(mwindow->preferences);
        next_command->change_type = CHANGE_ALL;
-       stop_command = new TransportCommand();
+       stop_command = new TransportCommand(mwindow->preferences);
        stop_command->command = STOP;
        stop_command->realtime = 1;
-       sent_command = new TransportCommand();
+       sent_command = new TransportCommand(mwindow->preferences);
        sent_command->command = -1;
+       send_active = 0;
        tracking_lock = new Mutex("PlaybackEngine::tracking_lock");
        renderengine_lock = new Mutex("PlaybackEngine::renderengine_lock");
        tracking_done = new Condition(1, "PlaybackEngine::tracking_done");
        pause_lock = new Condition(0, "PlaybackEngine::pause_lock");
        start_lock = new Condition(0, "PlaybackEngine::start_lock");
+       cache_lock = new Mutex("PlaybackEngine::cache_lock");
        input_lock = new Condition(1, "PlaybackEngine::input_lock");
        output_lock = new Condition(0, "PlaybackEngine::output_lock", 1);
 
@@ -78,14 +84,17 @@ PlaybackEngine::~PlaybackEngine()
        done = 1;
        output_lock->unlock();
        Thread::join();
-       delete preferences;
        delete_render_engine();
-       delete audio_cache;
-       delete video_cache;
+       delete preferences;
+       if( audio_cache )
+               audio_cache->remove_user();
+       if( video_cache )
+               video_cache->remove_user();
        delete tracking_lock;
        delete tracking_done;
        delete pause_lock;
        delete start_lock;
+       delete cache_lock;
        delete renderengine_lock;
        delete command;
        delete next_command;
@@ -128,20 +137,28 @@ int PlaybackEngine::create_render_engine()
 void PlaybackEngine::delete_render_engine()
 {
        renderengine_lock->lock("PlaybackEngine::delete_render_engine");
-       delete render_engine;  render_engine = 0;
+       if( render_engine ) {
+               render_engine->interrupt_playback();
+               render_engine->wait_done();
+               delete render_engine;  render_engine = 0;
+       }
        renderengine_lock->unlock();
 }
 
 void PlaybackEngine::arm_render_engine()
 {
+       renderengine_lock->lock("PlaybackEngine::arm_render_engine");
        if( render_engine )
                render_engine->arm_command(command);
+       renderengine_lock->unlock();
 }
 
 void PlaybackEngine::start_render_engine()
 {
+       renderengine_lock->lock("PlaybackEngine::start_render_engine");
        if( render_engine )
                render_engine->start_command();
+       renderengine_lock->unlock();
 }
 
 void PlaybackEngine::wait_render_engine()
@@ -153,24 +170,35 @@ void PlaybackEngine::wait_render_engine()
 
 void PlaybackEngine::create_cache()
 {
-       if(audio_cache) { delete audio_cache;  audio_cache = 0; }
-       if(video_cache) { delete video_cache;  video_cache = 0; }
-       if(!audio_cache) audio_cache = new CICache(preferences);
-       if(!video_cache) video_cache = new CICache(preferences);
+       cache_lock->lock("PlaybackEngine::create_cache");
+       if( audio_cache )
+               audio_cache->remove_user();
+       if( video_cache )
+               video_cache->remove_user();
+       audio_cache = new CICache(preferences);
+       video_cache = new CICache(preferences);
+       cache_lock->unlock();
 }
 
 
 void PlaybackEngine::perform_change()
 {
        switch( command->change_type ) {
-               case CHANGE_ALL:
-                       create_cache();
-               case CHANGE_EDL:
-                       create_render_engine();
-                       break;
-               case CHANGE_PARAMS:
-                       render_engine->get_edl()->synchronize_params(command->get_edl());
-               case CHANGE_NONE:
+       case CHANGE_ALL:
+               create_cache();
+       case CHANGE_EDL:
+               create_render_engine();
+               break;
+       case CHANGE_PARAMS: {
+               renderengine_lock->lock("PlaybackEngine::perform_change");
+               EDL *edl = render_engine ? render_engine->get_edl() : 0;
+               if( edl ) edl->add_user();
+               renderengine_lock->unlock();
+               if( !edl ) break;
+               edl->synchronize_params(command->get_edl());
+               edl->remove_user();
+               }
+       case CHANGE_NONE:
                        break;
        }
 }
@@ -249,8 +277,9 @@ void PlaybackEngine::init_tracking()
        init_meters();
 }
 
-void PlaybackEngine::stop_tracking()
+void PlaybackEngine::stop_tracking(double position)
 {
+       tracking_position = position;
        tracking_active = 0;
        stop_cursor();
        tracking_done->unlock();
@@ -358,10 +387,14 @@ void PlaybackEngine::run()
 // Wait for current command to finish
                output_lock->lock("PlaybackEngine::run");
                if( done ) break;
-//printf("sent command=%d\n", sent_command->command);
 // Read the new command
+               input_lock->lock("PlaybackEngine::run");
                command->copy_from(sent_command);
+//printf("sent command=%d\n", sent_command->command);
+               int active = this->send_active;
+               this->send_active = 0;
                input_lock->unlock();
+               if( !active ) continue;
 
                interrupt_playback(0);
                wait_render_engine();
@@ -389,19 +422,18 @@ void PlaybackEngine::run()
 // Dispatch the command
                        start_render_engine();
                        break;
-
-               case SINGLE_FRAME_FWD:
-               case SINGLE_FRAME_REWIND:
 // fall through
                default:
                        is_playing_back = 1;
+               case REWIND:
+               case GOTO_END:
                        perform_change();
                        arm_render_engine();
 
 // Start tracking after arming so the tracking position doesn't change.
 // The tracking for a single frame command occurs during PAUSE
                        init_tracking();
-
+                       clear_borders();
 // Dispatch the command
                        start_render_engine();
                        break;
@@ -410,31 +442,63 @@ void PlaybackEngine::run()
        }
 }
 
+void PlaybackEngine::clear_borders()
+{
+       EDL *edl = command->get_edl();
+       PlaybackConfig *config = edl->session->playback_config;
+       if( config->vconfig->driver == PLAYBACK_X11_GL ) {
+               if( render_engine && render_engine->video ) {
+                       VDeviceBase *vdriver = render_engine->video->get_output_base();
+                       ((VDeviceX11*)vdriver)->clear_output();
+                       return;
+               }
+       }
+       BC_WindowBase *window = output->get_canvas();
+       if( !window ) return;
+       window->lock_window("PlaybackEngine::clear_output");
+       output->clear_borders(edl);
+       window->unlock_window();
+}
 
 void PlaybackEngine::stop_playback(int wait_tracking)
 {
        transport_stop(wait_tracking);
        renderengine_lock->lock("PlaybackEngine::stop_playback");
-       if( render_engine )
+       if( render_engine ) {
+               render_engine->interrupt_playback();
                render_engine->wait_done();
+       }
        renderengine_lock->unlock();
 }
 
+int PlaybackEngine::get_direction()
+{
+       int curr_command = is_playing_back ? this->command->command : STOP;
+       return TransportCommand::get_direction(curr_command);
+}
+
+void PlaybackEngine::update_preferences(Preferences *prefs)
+{
+       preferences->copy_from(prefs);
+       create_render_engine();
+}
 
 void PlaybackEngine::send_command(int command, EDL *edl, int wait_tracking, int use_inout)
 {
 //printf("PlaybackEngine::send_command 1 %d\n", command);
 // Stop requires transferring the output buffer to a refresh buffer.
-       int do_stop = 0, do_resume = 0;
-       int curr_command = this->command->command;
+       int curr_command = is_playing_back ? this->command->command : STOP;
        int curr_single_frame = TransportCommand::single_frame(curr_command);
        int curr_audio = this->command->toggle_audio ?
                !curr_single_frame : curr_single_frame;
        int single_frame = TransportCommand::single_frame(command);
        int next_audio = next_command->toggle_audio ? !single_frame : single_frame;
-
+       float next_speed = next_command->speed;
 // Dispatch command
        switch( command ) {
+       case STOP:
+               transport_stop(wait_tracking);
+               break;
        case FAST_REWIND:       // Commands that play back
        case NORMAL_REWIND:
        case SLOW_REWIND:
@@ -445,56 +509,56 @@ void PlaybackEngine::send_command(int command, EDL *edl, int wait_tracking, int
        case FAST_FWD:
        case CURRENT_FRAME:
        case LAST_FRAME:
-               if( curr_command == command && !next_command->speed &&
-                   !curr_single_frame && curr_audio == next_audio ) {
+// run shuttle as no prev command
+               if( next_speed ) curr_command = COMMAND_NONE;
 // Same direction pressed twice, not shuttle, and no change in audio state,  Stop
-                       do_stop = 1;
+               if( curr_command == command && !curr_single_frame &&
+                   curr_audio == next_audio ) {
+                       transport_stop(wait_tracking);
                        break;
                }
 // Resume or change direction
                switch( curr_command ) {
-               default:
-                       transport_stop(0);
-                       do_resume = 1;
-// fall through
+               case REWIND:
+               case GOTO_END:
                case STOP:
                case COMMAND_NONE:
                case SINGLE_FRAME_FWD:
                case SINGLE_FRAME_REWIND:
                case CURRENT_FRAME:
                case LAST_FRAME:
-                       next_command->realtime = 1;
-                       next_command->resume = do_resume;
-// Start from scratch
-                       transport_command(command, CHANGE_NONE, edl, use_inout);
+// already stopped
+                       break;
+               default:
+                       transport_stop(0);
+                       next_command->resume = 1;
                        break;
                }
+               next_command->realtime = 1;
+               transport_command(command, CHANGE_NONE, edl, use_inout);
                break;
-
-// Commands that stop
-       case STOP:
        case REWIND:
        case GOTO_END:
-               do_stop = 1;
+               transport_stop(1);
+               next_command->realtime = 1;
+               transport_command(command, CHANGE_NONE, edl, use_inout);
+               stop_tracking(this->command->playbackstart);
                break;
        }
-
-       if( do_stop ) {
-               transport_stop(wait_tracking);
-       }
 }
 
 int PlaybackEngine::put_command(TransportCommand *command, int reset)
 {
-// commands can deadlock updating tracking,meters,clock...
-       int mlocked = mwindow->gui->break_lock();
        input_lock->lock("PlaybackEngine::put_command");
+       int prev_change_type = sent_command->change_type;
        sent_command->copy_from(command);
-       if( reset )
-               command->reset();
+// run only last command, sum change type
+       if( send_active )
+               sent_command->change_type |= prev_change_type;
+       send_active = 1;
+       if( reset ) command->reset();
        output_lock->unlock();
-       if( mlocked )
-               mwindow->gui->lock_window("PlaybackEngine::put_command");
+       input_lock->unlock();
        return 0;
 }
 
@@ -517,15 +581,10 @@ int PlaybackEngine::transport_command(int command, int change_type, EDL *new_edl
 // Just change the EDL if the change requires it because renderengine
 // structures won't point to the new EDL otherwise and because copying the
 // EDL for every cursor movement is slow.
-               switch( change_type ) {
-               case CHANGE_EDL:
-               case CHANGE_ALL:
+               if( change_type == CHANGE_EDL || change_type == CHANGE_ALL )
                        next_command->get_edl()->copy_all(new_edl);
-                       break;
-               case CHANGE_PARAMS:
+               else if( change_type == CHANGE_PARAMS )
                        next_command->get_edl()->synchronize_params(new_edl);
-                       break;
-               }
                next_command->set_playback_range(new_edl, use_inout,
                                preferences->forward_render_displacement);
        }