1 /*****************************************************************************
2 * video_output.c : video output thread
4 * This module describes the programming interface for video output threads.
5 * It includes functions allowing to open a new thread, send pictures to a
6 * thread, and destroy a previously oppened video output thread.
7 *****************************************************************************
8 * Copyright (C) 2000-2007 the VideoLAN team
11 * Authors: Vincent Seguin <seguin@via.ecp.fr>
12 * Gildas Bazin <gbazin@videolan.org>
13 * Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
28 *****************************************************************************/
30 /*****************************************************************************
32 *****************************************************************************/
37 #include <vlc_common.h>
39 #include <stdlib.h> /* free() */
45 #include <vlc_filter.h>
46 #include <vlc_vout_osd.h>
49 #include "vout_internal.h"
50 #include "interlacing.h"
51 #include "postprocessing.h"
54 /*****************************************************************************
56 *****************************************************************************/
57 static void *Thread(void *);
58 static void VoutDestructor(vlc_object_t
*);
60 /* Maximum delay between 2 displayed pictures.
61 * XXX it is needed for now but should be removed in the long term.
63 #define VOUT_REDISPLAY_DELAY (INT64_C(80000))
66 * Late pictures having a delay higher than this value are thrashed.
68 #define VOUT_DISPLAY_LATE_THRESHOLD (INT64_C(20000))
70 /* Better be in advance when awakening than late... */
71 #define VOUT_MWAIT_TOLERANCE (INT64_C(1000))
74 static int VoutValidateFormat(video_format_t
*dst
,
75 const video_format_t
*src
)
77 if (src
->i_width
<= 0 || src
->i_height
<= 0)
79 if (src
->i_sar_num
<= 0 || src
->i_sar_den
<= 0)
83 video_format_Copy(dst
, src
);
84 dst
->i_chroma
= vlc_fourcc_GetCodec(VIDEO_ES
, src
->i_chroma
);
85 vlc_ureduce( &dst
->i_sar_num
, &dst
->i_sar_den
,
86 src
->i_sar_num
, src
->i_sar_den
, 50000 );
87 if (dst
->i_sar_num
<= 0 || dst
->i_sar_den
<= 0) {
91 video_format_FixRgb(dst
);
95 static vout_thread_t
*VoutCreate(vlc_object_t
*object
,
96 const vout_configuration_t
*cfg
)
98 video_format_t original
;
99 if (VoutValidateFormat(&original
, cfg
->fmt
))
102 /* Allocate descriptor */
103 vout_thread_t
*vout
= vlc_custom_create(object
,
104 sizeof(*vout
) + sizeof(*vout
->p
),
105 VLC_OBJECT_VOUT
, "video output");
107 video_format_Clean(&original
);
112 vout
->p
= (vout_thread_sys_t
*)&vout
[1];
114 vout
->p
->original
= original
;
115 vout
->p
->dpb_size
= cfg
->dpb_size
;
117 vout_control_Init(&vout
->p
->control
);
118 vout_control_PushVoid(&vout
->p
->control
, VOUT_CONTROL_INIT
);
120 vout_statistic_Init(&vout
->p
->statistic
);
122 vout
->p
->i_par_den
= 1;
124 vout_snapshot_Init(&vout
->p
->snapshot
);
126 /* Initialize locks */
127 vlc_mutex_init(&vout
->p
->picture_lock
);
128 vlc_mutex_init(&vout
->p
->vfilter_lock
);
129 vlc_mutex_init(&vout
->p
->spu_lock
);
131 /* Attach the new object now so we can use var inheritance below */
132 vlc_object_attach(vout
, object
);
134 /* Initialize subpicture unit */
135 vout
->p
->p_spu
= spu_Create(vout
);
137 /* Take care of some "interface/control" related initialisations */
140 /* Get splitter name if present */
141 char *splitter_name
= var_GetNonEmptyString(vout
, "vout-filter");
143 if (asprintf(&vout
->p
->splitter_name
, "%s,none", splitter_name
) < 0)
144 vout
->p
->splitter_name
= NULL
;
147 vout
->p
->splitter_name
= NULL
;
151 vout_InitInterlacingSupport(vout
, vout
->p
->displayed
.is_interlaced
);
154 vlc_object_set_destructor(vout
, VoutDestructor
);
157 if (vlc_clone(&vout
->p
->thread
, Thread
, vout
,
158 VLC_THREAD_PRIORITY_OUTPUT
)) {
159 spu_Destroy(vout
->p
->p_spu
);
160 vlc_object_release(vout
);
164 vout_control_WaitEmpty(&vout
->p
->control
);
167 msg_Err(vout
, "video output creation failed");
168 vout_CloseAndRelease(vout
);
172 vout
->p
->input
= cfg
->input
;
174 spu_Attach(vout
->p
->p_spu
, vout
->p
->input
, true);
179 vout_thread_t
*(vout_Request
)(vlc_object_t
*object
,
180 const vout_configuration_t
*cfg
)
182 vout_thread_t
*vout
= cfg
->vout
;
183 if (cfg
->change_fmt
&& !cfg
->fmt
) {
185 vout_CloseAndRelease(vout
);
189 /* If a vout is provided, try reusing it */
191 if (vout
->p
->input
!= cfg
->input
) {
193 spu_Attach(vout
->p
->p_spu
, vout
->p
->input
, false);
194 vout
->p
->input
= cfg
->input
;
196 spu_Attach(vout
->p
->p_spu
, vout
->p
->input
, true);
199 if (cfg
->change_fmt
) {
200 vout_control_cmd_t cmd
;
201 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_REINIT
);
204 vout_control_Push(&vout
->p
->control
, &cmd
);
205 vout_control_WaitEmpty(&vout
->p
->control
);
208 if (!vout
->p
->dead
) {
209 msg_Dbg(object
, "reusing provided vout");
212 vout_CloseAndRelease(vout
);
214 msg_Warn(object
, "cannot reuse provided vout");
216 return VoutCreate(object
, cfg
);
219 void vout_Close(vout_thread_t
*vout
)
224 spu_Attach(vout
->p
->p_spu
, vout
->p
->input
, false);
226 vout_snapshot_End(&vout
->p
->snapshot
);
228 vout_control_PushVoid(&vout
->p
->control
, VOUT_CONTROL_CLEAN
);
229 vlc_join(vout
->p
->thread
, NULL
);
231 vlc_mutex_lock(&vout
->p
->spu_lock
);
232 spu_Destroy(vout
->p
->p_spu
);
233 vout
->p
->p_spu
= NULL
;
234 vlc_mutex_unlock(&vout
->p
->spu_lock
);
238 static void VoutDestructor(vlc_object_t
*object
)
240 vout_thread_t
*vout
= (vout_thread_t
*)object
;
242 /* Make sure the vout was stopped first */
243 //assert(!vout->p_module);
245 free(vout
->p
->splitter_name
);
247 /* Destroy the locks */
248 vlc_mutex_destroy(&vout
->p
->spu_lock
);
249 vlc_mutex_destroy(&vout
->p
->picture_lock
);
250 vlc_mutex_destroy(&vout
->p
->vfilter_lock
);
251 vout_control_Clean(&vout
->p
->control
);
254 vout_statistic_Clean(&vout
->p
->statistic
);
257 vout_snapshot_Clean(&vout
->p
->snapshot
);
259 video_format_Clean(&vout
->p
->original
);
263 void vout_ChangePause(vout_thread_t
*vout
, bool is_paused
, mtime_t date
)
265 vout_control_cmd_t cmd
;
266 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_PAUSE
);
267 cmd
.u
.pause
.is_on
= is_paused
;
268 cmd
.u
.pause
.date
= date
;
269 vout_control_Push(&vout
->p
->control
, &cmd
);
271 vout_control_WaitEmpty(&vout
->p
->control
);
274 void vout_GetResetStatistic(vout_thread_t
*vout
, int *displayed
, int *lost
)
276 vout_statistic_GetReset( &vout
->p
->statistic
, displayed
, lost
);
279 void vout_Flush(vout_thread_t
*vout
, mtime_t date
)
281 vout_control_PushTime(&vout
->p
->control
, VOUT_CONTROL_FLUSH
, date
);
282 vout_control_WaitEmpty(&vout
->p
->control
);
285 void vout_Reset(vout_thread_t
*vout
)
287 vout_control_PushVoid(&vout
->p
->control
, VOUT_CONTROL_RESET
);
288 vout_control_WaitEmpty(&vout
->p
->control
);
291 bool vout_IsEmpty(vout_thread_t
*vout
)
293 vlc_mutex_lock(&vout
->p
->picture_lock
);
295 picture_t
*picture
= picture_fifo_Peek(vout
->p
->decoder_fifo
);
297 picture_Release(picture
);
299 vlc_mutex_unlock(&vout
->p
->picture_lock
);
304 void vout_FixLeaks( vout_thread_t
*vout
)
306 vlc_mutex_lock(&vout
->p
->picture_lock
);
308 picture_t
*picture
= picture_fifo_Peek(vout
->p
->decoder_fifo
);
310 picture
= picture_pool_Get(vout
->p
->decoder_pool
);
314 picture_Release(picture
);
315 /* Not all pictures has been displayed yet or some are
317 vlc_mutex_unlock(&vout
->p
->picture_lock
);
321 /* There is no reason that no pictures are available, force one
322 * from the pool, becarefull with it though */
323 msg_Err(vout
, "pictures leaked, trying to workaround");
326 picture_pool_NonEmpty(vout
->p
->decoder_pool
, false);
328 vlc_mutex_unlock(&vout
->p
->picture_lock
);
330 void vout_NextPicture(vout_thread_t
*vout
, mtime_t
*duration
)
332 vout_control_cmd_t cmd
;
333 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_STEP
);
334 cmd
.u
.time_ptr
= duration
;
336 vout_control_Push(&vout
->p
->control
, &cmd
);
337 vout_control_WaitEmpty(&vout
->p
->control
);
340 void vout_DisplayTitle(vout_thread_t
*vout
, const char *title
)
343 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_OSD_TITLE
, title
);
346 void vout_PutSubpicture( vout_thread_t
*vout
, subpicture_t
*subpic
)
348 vout_control_cmd_t cmd
;
349 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_SUBPICTURE
);
350 cmd
.u
.subpicture
= subpic
;
352 vout_control_Push(&vout
->p
->control
, &cmd
);
354 int vout_RegisterSubpictureChannel( vout_thread_t
*vout
)
356 int channel
= SPU_DEFAULT_CHANNEL
;
358 vlc_mutex_lock(&vout
->p
->spu_lock
);
360 channel
= spu_RegisterChannel(vout
->p
->p_spu
);
361 vlc_mutex_unlock(&vout
->p
->spu_lock
);
365 void vout_FlushSubpictureChannel( vout_thread_t
*vout
, int channel
)
367 vout_control_PushInteger(&vout
->p
->control
, VOUT_CONTROL_FLUSH_SUBPICTURE
,
371 /* vout_Control* are usable by anyone at anytime */
372 void vout_ControlChangeFullscreen(vout_thread_t
*vout
, bool fullscreen
)
374 vout_control_PushBool(&vout
->p
->control
, VOUT_CONTROL_FULLSCREEN
,
377 void vout_ControlChangeOnTop(vout_thread_t
*vout
, bool is_on_top
)
379 vout_control_PushBool(&vout
->p
->control
, VOUT_CONTROL_ON_TOP
,
382 void vout_ControlChangeDisplayFilled(vout_thread_t
*vout
, bool is_filled
)
384 vout_control_PushBool(&vout
->p
->control
, VOUT_CONTROL_DISPLAY_FILLED
,
387 void vout_ControlChangeZoom(vout_thread_t
*vout
, int num
, int den
)
389 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_ZOOM
,
392 void vout_ControlChangeSampleAspectRatio(vout_thread_t
*vout
,
393 unsigned num
, unsigned den
)
395 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_ASPECT_RATIO
,
398 void vout_ControlChangeCropRatio(vout_thread_t
*vout
,
399 unsigned num
, unsigned den
)
401 vout_control_PushPair(&vout
->p
->control
, VOUT_CONTROL_CROP_RATIO
,
404 void vout_ControlChangeCropWindow(vout_thread_t
*vout
,
405 int x
, int y
, int width
, int height
)
407 vout_control_cmd_t cmd
;
408 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_CROP_WINDOW
);
411 cmd
.u
.window
.width
= width
;
412 cmd
.u
.window
.height
= height
;
414 vout_control_Push(&vout
->p
->control
, &cmd
);
416 void vout_ControlChangeCropBorder(vout_thread_t
*vout
,
417 int left
, int top
, int right
, int bottom
)
419 vout_control_cmd_t cmd
;
420 vout_control_cmd_Init(&cmd
, VOUT_CONTROL_CROP_BORDER
);
421 cmd
.u
.border
.left
= left
;
422 cmd
.u
.border
.top
= top
;
423 cmd
.u
.border
.right
= right
;
424 cmd
.u
.border
.bottom
= bottom
;
426 vout_control_Push(&vout
->p
->control
, &cmd
);
428 void vout_ControlChangeFilters(vout_thread_t
*vout
, const char *filters
)
430 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_CHANGE_FILTERS
,
433 void vout_ControlChangeSubFilters(vout_thread_t
*vout
, const char *filters
)
435 vout_control_PushString(&vout
->p
->control
, VOUT_CONTROL_CHANGE_SUB_FILTERS
,
438 void vout_ControlChangeSubMargin(vout_thread_t
*vout
, int margin
)
440 vout_control_PushInteger(&vout
->p
->control
, VOUT_CONTROL_CHANGE_SUB_MARGIN
,
445 static void VoutGetDisplayCfg(vout_thread_t
*vout
, vout_display_cfg_t
*cfg
, const char *title
)
447 /* Load configuration */
448 cfg
->is_fullscreen
= var_CreateGetBool(vout
, "fullscreen");
449 cfg
->display
.title
= title
;
450 const int display_width
= var_CreateGetInteger(vout
, "width");
451 const int display_height
= var_CreateGetInteger(vout
, "height");
452 cfg
->display
.width
= display_width
> 0 ? display_width
: 0;
453 cfg
->display
.height
= display_height
> 0 ? display_height
: 0;
454 cfg
->is_display_filled
= var_CreateGetBool(vout
, "autoscale");
455 cfg
->display
.sar
.num
= 1; /* TODO monitor AR */
456 cfg
->display
.sar
.den
= 1;
457 unsigned zoom_den
= 1000;
458 unsigned zoom_num
= zoom_den
* var_CreateGetFloat(vout
, "scale");
459 vlc_ureduce(&zoom_num
, &zoom_den
, zoom_num
, zoom_den
, 0);
460 cfg
->zoom
.num
= zoom_num
;
461 cfg
->zoom
.den
= zoom_den
;
462 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_CENTER
;
463 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_CENTER
;
464 const int align_mask
= var_CreateGetInteger(vout
, "align");
465 if (align_mask
& 0x1)
466 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_LEFT
;
467 else if (align_mask
& 0x2)
468 cfg
->align
.horizontal
= VOUT_DISPLAY_ALIGN_RIGHT
;
469 if (align_mask
& 0x4)
470 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_TOP
;
471 else if (align_mask
& 0x8)
472 cfg
->align
.vertical
= VOUT_DISPLAY_ALIGN_BOTTOM
;
475 vout_window_t
* vout_NewDisplayWindow(vout_thread_t
*vout
, vout_display_t
*vd
,
476 const vout_window_cfg_t
*cfg
)
479 vout_window_cfg_t cfg_override
= *cfg
;
481 if (!var_InheritBool( vout
, "embedded-video"))
482 cfg_override
.is_standalone
= true;
484 if (vout
->p
->window
.is_unused
&& vout
->p
->window
.object
) {
485 assert(!vout
->p
->splitter_name
);
486 if (!cfg_override
.is_standalone
== !vout
->p
->window
.cfg
.is_standalone
&&
487 cfg_override
.type
== vout
->p
->window
.cfg
.type
) {
488 /* Reuse the stored window */
489 msg_Dbg(vout
, "Reusing previous vout window");
490 vout_window_t
*window
= vout
->p
->window
.object
;
491 if (cfg_override
.width
!= vout
->p
->window
.cfg
.width
||
492 cfg_override
.height
!= vout
->p
->window
.cfg
.height
)
493 vout_window_SetSize(window
,
494 cfg_override
.width
, cfg_override
.height
);
495 vout
->p
->window
.is_unused
= false;
496 vout
->p
->window
.cfg
= cfg_override
;
500 vout_window_Delete(vout
->p
->window
.object
);
501 vout
->p
->window
.is_unused
= true;
502 vout
->p
->window
.object
= NULL
;
505 vout_window_t
*window
= vout_window_New(VLC_OBJECT(vout
), "$window",
509 if (!vout
->p
->splitter_name
) {
510 vout
->p
->window
.is_unused
= false;
511 vout
->p
->window
.cfg
= cfg_override
;
512 vout
->p
->window
.object
= window
;
517 void vout_DeleteDisplayWindow(vout_thread_t
*vout
, vout_display_t
*vd
,
518 vout_window_t
*window
)
521 if (!vout
->p
->window
.is_unused
&& vout
->p
->window
.object
== window
) {
522 vout
->p
->window
.is_unused
= true;
523 } else if (vout
->p
->window
.is_unused
&& vout
->p
->window
.object
&& !window
) {
524 vout_window_Delete(vout
->p
->window
.object
);
525 vout
->p
->window
.is_unused
= true;
526 vout
->p
->window
.object
= NULL
;
528 vout_window_Delete(window
);
533 static picture_t
*VoutVideoFilterNewPicture(filter_t
*filter
)
535 vout_thread_t
*vout
= (vout_thread_t
*)filter
->p_owner
;
536 return picture_pool_Get(vout
->p
->private_pool
);
538 static void VoutVideoFilterDelPicture(filter_t
*filter
, picture_t
*picture
)
541 picture_Release(picture
);
543 static int VoutVideoFilterAllocationSetup(filter_t
*filter
, void *data
)
545 filter
->pf_video_buffer_new
= VoutVideoFilterNewPicture
;
546 filter
->pf_video_buffer_del
= VoutVideoFilterDelPicture
;
547 filter
->p_owner
= data
; /* vout */
552 static picture_t
*ThreadDisplayGetDecodedPicture(vout_thread_t
*vout
,
553 int *lost_count
, bool *is_forced
,
554 bool now
, mtime_t
*deadline
)
556 vout_display_t
*vd
= vout
->p
->display
.vd
;
558 const mtime_t date
= mdate();
559 const bool is_paused
= vout
->p
->pause
.is_on
;
560 bool redisplay
= is_paused
&& !now
&& vout
->p
->displayed
.decoded
;
562 /* FIXME/XXX we must redisplay the last decoded picture (because
563 * of potential vout updated, or filters update or SPU update)
564 * For now a high update period is needed but it coulmd be removed
566 * - vout module emits events from theselves.
567 * - *and* SPU is modified to emit an event or a deadline when needed.
569 * So it will be done latter.
572 picture_t
*peek
= picture_fifo_Peek(vout
->p
->decoder_fifo
);
574 *is_forced
= peek
->b_force
|| is_paused
|| now
;
575 *deadline
= (*is_forced
? date
: peek
->date
) - vout_chrono_GetHigh(&vout
->p
->render
);
576 picture_Release(peek
);
582 /* FIXME a better way for this delay is needed */
583 const mtime_t date_update
= vout
->p
->displayed
.date
+ VOUT_REDISPLAY_DELAY
;
584 if (date_update
> date
|| !vout
->p
->displayed
.decoded
) {
585 *deadline
= vout
->p
->displayed
.decoded
? date_update
: VLC_TS_INVALID
;
590 *deadline
= date
- vout_chrono_GetHigh(&vout
->p
->render
);
592 if (*deadline
> VOUT_MWAIT_TOLERANCE
)
593 *deadline
-= VOUT_MWAIT_TOLERANCE
;
595 /* If we are too early and can wait, do it */
596 if (date
< *deadline
&& !now
)
601 decoded
= vout
->p
->displayed
.decoded
;
602 vout
->p
->displayed
.decoded
= NULL
;
604 decoded
= picture_fifo_Pop(vout
->p
->decoder_fifo
);
606 if (!*is_forced
&& !vout
->p
->is_late_dropped
) {
607 const mtime_t predicted
= date
+ vout_chrono_GetLow(&vout
->p
->render
);
608 const mtime_t late
= predicted
- decoded
->date
;
610 msg_Dbg(vout
, "picture might be displayed late (missing %d ms)", (int)(late
/1000));
611 if (late
> VOUT_DISPLAY_LATE_THRESHOLD
) {
612 msg_Warn(vout
, "rejected picture because of render time");
614 picture_Release(decoded
);
621 vout
->p
->displayed
.is_interlaced
= !decoded
->b_progressive
;
622 vout
->p
->displayed
.qtype
= decoded
->i_qtype
;
624 vout
->p
->displayed
.timestamp
= decoded
->date
;
627 if (vout
->p
->displayed
.decoded
)
628 picture_Release(vout
->p
->displayed
.decoded
);
629 picture_Hold(decoded
);
630 vout
->p
->displayed
.decoded
= decoded
;
635 static int ThreadDisplayPicture(vout_thread_t
*vout
,
636 bool now
, mtime_t
*deadline
)
638 vout_display_t
*vd
= vout
->p
->display
.vd
;
639 int displayed_count
= 0;
644 picture_t
*decoded
= ThreadDisplayGetDecodedPicture(vout
,
645 &lost_count
, &is_forced
,
651 vout_chrono_Start(&vout
->p
->render
);
653 picture_t
*filtered
= NULL
;
655 vlc_mutex_lock(&vout
->p
->vfilter_lock
);
656 filtered
= filter_chain_VideoFilter(vout
->p
->vfilter_chain
, decoded
);
657 //assert(filtered == decoded); // TODO implement
658 vlc_mutex_unlock(&vout
->p
->vfilter_lock
);
664 * Check for subpictures to display
666 const bool do_snapshot
= vout_snapshot_IsRequested(&vout
->p
->snapshot
);
667 mtime_t spu_render_time
= is_forced
? mdate() : filtered
->date
;
668 if (vout
->p
->pause
.is_on
)
669 spu_render_time
= vout
->p
->pause
.date
;
671 spu_render_time
= filtered
->date
> 1 ? filtered
->date
: mdate();
673 subpicture_t
*subpic
= spu_SortSubpictures(vout
->p
->p_spu
,
680 * - be sure to end up with a direct buffer.
681 * - blend subtitles, and in a fast access buffer
683 picture_t
*direct
= NULL
;
685 (vout
->p
->decoder_pool
!= vout
->p
->display_pool
|| subpic
)) {
687 if (vout
->p
->is_decoder_pool_slow
)
688 render
= picture_NewFromFormat(&vd
->source
);
689 else if (vout
->p
->decoder_pool
!= vout
->p
->display_pool
)
690 render
= picture_pool_Get(vout
->p
->display_pool
);
692 render
= picture_pool_Get(vout
->p
->private_pool
);
695 picture_Copy(render
, filtered
);
697 spu_RenderSubpictures(vout
->p
->p_spu
,
699 subpic
, &vd
->source
, spu_render_time
);
701 if (vout
->p
->is_decoder_pool_slow
) {
702 direct
= picture_pool_Get(vout
->p
->display_pool
);
704 picture_Copy(direct
, render
);
705 picture_Release(render
);
710 picture_Release(filtered
);
717 * Take a snapshot if requested
719 if (direct
&& do_snapshot
)
720 vout_snapshot_Set(&vout
->p
->snapshot
, &vd
->source
, direct
);
722 /* Render the direct buffer returned by vout_RenderPicture */
724 vout_RenderWrapper(vout
, direct
);
726 vout_chrono_Stop(&vout
->p
->render
);
731 msg_Info(vout
, "render: avg %d ms var %d ms",
732 (int)(vout
->p
->render
.avg
/1000), (int)(vout
->p
->render
.var
/1000));
737 /* Wait the real date (for rendering jitter) */
741 /* Display the direct buffer returned by vout_RenderPicture */
742 vout
->p
->displayed
.date
= mdate();
744 vout_DisplayWrapper(vout
, direct
);
750 vout_statistic_Update(&vout
->p
->statistic
, displayed_count
, lost_count
);
751 if (displayed_count
<= 0)
756 static void ThreadManage(vout_thread_t
*vout
,
758 vout_interlacing_support_t
*interlacing
,
759 vout_postprocessing_support_t
*postprocessing
)
761 vlc_mutex_lock(&vout
->p
->picture_lock
);
763 *deadline
= VLC_TS_INVALID
;
764 ThreadDisplayPicture(vout
, false, deadline
);
766 const int picture_qtype
= vout
->p
->displayed
.qtype
;
767 const bool picture_interlaced
= vout
->p
->displayed
.is_interlaced
;
769 vlc_mutex_unlock(&vout
->p
->picture_lock
);
771 /* Post processing */
772 vout_SetPostProcessingState(vout
, postprocessing
, picture_qtype
);
775 vout_SetInterlacingState(vout
, interlacing
, picture_interlaced
);
777 vout_ManageWrapper(vout
);
780 static void ThreadDisplaySubpicture(vout_thread_t
*vout
,
781 subpicture_t
*subpicture
)
783 spu_DisplaySubpicture(vout
->p
->p_spu
, subpicture
);
786 static void ThreadFlushSubpicture(vout_thread_t
*vout
, int channel
)
788 spu_ClearChannel(vout
->p
->p_spu
, channel
);
791 static void ThreadDisplayOsdTitle(vout_thread_t
*vout
, const char *string
)
793 if (!vout
->p
->title
.show
)
796 vout_OSDText(vout
, SPU_DEFAULT_CHANNEL
,
797 vout
->p
->title
.position
, INT64_C(1000) * vout
->p
->title
.timeout
,
801 static void ThreadChangeFilters(vout_thread_t
*vout
, const char *filters
)
804 es_format_Init(&fmt
, VIDEO_ES
, vout
->p
->original
.i_chroma
);
805 fmt
.video
= vout
->p
->original
;
807 vlc_mutex_lock(&vout
->p
->vfilter_lock
);
809 filter_chain_Reset(vout
->p
->vfilter_chain
, &fmt
, &fmt
);
810 if (filter_chain_AppendFromString(vout
->p
->vfilter_chain
,
812 msg_Err(vout
, "Video filter chain creation failed");
814 vlc_mutex_unlock(&vout
->p
->vfilter_lock
);
817 static void ThreadChangeSubFilters(vout_thread_t
*vout
, const char *filters
)
819 spu_ChangeFilters(vout
->p
->p_spu
, filters
);
821 static void ThreadChangeSubMargin(vout_thread_t
*vout
, int margin
)
823 spu_ChangeMargin(vout
->p
->p_spu
, margin
);
826 static void ThreadChangePause(vout_thread_t
*vout
, bool is_paused
, mtime_t date
)
828 assert(!vout
->p
->pause
.is_on
|| !is_paused
);
830 if (vout
->p
->pause
.is_on
) {
831 const mtime_t duration
= date
- vout
->p
->pause
.date
;
833 if (vout
->p
->step
.timestamp
> VLC_TS_INVALID
)
834 vout
->p
->step
.timestamp
+= duration
;
835 if (vout
->p
->step
.last
> VLC_TS_INVALID
)
836 vout
->p
->step
.last
+= duration
;
837 picture_fifo_OffsetDate(vout
->p
->decoder_fifo
, duration
);
838 if (vout
->p
->displayed
.decoded
)
839 vout
->p
->displayed
.decoded
->date
+= duration
;
841 spu_OffsetSubtitleDate(vout
->p
->p_spu
, duration
);
843 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
844 vout
->p
->step
.last
= VLC_TS_INVALID
;
846 vout
->p
->pause
.is_on
= is_paused
;
847 vout
->p
->pause
.date
= date
;
850 static void ThreadFlush(vout_thread_t
*vout
, bool below
, mtime_t date
)
852 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
853 vout
->p
->step
.last
= VLC_TS_INVALID
;
855 picture_t
*last
= vout
->p
->displayed
.decoded
;
857 if (( below
&& last
->date
<= date
) ||
858 (!below
&& last
->date
>= date
)) {
859 picture_Release(last
);
861 vout
->p
->displayed
.decoded
= NULL
;
862 vout
->p
->displayed
.date
= VLC_TS_INVALID
;
863 vout
->p
->displayed
.timestamp
= VLC_TS_INVALID
;
866 picture_fifo_Flush(vout
->p
->decoder_fifo
, date
, below
);
869 static void ThreadReset(vout_thread_t
*vout
)
871 ThreadFlush(vout
, true, INT64_MAX
);
872 if (vout
->p
->decoder_pool
)
873 picture_pool_NonEmpty(vout
->p
->decoder_pool
, true);
874 vout
->p
->pause
.is_on
= false;
875 vout
->p
->pause
.date
= mdate();
878 static void ThreadStep(vout_thread_t
*vout
, mtime_t
*duration
)
882 if (vout
->p
->step
.last
<= VLC_TS_INVALID
)
883 vout
->p
->step
.last
= vout
->p
->displayed
.timestamp
;
886 if (ThreadDisplayPicture(vout
, true, &dummy
))
889 vout
->p
->step
.timestamp
= vout
->p
->displayed
.timestamp
;
891 if (vout
->p
->step
.last
> VLC_TS_INVALID
&&
892 vout
->p
->step
.timestamp
> vout
->p
->step
.last
) {
893 *duration
= vout
->p
->step
.timestamp
- vout
->p
->step
.last
;
894 vout
->p
->step
.last
= vout
->p
->step
.timestamp
;
895 /* TODO advance subpicture by the duration ... */
899 static void ThreadChangeFullscreen(vout_thread_t
*vout
, bool fullscreen
)
901 /* FIXME not sure setting "fullscreen" is good ... */
902 var_SetBool(vout
, "fullscreen", fullscreen
);
903 vout_SetDisplayFullscreen(vout
->p
->display
.vd
, fullscreen
);
906 static void ThreadChangeOnTop(vout_thread_t
*vout
, bool is_on_top
)
908 vout_SetWindowState(vout
->p
->display
.vd
,
909 is_on_top
? VOUT_WINDOW_STATE_ABOVE
:
910 VOUT_WINDOW_STATE_NORMAL
);
913 static void ThreadChangeDisplayFilled(vout_thread_t
*vout
, bool is_filled
)
915 vout_SetDisplayFilled(vout
->p
->display
.vd
, is_filled
);
918 static void ThreadChangeZoom(vout_thread_t
*vout
, int num
, int den
)
920 if (num
* 10 < den
) {
923 } else if (num
> den
* 10) {
927 vout_SetDisplayZoom(vout
->p
->display
.vd
, num
, den
);
930 static void ThreadChangeAspectRatio(vout_thread_t
*vout
,
931 unsigned num
, unsigned den
)
933 const video_format_t
*source
= &vout
->p
->original
;
935 if (num
> 0 && den
> 0) {
936 num
*= source
->i_visible_height
;
937 den
*= source
->i_visible_width
;
938 vlc_ureduce(&num
, &den
, num
, den
, 0);
940 vout_SetDisplayAspect(vout
->p
->display
.vd
, num
, den
);
944 static void ThreadExecuteCropWindow(vout_thread_t
*vout
,
945 unsigned crop_num
, unsigned crop_den
,
946 unsigned x
, unsigned y
,
947 unsigned width
, unsigned height
)
949 const video_format_t
*source
= &vout
->p
->original
;
951 vout_SetDisplayCrop(vout
->p
->display
.vd
,
953 source
->i_x_offset
+ x
,
954 source
->i_y_offset
+ y
,
957 static void ThreadExecuteCropBorder(vout_thread_t
*vout
,
958 unsigned left
, unsigned top
,
959 unsigned right
, unsigned bottom
)
961 const video_format_t
*source
= &vout
->p
->original
;
962 ThreadExecuteCropWindow(vout
, 0, 0,
965 /* At worst, it becomes < 0 (but unsigned) and will be rejected */
966 source
->i_visible_width
- (left
+ right
),
967 source
->i_visible_height
- (top
+ bottom
));
970 static void ThreadExecuteCropRatio(vout_thread_t
*vout
,
971 unsigned num
, unsigned den
)
973 const video_format_t
*source
= &vout
->p
->original
;
974 ThreadExecuteCropWindow(vout
, num
, den
,
976 source
->i_visible_width
,
977 source
->i_visible_height
);
980 static int ThreadStart(vout_thread_t
*vout
, const vout_display_state_t
*state
)
982 vlc_mouse_Init(&vout
->p
->mouse
);
983 vout
->p
->decoder_fifo
= picture_fifo_New();
984 vout
->p
->decoder_pool
= NULL
;
985 vout
->p
->display_pool
= NULL
;
986 vout
->p
->private_pool
= NULL
;
988 vout
->p
->vfilter_chain
=
989 filter_chain_New( vout
, "video filter2", false,
990 VoutVideoFilterAllocationSetup
, NULL
, vout
);
992 vout_display_state_t state_default
;
994 VoutGetDisplayCfg(vout
, &state_default
.cfg
, vout
->p
->display
.title
);
995 state_default
.wm_state
= var_CreateGetBool(vout
, "video-on-top") ? VOUT_WINDOW_STATE_ABOVE
:
996 VOUT_WINDOW_STATE_NORMAL
;
997 state_default
.sar
.num
= 0;
998 state_default
.sar
.den
= 0;
1000 state
= &state_default
;
1003 if (vout_OpenWrapper(vout
, vout
->p
->splitter_name
, state
))
1004 return VLC_EGENERIC
;
1005 if (vout_InitWrapper(vout
))
1006 return VLC_EGENERIC
;
1007 assert(vout
->p
->decoder_pool
);
1009 vout
->p
->displayed
.decoded
= NULL
;
1010 vout
->p
->displayed
.date
= VLC_TS_INVALID
;
1011 vout
->p
->displayed
.decoded
= NULL
;
1012 vout
->p
->displayed
.timestamp
= VLC_TS_INVALID
;
1013 vout
->p
->displayed
.qtype
= QTYPE_NONE
;
1014 vout
->p
->displayed
.is_interlaced
= false;
1016 vout
->p
->step
.last
= VLC_TS_INVALID
;
1017 vout
->p
->step
.timestamp
= VLC_TS_INVALID
;
1019 video_format_Print(VLC_OBJECT(vout
), "original format", &vout
->p
->original
);
1023 static void ThreadStop(vout_thread_t
*vout
, vout_display_state_t
*state
)
1025 /* Destroy the video filters2 */
1026 filter_chain_Delete(vout
->p
->vfilter_chain
);
1028 /* Destroy translation tables */
1029 if (vout
->p
->display
.vd
) {
1030 if (vout
->p
->decoder_pool
) {
1031 ThreadFlush(vout
, true, INT64_MAX
);
1032 vout_EndWrapper(vout
);
1034 vout_CloseWrapper(vout
, state
);
1037 if (vout
->p
->decoder_fifo
)
1038 picture_fifo_Delete(vout
->p
->decoder_fifo
);
1039 assert(!vout
->p
->decoder_pool
);
1042 static void ThreadInit(vout_thread_t
*vout
)
1044 vout
->p
->window
.is_unused
= true;
1045 vout
->p
->window
.object
= NULL
;
1046 vout
->p
->dead
= false;
1047 vout
->p
->is_late_dropped
= var_InheritBool(vout
, "drop-late-frames");
1048 vout
->p
->pause
.is_on
= false;
1049 vout
->p
->pause
.date
= VLC_TS_INVALID
;
1051 vout_chrono_Init(&vout
->p
->render
, 5, 10000); /* Arbitrary initial time */
1054 static void ThreadClean(vout_thread_t
*vout
)
1056 if (vout
->p
->window
.object
) {
1057 assert(vout
->p
->window
.is_unused
);
1058 vout_window_Delete(vout
->p
->window
.object
);
1060 vout_chrono_Clean(&vout
->p
->render
);
1061 vout
->p
->dead
= true;
1062 vout_control_Dead(&vout
->p
->control
);
1065 static int ThreadReinit(vout_thread_t
*vout
,
1066 const vout_configuration_t
*cfg
)
1068 video_format_t original
;
1069 if (VoutValidateFormat(&original
, cfg
->fmt
)) {
1070 ThreadStop(vout
, NULL
);
1072 return VLC_EGENERIC
;
1074 if (video_format_IsSimilar(&original
, &vout
->p
->original
)) {
1075 if (cfg
->dpb_size
<= vout
->p
->dpb_size
)
1077 msg_Warn(vout
, "DPB need to be increased");
1080 vout_display_state_t state
;
1081 memset(&state
, 0, sizeof(state
));
1083 ThreadStop(vout
, &state
);
1085 if (!state
.cfg
.is_fullscreen
) {
1086 state
.cfg
.display
.width
= 0;
1087 state
.cfg
.display
.height
= 0;
1091 /* FIXME current vout "variables" are not in sync here anymore
1092 * and I am not sure what to do */
1094 vout
->p
->original
= original
;
1095 vout
->p
->dpb_size
= cfg
->dpb_size
;
1096 if (ThreadStart(vout
, &state
)) {
1098 return VLC_EGENERIC
;
1103 /*****************************************************************************
1104 * Thread: video output thread
1105 *****************************************************************************
1106 * Video output thread. This function does only returns when the thread is
1107 * terminated. It handles the pictures arriving in the video heap and the
1108 * display device events.
1109 *****************************************************************************/
1110 static void *Thread(void *object
)
1112 vout_thread_t
*vout
= object
;
1114 vout_interlacing_support_t interlacing
= {
1115 .is_interlaced
= false,
1118 vout_postprocessing_support_t postprocessing
= {
1119 .qtype
= QTYPE_NONE
,
1122 mtime_t deadline
= VLC_TS_INVALID
;
1124 vout_control_cmd_t cmd
;
1126 /* FIXME remove thoses ugly timeouts
1128 while (!vout_control_Pop(&vout
->p
->control
, &cmd
, deadline
, 100000)) {
1130 case VOUT_CONTROL_INIT
:
1132 if (ThreadStart(vout
, NULL
)) {
1133 ThreadStop(vout
, NULL
);
1138 case VOUT_CONTROL_CLEAN
:
1139 ThreadStop(vout
, NULL
);
1142 case VOUT_CONTROL_REINIT
:
1143 if (ThreadReinit(vout
, cmd
.u
.cfg
))
1146 case VOUT_CONTROL_SUBPICTURE
:
1147 ThreadDisplaySubpicture(vout
, cmd
.u
.subpicture
);
1148 cmd
.u
.subpicture
= NULL
;
1150 case VOUT_CONTROL_FLUSH_SUBPICTURE
:
1151 ThreadFlushSubpicture(vout
, cmd
.u
.integer
);
1153 case VOUT_CONTROL_OSD_TITLE
:
1154 ThreadDisplayOsdTitle(vout
, cmd
.u
.string
);
1156 case VOUT_CONTROL_CHANGE_FILTERS
:
1157 ThreadChangeFilters(vout
, cmd
.u
.string
);
1159 case VOUT_CONTROL_CHANGE_SUB_FILTERS
:
1160 ThreadChangeSubFilters(vout
, cmd
.u
.string
);
1162 case VOUT_CONTROL_CHANGE_SUB_MARGIN
:
1163 ThreadChangeSubMargin(vout
, cmd
.u
.integer
);
1165 case VOUT_CONTROL_PAUSE
:
1166 ThreadChangePause(vout
, cmd
.u
.pause
.is_on
, cmd
.u
.pause
.date
);
1168 case VOUT_CONTROL_FLUSH
:
1169 ThreadFlush(vout
, false, cmd
.u
.time
);
1171 case VOUT_CONTROL_RESET
:
1174 case VOUT_CONTROL_STEP
:
1175 ThreadStep(vout
, cmd
.u
.time_ptr
);
1177 case VOUT_CONTROL_FULLSCREEN
:
1178 ThreadChangeFullscreen(vout
, cmd
.u
.boolean
);
1180 case VOUT_CONTROL_ON_TOP
:
1181 ThreadChangeOnTop(vout
, cmd
.u
.boolean
);
1183 case VOUT_CONTROL_DISPLAY_FILLED
:
1184 ThreadChangeDisplayFilled(vout
, cmd
.u
.boolean
);
1186 case VOUT_CONTROL_ZOOM
:
1187 ThreadChangeZoom(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1189 case VOUT_CONTROL_ASPECT_RATIO
:
1190 ThreadChangeAspectRatio(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1192 case VOUT_CONTROL_CROP_RATIO
:
1193 ThreadExecuteCropRatio(vout
, cmd
.u
.pair
.a
, cmd
.u
.pair
.b
);
1195 case VOUT_CONTROL_CROP_WINDOW
:
1196 ThreadExecuteCropWindow(vout
, 0, 0,
1197 cmd
.u
.window
.x
, cmd
.u
.window
.y
,
1198 cmd
.u
.window
.width
, cmd
.u
.window
.height
);
1200 case VOUT_CONTROL_CROP_BORDER
:
1201 ThreadExecuteCropBorder(vout
,
1202 cmd
.u
.border
.left
, cmd
.u
.border
.top
,
1203 cmd
.u
.border
.right
, cmd
.u
.border
.bottom
);
1208 vout_control_cmd_Clean(&cmd
);
1211 ThreadManage(vout
, &deadline
, &interlacing
, &postprocessing
);