1 /*****************************************************************************
2 * vout_display.c: "vout display" -> "video output" wrapper
3 *****************************************************************************
4 * Copyright (C) 2009 Laurent Aimar
7 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_vout_display.h>
34 #include <vlc_vout_wrapper.h>
36 #include "../video_filter/filter_common.h"
39 /*****************************************************************************
41 *****************************************************************************/
42 static int Open (vlc_object_t
*);
43 static void Close(vlc_object_t
*);
46 set_category( CAT_VIDEO
)
47 set_subcategory( SUBCAT_VIDEO_VOUT
)
49 set_description( "Transitional video display wrapper" )
50 set_shortname( "Video display wrapper" )
51 set_capability( "video output", 210 )
52 set_callbacks( Open
, Close
)
56 /*****************************************************************************
58 *****************************************************************************/
65 struct picture_sys_t
{
69 /*****************************************************************************
71 *****************************************************************************/
72 static int Init (vout_thread_t
*);
73 static void End (vout_thread_t
*);
74 static int Manage (vout_thread_t
*);
75 static void Render (vout_thread_t
*, picture_t
*);
76 static void Display(vout_thread_t
*, picture_t
*);
78 static void VoutGetDisplayCfg(vout_thread_t
*,
79 vout_display_cfg_t
*, const char *title
);
81 static int Forward(vlc_object_t
*, char const *,
82 vlc_value_t
, vlc_value_t
, void *);
85 /*****************************************************************************
87 *****************************************************************************/
88 static int Open(vlc_object_t
*object
)
90 vout_thread_t
*vout
= (vout_thread_t
*)object
;
93 msg_Dbg(vout
, "Opening vout display wrapper");
96 sys
= malloc(sizeof(*sys
));
100 sys
->title
= var_CreateGetNonEmptyString(vout
, "video-title");
103 video_format_t source
= vout
->fmt_render
;
104 source
.i_visible_width
= source
.i_width
;
105 source
.i_visible_height
= source
.i_height
;
106 source
.i_x_offset
= 0;
107 source
.i_y_offset
= 0;
109 vout_display_state_t state
;
110 VoutGetDisplayCfg(vout
, &state
.cfg
, sys
->title
);
111 state
.is_on_top
= var_CreateGetBool(vout
, "video-on-top");
115 const mtime_t double_click_timeout
= 300000;
116 const mtime_t hide_timeout
= var_CreateGetInteger(vout
, "mouse-hide-timeout") * 1000;
118 sys
->vd
= vout_NewDisplay(vout
, &source
, &state
, "$vout",
119 double_click_timeout
, hide_timeout
);
128 var_Create(vout
, "direct3d-desktop", VLC_VAR_BOOL
|VLC_VAR_DOINHERIT
);
129 var_AddCallback(vout
, "direct3d-desktop", Forward
, NULL
);
130 var_Create(vout
, "video-wallpaper", VLC_VAR_BOOL
|VLC_VAR_DOINHERIT
);
131 var_AddCallback(vout
, "video-wallpaper", Forward
, NULL
);
135 vout
->pf_init
= Init
;
137 vout
->pf_manage
= Manage
;
138 vout
->pf_render
= Render
;
139 vout
->pf_display
= Display
;
140 vout
->pf_control
= NULL
;
146 /*****************************************************************************
148 *****************************************************************************/
149 static void Close(vlc_object_t
*object
)
151 vout_thread_t
*vout
= (vout_thread_t
*)object
;
152 vout_sys_t
*sys
= vout
->p_sys
;
155 var_DelCallback(vout
, "direct3d-desktop", Forward
, NULL
);
156 var_DelCallback(vout
, "video-wallpaper", Forward
, NULL
);
158 vout_DeleteDisplay(sys
->vd
, NULL
);
163 /*****************************************************************************
165 *****************************************************************************/
166 static int Init(vout_thread_t
*vout
)
168 vout_sys_t
*sys
= vout
->p_sys
;
169 vout_display_t
*vd
= sys
->vd
;
172 video_format_t source
= vd
->source
;
174 vout
->output
.i_chroma
= source
.i_chroma
;
175 vout
->output
.i_width
= source
.i_width
;
176 vout
->output
.i_height
= source
.i_height
;
177 vout
->output
.i_aspect
= (int64_t)source
.i_sar_num
* source
.i_width
* VOUT_ASPECT_FACTOR
/ source
.i_sar_den
/ source
.i_height
;
178 vout
->output
.i_rmask
= source
.i_rmask
;
179 vout
->output
.i_gmask
= source
.i_gmask
;
180 vout
->output
.i_bmask
= source
.i_bmask
;
181 vout
->output
.pf_setpalette
= NULL
; /* FIXME What to do ? Seems unused anyway */
183 /* also set fmt_out (completly broken API) */
184 vout
->fmt_out
.i_chroma
= vout
->output
.i_chroma
;
185 vout
->fmt_out
.i_width
=
186 vout
->fmt_out
.i_visible_width
= vout
->output
.i_width
;
187 vout
->fmt_out
.i_height
=
188 vout
->fmt_out
.i_visible_height
= vout
->output
.i_height
;
189 vout
->fmt_out
.i_sar_num
= vout
->output
.i_aspect
* vout
->output
.i_height
;
190 vout
->fmt_out
.i_sar_den
= VOUT_ASPECT_FACTOR
* vout
->output
.i_width
;
191 vout
->fmt_out
.i_x_offset
= 0;
192 vout
->fmt_out
.i_y_offset
= 0;
194 if (vout
->fmt_in
.i_visible_width
!= source
.i_visible_width
||
195 vout
->fmt_in
.i_visible_height
!= source
.i_visible_height
||
196 vout
->fmt_in
.i_x_offset
!= source
.i_x_offset
||
197 vout
->fmt_in
.i_y_offset
!= source
.i_y_offset
)
198 vout
->i_changes
|= VOUT_CROP_CHANGE
;
201 vout_SetWindowState(vd
, VOUT_WINDOW_STATE_ABOVE
);
203 /* XXX For non dr case, the current vout implementation force us to
204 * create at most 1 direct picture (otherwise the buffers will be kept
205 * referenced even through the Init/End.
207 sys
->use_dr
= !vout_IsDisplayFiltered(vd
);
208 const bool allow_dr
= !vd
->info
.has_pictures_invalid
&& sys
->use_dr
;
209 const int picture_max
= allow_dr
? VOUT_MAX_PICTURES
: 1;
210 for (vout
->output
.i_pictures
= 0;
211 vout
->output
.i_pictures
< picture_max
;
212 vout
->output
.i_pictures
++) {
213 /* Find an empty picture slot */
214 picture_t
*picture
= NULL
;
215 for (int index
= 0; index
< VOUT_MAX_PICTURES
; index
++) {
216 if (vout
->p_picture
[index
].i_status
== FREE_PICTURE
) {
217 picture
= &vout
->p_picture
[index
];
223 memset(picture
, 0, sizeof(*picture
));
225 picture
->p_sys
= malloc(sizeof(*picture
->p_sys
));
228 picture_pool_t
*pool
= vout_display_Pool(vd
, picture_max
);
231 picture_t
*direct
= picture_pool_Get(pool
);
234 picture
->format
= direct
->format
;
235 picture
->i_planes
= direct
->i_planes
;
236 for (int i
= 0; i
< direct
->i_planes
; i
++)
237 picture
->p
[i
] = direct
->p
[i
];
238 picture
->b_slow
= vd
->info
.is_slow
;
240 picture
->p_sys
->direct
= direct
;
242 vout_AllocatePicture(VLC_OBJECT(vd
), picture
,
244 vd
->source
.i_width
, vd
->source
.i_height
,
245 vd
->source
.i_sar_num
, vd
->source
.i_sar_den
);
246 if (!picture
->i_planes
)
248 picture
->p_sys
->direct
= NULL
;
250 picture
->i_status
= DESTROYED_PICTURE
;
251 picture
->i_type
= DIRECT_PICTURE
;
253 vout
->output
.pp_picture
[vout
->output
.i_pictures
] = picture
;
258 /*****************************************************************************
260 *****************************************************************************/
261 static void End(vout_thread_t
*vout
)
263 vout_sys_t
*sys
= vout
->p_sys
;
265 for (int i
= 0; i
< VOUT_MAX_PICTURES
; i
++) {
266 picture_t
*picture
= &vout
->p_picture
[i
];
268 if (picture
->i_type
!= DIRECT_PICTURE
)
271 if (picture
->p_sys
->direct
)
272 picture_Release(picture
->p_sys
->direct
);
274 free(picture
->p_data_orig
);
275 free(picture
->p_sys
);
277 picture
->i_status
= FREE_PICTURE
;
279 if (sys
->use_dr
&& vout_AreDisplayPicturesInvalid(sys
->vd
))
280 vout_ManageDisplay(sys
->vd
, true);
283 /*****************************************************************************
285 *****************************************************************************/
286 static int Manage(vout_thread_t
*vout
)
288 vout_sys_t
*sys
= vout
->p_sys
;
289 vout_display_t
*vd
= sys
->vd
;
291 while (vout
->i_changes
& (VOUT_FULLSCREEN_CHANGE
|
298 if (vout
->i_changes
& VOUT_FULLSCREEN_CHANGE
) {
299 vout
->b_fullscreen
= !vout
->b_fullscreen
;
301 var_SetBool(vout
, "fullscreen", vout
->b_fullscreen
);
302 vout_SetDisplayFullscreen(vd
, vout
->b_fullscreen
);
303 vout
->i_changes
&= ~VOUT_FULLSCREEN_CHANGE
;
305 if (vout
->i_changes
& VOUT_ASPECT_CHANGE
) {
306 vout
->output
.i_aspect
= (int64_t)vout
->fmt_in
.i_sar_num
* vout
->fmt_in
.i_width
* VOUT_ASPECT_FACTOR
/
307 vout
->fmt_in
.i_sar_den
/ vout
->fmt_in
.i_height
;
308 vout
->fmt_out
.i_sar_num
= vout
->fmt_in
.i_sar_num
;
309 vout
->fmt_out
.i_sar_den
= vout
->fmt_in
.i_sar_den
;
311 vout_SetDisplayAspect(vd
, vout
->fmt_in
.i_sar_num
, vout
->fmt_in
.i_sar_den
);
313 vout
->i_changes
&= ~VOUT_ASPECT_CHANGE
;
315 if (vout
->i_changes
& VOUT_ZOOM_CHANGE
) {
316 const float zoom
= var_GetFloat(vout
, "scale");
318 unsigned den
= ZOOM_FP_FACTOR
;
319 unsigned num
= den
* zoom
;
320 if (num
< (ZOOM_FP_FACTOR
+9) / 10)
321 num
= (ZOOM_FP_FACTOR
+9) / 10;
322 else if (num
> ZOOM_FP_FACTOR
* 10)
323 num
= ZOOM_FP_FACTOR
* 10;
325 vout_SetDisplayZoom(vd
, num
, den
);
327 vout
->i_changes
&= ~VOUT_ZOOM_CHANGE
;
329 if (vout
->i_changes
& VOUT_SCALE_CHANGE
) {
330 const bool is_display_filled
= var_GetBool(vout
, "autoscale");
332 vout_SetDisplayFilled(vd
, is_display_filled
);
334 vout
->i_changes
&= ~VOUT_SCALE_CHANGE
;
336 if (vout
->i_changes
& VOUT_ON_TOP_CHANGE
) {
337 vout_SetWindowState(vd
, vout
->b_on_top
338 ? VOUT_WINDOW_STATE_ABOVE
339 : VOUT_WINDOW_STATE_NORMAL
);
341 vout
->i_changes
&= ~VOUT_ON_TOP_CHANGE
;
343 if (vout
->i_changes
& VOUT_CROP_CHANGE
) {
344 const video_format_t crop
= vout
->fmt_in
;
345 const video_format_t org
= vout
->fmt_render
;
346 /* FIXME because of rounding errors, the reconstructed ratio is wrong */
349 if (crop
.i_x_offset
== org
.i_x_offset
&&
350 crop
.i_visible_width
== org
.i_visible_width
&&
351 crop
.i_y_offset
== org
.i_y_offset
+ (org
.i_visible_height
- crop
.i_visible_height
)/2) {
352 vlc_ureduce(&num
, &den
,
353 crop
.i_visible_width
* crop
.i_sar_num
,
354 crop
.i_visible_height
* crop
.i_sar_den
, 0);
355 } else if (crop
.i_y_offset
== org
.i_y_offset
&&
356 crop
.i_visible_height
== org
.i_visible_height
&&
357 crop
.i_x_offset
== org
.i_x_offset
+ (org
.i_visible_width
- crop
.i_visible_width
)/2) {
358 vlc_ureduce(&num
, &den
,
359 crop
.i_visible_width
* crop
.i_sar_num
,
360 crop
.i_visible_height
* crop
.i_sar_den
, 0);
362 vout_SetDisplayCrop(vd
, num
, den
,
363 crop
.i_x_offset
, crop
.i_y_offset
,
364 crop
.i_visible_width
, crop
.i_visible_height
);
365 vout
->i_changes
&= ~VOUT_CROP_CHANGE
;
370 if (sys
->use_dr
&& vout_AreDisplayPicturesInvalid(vd
)) {
371 vout
->i_changes
|= VOUT_PICTURE_BUFFERS_CHANGE
;
373 vout_ManageDisplay(vd
, !sys
->use_dr
);
377 /*****************************************************************************
379 *****************************************************************************/
380 static void Render(vout_thread_t
*vout
, picture_t
*picture
)
382 vout_sys_t
*sys
= vout
->p_sys
;
383 vout_display_t
*vd
= sys
->vd
;
385 assert(sys
->use_dr
|| !picture
->p_sys
->direct
);
386 assert(vout_IsDisplayFiltered(vd
) == !sys
->use_dr
);
389 assert(picture
->p_sys
->direct
);
390 vout_display_Prepare(vd
, picture
->p_sys
->direct
);
392 picture_t
*direct
= picture
->p_sys
->direct
= vout_FilterDisplay(vd
, picture
);
394 vout_display_Prepare(vd
, direct
);
399 /*****************************************************************************
401 *****************************************************************************/
402 static void Display(vout_thread_t
*vout
, picture_t
*picture
)
404 vout_sys_t
*sys
= vout
->p_sys
;
405 vout_display_t
*vd
= sys
->vd
;
407 picture_t
*direct
= picture
->p_sys
->direct
;
411 /* XXX This is a hack that will work with current vout_display_t modules */
413 picture_Hold(direct
);
415 vout_display_Display(vd
, direct
);
418 for (int i
= 0; i
< picture
->i_planes
; i
++) {
419 picture
->p
[i
].p_pixels
= direct
->p
[i
].p_pixels
;
420 picture
->p
[i
].i_pitch
= direct
->p
[i
].i_pitch
;
421 picture
->p
[i
].i_lines
= direct
->p
[i
].i_lines
;
424 picture
->p_sys
->direct
= NULL
;
428 static void VoutGetDisplayCfg(vout_thread_t
*vout
, vout_display_cfg_t
*cfg
, const char *title
)
430 /* Load configuration */
431 cfg
->is_fullscreen
= var_CreateGetBool(vout
, "fullscreen");
432 cfg
->display
.title
= title
;
433 const int display_width
= var_CreateGetInteger(vout
, "width");
434 const int display_height
= var_CreateGetInteger(vout
, "height");
435 cfg
->display
.width
= display_width
> 0 ? display_width
: 0;
436 cfg
->display
.height
= display_height
> 0 ? display_height
: 0;
437 cfg
->is_display_filled
= var_CreateGetBool(vout
, "autoscale");
438 cfg
->display
.sar
.num
= 1; /* TODO monitor AR */
439 cfg
->display
.sar
.den
= 1;
440 unsigned zoom_den
= 1000;
441 unsigned zoom_num
= zoom_den
* var_CreateGetFloat(vout
, "scale");
442 vlc_ureduce(&zoom_num
, &zoom_den
, zoom_num
, zoom_den
, 0);
443 cfg
->zoom
.num
= zoom_num
;
444 cfg
->zoom
.den
= zoom_den
;
445 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_CENTER
;
446 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_CENTER
;
447 const int align_mask
= var_CreateGetInteger(vout
, "align");
448 if (align_mask
& 0x1)
449 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_LEFT
;
450 else if (align_mask
& 0x2)
451 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_RIGHT
;
452 if (align_mask
& 0x4)
453 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_TOP
;
454 else if (align_mask
& 0x8)
455 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_BOTTOM
;
459 static int Forward(vlc_object_t
*object
, char const *var
,
460 vlc_value_t oldval
, vlc_value_t newval
, void *data
)
462 vout_thread_t
*vout
= (vout_thread_t
*)object
;
466 return var_Set(vout
->p_sys
->vd
, var
, newval
);