1 /*****************************************************************************
2 * fb.c : framebuffer plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2000-2009 VLC authors and VideoLAN
7 * Authors: Samuel Hocevar <sam@zoy.org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <signal.h> /* SIGUSR1, SIGUSR2 */
34 #include <fcntl.h> /* open() */
35 #include <unistd.h> /* close() */
37 #include <termios.h> /* struct termios */
38 #include <sys/ioctl.h>
39 #include <sys/mman.h> /* mmap() */
42 #include <linux/vt.h> /* VT_* */
43 #include <linux/kd.h> /* KD* */
45 #include <vlc_common.h>
46 #include <vlc_plugin.h>
47 #include <vlc_vout_display.h>
48 #include <vlc_picture_pool.h>
51 /*****************************************************************************
53 *****************************************************************************/
54 #define FB_DEV_VAR "fbdev"
56 #define DEVICE_TEXT N_("Framebuffer device")
57 #define DEVICE_LONGTEXT N_(\
58 "Framebuffer device to use for rendering (usually /dev/fb0).")
60 #define TTY_TEXT N_("Run fb on current tty")
61 #define TTY_LONGTEXT N_(\
62 "Run framebuffer on current TTY device (default enabled). " \
63 "(disable tty handling with caution)")
65 #define FB_MODE_TEXT N_("Framebuffer resolution to use")
66 #define FB_MODE_LONGTEXT N_(\
67 "Select the resolution for the framebuffer. Currently it supports " \
68 "the values 0=QCIF 1=CIF 2=NTSC 3=PAL, 4=auto (default 4=auto)")
70 #define HW_ACCEL_TEXT N_("Framebuffer uses hw acceleration")
71 #define HW_ACCEL_LONGTEXT N_("Disable for double buffering in software.")
73 #define CHROMA_TEXT N_("Image format (default RGB)")
74 #define CHROMA_LONGTEXT N_("Chroma fourcc used by the framebuffer. Default is RGB since the fb device has no way to report its chroma.")
76 static int Open (vlc_object_t
*);
77 static void Close(vlc_object_t
*);
80 set_shortname("Framebuffer")
81 set_category(CAT_VIDEO
)
82 set_subcategory(SUBCAT_VIDEO_VOUT
)
83 add_loadfile(FB_DEV_VAR
, "/dev/fb0", DEVICE_TEXT
, DEVICE_LONGTEXT
,
85 add_bool("fb-tty", true, TTY_TEXT
, TTY_LONGTEXT
, true)
86 add_string( "fb-chroma", NULL
, CHROMA_TEXT
, CHROMA_LONGTEXT
, true )
87 add_obsolete_string("fb-aspect-ratio")
88 add_integer("fb-mode", 4, FB_MODE_TEXT
, FB_MODE_LONGTEXT
,
90 add_bool("fb-hw-accel", true, HW_ACCEL_TEXT
, HW_ACCEL_LONGTEXT
,
92 set_description(N_("GNU/Linux framebuffer video output"))
93 set_capability("vout display", 30)
94 set_callbacks(Open
, Close
)
97 /*****************************************************************************
99 *****************************************************************************/
100 static picture_pool_t
*Pool (vout_display_t
*, unsigned);
101 static void Display(vout_display_t
*, picture_t
*, subpicture_t
*);
102 static int Control(vout_display_t
*, int, va_list);
105 static int OpenDisplay (vout_display_t
*, bool force_resolution
);
106 static void CloseDisplay (vout_display_t
*);
108 static void SwitchDisplay(int i_signal
);
110 static void TextMode (int tty
);
111 static void GfxMode (int tty
);
113 static int TtyInit(vout_display_t
*);
114 static void TtyExit(vout_display_t
*);
117 struct vout_display_sys_t
{
118 /* System information */
119 int tty
; /* tty device handle */
121 struct termios old_termios
;
123 /* Original configuration information */
125 struct sigaction sig_usr1
; /* USR1 previous handler */
126 struct sigaction sig_usr2
; /* USR2 previous handler */
128 struct vt_mode vt_mode
; /* previous VT mode */
130 /* Framebuffer information */
131 int fd
; /* device handle */
132 struct fb_var_screeninfo old_info
; /* original mode information */
133 struct fb_var_screeninfo var_info
; /* current mode information */
134 bool has_pan
; /* does device supports panning ? */
135 struct fb_cmap fb_cmap
; /* original colormap */
136 uint16_t *palette
; /* original palette */
137 bool is_hw_accel
; /* has hardware support */
139 /* Video information */
142 uint32_t line_length
;
147 uint8_t *video_ptr
; /* base address */
148 size_t video_size
; /* page size */
151 picture_pool_t
*pool
;
155 static void ClearScreen(vout_display_sys_t
*sys
)
157 switch (sys
->chroma
) {
158 /* XXX: add other chromas */
159 case VLC_CODEC_UYVY
: {
160 unsigned int j
, size
= sys
->video_size
/ 4;
161 uint32_t *ptr
= (uint32_t*)((uintptr_t)(sys
->video_ptr
+ 3) & ~3);
162 for(j
=0; j
< size
; j
++)
163 ptr
[j
] = 0x10801080; /* U = V = 16, Y = 128 */
167 memset(sys
->video_ptr
, 0, sys
->video_size
);
172 * This function allocates and initializes a FB vout method.
174 static int Open(vlc_object_t
*object
)
176 vout_display_t
*vd
= (vout_display_t
*)object
;
177 vout_display_sys_t
*sys
;
179 if (vout_display_IsWindowed(vd
))
182 /* Allocate instance and initialize some members */
183 vd
->sys
= sys
= calloc(1, sizeof(*sys
));
187 /* Does the framebuffer uses hw acceleration? */
188 sys
->is_hw_accel
= var_InheritBool(vd
, "fb-hw-accel");
190 /* Set tty and fb devices */
191 sys
->tty
= 0; /* 0 == /dev/tty0 == current console */
192 sys
->is_tty
= var_InheritBool(vd
, "fb-tty");
193 #if !defined(_WIN32) && defined(HAVE_ISATTY)
194 /* Check that stdin is a TTY */
195 if (sys
->is_tty
&& !isatty(0)) {
196 msg_Warn(vd
, "standard input is not a TTY");
200 msg_Warn(vd
, "disabling TTY handling, use with caution because "
201 "there is no way to return to the TTY");
204 const int mode
= var_InheritInteger(vd
, "fb-mode");
205 bool force_resolution
= true;
225 force_resolution
= false;
229 char *chroma
= var_InheritString(vd
, "fb-chroma");
231 sys
->chroma
= vlc_fourcc_GetCodecFromString(VIDEO_ES
, chroma
);
234 msg_Dbg(vd
, "forcing chroma '%s'", chroma
);
236 msg_Warn(vd
, "chroma %s invalid, using default", chroma
);
243 if (sys
->is_tty
&& TtyInit(vd
)) {
249 sys
->video_ptr
= MAP_FAILED
;
253 if (OpenDisplay(vd
, force_resolution
)) {
254 Close(VLC_OBJECT(vd
));
260 video_format_ApplyRotation(&fmt
, &vd
->fmt
);
263 fmt
.i_chroma
= sys
->chroma
;
267 msg_Dbg(vd
, "%d bppd", sys
->var_info
.bits_per_pixel
);
268 switch (sys
->var_info
.bits_per_pixel
) {
269 case 8: /* FIXME: set the palette */
270 fmt
.i_chroma
= VLC_CODEC_RGB8
;
273 fmt
.i_chroma
= VLC_CODEC_RGB15
;
276 fmt
.i_chroma
= VLC_CODEC_RGB16
;
279 fmt
.i_chroma
= VLC_CODEC_RGB24
;
282 fmt
.i_chroma
= VLC_CODEC_RGB32
;
285 msg_Err(vd
, "unknown screendepth %i", sys
->var_info
.bits_per_pixel
);
286 Close(VLC_OBJECT(vd
));
289 if (sys
->var_info
.bits_per_pixel
!= 8) {
290 fmt
.i_rmask
= ((1 << sys
->var_info
.red
.length
) - 1)
291 << sys
->var_info
.red
.offset
;
292 fmt
.i_gmask
= ((1 << sys
->var_info
.green
.length
) - 1)
293 << sys
->var_info
.green
.offset
;
294 fmt
.i_bmask
= ((1 << sys
->var_info
.blue
.length
) - 1)
295 << sys
->var_info
.blue
.offset
;
299 fmt
.i_visible_width
= sys
->width
;
300 fmt
.i_visible_height
= sys
->height
;
306 vd
->display
= Display
;
307 vd
->control
= Control
;
310 vout_display_SendEventDisplaySize(vd
, fmt
.i_visible_width
, fmt
.i_visible_height
);
315 * Terminate an output method created by Open
317 static void Close(vlc_object_t
*object
)
319 vout_display_t
*vd
= (vout_display_t
*)object
;
320 vout_display_sys_t
*sys
= vd
->sys
;
323 picture_pool_Release(sys
->pool
);
324 if (!sys
->is_hw_accel
&& sys
->picture
)
325 picture_Release(sys
->picture
);
336 static picture_pool_t
*Pool(vout_display_t
*vd
, unsigned count
)
338 vout_display_sys_t
*sys
= vd
->sys
;
342 picture_resource_t rsc
;
344 memset(&rsc
, 0, sizeof(rsc
));
345 rsc
.p
[0].p_pixels
= sys
->video_ptr
;
346 rsc
.p
[0].i_lines
= sys
->var_info
.yres
;
347 rsc
.p
[0].i_pitch
= sys
->line_length
;
349 sys
->picture
= picture_NewFromResource(&vd
->fmt
, &rsc
);
354 if (sys
->is_hw_accel
)
355 sys
->pool
= picture_pool_New(1, &sys
->picture
);
357 sys
->pool
= picture_pool_NewFromFormat(&vd
->fmt
, count
);
361 static void Display(vout_display_t
*vd
, picture_t
*picture
, subpicture_t
*subpicture
)
363 vout_display_sys_t
*sys
= vd
->sys
;
365 /* swap the two Y offsets if the drivers supports panning */
367 sys
->var_info
.yoffset
= 0;
368 /*vd->sys->var_info.yoffset = vd->sys->var_info.yres; */
370 /* the X offset should be 0, but who knows ...
371 * some other app might have played with the framebuffer */
372 sys
->var_info
.xoffset
= 0;
374 /* FIXME 'static' is damn wrong and it's dead code ... */
375 static int panned
= 0;
377 ioctl(sys
->fd
, FBIOPAN_DISPLAY
, &sys
->var_info
);
382 if (!sys
->is_hw_accel
)
383 picture_Copy(sys
->picture
, picture
);
385 picture_Release(picture
);
386 VLC_UNUSED(subpicture
);
388 static int Control(vout_display_t
*vd
, int query
, va_list args
)
390 (void) vd
; (void) query
; (void) args
;
394 /* following functions are local */
395 static int TtyInit(vout_display_t
*vd
)
397 vout_display_sys_t
*sys
= vd
->sys
;
399 struct termios new_termios
;
403 /* Set keyboard settings */
404 if (tcgetattr(0, &sys
->old_termios
) == -1) {
405 msg_Err(vd
, "tcgetattr failed");
408 if (tcgetattr(0, &new_termios
) == -1) {
409 msg_Err(vd
, "tcgetattr failed");
412 /* new_termios.c_lflag &= ~ (ICANON | ISIG);
413 new_termios.c_lflag |= (ECHO | ECHOCTL); */
414 new_termios
.c_lflag
&= ~ (ICANON
);
415 new_termios
.c_lflag
&= ~(ECHO
| ECHOCTL
);
416 new_termios
.c_iflag
= 0;
417 new_termios
.c_cc
[VMIN
] = 1;
418 new_termios
.c_cc
[VTIME
] = 0;
420 if (tcsetattr(0, TCSAFLUSH
, &new_termios
) == -1) {
421 msg_Err(vd
, "tcsetattr failed");
424 ioctl(sys
->tty
, VT_RELDISP
, VT_ACKACQ
);
427 /* Set-up tty signal handler to be aware of tty changes */
428 struct sigaction sig_tty
;
429 memset(&sig_tty
, 0, sizeof(sig_tty
));
430 sig_tty
.sa_handler
= SwitchDisplay
;
431 sigemptyset(&sig_tty
.sa_mask
);
432 if (sigaction(SIGUSR1
, &sig_tty
, &sys
->sig_usr1
) ||
433 sigaction(SIGUSR2
, &sig_tty
, &sys
->sig_usr2
)) {
434 msg_Err(vd
, "cannot set signal handler (%s)", vlc_strerror_c(errno
));
435 /* FIXME SIGUSR1 could have succeed */
440 /* Set-up tty according to new signal handler */
441 if (-1 == ioctl(sys
->tty
, VT_GETMODE
, &sys
->vt_mode
)) {
442 msg_Err(vd
, "cannot get terminal mode (%s)", vlc_strerror_c(errno
));
445 struct vt_mode vt_mode
= sys
->vt_mode
;
446 vt_mode
.mode
= VT_PROCESS
;
448 vt_mode
.relsig
= SIGUSR1
;
449 vt_mode
.acqsig
= SIGUSR2
;
451 if (-1 == ioctl(sys
->tty
, VT_SETMODE
, &vt_mode
)) {
452 msg_Err(vd
, "cannot set terminal mode (%s)", vlc_strerror_c(errno
));
459 sigaction(SIGUSR1
, &sys
->sig_usr1
, NULL
);
460 sigaction(SIGUSR2
, &sys
->sig_usr2
, NULL
);
463 tcsetattr(0, 0, &sys
->old_termios
);
467 static void TtyExit(vout_display_t
*vd
)
469 vout_display_sys_t
*sys
= vd
->sys
;
471 /* Reset the terminal */
472 ioctl(sys
->tty
, VT_SETMODE
, &sys
->vt_mode
);
475 /* Remove signal handlers */
476 sigaction(SIGUSR1
, &sys
->sig_usr1
, NULL
);
477 sigaction(SIGUSR2
, &sys
->sig_usr2
, NULL
);
480 /* Reset the keyboard state */
481 tcsetattr(0, 0, &sys
->old_termios
);
483 /* Return to text mode */
487 /*****************************************************************************
488 * OpenDisplay: initialize framebuffer
489 *****************************************************************************/
490 static int OpenDisplay(vout_display_t
*vd
, bool force_resolution
)
492 vout_display_sys_t
*sys
= vd
->sys
;
493 char *psz_device
; /* framebuffer device path */
495 /* Open framebuffer device */
496 if (!(psz_device
= var_InheritString(vd
, FB_DEV_VAR
))) {
497 msg_Err(vd
, "don't know which fb device to open");
501 sys
->fd
= vlc_open(psz_device
, O_RDWR
);
503 msg_Err(vd
, "cannot open %s (%s)", psz_device
, vlc_strerror_c(errno
));
509 /* Get framebuffer device information */
510 if (ioctl(sys
->fd
, FBIOGET_VSCREENINFO
, &sys
->var_info
)) {
511 msg_Err(vd
, "cannot get fb info (%s)", vlc_strerror_c(errno
));
515 sys
->old_info
= sys
->var_info
;
517 /* Get some info on the framebuffer itself */
518 if (force_resolution
) {
519 sys
->var_info
.xres
= sys
->var_info
.xres_virtual
= sys
->width
;
520 sys
->var_info
.yres
= sys
->var_info
.yres_virtual
= sys
->height
;
523 /* Set some attributes */
524 sys
->var_info
.activate
= sys
->is_tty
? FB_ACTIVATE_NXTOPEN
:
526 sys
->var_info
.xoffset
= 0;
527 sys
->var_info
.yoffset
= 0;
529 if (ioctl(sys
->fd
, FBIOPUT_VSCREENINFO
, &sys
->var_info
)) {
530 msg_Err(vd
, "cannot set fb info (%s)", vlc_strerror_c(errno
));
535 struct fb_fix_screeninfo fix_info
;
536 /* Get some information again, in the definitive configuration */
537 if (ioctl(sys
->fd
, FBIOGET_FSCREENINFO
, &fix_info
) ||
538 ioctl(sys
->fd
, FBIOGET_VSCREENINFO
, &sys
->var_info
)) {
539 msg_Err(vd
, "cannot get additional fb info (%s)",
540 vlc_strerror_c(errno
));
542 /* Restore fb config */
543 ioctl(sys
->fd
, FBIOPUT_VSCREENINFO
, &sys
->old_info
);
549 /* If the fb has limitations on mode change,
550 * then keep the resolution of the fb */
551 if ((sys
->height
!= sys
->var_info
.yres
) ||
552 (sys
->width
!= sys
->var_info
.xres
)) {
554 "using framebuffer native resolution instead of requested (%ix%i)",
555 sys
->width
, sys
->height
);
557 sys
->height
= sys
->var_info
.yres
;
558 sys
->width
= sys
->var_info
.xres_virtual
? sys
->var_info
.xres_virtual
:
560 sys
->line_length
= fix_info
.line_length
;
562 /* FIXME: if the image is full-size, it gets cropped on the left
563 * because of the xres / xres_virtual slight difference */
564 msg_Dbg(vd
, "%ix%i (virtual %ix%i) (request %ix%i)",
565 sys
->var_info
.xres
, sys
->var_info
.yres
,
566 sys
->var_info
.xres_virtual
,
567 sys
->var_info
.yres_virtual
,
568 sys
->width
, sys
->height
);
571 sys
->has_pan
= (fix_info
.ypanstep
|| fix_info
.ywrapstep
);
573 switch (sys
->var_info
.bits_per_pixel
) {
575 sys
->palette
= malloc(4 * 256 * sizeof(uint16_t));
577 /* Restore fb config */
578 ioctl(sys
->fd
, FBIOPUT_VSCREENINFO
, &sys
->old_info
);
583 sys
->fb_cmap
.start
= 0;
584 sys
->fb_cmap
.len
= 256;
585 sys
->fb_cmap
.red
= sys
->palette
;
586 sys
->fb_cmap
.green
= sys
->palette
+ 256;
587 sys
->fb_cmap
.blue
= sys
->palette
+ 2 * 256;
588 sys
->fb_cmap
.transp
= sys
->palette
+ 3 * 256;
590 /* Save the colormap */
591 ioctl(sys
->fd
, FBIOGETCMAP
, &sys
->fb_cmap
);
593 sys
->bytes_per_pixel
= 1;
598 sys
->bytes_per_pixel
= 2;
602 sys
->bytes_per_pixel
= 3;
606 sys
->bytes_per_pixel
= 4;
610 msg_Err(vd
, "screen depth %d is not supported",
611 sys
->var_info
.bits_per_pixel
);
613 /* Restore fb config */
614 ioctl(sys
->fd
, FBIOPUT_VSCREENINFO
, &sys
->old_info
);
620 sys
->video_size
= sys
->line_length
* sys
->var_info
.yres_virtual
;
622 /* Map a framebuffer at the beginning */
623 sys
->video_ptr
= mmap(NULL
, sys
->video_size
,
624 PROT_READ
| PROT_WRITE
, MAP_SHARED
, sys
->fd
, 0);
626 if (sys
->video_ptr
== MAP_FAILED
) {
627 msg_Err(vd
, "cannot map video memory (%s)", vlc_strerror_c(errno
));
629 if (sys
->var_info
.bits_per_pixel
== 8) {
634 /* Restore fb config */
635 ioctl(sys
->fd
, FBIOPUT_VSCREENINFO
, &sys
->old_info
);
644 "framebuffer type=%d, visual=%d, ypanstep=%d, ywrap=%d, accel=%d",
645 fix_info
.type
, fix_info
.visual
,
646 fix_info
.ypanstep
, fix_info
.ywrapstep
, fix_info
.accel
);
650 /*****************************************************************************
651 * CloseDisplay: terminate FB video thread output method
652 *****************************************************************************/
653 static void CloseDisplay(vout_display_t
*vd
)
655 vout_display_sys_t
*sys
= vd
->sys
;
657 if (sys
->video_ptr
!= MAP_FAILED
) {
659 munmap(sys
->video_ptr
, sys
->video_size
);
663 /* Restore palette */
664 if (sys
->var_info
.bits_per_pixel
== 8) {
665 ioctl(sys
->fd
, FBIOPUTCMAP
, &sys
->fb_cmap
);
670 /* Restore fb config */
671 ioctl(sys
->fd
, FBIOPUT_VSCREENINFO
, &sys
->old_info
);
679 /*****************************************************************************
680 * SwitchDisplay: VT change signal handler
681 *****************************************************************************
682 * This function activates or deactivates the output of the thread. It is
683 * called by the VT driver, on terminal change.
684 *****************************************************************************/
685 static void SwitchDisplay(int i_signal
)
689 vlc_mutex_lock(&p_vout_bank
->lock
);
691 /* XXX: only test the first video output */
692 if (p_vout_bank
->i_count
)
694 vd
= p_vout_bank
->pp_vout
[0];
698 case SIGUSR1
: /* vt has been released */
700 ioctl(sys
->tty
, VT_RELDISP
, 1);
702 case SIGUSR2
: /* vt has been acquired */
704 ioctl(sys
->tty
, VT_RELDISP
, VT_ACTIVATE
);
705 /* handle blanking */
706 vlc_mutex_lock(&vd
->change_lock
);
707 vd
->i_changes
|= VOUT_SIZE_CHANGE
;
708 vlc_mutex_unlock(&vd
->change_lock
);
713 vlc_mutex_unlock(&p_vout_bank
->lock
);
717 /*****************************************************************************
718 * TextMode and GfxMode : switch tty to text/graphic mode
719 *****************************************************************************
720 * These functions toggle the tty mode.
721 *****************************************************************************/
722 static void TextMode(int tty
)
724 /* return to text mode */
725 if (-1 == ioctl(tty
, KDSETMODE
, KD_TEXT
)) {
726 /*msg_Err(vd, "failed ioctl KDSETMODE KD_TEXT");*/
730 static void GfxMode(int tty
)
732 /* switch to graphic mode */
733 if (-1 == ioctl(tty
, KDSETMODE
, KD_GRAPHICS
)) {
734 /*msg_Err(vd, "failed ioctl KDSETMODE KD_GRAPHICS");*/