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