Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / vrender.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2009 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 "cache.h"
25 #include "clip.h"
26 #include "condition.h"
27 #include "datatype.h"
28 #include "edits.h"
29 #include "edl.h"
30 #include "edlsession.h"
31 #include "file.h"
32 #include "interlacemodes.h"
33 #include "localsession.h"
34 #include "mainsession.h"
35 #include "mwindow.h"
36 #include "overlayframe.h"
37 #include "playabletracks.h"
38 #include "playbackengine.h"
39 #include "preferences.h"
40 #include "preferencesthread.h"
41 #include "renderengine.h"
42 #include "strategies.inc"
43 #include "tracks.h"
44 #include "transportque.h"
45 #include "units.h"
46 #include "vedit.h"
47 #include "vframe.h"
48 #include "videoconfig.h"
49 #include "videodevice.h"
50 #include "virtualconsole.h"
51 #include "virtualvconsole.h"
52 #include "vmodule.h"
53 #include "vrender.h"
54 #include "vtrack.h"
55
56
57
58
59
60 VRender::VRender(RenderEngine *renderengine)
61  : CommonRender(renderengine)
62 {
63         data_type = TRACK_VIDEO;
64         transition_temp = 0;
65         overlayer = new OverlayFrame(renderengine->preferences->processors);
66         input_temp = 0;
67         vmodule_render_fragment = 0;
68         playback_buffer = 0;
69         session_frame = 0;
70         asynchronous = 0;     // render 1 frame at a time
71         framerate_counter = 0;
72         video_out = 0;
73         render_strategy = -1;
74 }
75
76 VRender::~VRender()
77 {
78         if(input_temp) delete input_temp;
79         if(transition_temp) delete transition_temp;
80         if(overlayer) delete overlayer;
81 }
82
83
84 VirtualConsole* VRender::new_vconsole_object()
85 {
86         return new VirtualVConsole(renderengine, this);
87 }
88
89 int VRender::get_total_tracks()
90 {
91         return renderengine->get_edl()->tracks->total_video_tracks();
92 }
93
94 Module* VRender::new_module(Track *track)
95 {
96         return new VModule(renderengine, this, 0, track);
97 }
98
99 int VRender::flash_output()
100 {
101         if(video_out)
102                 return renderengine->video->write_buffer(video_out, renderengine->get_edl());
103         else
104                 return 0;
105 }
106
107 int VRender::process_buffer(VFrame *video_out,
108         int64_t input_position,
109         int use_opengl)
110 {
111 // process buffer for non realtime
112         int64_t render_len = 1;
113         int reconfigure = 0;
114
115
116         this->video_out = video_out;
117
118         current_position = input_position;
119
120         reconfigure = vconsole->test_reconfigure(input_position,
121                 render_len);
122
123         if(reconfigure) restart_playback();
124         return process_buffer(input_position, use_opengl);
125 }
126
127
128 int VRender::process_buffer(int64_t input_position,
129         int use_opengl)
130 {
131         VEdit *playable_edit = 0;
132         int colormodel;
133         int use_vconsole = 1;
134         int use_brender = 0;
135         int result = 0;
136         int use_cache = renderengine->command->single_frame();
137         int use_asynchronous =
138                 renderengine->command->realtime &&
139                 renderengine->get_edl()->session->video_every_frame &&
140                 renderengine->get_edl()->session->video_asynchronous;
141         const int debug = 0;
142
143 // Determine the rendering strategy for this frame.
144         use_vconsole = get_use_vconsole(&playable_edit, input_position, use_brender);
145         if(debug) printf("VRender::process_buffer %d use_vconsole=%d\n", __LINE__, use_vconsole);
146
147         if( playable_edit ) { 
148 // Asset and output device must have same resulting de-interlacing method
149                 Indexable *source = playable_edit->get_source();
150                 if( source->is_asset ) {
151                         Asset *asset = (Asset *)source;
152                         if( ilaceautofixmethod2(renderengine->edl->session->interlace_mode,
153                                         asset->interlace_autofixoption, asset->interlace_mode,
154                                         asset->interlace_fixmethod) != BC_ILACE_FIXMETHOD_NONE )
155                                 return 1;
156                 }
157         }
158
159 // Negotiate color model
160         colormodel = get_colormodel(playable_edit, use_vconsole, use_brender);
161         if(debug) printf("VRender::process_buffer %d\n", __LINE__);
162
163
164
165
166 // Get output buffer from device
167         if(renderengine->command->realtime &&
168                 !renderengine->is_nested)
169         {
170                 renderengine->video->new_output_buffer(&video_out, colormodel);
171         }
172
173         if(debug) printf("VRender::process_buffer %d video_out=%p\n", __LINE__, video_out);
174
175 // printf("VRender::process_buffer use_vconsole=%d colormodel=%d video_out=%p\n",
176 // use_vconsole,
177 // colormodel,
178 // video_out);
179 // Read directly from file to video_out
180         if(!use_vconsole)
181         {
182
183                 if(use_brender)
184                 {
185                         Asset *asset = renderengine->preferences->brender_asset;
186                         File *file = renderengine->get_vcache()->check_out(asset,
187                                 renderengine->get_edl());
188
189                         if(file)
190                         {
191                                 int64_t corrected_position = current_position;
192                                 if(renderengine->command->get_direction() == PLAY_REVERSE)
193                                         corrected_position--;
194
195 // Cache single frames only
196                                 if(use_asynchronous)
197                                         file->start_video_decode_thread();
198                                 else
199                                         file->stop_video_thread();
200                                 if(use_cache) file->set_cache_frames(1);
201                                 int64_t normalized_position = (int64_t)(corrected_position *
202                                         asset->frame_rate /
203                                         renderengine->get_edl()->session->frame_rate);
204
205                                 file->set_video_position(normalized_position,
206                                         0);
207                                 file->read_frame(video_out);
208
209
210                                 if(use_cache) file->set_cache_frames(0);
211                                 renderengine->get_vcache()->check_in(asset);
212                         }
213
214                 }
215                 else
216                 if(playable_edit)
217                 {
218                         if(debug) printf("VRender::process_buffer %d\n", __LINE__);
219                         result = ((VEdit*)playable_edit)->read_frame(video_out,
220                                 current_position,
221                                 renderengine->command->get_direction(),
222                                 renderengine->get_vcache(),
223                                 1,
224                                 use_cache,
225                                 use_asynchronous);
226 /* Insert timecode */
227                         if(renderengine->show_tc)
228                                 insert_timecode(playable_edit,
229                                         input_position,
230                                         video_out);
231                         if(debug) printf("VRender::process_buffer %d\n", __LINE__);
232                 }
233
234
235
236                 video_out->set_opengl_state(VFrame::RAM);
237         }
238         else
239 // Read into virtual console
240         {
241
242 // process this buffer now in the virtual console
243                 result = ((VirtualVConsole*)vconsole)->process_buffer(input_position,
244                         use_opengl);
245         }
246
247         return result;
248 }
249
250 // Determine if virtual console is needed
251 int VRender::get_use_vconsole(VEdit **playable_edit,
252         int64_t position, int &use_brender)
253 {
254         *playable_edit = 0;
255
256 // Background rendering completed
257         if((use_brender = renderengine->brender_available(position,
258                 renderengine->command->get_direction())) != 0)
259                 return 0;
260
261 // Descend into EDL nest
262         return renderengine->get_edl()->get_use_vconsole(playable_edit,
263                 position,
264                 renderengine->command->get_direction(),
265                 vconsole->playable_tracks);
266 }
267
268
269 int VRender::insert_timecode(Edit* playable_edit,
270                         int64_t position,
271                         VFrame *output)
272 {
273 #if 0
274         EDLSession *session = renderengine->edl->session;
275         /* Create a vframe with TC and SRC timecode in white
276          * with a black border */
277         VFrame *input = new VFrame(0,
278                 output->get_w(), MIN(output->get_h(), 50),
279                 output->get_color_model(), output->get_bytes_per_line());
280         char etc[12];
281         char srctc[12];
282         int src_position = 0;
283
284 TRACE("VRender::insert_timecode 10")
285
286         /* Edited TC */
287         Units::totext(etc,
288                 (renderengine->vrender->current_position +
289                         session->get_frame_offset()) / session->frame_rate,
290                 session->time_format,
291                 session->sample_rate,
292                 session->frame_rate,
293                 session->frames_per_foot);
294
295 TRACE("VRender::insert_timecode 20")
296
297         if(playable_edit)
298         {
299 TRACE("VRender::insert_timecode 30")
300                 src_position = renderengine->vrender->current_position -
301                         playable_edit->startproject +
302                         playable_edit->startsource +
303                         playable_edit->asset->tcstart;
304 TRACE("VRender::insert_timecode 40")
305                 Units::totext(srctc,
306                         src_position / playable_edit->asset->frame_rate,
307                         session->time_format,
308                         session->sample_rate,
309                         playable_edit->asset->frame_rate,
310                         session->frames_per_foot);
311         }
312         else
313         {
314 TRACE("VRender::insert_timecode 50")
315                 Units::totext(srctc,
316                         0.0,
317 //                      (renderengine->vrender->current_position - position) / session->frame_rate,
318                         session->time_format,
319                         session->sample_rate,
320                         session->frame_rate,
321                         session->frames_per_foot);
322         }
323 TRACE("VRender::insert_timecode 60")
324
325 //printf("re position %i position %i\n",
326 //      renderengine->vrender->current_position, position);
327 //printf("SRC %s   TC %s\n", srctc, etc);
328
329         /* Insert the timecode data onto the input frame */
330
331         vrender->overlayer->overlay(output, input,
332                 input->x, input->y, input->width, input->height,
333                 output->x, output->y, output->width, output->height,
334                 1, TRANSFER_REPLACE,
335                 renderengine->edl->session->interpolation_type);
336         delete(input);
337 UNTRACE
338 #endif
339         return 0;
340 }
341
342 int VRender::get_colormodel(VEdit *playable_edit,
343         int use_vconsole, int use_brender)
344 {
345         int colormodel = renderengine->get_edl()->session->color_model;
346
347         if(!use_vconsole && !renderengine->command->single_frame())
348         {
349 // Get best colormodel supported by the file
350                 int driver = renderengine->config->vconfig->driver;
351                 File *file;
352                 Asset *asset;
353
354                 if(use_brender)
355                 {
356                         asset = renderengine->preferences->brender_asset;
357                 }
358                 else
359                 {
360                         int64_t source_position = 0;
361                         asset = playable_edit->get_nested_asset(&source_position,
362                                 current_position,
363                                 renderengine->command->get_direction());
364                 }
365
366                 if(asset)
367                 {
368                         file = renderengine->get_vcache()->check_out(asset,
369                                 renderengine->get_edl());
370
371                         if(file)
372                         {
373                                 colormodel = file->get_best_colormodel(driver);
374                                 renderengine->get_vcache()->check_in(asset);
375                         }
376                 }
377         }
378
379         return colormodel;
380 }
381
382
383
384
385
386
387
388 void VRender::run()
389 {
390         int reconfigure;
391         const int debug = 0;
392
393 // Want to know how many samples rendering each frame takes.
394 // Then use this number to predict the next frame that should be rendered.
395 // Be suspicious of frames that render late so have a countdown
396 // before we start dropping.
397         int64_t current_sample, start_sample, end_sample; // Absolute counts.
398         int64_t skip_countdown = VRENDER_THRESHOLD;    // frames remaining until drop
399         int64_t delay_countdown = VRENDER_THRESHOLD;  // Frames remaining until delay
400 // Number of frames before next reconfigure
401         int64_t current_input_length;
402 // Number of frames to skip.
403         int64_t frame_step = 1;
404         int use_opengl = (renderengine->video &&
405                 renderengine->video->out_config->driver == PLAYBACK_X11_GL);
406
407         first_frame = 1;
408
409 // Number of frames since start of rendering
410         session_frame = 0;
411         framerate_counter = 0;
412         framerate_timer.update();
413
414         start_lock->unlock();
415         if(debug) printf("VRender::run %d\n", __LINE__);
416
417
418         while(!done && !interrupt )
419         {
420 // Perform the most time consuming part of frame decompression now.
421 // Want the condition before, since only 1 frame is rendered
422 // and the number of frames skipped after this frame varies.
423                 current_input_length = 1;
424
425                 reconfigure = vconsole->test_reconfigure(current_position,
426                         current_input_length);
427
428
429                 if(debug) printf("VRender::run %d\n", __LINE__);
430                 if(reconfigure) restart_playback();
431
432                 if(debug) printf("VRender::run %d\n", __LINE__);
433                 process_buffer(current_position, use_opengl);
434
435
436                 if(debug) printf("VRender::run %d\n", __LINE__);
437
438                 if(renderengine->command->single_frame())
439                 {
440                         if(debug) printf("VRender::run %d\n", __LINE__);
441                         flash_output();
442                         frame_step = 1;
443                         done = 1;
444                 }
445                 else
446 // Perform synchronization
447                 {
448 // Determine the delay until the frame needs to be shown.
449                         current_sample = (int64_t)(renderengine->sync_position() *
450                                 renderengine->command->get_speed());
451 // latest sample at which the frame can be shown.
452                         end_sample = Units::tosamples(session_frame,
453                                 renderengine->get_edl()->session->sample_rate,
454                                 renderengine->get_edl()->session->frame_rate);
455 // earliest sample by which the frame needs to be shown.
456                         start_sample = Units::tosamples(session_frame - 1,
457                                 renderengine->get_edl()->session->sample_rate,
458                                 renderengine->get_edl()->session->frame_rate);
459
460                         if(first_frame || end_sample < current_sample)
461                         {
462 // Frame rendered late or this is the first frame.  Flash it now.
463 //printf("VRender::run %d\n", __LINE__);
464                                 flash_output();
465
466                                 if(renderengine->get_edl()->session->video_every_frame)
467                                 {
468 // User wants every frame.
469                                         frame_step = 1;
470                                 }
471                                 else
472                                 if(skip_countdown > 0)
473                                 {
474 // Maybe just a freak.
475                                         frame_step = 1;
476                                         skip_countdown--;
477                                 }
478                                 else
479                                 {
480 // Get the frames to skip.
481                                         delay_countdown = VRENDER_THRESHOLD;
482                                         frame_step = 1;
483                                         frame_step += (int64_t)Units::toframes(current_sample,
484                                                         renderengine->get_edl()->session->sample_rate,
485                                                         renderengine->get_edl()->session->frame_rate);
486                                         frame_step -= (int64_t)Units::toframes(end_sample,
487                                                                 renderengine->get_edl()->session->sample_rate,
488                                                                 renderengine->get_edl()->session->frame_rate);
489                                 }
490                         }
491                         else
492                         {
493 // Frame rendered early or just in time.
494                                 frame_step = 1;
495
496                                 if(delay_countdown > 0)
497                                 {
498 // Maybe just a freak
499                                         delay_countdown--;
500                                 }
501                                 else
502                                 {
503                                         skip_countdown = VRENDER_THRESHOLD;
504                                         if(start_sample > current_sample)
505                                         {
506                                                 int64_t delay_time = (int64_t)((float)(start_sample - current_sample) *
507                                                         1000 / renderengine->get_edl()->session->sample_rate);
508                                                 if( delay_time > 1000 ) delay_time = 1000;
509                                                 timer.delay(delay_time);
510                                         }
511                                         else
512                                         {
513 // Came after the earliest sample so keep going
514                                         }
515                                 }
516
517 // Flash frame now.
518 //printf("VRender::run %d %jd\n", __LINE__, current_input_length);
519                                 flash_output();
520                         }
521                 }
522                 if(debug) printf("VRender::run %d\n", __LINE__);
523
524 // Trigger audio to start
525                 if(first_frame)
526                 {
527                         renderengine->first_frame_lock->unlock();
528                         first_frame = 0;
529                         renderengine->reset_sync_position();
530                 }
531                 if(debug) printf("VRender::run %d\n", __LINE__);
532
533                 session_frame += frame_step;
534
535 // advance position in project
536                 current_input_length = frame_step;
537
538
539 // Subtract frame_step in a loop to allow looped playback to drain
540 // printf("VRender::run %d %d %d %d\n",
541 // __LINE__,
542 // done,
543 // frame_step,
544 // current_input_length);
545                 while(frame_step && current_input_length)
546                 {
547 // trim current_input_length to range
548                         get_boundaries(current_input_length);
549 // advance 1 frame
550                         advance_position(current_input_length);
551                         frame_step -= current_input_length;
552                         current_input_length = frame_step;
553                         if(done) break;
554 // printf("VRender::run %d %d %d %d\n",
555 // __LINE__,
556 // done,
557 // frame_step,
558 // current_input_length);
559                 }
560
561                 if(debug) printf("VRender::run %d current_position=%jd done=%d\n",
562                         __LINE__, current_position, done);
563
564 // Update tracking.
565                 if(renderengine->command->realtime &&
566                         renderengine->playback_engine &&
567                         renderengine->command->command != CURRENT_FRAME)
568                 {
569                         renderengine->playback_engine->update_tracking(fromunits(current_position));
570                 }
571                 if(debug) printf("VRender::run %d\n", __LINE__);
572
573 // Calculate the framerate counter
574                 framerate_counter++;
575                 if(framerate_counter >= renderengine->get_edl()->session->frame_rate &&
576                         renderengine->command->realtime)
577                 {
578                         renderengine->update_framerate((float)framerate_counter /
579                                 ((float)framerate_timer.get_difference() / 1000));
580                         framerate_counter = 0;
581                         framerate_timer.update();
582                 }
583                 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
584                 if( !interrupt )
585                         interrupt = renderengine->video->interrupt;
586         }
587
588
589 // In case we were interrupted before the first loop
590         renderengine->first_frame_lock->unlock();
591         stop_plugins();
592         if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
593 }
594
595 int VRender::start_playback()
596 {
597 // start reading input and sending to vrenderthread
598 // use a thread only if there's a video device
599         if(renderengine->command->realtime)
600         {
601                 start();
602         }
603         return 0;
604 }
605
606 int64_t VRender::tounits(double position, int round)
607 {
608         if(round)
609                 return Units::round(position * renderengine->get_edl()->session->frame_rate);
610         else
611                 return Units::to_int64(position * renderengine->get_edl()->session->frame_rate);
612 }
613
614 double VRender::fromunits(int64_t position)
615 {
616         return (double)position / renderengine->get_edl()->session->frame_rate;
617 }
618