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