31873e6a4c1b79d7687cc8231f27798baf5e64c5
[goodguy/history.git] / cinelerra-5.1 / cinelerra / vdevicex11.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 "assets.h"
23 #include "auto.h"
24 #include "bccapture.h"
25 #include "bcsignals.h"
26 #include "canvas.h"
27 #include "bccmodels.h"
28 #include "edl.h"
29 #include "edlsession.h"
30 #include "maskautos.h"
31 #include "maskauto.h"
32 #include "mwindow.h"
33 #include "playback3d.h"
34 #include "playbackconfig.h"
35 #include "preferences.h"
36 #include "recordconfig.h"
37 #include "strategies.inc"
38 #include "vdevicex11.h"
39 #include "vframe.h"
40 #include "videodevice.h"
41 #include "videowindow.h"
42 #include "videowindowgui.h"
43
44 #include <string.h>
45 #include <unistd.h>
46
47 VDeviceX11::VDeviceX11(VideoDevice *device, Canvas *output)
48  : VDeviceBase(device)
49 {
50         reset_parameters();
51         this->output = output;
52 }
53
54 VDeviceX11::~VDeviceX11()
55 {
56         close_all();
57 }
58
59 int VDeviceX11::reset_parameters()
60 {
61         output_frame = 0;
62         window_id = 0;
63         bitmap = 0;
64         bitmap_type = 0;
65         bitmap_w = 0;
66         bitmap_h = 0;
67         output_x1 = 0;
68         output_y1 = 0;
69         output_x2 = 0;
70         output_y2 = 0;
71         canvas_x1 = 0;
72         canvas_y1 = 0;
73         canvas_x2 = 0;
74         canvas_y2 = 0;
75         capture_bitmap = 0;
76         color_model_selected = 0;
77         is_cleared = 0;
78         return 0;
79 }
80
81 int VDeviceX11::open_input()
82 {
83 //printf("VDeviceX11::open_input 1\n");
84         capture_bitmap = new BC_Capture(device->in_config->w,
85                 device->in_config->h,
86                 device->in_config->screencapture_display);
87 //printf("VDeviceX11::open_input 2\n");
88
89         return 0;
90 }
91
92 int VDeviceX11::open_output()
93 {
94         if(output)
95         {
96                 output->lock_canvas("VDeviceX11::open_output");
97                 output->get_canvas()->lock_window("VDeviceX11::open_output");
98                 if(!device->single_frame)
99                         output->start_video();
100                 else
101                         output->start_single();
102                 output->get_canvas()->unlock_window();
103
104 // Enable opengl in the first routine that needs it, to reduce the complexity.
105
106                 output->unlock_canvas();
107         }
108         return 0;
109 }
110
111
112 int VDeviceX11::output_visible()
113 {
114         if(!output) return 0;
115
116         output->lock_canvas("VDeviceX11::output_visible");
117         if(output->get_canvas()->get_hidden())
118         {
119                 output->unlock_canvas();
120                 return 0;
121         }
122         else
123         {
124                 output->unlock_canvas();
125                 return 1;
126         }
127 }
128
129
130 int VDeviceX11::close_all()
131 {
132         if(output)
133         {
134                 output->lock_canvas("VDeviceX11::close_all 1");
135                 output->get_canvas()->lock_window("VDeviceX11::close_all 1");
136         }
137
138         if(output && output_frame)
139         {
140                 output->update_refresh(device, output_frame);
141                 if( device->single_frame )
142                         output->draw_refresh();
143         }
144
145
146
147
148         if(bitmap)
149         {
150                 delete bitmap;
151                 bitmap = 0;
152         }
153
154         if(output_frame)
155         {
156                 delete output_frame;
157                 output_frame = 0;
158         }
159
160         if(capture_bitmap) delete capture_bitmap;
161
162         if(output)
163         {
164
165 // Update the status bug
166                 if(!device->single_frame)
167                 {
168                         output->stop_video();
169                 }
170                 else
171                 {
172                         output->stop_single();
173                 }
174
175                 output->get_canvas()->unlock_window();
176                 output->unlock_canvas();
177         }
178
179
180         reset_parameters();
181         return 0;
182 }
183
184 int VDeviceX11::read_buffer(VFrame *frame)
185 {
186         capture_bitmap->capture_frame(frame, device->input_x, device->input_y);
187         return 0;
188 }
189
190
191 int VDeviceX11::get_best_colormodel(Asset *asset)
192 {
193         return BC_RGB888;
194 }
195
196
197 int VDeviceX11::get_best_colormodel(int colormodel)
198 {
199         int result = -1;
200
201         if(device->out_config->driver == PLAYBACK_X11_GL)
202         {
203                 if(colormodel == BC_RGB888 ||
204                         colormodel == BC_RGBA8888 ||
205                         colormodel == BC_YUV888 ||
206                         colormodel == BC_YUVA8888 ||
207                         colormodel == BC_RGB_FLOAT ||
208                         colormodel == BC_RGBA_FLOAT)
209                 {
210                         return colormodel;
211                 }
212                 return BC_RGB888;
213         }
214
215         if(!device->single_frame)
216         {
217                 switch(colormodel)
218                 {
219                         case BC_YUV420P:
220                         case BC_YUV422P:
221                         case BC_YUV422:
222                                 result = colormodel;
223                                 break;
224                 }
225         }
226
227 // 2 more colormodels are supported by OpenGL
228         if(device->out_config->driver == PLAYBACK_X11_GL)
229         {
230                 if(colormodel == BC_RGB_FLOAT ||
231                         colormodel == BC_RGBA_FLOAT)
232                         result = colormodel;
233         }
234
235         if(result < 0)
236         {
237                 switch(colormodel)
238                 {
239                         case BC_RGB888:
240                         case BC_RGBA8888:
241                         case BC_YUV888:
242                         case BC_YUVA8888:
243                                 result = colormodel;
244                                 break;
245
246                         default:
247                                 output->lock_canvas("VDeviceX11::get_best_colormodel");
248                                 result = output->get_canvas()->get_color_model();
249                                 output->unlock_canvas();
250                                 break;
251                 }
252         }
253
254         return result;
255 }
256
257
258 void VDeviceX11::new_output_buffer(VFrame **result, int colormodel)
259 {
260 //printf("VDeviceX11::new_output_buffer 1\n");
261         output->lock_canvas("VDeviceX11::new_output_buffer");
262         output->get_canvas()->lock_window("VDeviceX11::new_output_buffer 1");
263
264 // Get the best colormodel the display can handle.
265         int best_colormodel = get_best_colormodel(colormodel);
266
267 // Only create OpenGL Pbuffer and texture.
268         if( device->out_config->driver == PLAYBACK_X11_GL ) {
269 // Create bitmap for initial load into texture.
270 // Not necessary to do through Playback3D.....yet
271                 if( !output_frame ) {
272                         output_frame = new VFrame(0, -1,
273                                 device->out_w, device->out_h, colormodel, -1);
274 //BUFFER2(output_frame->get_rows()[0], "VDeviceX11::new_output_buffer 1");
275                 }
276
277                 window_id = output->get_canvas()->get_id();
278                 output_frame->set_opengl_state(VFrame::RAM);
279         }
280         else {
281 // Conform existing bitmap to new colormodel and output size
282                 if( bitmap ) {
283 // Restart if output size changed or output colormodel changed.
284 // May have to recreate if transferring between windowed and fullscreen.
285                         if( !color_model_selected || ( !bitmap->hardware_scaling() &&
286                                 (bitmap->get_w() != output->get_canvas()->get_w() ||
287                                  bitmap->get_h() != output->get_canvas()->get_h()) ) ||
288                             colormodel != output_frame->get_color_model() ) {
289                                 int size_change =
290                                         bitmap->get_w() != output->get_canvas()->get_w() ||
291                                         bitmap->get_h() != output->get_canvas()->get_h();
292 //printf("VDeviceX11::new_output_buffer %d\n", __LINE__);
293                                 delete bitmap;        bitmap = 0;
294                                 delete output_frame;  output_frame = 0;
295
296 // Blank only if size changed
297                                 if( size_change ) {
298                                         output->get_canvas()->set_color(BLACK);
299                                         output->get_canvas()->draw_box(0, 0, output->w, output->h);
300                                         output->get_canvas()->flash();
301                                 }
302                         }
303                 }
304
305 // Create new bitmap
306                 if( !bitmap ) {
307 // Try hardware accelerated
308                         bitmap_type = BITMAP_TEMP;
309                         switch( best_colormodel ) {
310                         case BC_YUV420P:
311                                 if( device->out_config->driver == PLAYBACK_X11_XV &&
312                                     output->get_canvas()->accel_available(best_colormodel, 0) &&
313                                     !output->use_scrollbars )
314                                         bitmap_type = BITMAP_PRIMARY;
315                                 break;
316
317                         case BC_YUV422P:
318                                 if( device->out_config->driver == PLAYBACK_X11_XV &&
319                                     output->get_canvas()->accel_available(best_colormodel, 0) &&
320                                     !output->use_scrollbars )
321                                         bitmap_type = BITMAP_PRIMARY;
322                                 else if( device->out_config->driver == PLAYBACK_X11_XV &&
323                                     output->get_canvas()->accel_available(BC_YUV422, 0) ) {
324                                         bitmap = new BC_Bitmap(output->get_canvas(),
325                                                 device->out_w, device->out_h, BC_YUV422, 1);
326                                 }
327                                 break;
328
329                         case BC_YUV422:
330                                 if( device->out_config->driver == PLAYBACK_X11_XV &&
331                                     output->get_canvas()->accel_available(best_colormodel, 0) &&
332                                     !output->use_scrollbars )
333                                         bitmap_type = BITMAP_PRIMARY;
334                                 else if( device->out_config->driver == PLAYBACK_X11_XV &&
335                                     output->get_canvas()->accel_available(BC_YUV422P, 0) ) {
336                                         bitmap = new BC_Bitmap(output->get_canvas(),
337                                                 device->out_w, device->out_h, BC_YUV422P, 1);
338                                 }
339                                 break;
340                         }
341                         if( bitmap_type == BITMAP_PRIMARY ) {
342                                 bitmap = new BC_Bitmap(output->get_canvas(),
343                                         device->out_w, device->out_h, best_colormodel, 1);
344                                 output_frame = new VFrame(bitmap,
345                                         device->out_w, device->out_h, best_colormodel, -1);
346                         }
347                         else {
348 // Try default colormodel
349                                 best_colormodel = output->get_canvas()->get_color_model();
350                                 bitmap = new BC_Bitmap(output->get_canvas(),
351                                         output->get_canvas()->get_w(), output->get_canvas()->get_h(),
352                                         best_colormodel, 1);
353 // Intermediate frame
354                                 output_frame = new VFrame(0, -1,
355                                         device->out_w, device->out_h, colormodel, -1);
356                         }
357
358                         color_model_selected = 1;
359                 }
360                 else if( bitmap_type == BITMAP_PRIMARY ) {
361 // Update the ring buffer
362                         output_frame->set_memory(bitmap);
363                 }
364         }
365
366         *result = output_frame;
367 //printf("VDeviceX11::new_output_buffer 10 %d\n", output->get_canvas()->get_window_lock());
368
369         output->get_canvas()->unlock_window();
370         output->unlock_canvas();
371 }
372
373
374 int VDeviceX11::start_playback()
375 {
376 // Record window is initialized when its monitor starts.
377         if(!device->single_frame)
378                 output->start_video();
379         return 0;
380 }
381
382 int VDeviceX11::stop_playback()
383 {
384         if(!device->single_frame)
385                 output->stop_video();
386 // Record window goes back to monitoring
387 // get the last frame played and store it in the video_out
388         return 0;
389 }
390
391 int VDeviceX11::write_buffer(VFrame *output_channels, EDL *edl)
392 {
393 // The reason for not drawing single frame is that it is _always_ drawn
394 // when drawing draw_refresh in cwindowgui and vwindowgui
395         if( device->single_frame )
396                 return 0;
397
398         output->lock_canvas("VDeviceX11::write_buffer");
399         output->get_canvas()->lock_window("VDeviceX11::write_buffer 1");
400
401 // Canvas may be a different size than the temporary bitmap for pure software
402         int xfr_w, xfr_h;
403         if(bitmap_type == BITMAP_TEMP && !bitmap->hardware_scaling()) {
404                 xfr_w = bitmap->get_w();
405                 xfr_h = bitmap->get_h();
406         }
407         else
408                 xfr_w = xfr_h = -1;
409
410 //printf("VDeviceX11::write_buffer %d %d\n", __LINE__, output->get_canvas()->get_video_on());
411         output->get_transfers(edl, output_x1, output_y1, output_x2, output_y2,
412                 canvas_x1, canvas_y1, canvas_x2, canvas_y2, xfr_w, xfr_h);
413
414 // Convert colormodel
415 #if 0
416 // this is handled by overlay call in virtualvnode, using flatten alpha
417 // invoked when is_nested = -1 is passed to vdevicex11->overlay(...)
418         if(device->out_config->driver == PLAYBACK_X11_GL &&
419             output_frame->get_color_model() != BC_RGB888) {
420                 int cmodel = BC_RGB888;
421                 output->get_canvas()->unlock_window();
422                 output->unlock_canvas();
423                 output->mwindow->playback_3d->convert_cmodel(output, output_frame, cmodel);
424                 output_frame->reallocate(0,-1,0,0,0,output_frame->get_w(),output_frame->get_h(),cmodel,-1);
425                 output->lock_canvas("VDeviceX11::write_buffer 3");
426                 output->get_canvas()->lock_window("VDeviceX11::write_buffer 3");
427         }
428         else
429 #endif
430         if( bitmap_type == BITMAP_TEMP ) {
431 // printf("VDeviceX11::write_buffer 1 %d %d, %d %d %d %d -> %d %d %d %d\n",
432 //  output->w, output->h, in_x, in_y, in_w, in_h, out_x, out_y, out_w, out_h );
433 // fflush(stdout);
434 //printf("VDeviceX11::write_buffer %d output_channels=%p\n", __LINE__, output_channels);
435                 if( bitmap->hardware_scaling() ) {
436                         BC_CModels::transfer(bitmap->get_row_pointers(), output_channels->get_rows(),
437                                 0, 0, 0, output_channels->get_y(), output_channels->get_u(), output_channels->get_v(),
438                                 0, 0, output_channels->get_w(), output_channels->get_h(),
439                                 0, 0, bitmap->get_w(), bitmap->get_h(),
440                                 output_channels->get_color_model(), bitmap->get_color_model(),
441                                 0, output_channels->get_w(), bitmap->get_w());
442                 }
443                 else {
444                         BC_CModels::transfer(bitmap->get_row_pointers(), output_channels->get_rows(),
445                                 0, 0, 0, output_channels->get_y(), output_channels->get_u(), output_channels->get_v(),
446                                 (int)output_x1, (int)output_y1, (int)(output_x2 - output_x1), (int)(output_y2 - output_y1),
447                                 0, 0, (int)(canvas_x2 - canvas_x1), (int)(canvas_y2 - canvas_y1),
448                                 output_channels->get_color_model(), bitmap->get_color_model(),
449                                 0, output_channels->get_w(), bitmap->get_w());
450                 }
451         }
452
453 //printf("VDeviceX11::write_buffer 4 %p\n", bitmap);
454 //for(i = 0; i < 1000; i += 4) bitmap->get_data()[i] = 128;
455 //printf("VDeviceX11::write_buffer 2 %d %d %d\n", bitmap_type,
456 //  bitmap->get_color_model(), output->get_color_model());
457 //fflush(stdout);
458 //printf("VDeviceX11::write_buffer 2 %d %d, %f %f %f %f -> %f %f %f %f\n",
459 // output->w, output->h, output_x1, output_y1, output_x2, output_y2,
460 // canvas_x1, canvas_y1, canvas_x2, canvas_y2);
461
462 // Cause X server to display it
463         if( device->out_config->driver == PLAYBACK_X11_GL ) {
464 // Output is drawn in close_all if no video.
465                 if( output->get_canvas()->get_video_on() ) {
466 // Draw output frame directly.  Not used for compositing.
467                         output->get_canvas()->unlock_window();
468                         output->unlock_canvas();
469                         output->mwindow->playback_3d->write_buffer(output,
470                                 output_frame, output_x1, output_y1, output_x2, output_y2,
471                                 canvas_x1, canvas_y1, canvas_x2, canvas_y2, is_cleared);
472                         is_cleared = 0;
473                         output->lock_canvas("VDeviceX11::write_buffer 2");
474                         output->get_canvas()->lock_window("VDeviceX11::write_buffer 2");
475                 }
476         }
477         else if( bitmap->hardware_scaling() ) {
478                 output->get_canvas()->draw_bitmap(bitmap, !device->single_frame,
479                         (int)canvas_x1, (int)canvas_y1,
480                         (int)(canvas_x2 - canvas_x1), (int)(canvas_y2 - canvas_y1),
481                         (int)output_x1, (int)output_y1,
482                         (int)(output_x2 - output_x1), (int)(output_y2 - output_y1), 0);
483         }
484         else {
485                 output->get_canvas()->draw_bitmap(bitmap, !device->single_frame,
486                         (int)canvas_x1, (int)canvas_y1,
487                         (int)(canvas_x2 - canvas_x1), (int)(canvas_y2 - canvas_y1), 0, 0,
488                         (int)(canvas_x2 - canvas_x1), (int)(canvas_y2 - canvas_y1), 0);
489         }
490
491
492         output->get_canvas()->unlock_window();
493         output->unlock_canvas();
494         return 0;
495 }
496
497
498 void VDeviceX11::clear_output()
499 {
500         is_cleared = 1;
501
502         output->mwindow->playback_3d->clear_output(output,
503                 output->get_canvas()->get_video_on() ? 0 : output_frame);
504
505 }
506
507
508 void VDeviceX11::clear_input(VFrame *frame)
509 {
510         this->output->mwindow->playback_3d->clear_input(this->output, frame);
511 }
512
513 void VDeviceX11::convert_cmodel(VFrame *output, int dst_cmodel)
514 {
515         this->output->mwindow->playback_3d->convert_cmodel(this->output,
516                 output,
517                 dst_cmodel);
518 }
519
520 void VDeviceX11::do_camera(VFrame *output, VFrame *input,
521         float in_x1, float in_y1, float in_x2, float in_y2,
522         float out_x1, float out_y1, float out_x2, float out_y2)
523 {
524         this->output->mwindow->playback_3d->do_camera(
525                 this->output, output, input,
526                 in_x1, in_y1, in_x2, in_y2,
527                 out_x1, out_y1, out_x2, out_y2);
528 }
529
530
531 void VDeviceX11::do_fade(VFrame *output_temp, float fade)
532 {
533         this->output->mwindow->playback_3d->do_fade(this->output, output_temp, fade);
534 }
535
536 bool VDeviceX11::can_mask(int64_t start_position_project, MaskAutos *keyframe_set)
537 {
538         Auto *current = 0;
539         MaskAuto *keyframe = (MaskAuto*)keyframe_set->
540                 get_prev_auto(start_position_project, PLAY_FORWARD, current);
541         return keyframe->disable_opengl_masking ? 0 : 1;
542 }
543
544 void VDeviceX11::do_mask(VFrame *output_temp,
545         int64_t start_position_project, MaskAutos *keyframe_set,
546         MaskAuto *keyframe, MaskAuto *default_auto)
547 {
548         this->output->mwindow->playback_3d->do_mask(output,
549                 output_temp, start_position_project,
550                 keyframe_set, keyframe, default_auto);
551 }
552
553 void VDeviceX11::overlay(VFrame *output_frame, VFrame *input,
554 // This is the transfer from track to output frame
555                 float in_x1, float in_y1, float in_x2, float in_y2,
556                 float out_x1, float out_y1, float out_x2, float out_y2,
557                 float alpha /*0-1*/, int mode, EDL *edl, int is_nested)
558 {
559         int interpolation_type = edl->session->interpolation_type;
560
561 // printf("VDeviceX11::overlay 1:\n"
562 // "in_x1=%f in_y1=%f in_x2=%f in_y2=%f\n"
563 // "out_x1=%f out_y1=%f out_x2=%f out_y2=%f\n",
564 // in_x1, in_y1, in_x2, in_y2,
565 // out_x1, out_y1, out_x2, out_y2);
566 // Convert node coords to canvas coords in here
567
568 // If single frame playback or nested EDL, use full sized PBuffer as output.
569         if(device->single_frame || is_nested > 0)
570         {
571                 output->mwindow->playback_3d->overlay(output, input,
572                         in_x1, in_y1, in_x2, in_y2,
573                         out_x1, out_y1, out_x2, out_y2,
574                         alpha, mode, interpolation_type,
575                         output_frame, is_nested);
576 // printf("VDeviceX11::overlay 1 %p %d %d %d\n",
577 // output_frame,
578 // output_frame->get_w(),
579 // output_frame->get_h(),
580 // output_frame->get_opengl_state());
581         }
582         else
583         {
584                 output->lock_canvas("VDeviceX11::overlay");
585                 output->get_canvas()->lock_window("VDeviceX11::overlay");
586
587 // This is the transfer from output frame to canvas
588                 output->get_transfers(edl,
589                         output_x1, output_y1, output_x2, output_y2,
590                         canvas_x1, canvas_y1, canvas_x2, canvas_y2,
591                         -1, -1);
592
593                 output->get_canvas()->unlock_window();
594                 output->unlock_canvas();
595
596
597 // Get transfer from track to canvas
598                 float track_xscale = (out_x2 - out_x1) / (in_x2 - in_x1);
599                 float track_yscale = (out_y2 - out_y1) / (in_y2 - in_y1);
600                 float canvas_xscale = (float)(canvas_x2 - canvas_x1) / (output_x2 - output_x1);
601                 float canvas_yscale = (float)(canvas_y2 - canvas_y1) / (output_y2 - output_y1);
602
603
604 // Get coordinates of canvas relative to track frame
605                 float track_x1 = (float)(output_x1 - out_x1) / track_xscale + in_x1;
606                 float track_y1 = (float)(output_y1 - out_y1) / track_yscale + in_y1;
607                 float track_x2 = (float)(output_x2 - out_x2) / track_xscale + in_x2;
608                 float track_y2 = (float)(output_y2 - out_y2) / track_yscale + in_y2;
609
610 // Clamp canvas coords to track boundary
611                 if(track_x1 < 0)
612                 {
613                         float difference = -track_x1;
614                         track_x1 += difference;
615                         canvas_x1 += difference * track_xscale * canvas_xscale;
616                 }
617                 if(track_y1 < 0)
618                 {
619                         float difference = -track_y1;
620                         track_y1 += difference;
621                         canvas_y1 += difference * track_yscale * canvas_yscale;
622                 }
623
624                 if(track_x2 > input->get_w())
625                 {
626                         float difference = track_x2 - input->get_w();
627                         track_x2 -= difference;
628                         canvas_x2 -= difference * track_xscale * canvas_xscale;
629                 }
630                 if(track_y2 > input->get_h())
631                 {
632                         float difference = track_y2 - input->get_h();
633                         track_y2 -= difference;
634                         canvas_y2 -= difference * track_yscale * canvas_yscale;
635                 }
636
637 // Overlay directly from track buffer to canvas, skipping output buffer
638                 if(track_x2 > track_x1 && track_y2 > track_y1 &&
639                         canvas_x2 > canvas_x1 && canvas_y2 > canvas_y1)
640                 {
641                         output->mwindow->playback_3d->overlay(output, input,
642                                 track_x1, track_y1, track_x2, track_y2,
643                                 canvas_x1, canvas_y1, canvas_x2, canvas_y2,
644                                 alpha, mode, interpolation_type, 0, is_nested);
645                 }
646         }
647 }
648
649 void VDeviceX11::run_plugin(PluginClient *client)
650 {
651         output->mwindow->playback_3d->run_plugin(output, client);
652 }
653
654 void VDeviceX11::copy_frame(VFrame *dst, VFrame *src)
655 {
656         output->mwindow->playback_3d->copy_from(output, dst, src, 1);
657 }
658