]> git.sesse.net Git - vlc/blob - modules/video_output/wrapper.c
Added a temporary "vout display" wrapper.
[vlc] / modules / video_output / wrapper.c
1 /*****************************************************************************
2  * vout_display.c: "vout display" -> "video output" wrapper
3  *****************************************************************************
4  * Copyright (C) 2009 Laurent Aimar
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_vout_display.h>
34 #include <vlc_vout_wrapper.h>
35 #include <vlc_vout.h>
36 #include "../video_filter/filter_common.h"
37 #include <assert.h>
38
39 /*****************************************************************************
40  * Module descriptor
41  *****************************************************************************/
42 static int  Open (vlc_object_t *, const char *module);
43 static void Close(vlc_object_t *);
44
45 #define DECLARE_OPEN(name) \
46         static int Open##name(vlc_object_t *object) { return Open(object, #name); }
47
48 DECLARE_OPEN(none);
49
50 #undef DECLARE_OPEN
51
52 #define DECLARE_MODULE(name, priority)                  \
53     set_description( "Video display "#name" wrapper" )  \
54     set_shortname( "Video display "#name" wrapper" )    \
55     set_capability( "video output", priority )          \
56     set_callbacks( Open##name, Close )                  \
57     add_shortcut( #name )
58
59 vlc_module_begin()
60     set_category( CAT_VIDEO )
61     set_subcategory( SUBCAT_VIDEO_VOUT )
62
63     DECLARE_MODULE(none, 0)
64
65 vlc_module_end()
66
67 #undef DECLARE_MODULE
68
69 /*****************************************************************************
70  *
71  *****************************************************************************/
72 struct vout_sys_t {
73     char           *title;
74     vout_display_t *vd;
75     bool           use_dr;
76 };
77
78 struct picture_sys_t {
79     picture_t *direct;
80 };
81
82 /*****************************************************************************
83  * Local prototypes
84  *****************************************************************************/
85 static int  Init   (vout_thread_t *);
86 static void End    (vout_thread_t *);
87 static int  Manage (vout_thread_t *);
88 static void Render (vout_thread_t *, picture_t *);
89 static void Display(vout_thread_t *, picture_t *);
90
91 static void VoutGetDisplayCfg(vout_thread_t *,
92                               vout_display_cfg_t *, const char *title);
93
94 /*****************************************************************************
95  *
96  *****************************************************************************/
97 static int Open(vlc_object_t *object, const char *module)
98 {
99     vout_thread_t *vout = (vout_thread_t *)object;
100     vout_sys_t *sys;
101
102     msg_Err(vout, "Opening vout display wrapper");
103
104     /* */
105     sys = malloc(sizeof(*sys));
106     if (!sys)
107         return VLC_ENOMEM;
108
109     sys->title = var_CreateGetNonEmptyString(vout, "video-title");
110
111     /* */
112     video_format_t source   = vout->fmt_render;
113     source.i_visible_width  = source.i_width;
114     source.i_visible_height = source.i_height;
115     source.i_x_offset       = 0;
116     source.i_y_offset       = 0;
117
118     vout_display_state_t state;
119     VoutGetDisplayCfg(vout, &state.cfg, sys->title);
120     state.is_on_top = var_CreateGetBool(vout, "video-on-top");
121     state.sar.num = 0;
122     state.sar.den = 0;
123
124     const mtime_t double_click_timeout = 300000;
125     const mtime_t hide_timeout = var_CreateGetInteger(vout, "mouse-hide-timeout") * 1000;
126
127     sys->vd = vout_NewDisplay(vout, &source, &state, module,
128                               double_click_timeout, hide_timeout);
129     if (!sys->vd) {
130         free(sys->title);
131         free(sys);
132         return VLC_EGENERIC;
133     }
134
135     /* */
136     vout->pf_init    = Init;
137     vout->pf_end     = End;
138     vout->pf_manage  = Manage;
139     vout->pf_render  = Render;
140     vout->pf_display = Display;
141     vout->pf_control = NULL;
142     vout->p_sys      = sys;
143
144     return VLC_SUCCESS;
145 }
146
147 /*****************************************************************************
148  *
149  *****************************************************************************/
150 static void Close(vlc_object_t *object)
151 {
152     vout_thread_t *vout = (vout_thread_t *)object;
153     vout_sys_t *sys = vout->p_sys;
154
155     if (sys->vd)
156         vout_DeleteDisplay(sys->vd, NULL);
157     free(sys->title);
158     free(sys );
159 }
160
161 /*****************************************************************************
162  *
163  *****************************************************************************/
164 static int Init(vout_thread_t *vout)
165 {
166     vout_sys_t *sys = vout->p_sys;
167     vout_display_t *vd = sys->vd;
168
169     /* */
170     video_format_t source = vd->source;
171
172     vout->output.i_chroma = source.i_chroma;
173     vout->output.i_width  = source.i_width;
174     vout->output.i_height = source.i_height;
175     vout->output.i_aspect = source.i_aspect;
176     vout->output.i_rmask  = source.i_rmask;
177     vout->output.i_gmask  = source.i_gmask;
178     vout->output.i_bmask  = source.i_bmask;
179     vout->output.pf_setpalette = NULL; /* FIXME What to do ? Seems unused anyway */
180
181     /* also set fmt_out (completly broken API) */
182     vout->fmt_out.i_chroma         = vout->output.i_chroma;
183     vout->fmt_out.i_width          =
184     vout->fmt_out.i_visible_width  = vout->output.i_width;
185     vout->fmt_out.i_height         =
186     vout->fmt_out.i_visible_height = vout->output.i_height;
187     vout->fmt_out.i_aspect         = vout->output.i_aspect;
188     vout->fmt_out.i_x_offset       = 0;
189     vout->fmt_out.i_y_offset       = 0;
190
191     /* TODO */
192 #if 0
193     if (p_vout->fmt_render.i_visible_width  != source.i_visible_width ||
194         p_vout->fmt_render.i_visible_height != source.i_visible_height ||
195         p_vout->fmt_render.i_x_offset != source.i_x_offset ||
196         p_vout->fmt_render.i_y_offset != source.i_y_offset )
197     {
198         p_vout->i_changes |= VOUT_CROP_CHANGE;
199     }
200 #endif
201     if (vout->b_on_top)
202         vout_SetDisplayOnTop(vd, true);
203
204     /* XXX For non dr case, the current vout implementation force us to
205      * create at most 1 direct picture (otherwise the buffers will be kept
206      * referenced even through the Init/End.
207      */
208     sys->use_dr = !vout_IsDisplayFiltered(vd);
209     const bool allow_dr = !vd->info.has_pictures_invalid && sys->use_dr;
210     const int picture_max = allow_dr ? VOUT_MAX_PICTURES : 1;
211     for (vout->output.i_pictures = 0;
212             vout->output.i_pictures < picture_max;
213                 vout->output.i_pictures++) {
214         /* Find an empty picture slot */
215         picture_t *picture = NULL;
216         for (int index = 0; index < VOUT_MAX_PICTURES; index++) {
217             if (vout->p_picture[index].i_status == FREE_PICTURE) {
218                 picture = &vout->p_picture[index];
219                 break;
220             }
221         }
222         if (!picture)
223             break;
224         memset(picture, 0, sizeof(*picture));
225
226         picture->p_sys = malloc(sizeof(*picture->p_sys));
227
228         if (sys->use_dr) {
229             picture_t *direct = vout_display_Get(vd);
230             if (!direct)
231                 break;
232             picture->format = direct->format;
233             picture->i_planes = direct->i_planes;
234             for (int i = 0; i < direct->i_planes; i++)
235                 picture->p[i] = direct->p[i];
236             picture->b_slow = vd->info.is_slow;
237
238             picture->p_sys->direct = direct;
239         } else {
240             vout_AllocatePicture(VLC_OBJECT(vd), picture,
241                                  vd->source.i_chroma,
242                                  vd->source.i_width, vd->source.i_height,
243                                  vd->source.i_aspect);
244             if (!picture->i_planes)
245                 break;
246             picture->p_sys->direct = NULL;
247         }
248         picture->i_status = DESTROYED_PICTURE;
249         picture->i_type    = DIRECT_PICTURE;
250
251         vout->output.pp_picture[vout->output.i_pictures] = picture;
252     }
253     return VLC_SUCCESS;
254 }
255
256 /*****************************************************************************
257  *
258  *****************************************************************************/
259 static void End(vout_thread_t *vout)
260 {
261     vout_sys_t *sys = vout->p_sys;
262
263     for (int i = 0; i < VOUT_MAX_PICTURES; i++) {
264         picture_t *picture = &vout->p_picture[i];
265
266         if (picture->i_type != DIRECT_PICTURE)
267             continue;
268
269         if (picture->p_sys->direct)
270             picture_Release(picture->p_sys->direct);
271         if (!sys->use_dr)
272             free(picture->p_data_orig);
273         free(picture->p_sys);
274     }
275 }
276
277 /*****************************************************************************
278  *
279  *****************************************************************************/
280 static int Manage(vout_thread_t *vout)
281 {
282     vout_sys_t *sys = vout->p_sys;
283     vout_display_t *vd = sys->vd;
284
285     while (vout->i_changes & (VOUT_FULLSCREEN_CHANGE |
286                               VOUT_ASPECT_CHANGE |
287                               VOUT_ZOOM_CHANGE |
288                               VOUT_SCALE_CHANGE |
289                               VOUT_ON_TOP_CHANGE |
290                               VOUT_CROP_CHANGE)) {
291         /* */
292         if (vout->i_changes & VOUT_FULLSCREEN_CHANGE) {
293             vout->b_fullscreen = !vout->b_fullscreen;
294
295             var_SetBool(vout, "fullscreen", vout->b_fullscreen);
296             vout_SetDisplayFullscreen(vd, vout->b_fullscreen);
297             vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
298         }
299         if (vout->i_changes & VOUT_ASPECT_CHANGE) {
300             vout->output.i_aspect   =
301             vout->fmt_out.i_aspect  = vout->fmt_in.i_aspect;
302             vout->fmt_out.i_sar_num = vout->fmt_in.i_sar_num;
303             vout->fmt_out.i_sar_den = vout->fmt_in.i_sar_den;
304
305             vout_SetDisplayAspect(vd, vout->fmt_in.i_sar_num, vout->fmt_in.i_sar_den);
306
307             vout->i_changes &= ~VOUT_ASPECT_CHANGE;
308         }
309         if (vout->i_changes & VOUT_ZOOM_CHANGE) {
310             const float zoom = var_GetFloat(vout, "scale");
311
312             unsigned den = ZOOM_FP_FACTOR;
313             unsigned num = den * zoom;
314             if (num < (ZOOM_FP_FACTOR+9) / 10)
315                 num = (ZOOM_FP_FACTOR+9) / 10;
316             else if (num > ZOOM_FP_FACTOR * 10)
317                 num = ZOOM_FP_FACTOR * 10;
318
319             vout_SetDisplayZoom(vd, num, den);
320
321             vout->i_changes &= ~VOUT_ZOOM_CHANGE;
322         }
323         if (vout->i_changes & VOUT_SCALE_CHANGE) {
324             const bool is_display_filled = var_GetBool(vout, "autoscale");
325
326             vout_SetDisplayFilled(vd, is_display_filled);
327
328             vout->i_changes &= ~VOUT_SCALE_CHANGE;
329         }
330         if (vout->i_changes & VOUT_ON_TOP_CHANGE) {
331             vout_SetDisplayOnTop(vd, vout->b_on_top);
332
333             vout->i_changes &= ~VOUT_ON_TOP_CHANGE;
334         }
335         if (vout->i_changes & VOUT_CROP_CHANGE) {
336             const video_format_t crop = vout->fmt_in;
337             const video_format_t org = vout->fmt_render;
338             /* FIXME because of rounding errors, the reconstructed ratio is wrong */
339             unsigned num = 0;
340             unsigned den = 0;
341             if (crop.i_x_offset == org.i_x_offset &&
342                 crop.i_visible_width == org.i_visible_width &&
343                 crop.i_y_offset == org.i_y_offset + (org.i_visible_height - crop.i_visible_height)/2) {
344                 vlc_ureduce(&num, &den,
345                             crop.i_visible_width * crop.i_sar_num,
346                             crop.i_visible_height * crop.i_sar_den, 0);
347             } else if (crop.i_y_offset == org.i_y_offset &&
348                        crop.i_visible_height == org.i_visible_height &&
349                        crop.i_x_offset == org.i_x_offset + (org.i_visible_width - crop.i_visible_width)/2) {
350                 vlc_ureduce(&num, &den,
351                             crop.i_visible_width * crop.i_sar_num,
352                             crop.i_visible_height * crop.i_sar_den, 0);
353             }
354             vout_SetDisplayCrop(vd, num, den,
355                                 crop.i_x_offset, crop.i_y_offset,
356                                 crop.i_visible_width, crop.i_visible_height);
357             vout->i_changes &= ~VOUT_CROP_CHANGE;
358         }
359
360     }
361
362     if (sys->use_dr && vout_AreDisplayPicturesInvalid(vd)) {
363         vout->i_changes |= VOUT_PICTURE_BUFFERS_CHANGE;
364     }
365
366     vout_ManageDisplay(vd);
367     return VLC_SUCCESS;
368 }
369
370 /*****************************************************************************
371  * Render
372  *****************************************************************************/
373 static void Render(vout_thread_t *vout, picture_t *picture)
374 {
375     vout_sys_t *sys = vout->p_sys;
376     vout_display_t *vd = sys->vd;
377
378     assert(sys->use_dr || !picture->p_sys->direct);
379
380     if (sys->use_dr) {
381         assert(!vout_IsDisplayFiltered(vd));
382         assert(picture->p_sys->direct);
383
384         vout_display_Prepare(vd, picture->p_sys->direct);
385     } else {
386         picture_t *filtered = vout_FilterDisplay(vd, picture);
387         if (filtered) {
388             picture_t *direct = picture->p_sys->direct = vout_display_Get(vd);
389             if (direct) {
390                 picture_Copy(direct, filtered);
391                 vout_display_Prepare(vd, direct);
392             }
393             picture_Release(filtered);
394         }
395     }
396 }
397
398 /*****************************************************************************
399  *
400  *****************************************************************************/
401 static void Display(vout_thread_t *vout, picture_t *picture)
402 {
403     vout_sys_t *sys = vout->p_sys;
404     vout_display_t *vd = sys->vd;
405
406     picture_t *direct = picture->p_sys->direct;
407     if (!direct)
408         return;
409
410     /* XXX This is a hack that will work with current vout_display_t modules */
411     if (sys->use_dr)
412         picture_Hold(direct);
413
414      vout_display_Display(vd, direct);
415
416      if (!sys->use_dr)
417          picture->p_sys->direct = NULL;
418 }
419
420 static void VoutGetDisplayCfg(vout_thread_t *vout, vout_display_cfg_t *cfg, const char *title)
421 {
422     /* Load configuration */
423     cfg->is_fullscreen = var_CreateGetBool(vout, "fullscreen");
424     cfg->display.title = title;
425     cfg->display.width   = var_CreateGetInteger(vout, "width");
426     cfg->display.height  = var_CreateGetInteger(vout, "height");
427     cfg->is_display_filled  = var_CreateGetBool(vout, "autoscale");
428     cfg->display.sar.num = 1; /* TODO monitor AR */
429     cfg->display.sar.den = 1;
430     unsigned zoom_den = 1000;
431     unsigned zoom_num = zoom_den * var_CreateGetFloat(vout, "scale");
432     vlc_ureduce(&zoom_num, &zoom_den, zoom_num, zoom_den, 0);
433     cfg->zoom.num = zoom_num;
434     cfg->zoom.den = zoom_den;
435     cfg->align.vertical = VOUT_DISPLAY_ALIGN_CENTER;
436     cfg->align.horizontal = VOUT_DISPLAY_ALIGN_CENTER;
437     const int align_mask = var_CreateGetInteger(vout, "align");
438     if (align_mask & 0x1)
439         cfg->align.horizontal = VOUT_DISPLAY_ALIGN_LEFT;
440     else if (align_mask & 0x2)
441         cfg->align.horizontal = VOUT_DISPLAY_ALIGN_RIGHT;
442     if (align_mask & 0x4)
443         cfg->align.horizontal = VOUT_DISPLAY_ALIGN_TOP;
444     else if (align_mask & 0x8)
445         cfg->align.horizontal = VOUT_DISPLAY_ALIGN_BOTTOM;
446 }
447