Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / brender.C
diff --git a/cinelerra-5.1/cinelerra/brender.C b/cinelerra-5.1/cinelerra/brender.C
new file mode 100644 (file)
index 0000000..94a569a
--- /dev/null
@@ -0,0 +1,636 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "asset.h"
+#include "bcsignals.h"
+#include "brender.h"
+#include "clip.h"
+#include "condition.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "language.h"
+#include "mainsession.h"
+#include "mtimebar.h"
+#include "mutex.h"
+#include "mwindowgui.h"
+#include "mwindow.h"
+#include "packagedispatcher.h"
+#include "preferences.h"
+#include "renderfarm.h"
+#include "tracks.h"
+#include "units.h"
+
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+
+
+extern "C"
+{
+#include <uuid/uuid.h>
+}
+
+
+
+
+
+
+BRender::BRender(MWindow *mwindow)
+ : Thread(1, 0, 0)
+{
+       this->mwindow = mwindow;
+       map_lock = new Mutex("BRender::map_lock");
+       completion_lock = new Condition(0, "BRender::completion_lock");
+       timer = new Timer;
+       socket_path[0] = 0;
+       thread = 0;
+       master_pid = -1;
+       arguments[0] = arguments[1] = arguments[2] = 0;
+       map = 0;
+       map_size = 0;
+       map_valid = 0;
+       last_contiguous = 0;
+       set_synchronous(1);
+}
+
+BRender::~BRender()
+{
+TRACE("BRender::~BRender 1\n");
+       if(thread)
+       {
+TRACE("BRender::~BRender 2\n");
+               stop();
+TRACE("BRender::~BRender 3\n");
+               delete thread;
+TRACE("BRender::~BRender 4\n");
+       }
+
+
+TRACE("BRender::~BRender 5\n");
+       if(master_pid >= 0)
+       {
+               kill(master_pid, SIGKILL);
+TRACE("BRender::~BRender 6\n");
+               Thread::join();
+TRACE("BRender::~BRender 7\n");
+       }
+
+TRACE("BRender::~BRender 8\n");
+       delete map_lock;
+TRACE("BRender::~BRender 9\n");
+       delete completion_lock;
+TRACE("BRender::~BRender 10\n");
+UNSET_TEMP(socket_path);
+       remove(socket_path);
+TRACE("BRender::~BRender 11\n");
+       if(arguments[0]) delete [] arguments[0];
+TRACE("BRender::~BRender 12\n");
+       if(arguments[1]) delete [] arguments[1];
+TRACE("BRender::~BRender 13\n");
+       if(arguments[2]) delete [] arguments[2];
+TRACE("BRender::~BRender 14\n");
+       if(map) delete [] map;
+TRACE("BRender::~BRender 15\n");
+       delete timer;
+TRACE("BRender::~BRender 100\n");
+}
+
+void BRender::initialize()
+{
+       timer->update();
+// Create socket for background process.
+       uuid_t socket_temp;
+       sprintf(socket_path, "/tmp/cinelerra.");
+       uuid_generate(socket_temp);
+       uuid_unparse(socket_temp, socket_path + strlen(socket_path));
+SET_TEMP(socket_path);
+
+// Start background instance of executable since codecs aren't reentrant
+       Thread::start();
+
+// Wait for local node to start
+       thread = new BRenderThread(mwindow, this);
+       thread->initialize();
+}
+
+void BRender::run()
+{
+       char string[BCTEXTLEN];
+       FILE *fd;
+//printf("BRender::run 1 %d\n", getpid());
+
+
+// Construct executable command with the designated filesystem port
+       fd = fopen("/proc/self/cmdline", "r");
+       if(fd)
+       {
+               (void)fread(string, 1, BCTEXTLEN, fd);
+               fclose(fd);
+       }
+       else
+               perror(_("BRender::fork_background: can't open /proc/self/cmdline.\n"));
+
+       arguments[0] = new char[strlen(string) + 1];
+       strcpy(arguments[0], string);
+
+       strcpy(string, "-b");
+       arguments[1] = new char[strlen(string) + 1];
+       strcpy(arguments[1], string);
+
+       arguments[2] = new char[strlen(socket_path) + 1];
+       strcpy(arguments[2], socket_path);
+//printf("BRender::fork_background 1 %s\n", socket_path);
+
+       arguments[3] = 0;
+
+       int pid = vfork();
+       if(!pid)
+       {
+               execvp(arguments[0], arguments);
+               perror("BRender::fork_background");
+               _exit(0);
+       }
+
+       master_pid = pid;
+//printf("BRender::fork_background 1 %d\n", master_pid);
+
+
+
+       int return_value;
+       if(waitpid(master_pid, &return_value, WUNTRACED) < 0)
+       {
+               perror("BRender::run waitpid");
+       }
+}
+
+// Give the last position of the EDL which hasn't changed.
+// We copy the EDL and restart rendering at the lesser of position and
+// our position.
+void BRender::restart(EDL *edl)
+{
+//printf("BRender::restart 1\n");
+       BRenderCommand *new_command = new BRenderCommand;
+       map_valid = 0;
+       new_command->copy_edl(edl);
+       new_command->command = BRenderCommand::BRENDER_RESTART;
+//printf("BRender::restart 2\n");
+       thread->send_command(new_command);
+//printf("BRender::restart 3\n");
+// Map should be reallocated before this returns.
+}
+
+void BRender::stop()
+{
+//printf("BRender::stop 1\n");
+       BRenderCommand *new_command = new BRenderCommand;
+//printf("BRender::stop 1\n");
+       new_command->command = BRenderCommand::BRENDER_STOP;
+//printf("BRender::stop 1\n");
+       thread->send_command(new_command);
+//printf("BRender::stop 1\n");
+       completion_lock->lock("BRender::stop");
+//printf("BRender::stop 2\n");
+}
+
+
+
+int BRender::get_last_contiguous(int64_t brender_start)
+{
+       int result;
+       map_lock->lock("BRender::get_last_contiguous");
+       if(map_valid)
+               result = last_contiguous;
+       else
+               result = brender_start;
+       map_lock->unlock();
+       return result;
+}
+
+void BRender::allocate_map(int64_t brender_start, int64_t start, int64_t end)
+{
+       map_lock->lock("BRender::allocate_map");
+       unsigned char *old_map = map;
+       map = new unsigned char[end];
+       if(old_map)
+       {
+               memcpy(map, old_map, start);
+               delete [] old_map;
+       }
+
+// Zero all before brender start
+       bzero(map, brender_start);
+// Zero all after current start
+       bzero(map + start, end - start);
+
+       map_size = end;
+       map_valid = 1;
+       last_contiguous = start;
+       mwindow->session->brender_end = (double)last_contiguous /
+               mwindow->edl->session->frame_rate;
+       map_lock->unlock();
+}
+
+int BRender::set_video_map(int64_t position, int value)
+{
+       int update_gui = 0;
+       map_lock->lock("BRender::set_video_map");
+
+
+       if(value == BRender::NOT_SCANNED)
+       {
+               printf(_("BRender::set_video_map called to set NOT_SCANNED\n"));
+       }
+
+// Preroll
+       if(position < 0)
+       {
+               ;
+       }
+       else
+// In range
+       if(position < map_size)
+       {
+               map[position] = value;
+       }
+       else
+// Obsolete EDL
+       {
+               printf(_("BRender::set_video_map %jd: attempt to set beyond end of map %jd.\n"),
+                       position, map_size);
+       }
+
+// Maintain last contiguous here to reduce search time
+       if(position == last_contiguous)
+       {
+               int i;
+               for(i = position + 1; i < map_size && map[i]; i++)
+               {
+                       ;
+               }
+               last_contiguous = i;
+               mwindow->session->brender_end = (double)last_contiguous /
+                       mwindow->edl->session->frame_rate;
+
+               if(timer->get_difference() > 1000 || last_contiguous >= map_size)
+               {
+                       update_gui = 1;
+                       timer->update();
+               }
+       }
+
+       map_lock->unlock();
+
+       if(update_gui)
+       {
+               mwindow->gui->lock_window("BRender::set_video_map");
+               mwindow->gui->update_timebar(1);
+               mwindow->gui->unlock_window();
+       }
+       return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+BRenderCommand::BRenderCommand()
+{
+       edl = 0;
+       command = BRENDER_NONE;
+       position = 0.0;
+}
+
+BRenderCommand::~BRenderCommand()
+{
+// EDL should be zeroed if copied
+       if(edl) edl->Garbage::remove_user();
+}
+
+void BRenderCommand::copy_from(BRenderCommand *src)
+{
+       this->edl = src->edl;
+       src->edl = 0;
+       this->position = src->position;
+       this->command = src->command;
+}
+
+
+void BRenderCommand::copy_edl(EDL *edl)
+{
+       this->edl = new EDL;
+       this->edl->create_objects();
+       this->edl->copy_all(edl);
+       this->position = 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+BRenderThread::BRenderThread(MWindow *mwindow, BRender *brender)
+ : Thread(1, 0, 0)
+{
+       this->mwindow = mwindow;
+       this->brender = brender;
+       input_lock = new Condition(0, "BRenderThread::input_lock");
+       thread_lock = new Mutex("BRenderThread::thread_lock");
+       total_frames_lock = new Mutex("BRenderThread::total_frames_lock");
+       command_queue = 0;
+       command = 0;
+       done = 0;
+       farm_server = 0;
+       farm_result = 0;
+       preferences = 0;
+}
+
+BRenderThread::~BRenderThread()
+{
+       thread_lock->lock("BRenderThread::~BRenderThread");
+       done = 1;
+       input_lock->unlock();
+       thread_lock->unlock();
+       Thread::join();
+       delete input_lock;
+       delete thread_lock;
+       delete total_frames_lock;
+       if(command) delete command;
+       if(command_queue) delete command_queue;
+       if(preferences) delete preferences;
+}
+
+
+void BRenderThread::initialize()
+{
+       Thread::start();
+}
+
+void BRenderThread::send_command(BRenderCommand *command)
+{
+TRACE("BRenderThread::send_command 1");
+       thread_lock->lock("BRenderThread::send_command");
+TRACE("BRenderThread::send_command 10");
+
+       if(this->command_queue)
+       {
+               delete this->command_queue;
+               this->command_queue = 0;
+       }
+       this->command_queue = command;
+TRACE("BRenderThread::send_command 20");
+
+
+       input_lock->unlock();
+       thread_lock->unlock();
+}
+
+int BRenderThread::is_done(int do_lock)
+{
+       if(do_lock) thread_lock->lock("BRenderThread::is_done");
+       int result = done;
+       if(do_lock) thread_lock->unlock();
+       return result;
+}
+
+void BRenderThread::run()
+{
+       while(!is_done(1))
+       {
+               BRenderCommand *new_command = 0;
+               thread_lock->lock("BRenderThread::run 1");
+
+// Got new command
+               if(command_queue)
+               {
+                       ;
+               }
+               else
+// Wait for new command
+               {
+                       thread_lock->unlock();
+                       input_lock->lock("BRenderThread::run 2");
+                       thread_lock->lock("BRenderThread::run 3");
+               }
+
+// Pull the command off
+               if(!is_done(0))
+               {
+                       new_command = command_queue;
+                       command_queue = 0;
+               }
+
+               thread_lock->unlock();
+
+
+
+
+// Process the command here to avoid delay.
+// Quit condition
+               if(!new_command)
+               {
+                       ;
+               }
+               else
+               if(new_command->command == BRenderCommand::BRENDER_STOP)
+               {
+                       stop();
+                       delete new_command;
+                       new_command = 0;
+//                     if(command) delete command;
+//                     command = new_command;
+               }
+               else
+               if(new_command->command == BRenderCommand::BRENDER_RESTART)
+               {
+// Compare EDL's and get last equivalent position in new EDL
+                       if(command && command->edl)
+                               new_command->position =
+                                       new_command->edl->equivalent_output(command->edl);
+                       else
+                               new_command->position = 0;
+
+
+                       stop();
+//printf("BRenderThread::run 4\n");
+                       brender->completion_lock->lock("BRenderThread::run 4");
+//printf("BRenderThread::run 5\n");
+
+                       if(new_command->edl->tracks->total_playable_vtracks())
+                       {
+                               if(command) delete command;
+                               command = new_command;
+//printf("BRenderThread::run 6\n");
+                               start();
+//printf("BRenderThread::run 7\n");
+                       }
+                       else
+                       {
+//printf("BRenderThread::run 8 %p\n", farm_server);
+                               delete new_command;
+                               new_command = 0;
+                       }
+               }
+       }
+}
+
+void BRenderThread::stop()
+{
+       if(farm_server)
+       {
+               farm_result = 1;
+               farm_server->wait_clients();
+               delete farm_server;
+               delete packages;
+               delete preferences;
+               farm_server = 0;
+               packages = 0;
+               preferences = 0;
+       }
+       brender->completion_lock->unlock();
+}
+
+void BRenderThread::start()
+{
+// Reset return parameters
+       farm_result = 0;
+       fps_result = 0;
+       total_frames = 0;
+       int result = 0;
+
+// Allocate render farm.
+       if(!farm_server)
+       {
+//printf("BRenderThread::start 1\n");
+               preferences = new Preferences;
+               preferences->copy_from(mwindow->preferences);
+               packages = new PackageDispatcher;
+
+// Fix preferences to use local node
+               if(!preferences->use_renderfarm)
+               {
+                       preferences->use_renderfarm = 1;
+                       preferences->delete_nodes();
+               }
+               preferences->add_node(brender->socket_path,
+                       0,
+                       1,
+                       preferences->local_rate);
+//printf("BRenderThread::start 1 %s\n", brender->socket_path);
+               preferences->brender_asset->use_header = 0;
+               preferences->brender_asset->frame_rate = command->edl->session->frame_rate;
+               preferences->brender_asset->width = command->edl->session->output_w;
+               preferences->brender_asset->height = command->edl->session->output_h;
+               preferences->brender_asset->interlace_mode = command->edl->session->interlace_mode;
+
+// Get last contiguous and reset map.
+// If the framerate changes, last good should be 0 from the user.
+               int brender_start = (int)(command->edl->session->brender_start *
+                       command->edl->session->frame_rate);
+               int last_contiguous = brender->last_contiguous;
+               int last_good = (int)(command->edl->session->frame_rate *
+                       command->position);
+               if(last_good < 0) last_good = last_contiguous;
+               int start_frame = MIN(last_contiguous, last_good);
+               start_frame = MAX(start_frame, brender_start);
+               int64_t end_frame = Units::round(command->edl->tracks->total_video_length() *
+                       command->edl->session->frame_rate);
+               if(end_frame < start_frame) end_frame = start_frame;
+
+
+printf("BRenderThread::start 1 map=%d equivalent=%d brender_start=%d result=%d end=%jd\n",
+  last_contiguous, last_good, brender_start, start_frame, end_frame);
+
+//sleep(1);
+
+               brender->allocate_map(brender_start, start_frame, end_frame);
+//sleep(1);
+//printf("BRenderThread::start 2\n");
+
+               result = packages->create_packages(mwindow,
+                       command->edl,
+                       preferences,
+                       BRENDER_FARM,
+                       preferences->brender_asset,
+                       (double)start_frame / command->edl->session->frame_rate,
+                       (double)end_frame / command->edl->session->frame_rate,
+                       0);
+
+//sleep(1);
+//printf("BRenderThread::start 3 %d\n", result);
+               farm_server = new RenderFarmServer(mwindow,
+                       packages,
+                       preferences,
+                       0,
+                       &farm_result,
+                       &total_frames,
+                       total_frames_lock,
+                       preferences->brender_asset,
+                       command->edl,
+                       brender);
+
+//sleep(1);
+//printf("BRenderThread::start 4\n");
+               result = farm_server->start_clients();
+
+//sleep(1);
+// No local rendering because of codec problems.
+
+
+// Abort
+               if(result)
+               {
+// No-one must be retrieving a package when packages are deleted.
+//printf("BRenderThread::start 7 %p\n", farm_server);
+                       delete farm_server;
+                       delete packages;
+//printf("BRenderThread::start 8 %p\n", preferences);
+                       delete preferences;
+//printf("BRenderThread::start 9\n");
+                       farm_server = 0;
+                       packages = 0;
+                       preferences = 0;
+               }
+//sleep(1);
+//printf("BRenderThread::start 10\n");
+
+       }
+}
+
+