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 /*****************************************************************************
96 * vout_Request: find a video output thread, create one, or destroy one.
97 *****************************************************************************
98 * This function looks for a video output thread matching the current
99 * properties. If not found, it spawns a new one.
100 *****************************************************************************/
101 vout_thread_t *(vout_Request)(vlc_object_t *object, vout_thread_t *vout,
102 const video_format_t *fmt)
106 vout_CloseAndRelease(vout);
110 /* If a vout is provided, try reusing it */
112 spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), false);
113 vlc_object_detach(vout);
114 vlc_object_attach(vout, object);
115 spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), true);
117 vout_control_cmd_t cmd;
118 vout_control_cmd_Init(&cmd, VOUT_CONTROL_REINIT);
119 cmd.u.reinit.fmt = fmt;
121 vout_control_Push(&vout->p->control, &cmd);
122 vout_control_WaitEmpty(&vout->p->control);
123 if (!vout->p->dead) {
124 msg_Dbg(object, "reusing provided vout");
127 vout_CloseAndRelease(vout);
129 msg_Warn(object, "cannot reuse provided vout");
131 return vout_Create(object, fmt);
134 /*****************************************************************************
135 * vout_Create: creates a new video output thread
136 *****************************************************************************
137 * This function creates a new video output thread, and returns a pointer
138 * to its description. On error, it returns NULL.
139 *****************************************************************************/
140 vout_thread_t *(vout_Create)(vlc_object_t *object, const video_format_t *fmt)
142 video_format_t original;
143 if (VoutValidateFormat(&original, fmt))
146 /* Allocate descriptor */
147 vout_thread_t *vout = vlc_custom_create(object,
148 sizeof(*vout) + sizeof(*vout->p),
149 VLC_OBJECT_VOUT, "video output");
151 video_format_Clean(&original);
156 vout->p = (vout_thread_sys_t*)&vout[1];
158 vout->p->original = original;
160 vout_control_Init(&vout->p->control);
161 vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_INIT);
163 vout_statistic_Init(&vout->p->statistic);
165 vout->p->i_par_den = 1;
167 vout_snapshot_Init(&vout->p->snapshot);
169 /* Initialize locks */
170 vlc_mutex_init(&vout->p->picture_lock);
171 vlc_mutex_init(&vout->p->vfilter_lock);
173 /* Attach the new object now so we can use var inheritance below */
174 vlc_object_attach(vout, object);
176 /* Initialize subpicture unit */
177 vout->p->p_spu = spu_Create(vout);
179 /* Take care of some "interface/control" related initialisations */
182 /* Get splitter name if present */
183 char *splitter_name = var_GetNonEmptyString(vout, "vout-filter");
185 if (asprintf(&vout->p->splitter_name, "%s,none", splitter_name) < 0)
186 vout->p->splitter_name = NULL;
189 vout->p->splitter_name = NULL;
193 vout_InitInterlacingSupport(vout, vout->p->displayed.is_interlaced);
196 vlc_object_set_destructor(vout, VoutDestructor);
199 if (vlc_clone(&vout->p->thread, Thread, vout,
200 VLC_THREAD_PRIORITY_OUTPUT)) {
201 vlc_object_release(vout);
204 spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), true);
206 vout_control_WaitEmpty(&vout->p->control);
209 msg_Err(vout, "video output creation failed");
210 vout_CloseAndRelease(vout);
217 /*****************************************************************************
218 * vout_Close: Close a vout created by vout_Create.
219 *****************************************************************************
220 * You HAVE to call it on vout created by vout_Create before vlc_object_release.
221 * You should NEVER call it on vout not obtained through vout_Create
222 * (like with vout_Request or vlc_object_find.)
223 * You can use vout_CloseAndRelease() as a convenience method.
224 *****************************************************************************/
225 void vout_Close(vout_thread_t *vout)
229 spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), false);
230 vlc_object_detach(vout->p->p_spu);
232 vout_snapshot_End(&vout->p->snapshot);
234 vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_CLEAN);
235 vlc_join(vout->p->thread, NULL);
239 static void VoutDestructor(vlc_object_t *object)
241 vout_thread_t *vout = (vout_thread_t *)object;
243 /* Make sure the vout was stopped first */
244 //assert(!vout->p_module);
246 free(vout->p->splitter_name);
249 spu_Destroy(vout->p->p_spu);
251 /* Destroy the locks */
252 vlc_mutex_destroy(&vout->p->picture_lock);
253 vlc_mutex_destroy(&vout->p->vfilter_lock);
254 vout_control_Clean(&vout->p->control);
257 vout_statistic_Clean(&vout->p->statistic);
260 vout_snapshot_Clean(&vout->p->snapshot);
262 video_format_Clean(&vout->p->original);
266 void vout_ChangePause(vout_thread_t *vout, bool is_paused, mtime_t date)
268 vout_control_cmd_t cmd;
269 vout_control_cmd_Init(&cmd, VOUT_CONTROL_PAUSE);
270 cmd.u.pause.is_on = is_paused;
271 cmd.u.pause.date = date;
272 vout_control_Push(&vout->p->control, &cmd);
274 vout_control_WaitEmpty(&vout->p->control);
277 void vout_GetResetStatistic(vout_thread_t *vout, int *displayed, int *lost)
279 vout_statistic_GetReset( &vout->p->statistic, displayed, lost );
282 void vout_Flush(vout_thread_t *vout, mtime_t date)
284 vout_control_PushTime(&vout->p->control, VOUT_CONTROL_FLUSH, date);
285 vout_control_WaitEmpty(&vout->p->control);
288 void vout_Reset(vout_thread_t *vout)
290 vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_RESET);
291 vout_control_WaitEmpty(&vout->p->control);
294 bool vout_IsEmpty(vout_thread_t *vout)
296 vlc_mutex_lock(&vout->p->picture_lock);
298 picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
300 picture_Release(picture);
302 vlc_mutex_unlock(&vout->p->picture_lock);
307 void vout_FixLeaks( vout_thread_t *vout )
309 vlc_mutex_lock(&vout->p->picture_lock);
311 picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo);
313 picture = picture_pool_Get(vout->p->decoder_pool);
317 picture_Release(picture);
318 /* Not all pictures has been displayed yet or some are
320 vlc_mutex_unlock(&vout->p->picture_lock);
324 /* There is no reason that no pictures are available, force one
325 * from the pool, becarefull with it though */
326 msg_Err(vout, "pictures leaked, trying to workaround");
329 picture_pool_NonEmpty(vout->p->decoder_pool, false);
331 vlc_mutex_unlock(&vout->p->picture_lock);
333 void vout_NextPicture(vout_thread_t *vout, mtime_t *duration)
335 vout_control_cmd_t cmd;
336 vout_control_cmd_Init(&cmd, VOUT_CONTROL_STEP);
337 cmd.u.time_ptr = duration;
339 vout_control_Push(&vout->p->control, &cmd);
340 vout_control_WaitEmpty(&vout->p->control);
343 void vout_DisplayTitle(vout_thread_t *vout, const char *title)
346 vout_control_PushString(&vout->p->control, VOUT_CONTROL_OSD_TITLE, title);
349 void vout_PutSubpicture( vout_thread_t *vout, subpicture_t *subpic )
351 spu_DisplaySubpicture(vout->p->p_spu, subpic);
353 int vout_RegisterSubpictureChannel( vout_thread_t *vout )
355 return spu_RegisterChannel(vout->p->p_spu);
357 void vout_FlushSubpictureChannel( vout_thread_t *vout, int channel )
359 spu_ClearChannel(vout->p->p_spu, channel);
362 /* vout_Control* are usable by anyone at anytime */
363 void vout_ControlChangeFullscreen(vout_thread_t *vout, bool fullscreen)
365 vout_control_PushBool(&vout->p->control, VOUT_CONTROL_FULLSCREEN,
368 void vout_ControlChangeOnTop(vout_thread_t *vout, bool is_on_top)
370 vout_control_PushBool(&vout->p->control, VOUT_CONTROL_ON_TOP,
373 void vout_ControlChangeDisplayFilled(vout_thread_t *vout, bool is_filled)
375 vout_control_PushBool(&vout->p->control, VOUT_CONTROL_DISPLAY_FILLED,
378 void vout_ControlChangeZoom(vout_thread_t *vout, int num, int den)
380 vout_control_PushPair(&vout->p->control, VOUT_CONTROL_ZOOM,
383 void vout_ControlChangeSampleAspectRatio(vout_thread_t *vout,
384 unsigned num, unsigned den)
386 vout_control_PushPair(&vout->p->control, VOUT_CONTROL_ASPECT_RATIO,
389 void vout_ControlChangeCropRatio(vout_thread_t *vout,
390 unsigned num, unsigned den)
392 vout_control_PushPair(&vout->p->control, VOUT_CONTROL_CROP_RATIO,
395 void vout_ControlChangeCropWindow(vout_thread_t *vout,
396 int x, int y, int width, int height)
398 vout_control_cmd_t cmd;
399 vout_control_cmd_Init(&cmd, VOUT_CONTROL_CROP_WINDOW);
402 cmd.u.window.width = width;
403 cmd.u.window.height = height;
405 vout_control_Push(&vout->p->control, &cmd);
407 void vout_ControlChangeCropBorder(vout_thread_t *vout,
408 int left, int top, int right, int bottom)
410 vout_control_cmd_t cmd;
411 vout_control_cmd_Init(&cmd, VOUT_CONTROL_CROP_BORDER);
412 cmd.u.border.left = left;
413 cmd.u.border.top = top;
414 cmd.u.border.right = right;
415 cmd.u.border.bottom = bottom;
417 vout_control_Push(&vout->p->control, &cmd);
419 void vout_ControlChangeFilters(vout_thread_t *vout, const char *filters)
421 vout_control_PushString(&vout->p->control, VOUT_CONTROL_CHANGE_FILTERS,
424 void vout_ControlChangeSubFilters(vout_thread_t *vout, const char *filters)
426 vout_control_PushString(&vout->p->control, VOUT_CONTROL_CHANGE_SUB_FILTERS,
431 static picture_t *VoutVideoFilterNewPicture(filter_t *filter)
433 vout_thread_t *vout = (vout_thread_t*)filter->p_owner;
434 return picture_pool_Get(vout->p->private_pool);
436 static void VoutVideoFilterDelPicture(filter_t *filter, picture_t *picture)
439 picture_Release(picture);
441 static int VoutVideoFilterAllocationSetup(filter_t *filter, void *data)
443 filter->pf_video_buffer_new = VoutVideoFilterNewPicture;
444 filter->pf_video_buffer_del = VoutVideoFilterDelPicture;
445 filter->p_owner = data; /* vout */
450 static int ThreadDisplayPicture(vout_thread_t *vout,
451 bool now, mtime_t *deadline)
453 vout_display_t *vd = vout->p->display.vd;
454 int displayed_count = 0;
458 const mtime_t date = mdate();
459 const bool is_paused = vout->p->pause.is_on;
460 bool redisplay = is_paused && !now && vout->p->displayed.decoded;
463 /* FIXME/XXX we must redisplay the last decoded picture (because
464 * of potential vout updated, or filters update or SPU update)
465 * For now a high update period is needed but it coulmd be removed
467 * - vout module emits events from theselves.
468 * - *and* SPU is modified to emit an event or a deadline when needed.
470 * So it will be done latter.
473 picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo);
475 is_forced = peek->b_force || is_paused || now;
476 *deadline = (is_forced ? date : peek->date) - vout_chrono_GetHigh(&vout->p->render);
477 picture_Release(peek);
483 /* FIXME a better way for this delay is needed */
484 const mtime_t date_update = vout->p->displayed.date + VOUT_REDISPLAY_DELAY;
485 if (date_update > date || !vout->p->displayed.decoded) {
486 *deadline = vout->p->displayed.decoded ? date_update : VLC_TS_INVALID;
491 *deadline = date - vout_chrono_GetHigh(&vout->p->render);
493 if (*deadline > VOUT_MWAIT_TOLERANCE)
494 *deadline -= VOUT_MWAIT_TOLERANCE;
496 /* If we are too early and can wait, do it */
497 if (date < *deadline && !now)
502 decoded = vout->p->displayed.decoded;
503 vout->p->displayed.decoded = NULL;
505 decoded = picture_fifo_Pop(vout->p->decoder_fifo);
507 if (!is_forced && !vout->p->is_late_dropped) {
508 const mtime_t predicted = date + vout_chrono_GetLow(&vout->p->render);
509 const mtime_t late = predicted - decoded->date;
511 msg_Dbg(vout, "picture might be displayed late (missing %d ms)", (int)(late/1000));
512 if (late > VOUT_DISPLAY_LATE_THRESHOLD) {
513 msg_Warn(vout, "rejected picture because of render time");
515 picture_Release(decoded);
522 vout->p->displayed.is_interlaced = !decoded->b_progressive;
523 vout->p->displayed.qtype = decoded->i_qtype;
525 vout->p->displayed.timestamp = decoded->date;
528 if (vout->p->displayed.decoded)
529 picture_Release(vout->p->displayed.decoded);
530 picture_Hold(decoded);
531 vout->p->displayed.decoded = decoded;
534 vout_chrono_Start(&vout->p->render);
536 picture_t *filtered = NULL;
538 vlc_mutex_lock(&vout->p->vfilter_lock);
539 filtered = filter_chain_VideoFilter(vout->p->vfilter_chain, decoded);
540 //assert(filtered == decoded); // TODO implement
541 vlc_mutex_unlock(&vout->p->vfilter_lock);
547 * Check for subpictures to display
549 const bool do_snapshot = vout_snapshot_IsRequested(&vout->p->snapshot);
550 mtime_t spu_render_time = is_forced ? mdate() : filtered->date;
551 if (vout->p->pause.is_on)
552 spu_render_time = vout->p->pause.date;
554 spu_render_time = filtered->date > 1 ? filtered->date : mdate();
556 subpicture_t *subpic = spu_SortSubpictures(vout->p->p_spu,
563 * - be sure to end up with a direct buffer.
564 * - blend subtitles, and in a fast access buffer
566 picture_t *direct = NULL;
568 (vout->p->decoder_pool != vout->p->display_pool || subpic)) {
570 if (vout->p->is_decoder_pool_slow)
571 render = picture_NewFromFormat(&vd->source);
572 else if (vout->p->decoder_pool != vout->p->display_pool)
573 render = picture_pool_Get(vout->p->display_pool);
575 render = picture_pool_Get(vout->p->private_pool);
578 picture_Copy(render, filtered);
580 spu_RenderSubpictures(vout->p->p_spu,
582 subpic, &vd->source, spu_render_time);
584 if (vout->p->is_decoder_pool_slow) {
585 direct = picture_pool_Get(vout->p->display_pool);
587 picture_Copy(direct, render);
588 picture_Release(render);
593 picture_Release(filtered);
600 * Take a snapshot if requested
602 if (direct && do_snapshot)
603 vout_snapshot_Set(&vout->p->snapshot, &vd->source, direct);
605 /* Render the direct buffer returned by vout_RenderPicture */
607 vout_RenderWrapper(vout, direct);
609 vout_chrono_Stop(&vout->p->render);
614 msg_Info(vout, "render: avg %d ms var %d ms",
615 (int)(vout->p->render.avg/1000), (int)(vout->p->render.var/1000));
620 /* Wait the real date (for rendering jitter) */
622 mwait(decoded->date);
624 /* Display the direct buffer returned by vout_RenderPicture */
625 vout->p->displayed.date = mdate();
627 vout_DisplayWrapper(vout, direct);
633 vout_statistic_Update(&vout->p->statistic, displayed_count, lost_count);
634 if (displayed_count <= 0)
639 static void ThreadManage(vout_thread_t *vout,
641 vout_interlacing_support_t *interlacing,
642 vout_postprocessing_support_t *postprocessing)
644 vlc_mutex_lock(&vout->p->picture_lock);
646 *deadline = VLC_TS_INVALID;
647 ThreadDisplayPicture(vout, false, deadline);
649 const int picture_qtype = vout->p->displayed.qtype;
650 const bool picture_interlaced = vout->p->displayed.is_interlaced;
652 vlc_mutex_unlock(&vout->p->picture_lock);
654 /* Post processing */
655 vout_SetPostProcessingState(vout, postprocessing, picture_qtype);
658 vout_SetInterlacingState(vout, interlacing, picture_interlaced);
660 vout_ManageWrapper(vout);
663 static void ThreadDisplayOsdTitle(vout_thread_t *vout, const char *string)
665 if (!vout->p->title.show)
668 vout_OSDText(vout, SPU_DEFAULT_CHANNEL,
669 vout->p->title.position, INT64_C(1000) * vout->p->title.timeout,
673 static void ThreadChangeFilters(vout_thread_t *vout, const char *filters)
676 es_format_Init(&fmt, VIDEO_ES, vout->p->original.i_chroma);
677 fmt.video = vout->p->original;
679 vlc_mutex_lock(&vout->p->vfilter_lock);
681 filter_chain_Reset(vout->p->vfilter_chain, &fmt, &fmt);
682 if (filter_chain_AppendFromString(vout->p->vfilter_chain,
684 msg_Err(vout, "Video filter chain creation failed");
686 vlc_mutex_unlock(&vout->p->vfilter_lock);
689 static void ThreadChangeSubFilters(vout_thread_t *vout, const char *filters)
691 spu_ChangeFilters(vout->p->p_spu, filters);
694 static void ThreadChangePause(vout_thread_t *vout, bool is_paused, mtime_t date)
696 assert(!vout->p->pause.is_on || !is_paused);
698 if (vout->p->pause.is_on) {
699 const mtime_t duration = date - vout->p->pause.date;
701 if (vout->p->step.timestamp > VLC_TS_INVALID)
702 vout->p->step.timestamp += duration;
703 if (vout->p->step.last > VLC_TS_INVALID)
704 vout->p->step.last += duration;
705 picture_fifo_OffsetDate(vout->p->decoder_fifo, duration);
706 if (vout->p->displayed.decoded)
707 vout->p->displayed.decoded->date += duration;
709 spu_OffsetSubtitleDate(vout->p->p_spu, duration);
711 vout->p->step.timestamp = VLC_TS_INVALID;
712 vout->p->step.last = VLC_TS_INVALID;
714 vout->p->pause.is_on = is_paused;
715 vout->p->pause.date = date;
718 static void ThreadFlush(vout_thread_t *vout, bool below, mtime_t date)
720 vout->p->step.timestamp = VLC_TS_INVALID;
721 vout->p->step.last = VLC_TS_INVALID;
723 picture_t *last = vout->p->displayed.decoded;
725 if (( below && last->date <= date) ||
726 (!below && last->date >= date)) {
727 picture_Release(last);
729 vout->p->displayed.decoded = NULL;
730 vout->p->displayed.date = VLC_TS_INVALID;
731 vout->p->displayed.timestamp = VLC_TS_INVALID;
734 picture_fifo_Flush(vout->p->decoder_fifo, date, below);
737 static void ThreadReset(vout_thread_t *vout)
739 ThreadFlush(vout, true, INT64_MAX);
740 if (vout->p->decoder_pool)
741 picture_pool_NonEmpty(vout->p->decoder_pool, true);
742 vout->p->pause.is_on = false;
743 vout->p->pause.date = mdate();
746 static void ThreadStep(vout_thread_t *vout, mtime_t *duration)
750 if (vout->p->step.last <= VLC_TS_INVALID)
751 vout->p->step.last = vout->p->displayed.timestamp;
754 if (ThreadDisplayPicture(vout, true, &dummy))
757 vout->p->step.timestamp = vout->p->displayed.timestamp;
759 if (vout->p->step.last > VLC_TS_INVALID &&
760 vout->p->step.timestamp > vout->p->step.last) {
761 *duration = vout->p->step.timestamp - vout->p->step.last;
762 vout->p->step.last = vout->p->step.timestamp;
763 /* TODO advance subpicture by the duration ... */
767 static void ThreadChangeFullscreen(vout_thread_t *vout, bool fullscreen)
769 /* FIXME not sure setting "fullscreen" is good ... */
770 var_SetBool(vout, "fullscreen", fullscreen);
771 vout_SetDisplayFullscreen(vout->p->display.vd, fullscreen);
774 static void ThreadChangeOnTop(vout_thread_t *vout, bool is_on_top)
776 vout_SetWindowState(vout->p->display.vd,
777 is_on_top ? VOUT_WINDOW_STATE_ABOVE :
778 VOUT_WINDOW_STATE_NORMAL);
781 static void ThreadChangeDisplayFilled(vout_thread_t *vout, bool is_filled)
783 vout_SetDisplayFilled(vout->p->display.vd, is_filled);
786 static void ThreadChangeZoom(vout_thread_t *vout, int num, int den)
788 if (num * 10 < den) {
791 } else if (num > den * 10) {
795 vout_SetDisplayZoom(vout->p->display.vd, num, den);
798 static void ThreadChangeAspectRatio(vout_thread_t *vout,
799 unsigned num, unsigned den)
801 const video_format_t *source = &vout->p->original;
803 if (num > 0 && den > 0) {
804 num *= source->i_visible_height;
805 den *= source->i_visible_width;
806 vlc_ureduce(&num, &den, num, den, 0);
808 vout_SetDisplayAspect(vout->p->display.vd, num, den);
812 static void ThreadExecuteCropWindow(vout_thread_t *vout,
813 unsigned crop_num, unsigned crop_den,
814 unsigned x, unsigned y,
815 unsigned width, unsigned height)
817 const video_format_t *source = &vout->p->original;
819 vout_SetDisplayCrop(vout->p->display.vd,
821 source->i_x_offset + x,
822 source->i_y_offset + y,
825 static void ThreadExecuteCropBorder(vout_thread_t *vout,
826 unsigned left, unsigned top,
827 unsigned right, unsigned bottom)
829 const video_format_t *source = &vout->p->original;
830 ThreadExecuteCropWindow(vout, 0, 0,
833 /* At worst, it becomes < 0 (but unsigned) and will be rejected */
834 source->i_visible_width - (left + right),
835 source->i_visible_height - (top + bottom));
838 static void ThreadExecuteCropRatio(vout_thread_t *vout,
839 unsigned num, unsigned den)
841 const video_format_t *source = &vout->p->original;
842 ThreadExecuteCropWindow(vout, num, den,
844 source->i_visible_width,
845 source->i_visible_height);
848 static int ThreadStart(vout_thread_t *vout)
850 vlc_mouse_Init(&vout->p->mouse);
851 vout->p->decoder_fifo = picture_fifo_New();
852 vout->p->decoder_pool = NULL;
853 vout->p->display_pool = NULL;
854 vout->p->private_pool = NULL;
856 vout->p->vfilter_chain =
857 filter_chain_New( vout, "video filter2", false,
858 VoutVideoFilterAllocationSetup, NULL, vout);
860 if (vout_OpenWrapper(vout, vout->p->splitter_name))
862 if (vout_InitWrapper(vout))
864 assert(vout->p->decoder_pool);
866 vout->p->displayed.decoded = NULL;
867 vout->p->displayed.date = VLC_TS_INVALID;
868 vout->p->displayed.decoded = NULL;
869 vout->p->displayed.timestamp = VLC_TS_INVALID;
870 vout->p->displayed.qtype = QTYPE_NONE;
871 vout->p->displayed.is_interlaced = false;
873 vout->p->step.last = VLC_TS_INVALID;
874 vout->p->step.timestamp = VLC_TS_INVALID;
876 video_format_Print(VLC_OBJECT(vout), "original format", &vout->p->original);
880 static void ThreadStop(vout_thread_t *vout)
882 /* Destroy the video filters2 */
883 filter_chain_Delete(vout->p->vfilter_chain);
885 /* Destroy translation tables */
886 if (vout->p->display.vd) {
887 if (vout->p->decoder_pool) {
888 ThreadFlush(vout, true, INT64_MAX);
889 vout_EndWrapper(vout);
891 vout_CloseWrapper(vout);
894 if (vout->p->decoder_fifo)
895 picture_fifo_Delete(vout->p->decoder_fifo);
896 assert(!vout->p->decoder_pool);
899 static void ThreadInit(vout_thread_t *vout)
901 vout->p->dead = false;
902 vout->p->is_late_dropped = var_InheritBool(vout, "drop-late-frames");
903 vout->p->pause.is_on = false;
904 vout->p->pause.date = VLC_TS_INVALID;
906 vout_chrono_Init(&vout->p->render, 5, 10000); /* Arbitrary initial time */
909 static void ThreadClean(vout_thread_t *vout)
911 vout_chrono_Clean(&vout->p->render);
912 vout->p->dead = true;
913 vout_control_Dead(&vout->p->control);
916 static int ThreadReinit(vout_thread_t *vout,
917 const video_format_t *fmt)
919 video_format_t original;
920 if (VoutValidateFormat(&original, fmt)) {
925 if (video_format_IsSimilar(&original, &vout->p->original))
930 vout->p->original = original;
931 if (ThreadStart(vout)) {
938 /*****************************************************************************
939 * Thread: video output thread
940 *****************************************************************************
941 * Video output thread. This function does only returns when the thread is
942 * terminated. It handles the pictures arriving in the video heap and the
943 * display device events.
944 *****************************************************************************/
945 static void *Thread(void *object)
947 vout_thread_t *vout = object;
949 vout_interlacing_support_t interlacing = {
950 .is_interlaced = false,
953 vout_postprocessing_support_t postprocessing = {
957 mtime_t deadline = VLC_TS_INVALID;
959 vout_control_cmd_t cmd;
961 /* FIXME remove thoses ugly timeouts
963 while (!vout_control_Pop(&vout->p->control, &cmd, deadline, 100000)) {
965 case VOUT_CONTROL_INIT:
967 if (ThreadStart(vout)) {
973 case VOUT_CONTROL_CLEAN:
977 case VOUT_CONTROL_REINIT:
978 if (ThreadReinit(vout, cmd.u.reinit.fmt))
981 case VOUT_CONTROL_OSD_TITLE:
982 ThreadDisplayOsdTitle(vout, cmd.u.string);
984 case VOUT_CONTROL_CHANGE_FILTERS:
985 ThreadChangeFilters(vout, cmd.u.string);
987 case VOUT_CONTROL_CHANGE_SUB_FILTERS:
988 ThreadChangeSubFilters(vout, cmd.u.string);
990 case VOUT_CONTROL_PAUSE:
991 ThreadChangePause(vout, cmd.u.pause.is_on, cmd.u.pause.date);
993 case VOUT_CONTROL_FLUSH:
994 ThreadFlush(vout, false, cmd.u.time);
996 case VOUT_CONTROL_RESET:
999 case VOUT_CONTROL_STEP:
1000 ThreadStep(vout, cmd.u.time_ptr);
1002 case VOUT_CONTROL_FULLSCREEN:
1003 ThreadChangeFullscreen(vout, cmd.u.boolean);
1005 case VOUT_CONTROL_ON_TOP:
1006 ThreadChangeOnTop(vout, cmd.u.boolean);
1008 case VOUT_CONTROL_DISPLAY_FILLED:
1009 ThreadChangeDisplayFilled(vout, cmd.u.boolean);
1011 case VOUT_CONTROL_ZOOM:
1012 ThreadChangeZoom(vout, cmd.u.pair.a, cmd.u.pair.b);
1014 case VOUT_CONTROL_ASPECT_RATIO:
1015 ThreadChangeAspectRatio(vout, cmd.u.pair.a, cmd.u.pair.b);
1017 case VOUT_CONTROL_CROP_RATIO:
1018 ThreadExecuteCropRatio(vout, cmd.u.pair.a, cmd.u.pair.b);
1020 case VOUT_CONTROL_CROP_WINDOW:
1021 ThreadExecuteCropWindow(vout, 0, 0,
1022 cmd.u.window.x, cmd.u.window.y,
1023 cmd.u.window.width, cmd.u.window.height);
1025 case VOUT_CONTROL_CROP_BORDER:
1026 ThreadExecuteCropBorder(vout,
1027 cmd.u.border.left, cmd.u.border.top,
1028 cmd.u.border.right, cmd.u.border.bottom);
1033 vout_control_cmd_Clean(&cmd);
1036 ThreadManage(vout, &deadline, &interlacing, &postprocessing);