tweak zoom/fullscr to remember cwdw scale after fullscr
[goodguy/cinelerra.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->project_smp);
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         renderengine->wait_done();
78         delete overlayer;
79         delete input_temp;
80         delete transition_temp;
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 // Negotiate color model
148         colormodel = get_colormodel(playable_edit, use_vconsole, use_brender);
149         if(debug) printf("VRender::process_buffer %d\n", __LINE__);
150
151
152 // Get output buffer from device
153         if(renderengine->command->realtime && !renderengine->is_nested)
154         {
155                 renderengine->video->new_output_buffer(&video_out, 
156                         colormodel, 
157                         renderengine->get_edl());
158         }
159
160         if(debug) printf("VRender::process_buffer %d video_out=%p\n", __LINE__, video_out);
161
162 // printf("VRender::process_buffer use_vconsole=%d colormodel=%d video_out=%p\n",
163 // use_vconsole,
164 // colormodel,
165 // video_out);
166 // Read directly from file to video_out
167         if(!use_vconsole)
168         {
169
170                 if(use_brender)
171                 {
172                         Asset *asset = renderengine->preferences->brender_asset;
173                         File *file = renderengine->get_vcache()->check_out(asset,
174                                 renderengine->get_edl());
175
176                         if(file)
177                         {
178                                 int64_t corrected_position = current_position;
179                                 if(renderengine->command->get_direction() == PLAY_REVERSE)
180                                         corrected_position--;
181
182 // Cache single frames only
183 //                              if(use_asynchronous)
184 //                                      file->start_video_decode_thread();
185 //                              else
186                                         file->stop_video_thread();
187                                 if(use_cache) file->set_cache_frames(1);
188                                 int64_t normalized_position = (int64_t)(corrected_position *
189                                         asset->frame_rate /
190                                         renderengine->get_edl()->session->frame_rate);
191
192                                 file->set_video_position(normalized_position,
193                                         0);
194                                 file->read_frame(video_out);
195
196
197                                 if(use_cache) file->set_cache_frames(0);
198                                 renderengine->get_vcache()->check_in(asset);
199                         }
200
201                 }
202                 else
203                 if(playable_edit)
204                 {
205                         if(debug) printf("VRender::process_buffer %d\n", __LINE__);
206                         result = ((VEdit*)playable_edit)->read_frame(video_out,
207                                 current_position,
208                                 renderengine->command->get_direction(),
209                                 renderengine->get_vcache(),
210                                 1,
211                                 use_cache,
212                                 0);
213 //                              use_asynchronous);
214                         if(debug) printf("VRender::process_buffer %d\n", __LINE__);
215                 }
216
217
218
219                 video_out->set_opengl_state(VFrame::RAM);
220         }
221         else
222 // Read into virtual console
223         {
224
225 // process this buffer now in the virtual console
226                 result = ((VirtualVConsole*)vconsole)->process_buffer(input_position,
227                         use_opengl);
228         }
229
230         return result;
231 }
232
233 // Determine if virtual console is needed
234 int VRender::get_use_vconsole(VEdit **playable_edit,
235         int64_t position, int &use_brender)
236 {
237         *playable_edit = 0;
238
239 // Background rendering completed
240         if((use_brender = renderengine->brender_available(position,
241                 renderengine->command->get_direction())) != 0)
242                 return 0;
243
244 // Descend into EDL nest
245         return renderengine->get_edl()->get_use_vconsole(playable_edit,
246                 position, renderengine->command->get_direction(),
247                 vconsole->playable_tracks);
248 }
249
250
251 int VRender::get_colormodel(VEdit *playable_edit, int use_vconsole, int use_brender)
252 {
253         EDL *edl = renderengine->get_edl();
254         int colormodel = renderengine->get_edl()->session->color_model;
255         VideoOutConfig *vconfig = renderengine->config->vconfig;
256 // check for playback: no plugins, not single frame
257         if( !use_vconsole && !renderengine->command->single_frame() ) {
258 // Get best colormodel supported by the file
259 // colormodel yuv/rgb affects mpeg/jpeg color range,
260 // dont mix them or loose color acccuracy
261                 int64_t source_position = 0;
262                 Asset *asset = use_brender ?
263                         renderengine->preferences->brender_asset :
264                         playable_edit->get_nested_asset(&source_position, current_position,
265                                 renderengine->command->get_direction());
266                 if( asset ) {
267                         File *file = renderengine->get_vcache()->check_out(asset, edl);
268                         if( file ) {
269 // damn the color range, full speed ahead
270                                 if( vconfig->driver == PLAYBACK_X11 && vconfig->use_direct_x11 &&
271                                     file->colormodel_supported(BC_BGR8888) == BC_BGR8888 )
272                                         colormodel = BC_BGR8888;
273                                 else {
274 // file favorite colormodel may mismatch rgb/yuv
275                                         int vstream = playable_edit ? playable_edit->channel : -1;
276                                         int best_colormodel = file->get_best_colormodel(vconfig->driver, vstream);
277                                         if( BC_CModels::is_yuv(best_colormodel) == BC_CModels::is_yuv(colormodel) )
278                                                 colormodel = best_colormodel;
279                                 }
280                                 renderengine->get_vcache()->check_in(asset);
281                         }
282                 }
283         }
284
285         return colormodel;
286 }
287
288
289 void VRender::run()
290 {
291         int reconfigure;
292         const int debug = 0;
293
294 // Want to know how many samples rendering each frame takes.
295 // Then use this number to predict the next frame that should be rendered.
296 // Be suspicious of frames that render late so have a countdown
297 // before we start dropping.
298         int64_t current_sample, start_sample, end_sample; // Absolute counts.
299         int64_t skip_countdown = VRENDER_THRESHOLD;    // frames remaining until drop
300         int64_t delay_countdown = 0;  // Frames remaining until delay
301 // Number of frames before next reconfigure
302         int64_t current_input_length;
303 // Number of frames to skip.
304         int64_t frame_step = 1;
305         int use_opengl = (renderengine->video &&
306                 renderengine->video->out_config->driver == PLAYBACK_X11_GL);
307
308         first_frame = 1;
309
310 // Number of frames since start of rendering
311         session_frame = 0;
312         framerate_counter = 0;
313         framerate_timer.update();
314
315         start_lock->unlock();
316         if(debug) printf("VRender::run %d\n", __LINE__);
317
318
319         while(!done && !interrupt )
320         {
321 // Perform the most time consuming part of frame decompression now.
322 // Want the condition before, since only 1 frame is rendered
323 // and the number of frames skipped after this frame varies.
324                 current_input_length = 1;
325
326                 reconfigure = vconsole->test_reconfigure(current_position,
327                         current_input_length);
328
329
330                 if(debug) printf("VRender::run %d\n", __LINE__);
331                 if(reconfigure) restart_playback();
332
333                 if(debug) printf("VRender::run %d\n", __LINE__);
334                 process_buffer(current_position, use_opengl);
335
336
337                 if(debug) printf("VRender::run %d\n", __LINE__);
338
339                 if(renderengine->command->single_frame())
340                 {
341                         if(debug) printf("VRender::run %d\n", __LINE__);
342                         flash_output();
343                         frame_step = 1;
344                         done = 1;
345                 }
346                 else
347 // Perform synchronization
348                 {
349 // Determine the delay until the frame needs to be shown.
350                         current_sample = (int64_t)(renderengine->sync_position() *
351                                 renderengine->command->get_speed());
352 // latest sample at which the frame can be shown.
353                         end_sample = Units::tosamples(session_frame + 1,
354                                 renderengine->get_edl()->session->sample_rate,
355                                 renderengine->get_edl()->session->frame_rate);
356 // earliest sample by which the frame needs to be shown.
357                         start_sample = Units::tosamples(session_frame,
358                                 renderengine->get_edl()->session->sample_rate,
359                                 renderengine->get_edl()->session->frame_rate);
360
361                         if(first_frame || end_sample < current_sample)
362                         {
363 // Frame rendered late or this is the first frame.  Flash it now.
364 //printf("VRender::run %d\n", __LINE__);
365                                 flash_output();
366
367                                 if(renderengine->get_edl()->session->video_every_frame)
368                                 {
369 // User wants every frame.
370                                         frame_step = 1;
371                                 }
372                                 else
373                                 if(skip_countdown > 0)
374                                 {
375 // Maybe just a freak.
376                                         frame_step = 1;
377                                         skip_countdown--;
378                                 }
379                                 else
380                                 {
381 // Get the frames to skip.
382                                         delay_countdown = VRENDER_THRESHOLD;
383                                         frame_step = 1;
384                                         frame_step += (int64_t)Units::toframes(current_sample,
385                                                         renderengine->get_edl()->session->sample_rate,
386                                                         renderengine->get_edl()->session->frame_rate);
387                                         frame_step -= (int64_t)Units::toframes(end_sample,
388                                                                 renderengine->get_edl()->session->sample_rate,
389                                                                 renderengine->get_edl()->session->frame_rate);
390                                 }
391                         }
392                         else
393                         {
394 // Frame rendered early or just in time.
395                                 frame_step = 1;
396
397                                 if(delay_countdown > 0)
398                                 {
399 // Maybe just a freak
400                                         delay_countdown--;
401                                 }
402                                 else
403                                 {
404                                         skip_countdown = VRENDER_THRESHOLD;
405                                         if(start_sample > current_sample)
406                                         {
407                                                 int64_t delay_time = (int64_t)((float)(start_sample - current_sample) *
408                                                         1000 / renderengine->get_edl()->session->sample_rate);
409                                                 if( delay_time > 1000 ) delay_time = 1000;
410                                                 timer.delay(delay_time);
411                                         }
412                                         else
413                                         {
414 // Came after the earliest sample so keep going
415                                         }
416                                 }
417
418 // Flash frame now.
419 //printf("VRender::run %d %jd\n", __LINE__, current_input_length);
420                                 flash_output();
421                         }
422                 }
423                 if(debug) printf("VRender::run %d\n", __LINE__);
424
425 // Trigger audio to start
426                 if(first_frame)
427                 {
428                         renderengine->first_frame_lock->unlock();
429                         first_frame = 0;
430                         renderengine->reset_sync_position();
431                 }
432                 if(debug) printf("VRender::run %d\n", __LINE__);
433
434                 session_frame += frame_step;
435
436 // advance position in project
437                 current_input_length = frame_step;
438
439
440 // Subtract frame_step in a loop to allow looped playback to drain
441 // printf("VRender::run %d %d %d %d\n",
442 // __LINE__,
443 // done,
444 // frame_step,
445 // current_input_length);
446                 while(frame_step && current_input_length)
447                 {
448 // trim current_input_length to range
449                         get_boundaries(current_input_length);
450 // advance 1 frame
451                         advance_position(current_input_length);
452                         frame_step -= current_input_length;
453                         current_input_length = frame_step;
454                         if(done) break;
455 // printf("VRender::run %d %d %d %d\n",
456 // __LINE__,
457 // done,
458 // frame_step,
459 // current_input_length);
460                 }
461
462                 if(debug) printf("VRender::run %d current_position=%jd done=%d\n",
463                         __LINE__, current_position, done);
464
465 // Update tracking.
466                 if(renderengine->command->realtime && renderengine->playback_engine &&
467                         renderengine->command->command != CURRENT_FRAME &&
468                         renderengine->command->command != LAST_FRAME)
469                 {
470                         renderengine->playback_engine->update_tracking(fromunits(current_position));
471                 }
472                 if(debug) printf("VRender::run %d\n", __LINE__);
473
474 // Calculate the framerate counter
475                 framerate_counter++;
476                 if(framerate_counter >= renderengine->get_edl()->session->frame_rate &&
477                         renderengine->command->realtime)
478                 {
479                         renderengine->update_framerate((float)framerate_counter /
480                                 ((float)framerate_timer.get_difference() / 1000));
481                         framerate_counter = 0;
482                         framerate_timer.update();
483                 }
484                 if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
485                 if( !interrupt ) interrupt = renderengine->interrupted;
486                 if( !interrupt ) interrupt = renderengine->video->interrupt;
487                 if( !interrupt ) interrupt = vconsole->interrupt;
488         }
489
490
491 // In case we were interrupted before the first loop
492         renderengine->first_frame_lock->unlock();
493         stop_plugins();
494         if(debug) printf("VRender::run %d done=%d\n", __LINE__, done);
495 }
496
497 int VRender::start_playback()
498 {
499 // start reading input and sending to vrenderthread
500 // use a thread only if there's a video device
501         if(renderengine->command->realtime)
502         {
503                 start();
504         }
505         return 0;
506 }
507
508 int64_t VRender::tounits(double position, int round)
509 {
510         if(round)
511                 return Units::round(position * renderengine->get_edl()->session->frame_rate);
512         else
513                 return Units::to_int64(position * renderengine->get_edl()->session->frame_rate);
514 }
515
516 double VRender::fromunits(int64_t position)
517 {
518         return (double)position / renderengine->get_edl()->session->frame_rate;
519 }
520