/* * CINELERRA * Copyright (C) 2008 Adam Williams * * 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 #include #include #include #include extern "C" { #include } 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", 1); 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() { if(thread) { stop(); delete thread; } if( master_pid >= 0 ) kill(master_pid, SIGKILL); Thread::join(); delete map_lock; delete completion_lock; UNSET_TEMP(socket_path); remove(socket_path); if(arguments[0]) delete [] arguments[0]; if(arguments[1]) delete [] arguments[1]; if(arguments[2]) delete [] arguments[2]; if(map) delete [] map; delete timer; } 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() { if( !running() || !thread->running() ) return; BRenderCommand *new_command = new BRenderCommand; new_command->command = BRenderCommand::BRENDER_STOP; thread->send_command(new_command); completion_lock->lock("BRender::stop"); } 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 && last_contiguous < map_size ) { 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; delete command; delete command_queue; delete preferences; } void BRenderThread::initialize() { Thread::start(); } void BRenderThread::send_command(BRenderCommand *command) { thread_lock->lock("BRenderThread::send_command"); delete this->command_queue; this->command_queue = command; input_lock->unlock(); thread_lock->unlock(); } void BRenderThread::run() { thread_lock->lock("BRenderThread::run"); while( !done ) { if( !command_queue ) { thread_lock->unlock(); input_lock->lock("BRenderThread::run"); thread_lock->lock("BRenderThread::run 1"); continue; } BRenderCommand *new_command = command_queue; command_queue = 0; if( !new_command ) continue; thread_lock->unlock(); stop(); switch( new_command->command ) { case BRenderCommand::BRENDER_STOP: delete new_command; brender->completion_lock->unlock(); break; case BRenderCommand::BRENDER_RESTART: new_command->position = command && command->edl ? new_command->edl->equivalent_output(command->edl) : 0; delete command; command = 0; if( new_command->edl->tracks->total_playable_vtracks() ) { command = new_command; start(); } break; } thread_lock->lock("BRenderThread::run 2"); } thread_lock->unlock(); } void BRenderThread::stop() { if( !farm_server ) return; farm_result = 1; farm_server->wait_clients(); delete farm_server; farm_server = 0; delete packages; packages = 0; delete preferences; preferences = 0; } 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); int64_t end_frame = Units::round(command->edl->session->brender_end * 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"); } }