Credit Andrew - improve in-tree documentation
[goodguy/cinelerra.git] / cinelerra / brender.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "asset.h"
23 #include "bcsignals.h"
24 #include "brender.h"
25 #include "clip.h"
26 #include "condition.h"
27 #include "edl.h"
28 #include "edlsession.h"
29 #include "language.h"
30 #include "mainsession.h"
31 #include "mtimebar.h"
32 #include "mutex.h"
33 #include "mwindowgui.h"
34 #include "mwindow.h"
35 #include "packagedispatcher.h"
36 #include "preferences.h"
37 #include "renderfarm.h"
38 #include "tracks.h"
39 #include "units.h"
40
41
42 #include <errno.h>
43 #include <signal.h>
44 #include <string.h>
45 #include <sys/wait.h>
46 #include <unistd.h>
47
48
49
50 extern "C"
51 {
52 #include <uuid.h>
53 }
54
55
56
57
58
59
60 BRender::BRender(MWindow *mwindow)
61  : Thread(1, 0, 0)
62 {
63         this->mwindow = mwindow;
64         map_lock = new Mutex("BRender::map_lock");
65         completion_lock = new Condition(0, "BRender::completion_lock", 1);
66         timer = new Timer;
67         socket_path[0] = 0;
68         thread = 0;
69         master_pid = -1;
70         arguments[0] = arguments[1] = arguments[2] = 0;
71         map = 0;
72         map_size = 0;
73         map_valid = 0;
74         last_contiguous = 0;
75         set_synchronous(1);
76 }
77
78 BRender::~BRender()
79 {
80         if(thread) {
81                 stop();
82                 delete thread;
83         }
84
85         if( master_pid >= 0 )
86                 kill(master_pid, SIGKILL);
87         Thread::join();
88
89         delete map_lock;
90         delete completion_lock;
91         UNSET_TEMP(socket_path);
92         remove(socket_path);
93         if(arguments[0]) delete [] arguments[0];
94         if(arguments[1]) delete [] arguments[1];
95         if(arguments[2]) delete [] arguments[2];
96         if(map) delete [] map;
97         delete timer;
98 }
99
100 void BRender::initialize()
101 {
102         timer->update();
103 // Create socket for background process.
104         uuid_t socket_temp;
105         sprintf(socket_path, "/tmp/cinelerra.");
106         uuid_generate(socket_temp);
107         uuid_unparse(socket_temp, socket_path + strlen(socket_path));
108 SET_TEMP(socket_path);
109
110 // Start background instance of executable since codecs aren't reentrant
111         Thread::start();
112
113 // Wait for local node to start
114         thread = new BRenderThread(mwindow, this);
115         thread->initialize();
116 }
117
118 void BRender::run()
119 {
120         char string[BCTEXTLEN];
121         FILE *fd;
122 //printf("BRender::run 1 %d\n", getpid());
123
124
125 // Construct executable command with the designated filesystem port
126         fd = fopen("/proc/self/cmdline", "r");
127         if(fd)
128         {
129                 (void)fread(string, 1, BCTEXTLEN, fd);
130                 fclose(fd);
131         }
132         else
133                 perror(_("BRender::fork_background: can't open /proc/self/cmdline.\n"));
134
135         arguments[0] = new char[strlen(string) + 1];
136         strcpy(arguments[0], string);
137
138         strcpy(string, "-b");
139         arguments[1] = new char[strlen(string) + 1];
140         strcpy(arguments[1], string);
141
142         arguments[2] = new char[strlen(socket_path) + 1];
143         strcpy(arguments[2], socket_path);
144 //printf("BRender::fork_background 1 %s\n", socket_path);
145
146         arguments[3] = 0;
147
148         int pid = vfork();
149         if(!pid)
150         {
151                 execvp(arguments[0], arguments);
152                 perror("BRender::fork_background");
153                 _exit(0);
154         }
155
156         master_pid = pid;
157 //printf("BRender::fork_background 1 %d\n", master_pid);
158
159
160
161         int return_value;
162         if(waitpid(master_pid, &return_value, WUNTRACED) < 0)
163         {
164                 perror("BRender::run waitpid");
165         }
166 }
167
168 // Give the last position of the EDL which hasn't changed.
169 // We copy the EDL and restart rendering at the lesser of position and
170 // our position.
171 void BRender::restart(EDL *edl)
172 {
173 //printf("BRender::restart 1\n");
174         BRenderCommand *new_command = new BRenderCommand;
175         map_valid = 0;
176         new_command->copy_edl(edl);
177         new_command->command = BRenderCommand::BRENDER_RESTART;
178 //printf("BRender::restart 2\n");
179         thread->send_command(new_command);
180 //printf("BRender::restart 3\n");
181 // Map should be reallocated before this returns.
182 }
183
184 void BRender::stop()
185 {
186         if( !running() || !thread->running() ) return;
187         BRenderCommand *new_command = new BRenderCommand;
188         new_command->command = BRenderCommand::BRENDER_STOP;
189         thread->send_command(new_command);
190         completion_lock->lock("BRender::stop");
191 }
192
193
194
195 int BRender::get_last_contiguous(int64_t brender_start)
196 {
197         int result;
198         map_lock->lock("BRender::get_last_contiguous");
199         if(map_valid)
200                 result = last_contiguous;
201         else
202                 result = brender_start;
203         map_lock->unlock();
204         return result;
205 }
206
207 void BRender::allocate_map(int64_t brender_start, int64_t start, int64_t end)
208 {
209         map_lock->lock("BRender::allocate_map");
210         unsigned char *old_map = map;
211         map = new unsigned char[end];
212         if(old_map)
213         {
214                 memcpy(map, old_map, start);
215                 delete [] old_map;
216         }
217
218 // Zero all before brender start
219         bzero(map, brender_start);
220 // Zero all after current start
221         bzero(map + start, end - start);
222
223         map_size = end;
224         map_valid = 1;
225         last_contiguous = start;
226         mwindow->session->brender_end = (double)last_contiguous /
227                 mwindow->edl->session->frame_rate;
228         map_lock->unlock();
229 }
230
231 int BRender::set_video_map(int64_t position, int value)
232 {
233         int update_gui = 0;
234         map_lock->lock("BRender::set_video_map");
235
236
237         if(value == BRender::NOT_SCANNED)
238         {
239                 printf(_("BRender::set_video_map called to set NOT_SCANNED\n"));
240         }
241
242 // Preroll
243         if(position < 0)
244         {
245                 ;
246         }
247         else
248 // In range
249         if(position < map_size)
250         {
251                 map[position] = value;
252         }
253         else
254 // Obsolete EDL
255         {
256                 printf(_("BRender::set_video_map %jd: attempt to set beyond end of map %jd.\n"),
257                         position, map_size);
258         }
259
260 // Maintain last contiguous here to reduce search time
261         if(position == last_contiguous && last_contiguous < map_size )
262         {
263                 int i;
264                 for(i = position + 1; i < map_size && map[i]; i++)
265                 {
266                         ;
267                 }
268                 last_contiguous = i;
269                 mwindow->session->brender_end = (double)last_contiguous /
270                         mwindow->edl->session->frame_rate;
271
272                 if(timer->get_difference() > 1000 || last_contiguous >= map_size)
273                 {
274                         update_gui = 1;
275                         timer->update();
276                 }
277         }
278
279         map_lock->unlock();
280
281         if(update_gui)
282         {
283                 mwindow->gui->lock_window("BRender::set_video_map");
284                 mwindow->gui->update_timebar(1);
285                 mwindow->gui->unlock_window();
286         }
287         return 0;
288 }
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303 BRenderCommand::BRenderCommand()
304 {
305         edl = 0;
306         command = BRENDER_NONE;
307         position = 0.0;
308 }
309
310 BRenderCommand::~BRenderCommand()
311 {
312 // EDL should be zeroed if copied
313         if(edl) edl->Garbage::remove_user();
314 }
315
316 void BRenderCommand::copy_from(BRenderCommand *src)
317 {
318         this->edl = src->edl;
319         src->edl = 0;
320         this->position = src->position;
321         this->command = src->command;
322 }
323
324
325 void BRenderCommand::copy_edl(EDL *edl)
326 {
327         this->edl = new EDL;
328         this->edl->create_objects();
329         this->edl->copy_all(edl);
330         this->position = 0;
331 }
332
333
334
335
336
337
338
339
340
341
342
343
344 BRenderThread::BRenderThread(MWindow *mwindow, BRender *brender)
345  : Thread(1, 0, 0)
346 {
347         this->mwindow = mwindow;
348         this->brender = brender;
349         input_lock = new Condition(0, "BRenderThread::input_lock");
350         thread_lock = new Mutex("BRenderThread::thread_lock");
351         total_frames_lock = new Mutex("BRenderThread::total_frames_lock");
352         command_queue = 0;
353         command = 0;
354         done = 0;
355         farm_server = 0;
356         farm_result = 0;
357         preferences = 0;
358 }
359
360 BRenderThread::~BRenderThread()
361 {
362         thread_lock->lock("BRenderThread::~BRenderThread");
363         done = 1;
364         input_lock->unlock();
365         thread_lock->unlock();
366         Thread::join();
367
368         delete input_lock;
369         delete thread_lock;
370         delete total_frames_lock;
371         delete command;
372         delete command_queue;
373         delete preferences;
374 }
375
376
377 void BRenderThread::initialize()
378 {
379         Thread::start();
380 }
381
382 void BRenderThread::send_command(BRenderCommand *command)
383 {
384         thread_lock->lock("BRenderThread::send_command");
385         delete this->command_queue;
386         this->command_queue = command;
387         input_lock->unlock();
388         thread_lock->unlock();
389 }
390
391 void BRenderThread::run()
392 {
393         thread_lock->lock("BRenderThread::run");
394         while( !done ) {
395                 if( !command_queue ) {
396                         thread_lock->unlock();
397                         input_lock->lock("BRenderThread::run");
398                         thread_lock->lock("BRenderThread::run 1");
399                         continue;
400                 }
401
402                 BRenderCommand *new_command = command_queue;
403                 command_queue = 0;
404                 if( !new_command ) continue;
405                 thread_lock->unlock();
406                 stop();
407                 switch( new_command->command ) {
408                 case BRenderCommand::BRENDER_STOP:
409                         delete new_command;
410                         brender->completion_lock->unlock();
411                         break;
412                 case BRenderCommand::BRENDER_RESTART:
413                         new_command->position = command && command->edl ?
414                                 new_command->edl->equivalent_output(command->edl) : 0;
415                         delete command;  command = 0;
416                         if( new_command->edl->tracks->total_playable_vtracks() ) {
417                                 command = new_command;
418                                 start();
419                         }
420                         break;
421                 }
422                 thread_lock->lock("BRenderThread::run 2");
423         }
424         thread_lock->unlock();
425 }
426
427 void BRenderThread::stop()
428 {
429         if( !farm_server ) return;
430         farm_result = 1;
431         farm_server->wait_clients();
432         delete farm_server;  farm_server = 0;
433         delete packages;     packages = 0;
434         delete preferences;  preferences = 0;
435 }
436
437 void BRenderThread::start()
438 {
439 // Reset return parameters
440         farm_result = 0;
441         fps_result = 0;
442         total_frames = 0;
443         int result = 0;
444
445 // Allocate render farm.
446         if(!farm_server)
447         {
448 //printf("BRenderThread::start 1\n");
449                 preferences = new Preferences;
450                 preferences->copy_from(mwindow->preferences);
451                 packages = new PackageDispatcher;
452
453 // Fix preferences to use local node
454                 if(!preferences->use_renderfarm)
455                 {
456                         preferences->use_renderfarm = 1;
457                         preferences->delete_nodes();
458                 }
459                 preferences->add_node(brender->socket_path,
460                         0,
461                         1,
462                         preferences->local_rate);
463 //printf("BRenderThread::start 1 %s\n", brender->socket_path);
464                 preferences->brender_asset->use_header = 0;
465                 preferences->brender_asset->frame_rate = command->edl->session->frame_rate;
466                 preferences->brender_asset->width = command->edl->session->output_w;
467                 preferences->brender_asset->height = command->edl->session->output_h;
468                 preferences->brender_asset->interlace_mode = command->edl->session->interlace_mode;
469
470 // Get last contiguous and reset map.
471 // If the framerate changes, last good should be 0 from the user.
472                 int brender_start = (int)(command->edl->session->brender_start *
473                         command->edl->session->frame_rate);
474                 int last_contiguous = brender->last_contiguous;
475                 int last_good = (int)(command->edl->session->frame_rate *
476                         command->position);
477                 if(last_good < 0) last_good = last_contiguous;
478                 int start_frame = MIN(last_contiguous, last_good);
479                 start_frame = MAX(start_frame, brender_start);
480 //              int64_t end_frame = Units::round(command->edl->tracks->total_video_length() *
481 //                      command->edl->session->frame_rate);
482                 int64_t end_frame = Units::round(command->edl->session->brender_end *
483                         command->edl->session->frame_rate);
484                 if(end_frame < start_frame) end_frame = start_frame;
485
486
487 printf("BRenderThread::start 1 map=%d equivalent=%d brender_start=%d result=%d end=%jd\n",
488   last_contiguous, last_good, brender_start, start_frame, end_frame);
489
490 //sleep(1);
491
492                 brender->allocate_map(brender_start, start_frame, end_frame);
493 //sleep(1);
494 //printf("BRenderThread::start 2\n");
495
496                 result = packages->create_packages(mwindow,
497                         command->edl,
498                         preferences,
499                         BRENDER_FARM,
500                         preferences->brender_asset,
501                         (double)start_frame / command->edl->session->frame_rate,
502                         (double)end_frame / command->edl->session->frame_rate,
503                         0);
504
505 //sleep(1);
506 //printf("BRenderThread::start 3 %d\n", result);
507                 farm_server = new RenderFarmServer(mwindow,
508                         packages,
509                         preferences,
510                         0,
511                         &farm_result,
512                         &total_frames,
513                         total_frames_lock,
514                         preferences->brender_asset,
515                         command->edl,
516                         brender);
517
518 //sleep(1);
519 //printf("BRenderThread::start 4\n");
520                 result = farm_server->start_clients();
521
522 //sleep(1);
523 // No local rendering because of codec problems.
524
525
526 // Abort
527                 if(result)
528                 {
529 // No-one must be retrieving a package when packages are deleted.
530 //printf("BRenderThread::start 7 %p\n", farm_server);
531                         delete farm_server;
532                         delete packages;
533 //printf("BRenderThread::start 8 %p\n", preferences);
534                         delete preferences;
535 //printf("BRenderThread::start 9\n");
536                         farm_server = 0;
537                         packages = 0;
538                         preferences = 0;
539                 }
540 //sleep(1);
541 //printf("BRenderThread::start 10\n");
542
543         }
544 }
545
546