1 /*****************************************************************
2  * gmerlin - a general purpose multimedia framework and applications
3  *
4  * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5  * gmerlin-general@lists.sourceforge.net
6  * http://gmerlin.sourceforge.net
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  * *****************************************************************/
21 
22 #include <string.h>
23 #include <x11/x11.h>
24 #include <x11/x11_window_private.h>
25 
26 #include <gmerlin/log.h>
27 #define LOG_DOMAIN "x11_video"
28 
29 static video_driver_t const * const drivers[] =
30   {
31     &ximage_driver,
32 #ifdef HAVE_LIBXV
33     &xv_driver,
34 #endif
35 #ifdef HAVE_GLX
36     &gl_driver,
37 #endif
38   };
39 
set_brightness(bg_x11_window_t * w)40 static int set_brightness(bg_x11_window_t * w)
41   {
42   if(w->video_open &&
43      w->current_driver->driver->set_brightness &&
44      (w->current_driver->flags & DRIVER_FLAG_BRIGHTNESS))
45     {
46     w->current_driver->driver->set_brightness(w->current_driver, w->brightness);
47     return 1;
48     }
49   return 0;
50   }
51 
bg_x11_window_set_brightness(bg_x11_window_t * w,float val)52 int bg_x11_window_set_brightness(bg_x11_window_t * w, float val)
53   {
54   w->brightness = val;
55   return set_brightness(w);
56   }
57 
set_saturation(bg_x11_window_t * w)58 static int set_saturation(bg_x11_window_t * w)
59   {
60   if(w->video_open &&
61      w->current_driver->driver->set_saturation &&
62      (w->current_driver->flags & DRIVER_FLAG_SATURATION))
63     {
64     w->current_driver->driver->set_saturation(w->current_driver, w->saturation);
65     return 1;
66     }
67   return 0;
68   }
69 
bg_x11_window_set_saturation(bg_x11_window_t * w,float val)70 int bg_x11_window_set_saturation(bg_x11_window_t * w, float val)
71   {
72   w->saturation = val;
73   return set_saturation(w);
74   }
75 
set_contrast(bg_x11_window_t * w)76 static int set_contrast(bg_x11_window_t * w)
77   {
78   if(w->video_open &&
79      w->current_driver->driver->set_contrast &&
80      (w->current_driver->flags & DRIVER_FLAG_CONTRAST))
81     {
82     w->current_driver->driver->set_contrast(w->current_driver, w->contrast);
83     return 1;
84     }
85   return 0;
86   }
87 
bg_x11_window_set_contrast(bg_x11_window_t * w,float val)88 int bg_x11_window_set_contrast(bg_x11_window_t * w, float val)
89   {
90   w->contrast = val;
91   return set_contrast(w);
92   }
93 
init(bg_x11_window_t * w)94 static void init(bg_x11_window_t * w)
95   {
96   int num_drivers, i;
97   num_drivers = sizeof(drivers) / sizeof(drivers[0]);
98 
99   for(i = 0; i < num_drivers; i++)
100     {
101     w->drivers[i].driver = drivers[i];
102     w->drivers[i].win = w;
103     if(w->drivers[i].driver->init)
104       w->drivers[i].driver->init(&w->drivers[i]);
105     }
106 
107   /*
108    *  Possible TODO: Get screen resolution
109    *  (maybe better if we don't care at all)
110    */
111 
112   w->window_format.pixel_width = 1;
113   w->window_format.pixel_height = 1;
114   w->idle_counter = 0;
115   }
116 
bg_x11_window_cleanup_video(bg_x11_window_t * w)117 void bg_x11_window_cleanup_video(bg_x11_window_t * w)
118   {
119   int num_drivers, i;
120   num_drivers = sizeof(drivers) / sizeof(drivers[0]);
121 
122   /* Not initialized */
123   if(!w->drivers[0].driver)
124     return;
125 
126   for(i = 0; i < num_drivers; i++)
127     {
128     if(w->drivers[i].driver->cleanup)
129       w->drivers[i].driver->cleanup(&w->drivers[i]);
130     if(w->drivers[i].pixelformats)
131       free(w->drivers[i].pixelformats);
132     }
133   }
134 
135 
136 /* For Video output */
137 
138 #define PAD_SIZE 16
139 #define PAD(sz) (((sz+PAD_SIZE-1) / PAD_SIZE) * PAD_SIZE)
140 
bg_x11_window_open_video(bg_x11_window_t * w,gavl_video_format_t * format)141 int bg_x11_window_open_video(bg_x11_window_t * w,
142                              gavl_video_format_t * format)
143   {
144   int num_drivers;
145   int i;
146   int force_hw_scale;
147 
148   int min_penalty, min_index;
149 
150   w->do_sw_scale = 0;
151   w->current_driver = NULL;
152   if(!w->drivers_initialized)
153     {
154     init(w);
155     w->drivers_initialized = 1;
156     }
157 
158 
159   gavl_video_format_copy(&w->video_format, format);
160 
161   /* Pad sizes, which might screw up some drivers */
162 
163   w->video_format.frame_width = PAD(w->video_format.frame_width);
164   w->video_format.frame_height = PAD(w->video_format.frame_height);
165 
166   if(w->auto_resize)
167     {
168     bg_x11_window_resize(w,
169                          (w->video_format.image_width *
170                           w->video_format.pixel_width) /
171                          w->video_format.pixel_height,
172                          w->video_format.image_height);
173     }
174 
175   num_drivers = sizeof(drivers) / sizeof(drivers[0]);
176 
177   /* Query the best pixelformats for each driver */
178 
179   for(i = 0; i < num_drivers; i++)
180     {
181     w->drivers[i].pixelformat =
182       gavl_pixelformat_get_best(format->pixelformat, w->drivers[i].pixelformats,
183                                 &w->drivers[i].penalty);
184     }
185 
186 
187   /*
188    *  Now, get the driver with the lowest penalty.
189    *  Scaling would be nice as well
190    */
191   force_hw_scale = w->force_hw_scale;
192 
193   while(1)
194     {
195     min_index = -1;
196     min_penalty = -1;
197 
198     /* Find out best driver. Drivers, which don't initialize,
199        have the pixelformat unset */
200     for(i = 0; i < num_drivers; i++)
201       {
202       if(!w->drivers[i].driver->can_scale && force_hw_scale)
203         continue;
204       if(w->drivers[i].pixelformat != GAVL_PIXELFORMAT_NONE)
205         {
206         if((min_penalty < 0) || w->drivers[i].penalty < min_penalty)
207           {
208           min_penalty = w->drivers[i].penalty;
209           min_index = i;
210           }
211         }
212       }
213 
214     /* If we have found no driver, playing video is doomed to failure */
215     if(min_penalty < 0)
216       {
217       if(force_hw_scale)
218         {
219         force_hw_scale = 0;
220         continue;
221         }
222       else
223         break;
224       }
225 
226 
227     if(!w->drivers[min_index].driver->open)
228       {
229       w->current_driver = &w->drivers[min_index];
230       break;
231       }
232     /* Flag as unusable */
233     bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Trying %s driver",
234            w->drivers[min_index].driver->name);
235     if(!w->drivers[min_index].driver->open(&w->drivers[min_index]))
236       {
237       bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Opening %s driver failed",
238              w->drivers[min_index].driver->name);
239 
240       w->drivers[min_index].pixelformat = GAVL_PIXELFORMAT_NONE;
241       }
242     else
243       {
244       w->current_driver = &w->drivers[min_index];
245       bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Opening %s driver succeeded",
246              w->drivers[min_index].driver->name);
247       break;
248       }
249     }
250   if(!w->current_driver)
251     return 0;
252 
253 
254   w->video_format.pixelformat = w->current_driver->pixelformat;
255 
256   gavl_video_format_copy(format, &w->video_format);
257 
258   /* All other values are already set or will be set by set_rectangles */
259   w->window_format.pixelformat = format->pixelformat;
260 
261   if(!w->current_driver->driver->can_scale)
262     {
263     w->do_sw_scale = 1;
264 
265     }
266 
267   set_contrast(w);
268   set_saturation(w);
269   set_brightness(w);
270 
271   XSync(w->dpy, False);
272   bg_x11_window_handle_events(w, 0);
273 
274   return 1;
275   }
276 
bg_x11_window_create_frame(bg_x11_window_t * w)277 gavl_video_frame_t * bg_x11_window_create_frame(bg_x11_window_t * w)
278   {
279   if(!w->do_sw_scale)
280     {
281     if(w->current_driver->driver->create_frame)
282       return w->current_driver->driver->create_frame(w->current_driver);
283     else
284       return gavl_video_frame_create(&w->video_format);
285     }
286   else
287     return gavl_video_frame_create(&w->video_format);
288   }
289 
bg_x11_window_destroy_frame(bg_x11_window_t * w,gavl_video_frame_t * f)290 void bg_x11_window_destroy_frame(bg_x11_window_t * w, gavl_video_frame_t * f)
291   {
292   if(!w->do_sw_scale)
293     {
294     if(w->current_driver->driver->destroy_frame)
295       w->current_driver->driver->destroy_frame(w->current_driver, f);
296     else
297       gavl_video_frame_destroy(f);
298     }
299   else
300     gavl_video_frame_destroy(f);
301   }
302 
303 #define PADD_SIZE 128
304 #define PADD_IMAGE_SIZE(s) \
305 s = ((s + PADD_SIZE - 1) / PADD_SIZE) * PADD_SIZE
306 
bg_x11_window_set_rectangles(bg_x11_window_t * w,gavl_rectangle_f_t * src_rect,gavl_rectangle_i_t * dst_rect)307 void bg_x11_window_set_rectangles(bg_x11_window_t * w,
308                                   gavl_rectangle_f_t * src_rect,
309                                   gavl_rectangle_i_t * dst_rect)
310   {
311   gavl_video_options_t * opt;
312   gavl_rectangle_f_copy(&w->src_rect, src_rect);
313   gavl_rectangle_i_copy(&w->dst_rect, dst_rect);
314 
315   if(!w->video_open)
316     w->video_open = 1;
317 
318   if(w->current_driver && w->do_sw_scale)
319     {
320 
321     if((w->window_format.image_width > w->window_format.frame_width) ||
322        (w->window_format.image_height > w->window_format.frame_height))
323       {
324       w->window_format.frame_width = w->window_format.image_width;
325       w->window_format.frame_height = w->window_format.image_height;
326 
327       PADD_IMAGE_SIZE(w->window_format.frame_width);
328       PADD_IMAGE_SIZE(w->window_format.frame_height);
329 
330       if(w->window_frame)
331         {
332         if(w->current_driver->driver->destroy_frame)
333           w->current_driver->driver->destroy_frame(w->current_driver,
334                                                    w->window_frame);
335         else
336           gavl_video_frame_destroy(w->window_frame);
337         w->window_frame = NULL;
338         }
339       }
340 
341     if(!w->window_frame)
342       {
343       if(w->current_driver->driver->create_frame)
344         w->window_frame = w->current_driver->driver->create_frame(w->current_driver);
345       else
346         w->window_frame = gavl_video_frame_create(&w->window_format);
347       }
348 
349     /* Clear window */
350     gavl_video_frame_clear(w->window_frame, &w->window_format);
351 
352     /* Reinitialize scaler */
353 
354     opt = gavl_video_scaler_get_options(w->scaler);
355     gavl_video_options_set_rectangles(opt, &w->src_rect,
356                                       &w->dst_rect);
357 
358     gavl_video_scaler_init(w->scaler,
359                            &w->video_format,
360                            &w->window_format);
361     }
362   bg_x11_window_clear(w);
363   }
364 
365 #undef PADD_IMAGE_SIZE
366 
bg_x11_window_put_frame_internal(bg_x11_window_t * w,gavl_video_frame_t * f)367 void bg_x11_window_put_frame_internal(bg_x11_window_t * w,
368                                       gavl_video_frame_t * f)
369   {
370 
371   if(w->do_sw_scale)
372     {
373     gavl_video_scaler_scale(w->scaler, f, w->window_frame);
374     w->current_driver->driver->put_frame(w->current_driver,
375                                          w->window_frame);
376     }
377   else
378     w->current_driver->driver->put_frame(w->current_driver, f);
379   }
380 
bg_x11_window_put_frame(bg_x11_window_t * w,gavl_video_frame_t * f)381 void bg_x11_window_put_frame(bg_x11_window_t * w, gavl_video_frame_t * f)
382   {
383   w->still_mode = 0;
384   bg_x11_window_put_frame_internal(w, f);
385   }
386 
bg_x11_window_put_still(bg_x11_window_t * w,gavl_video_frame_t * f)387 void bg_x11_window_put_still(bg_x11_window_t * w, gavl_video_frame_t * f)
388   {
389   w->still_mode = 1;
390   if(!w->still_frame)
391     {
392     w->still_frame = bg_x11_window_create_frame(w);
393     }
394   gavl_video_frame_copy(&w->video_format, w->still_frame, f);
395   bg_x11_window_put_frame_internal(w, w->still_frame);
396   }
397 
398 
bg_x11_window_close_video(bg_x11_window_t * w)399 void bg_x11_window_close_video(bg_x11_window_t * w)
400   {
401   if(w->window_frame)
402     {
403     if(w->current_driver->driver->destroy_frame)
404       w->current_driver->driver->destroy_frame(w->current_driver, w->window_frame);
405     else
406       gavl_video_frame_destroy(w->window_frame);
407     w->window_frame = NULL;
408     }
409   if(w->still_frame)
410     {
411     bg_x11_window_destroy_frame(w, w->still_frame);
412     w->still_frame = NULL;
413     }
414 
415   if(w->overlay_streams)
416     {
417     free(w->overlay_streams);
418     w->num_overlay_streams = 0;
419     w->overlay_streams = NULL;
420     }
421 
422   if(w->current_driver->driver->close)
423     w->current_driver->driver->close(w->current_driver);
424 
425   w->video_open = 0;
426 
427   XSync(w->dpy, False);
428   bg_x11_window_handle_events(w, 0);
429   }
430 
431