00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <errno.h>
00028 #include <signal.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <fcntl.h>
00032 #include <unistd.h>
00033
00034 #include <termios.h>
00035 #include <sys/ioctl.h>
00036 #include <sys/mman.h>
00037
00038 #include <linux/fb.h>
00039 #include <linux/vt.h>
00040 #include <linux/kd.h>
00041
00042 #include <vlc/vlc.h>
00043 #include <vlc/vout.h>
00044
00045
00046
00047
00048 static int Create ( vlc_object_t * );
00049 static void Destroy ( vlc_object_t * );
00050
00051 static int Init ( vout_thread_t * );
00052 static void End ( vout_thread_t * );
00053 static int Manage ( vout_thread_t * );
00054 static void Display ( vout_thread_t *, picture_t * );
00055
00056 static int OpenDisplay ( vout_thread_t * );
00057 static void CloseDisplay ( vout_thread_t * );
00058 static void SwitchDisplay ( int i_signal );
00059 static void TextMode ( int i_tty );
00060 static void GfxMode ( int i_tty );
00061
00062
00063
00064
00065 #define FB_DEV_VAR "fbdev"
00066
00067 #define DEVICE_TEXT N_("Framebuffer device")
00068 #define DEVICE_LONGTEXT N_( \
00069 "You can select here the framebuffer device that will be used " \
00070 "for rendering (usually /dev/fb0).")
00071
00072 vlc_module_begin();
00073 set_shortname( "Framebuffer" );
00074 set_category( CAT_VIDEO );
00075 set_subcategory( SUBCAT_VIDEO_VOUT );
00076 add_file( FB_DEV_VAR, "/dev/fb0", NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
00077 VLC_FALSE );
00078 set_description( _("GNU/Linux console framebuffer video output") );
00079 set_capability( "video output", 30 );
00080 set_callbacks( Create, Destroy );
00081 vlc_module_end();
00082
00083
00084
00085
00086
00087
00088
00089 struct vout_sys_t
00090 {
00091
00092 int i_tty;
00093 struct termios old_termios;
00094
00095
00096 struct sigaction sig_usr1;
00097 struct sigaction sig_usr2;
00098 struct vt_mode vt_mode;
00099
00100
00101 int i_fd;
00102 struct fb_var_screeninfo old_info;
00103 struct fb_var_screeninfo var_info;
00104 vlc_bool_t b_pan;
00105 struct fb_cmap fb_cmap;
00106 uint16_t *p_palette;
00107
00108
00109 int i_width;
00110 int i_height;
00111 int i_bytes_per_pixel;
00112
00113
00114 byte_t * p_video;
00115 size_t i_page_size;
00116 };
00117
00118
00119
00120
00121
00122
00123 static int Create( vlc_object_t *p_this )
00124 {
00125 vout_thread_t *p_vout = (vout_thread_t *)p_this;
00126
00127 struct sigaction sig_tty;
00128 struct vt_mode vt_mode;
00129 struct termios new_termios;
00130
00131
00132 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
00133 if( p_vout->p_sys == NULL )
00134 {
00135 return VLC_ENOMEM;
00136 };
00137
00138 p_vout->pf_init = Init;
00139 p_vout->pf_end = End;
00140 p_vout->pf_manage = Manage;
00141 p_vout->pf_render = NULL;
00142 p_vout->pf_display = Display;
00143
00144
00145 p_vout->p_sys->i_tty = 0;
00146
00147 GfxMode( p_vout->p_sys->i_tty );
00148
00149
00150 if (tcgetattr(0, &p_vout->p_sys->old_termios) == -1)
00151 {
00152 msg_Err( p_vout, "tcgetattr failed" );
00153 }
00154
00155 if (tcgetattr(0, &new_termios) == -1)
00156 {
00157 msg_Err( p_vout, "tcgetattr failed" );
00158 }
00159
00160
00161
00162 new_termios.c_lflag &= ~ (ICANON);
00163 new_termios.c_lflag &= ~(ECHO | ECHOCTL);
00164 new_termios.c_iflag = 0;
00165 new_termios.c_cc[VMIN] = 1;
00166 new_termios.c_cc[VTIME] = 0;
00167
00168 if (tcsetattr(0, TCSAFLUSH, &new_termios) == -1)
00169 {
00170 msg_Err( p_vout, "tcsetattr failed" );
00171 }
00172
00173 ioctl( p_vout->p_sys->i_tty, VT_RELDISP, VT_ACKACQ );
00174
00175
00176 memset( &sig_tty, 0, sizeof( sig_tty ) );
00177 sig_tty.sa_handler = SwitchDisplay;
00178 sigemptyset( &sig_tty.sa_mask );
00179 if( sigaction( SIGUSR1, &sig_tty, &p_vout->p_sys->sig_usr1 ) ||
00180 sigaction( SIGUSR2, &sig_tty, &p_vout->p_sys->sig_usr2 ) )
00181 {
00182 msg_Err( p_vout, "cannot set signal handler (%s)", strerror(errno) );
00183 tcsetattr(0, 0, &p_vout->p_sys->old_termios);
00184 TextMode( p_vout->p_sys->i_tty );
00185 free( p_vout->p_sys );
00186 return VLC_EGENERIC;
00187 }
00188
00189
00190 if( -1 == ioctl( p_vout->p_sys->i_tty,
00191 VT_GETMODE, &p_vout->p_sys->vt_mode ) )
00192 {
00193 msg_Err( p_vout, "cannot get terminal mode (%s)", strerror(errno) );
00194 sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
00195 sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
00196 tcsetattr(0, 0, &p_vout->p_sys->old_termios);
00197 TextMode( p_vout->p_sys->i_tty );
00198 free( p_vout->p_sys );
00199 return VLC_EGENERIC;
00200 }
00201 memcpy( &vt_mode, &p_vout->p_sys->vt_mode, sizeof( vt_mode ) );
00202 vt_mode.mode = VT_PROCESS;
00203 vt_mode.waitv = 0;
00204 vt_mode.relsig = SIGUSR1;
00205 vt_mode.acqsig = SIGUSR2;
00206
00207 if( -1 == ioctl( p_vout->p_sys->i_tty, VT_SETMODE, &vt_mode ) )
00208 {
00209 msg_Err( p_vout, "cannot set terminal mode (%s)", strerror(errno) );
00210 sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
00211 sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
00212 tcsetattr(0, 0, &p_vout->p_sys->old_termios);
00213 TextMode( p_vout->p_sys->i_tty );
00214 free( p_vout->p_sys );
00215 return VLC_EGENERIC;
00216 }
00217
00218 if( OpenDisplay( p_vout ) )
00219 {
00220 ioctl( p_vout->p_sys->i_tty, VT_SETMODE, &p_vout->p_sys->vt_mode );
00221 sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
00222 sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
00223 tcsetattr(0, 0, &p_vout->p_sys->old_termios);
00224 TextMode( p_vout->p_sys->i_tty );
00225 free( p_vout->p_sys );
00226 return VLC_EGENERIC;
00227 }
00228
00229 return VLC_SUCCESS;
00230 }
00231
00232
00233
00234
00235 static int Init( vout_thread_t *p_vout )
00236 {
00237 int i_index;
00238 picture_t *p_pic;
00239
00240 I_OUTPUTPICTURES = 0;
00241
00242
00243
00244 switch( p_vout->p_sys->var_info.bits_per_pixel )
00245 {
00246 case 8:
00247 p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2'); break;
00248 case 15:
00249 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5'); break;
00250 case 16:
00251 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6'); break;
00252 case 24:
00253 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4'); break;
00254 case 32:
00255 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2'); break;
00256 default:
00257 msg_Err( p_vout, "unknown screen depth %i",
00258 p_vout->p_sys->var_info.bits_per_pixel );
00259 return VLC_EGENERIC;
00260 }
00261
00262
00263 p_vout->output.i_rmask = ( (1 << p_vout->p_sys->var_info.red.length) - 1 )
00264 << p_vout->p_sys->var_info.red.offset;
00265 p_vout->output.i_gmask = ( (1 << p_vout->p_sys->var_info.green.length) - 1 )
00266 << p_vout->p_sys->var_info.green.offset;
00267 p_vout->output.i_bmask = ( (1 << p_vout->p_sys->var_info.blue.length) - 1 )
00268 << p_vout->p_sys->var_info.blue.offset;
00269
00270 p_vout->output.i_width = p_vout->p_sys->i_width;
00271 p_vout->output.i_height = p_vout->p_sys->i_height;
00272
00273
00274 p_vout->output.i_aspect = p_vout->p_sys->i_width
00275 * VOUT_ASPECT_FACTOR / p_vout->p_sys->i_height;
00276
00277
00278 memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size );
00279
00280
00281 p_pic = NULL;
00282
00283
00284 for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
00285 {
00286 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
00287 {
00288 p_pic = p_vout->p_picture + i_index;
00289 break;
00290 }
00291 }
00292
00293
00294 if( p_pic == NULL )
00295 {
00296 return VLC_EGENERIC;
00297 }
00298
00299
00300
00301 p_pic->p->p_pixels = p_vout->p_sys->p_video;
00302 p_pic->p->i_pixel_pitch = p_vout->p_sys->i_bytes_per_pixel;
00303 p_pic->p->i_lines = p_vout->p_sys->var_info.yres;
00304 p_pic->p->i_visible_lines = p_vout->p_sys->var_info.yres;
00305
00306 if( p_vout->p_sys->var_info.xres_virtual )
00307 {
00308 p_pic->p->i_pitch = p_vout->p_sys->var_info.xres_virtual
00309 * p_vout->p_sys->i_bytes_per_pixel;
00310 }
00311 else
00312 {
00313 p_pic->p->i_pitch = p_vout->p_sys->var_info.xres
00314 * p_vout->p_sys->i_bytes_per_pixel;
00315 }
00316
00317 p_pic->p->i_visible_pitch = p_vout->p_sys->var_info.xres
00318 * p_vout->p_sys->i_bytes_per_pixel;
00319
00320 p_pic->i_planes = 1;
00321
00322 p_pic->i_status = DESTROYED_PICTURE;
00323 p_pic->i_type = DIRECT_PICTURE;
00324
00325 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
00326
00327 I_OUTPUTPICTURES++;
00328
00329 return VLC_SUCCESS;
00330 }
00331
00332
00333
00334
00335 static void End( vout_thread_t *p_vout )
00336 {
00337
00338 memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size );
00339 }
00340
00341
00342
00343
00344
00345
00346 static void Destroy( vlc_object_t *p_this )
00347 {
00348 vout_thread_t *p_vout = (vout_thread_t *)p_this;
00349
00350 CloseDisplay( p_vout );
00351
00352
00353 ioctl( p_vout->p_sys->i_tty, VT_SETMODE, &p_vout->p_sys->vt_mode );
00354
00355
00356 sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
00357 sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
00358
00359
00360 tcsetattr( 0, 0, &p_vout->p_sys->old_termios );
00361
00362
00363 TextMode( p_vout->p_sys->i_tty );
00364
00365
00366 free( p_vout->p_sys );
00367 }
00368
00369
00370
00371
00372
00373
00374
00375 static int Manage( vout_thread_t *p_vout )
00376 {
00377 #if 0
00378 uint8_t buf;
00379
00380 if ( read(0, &buf, 1) == 1)
00381 {
00382 switch( buf )
00383 {
00384 case 'q':
00385 p_vout->p_vlc->b_die = 1;
00386 break;
00387
00388 default:
00389 break;
00390 }
00391 }
00392 #endif
00393
00394
00395
00396
00397 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
00398 {
00399 msg_Dbg( p_vout, "reinitializing framebuffer screen" );
00400 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
00401
00402
00403 End( p_vout );
00404
00405
00406 if( Init( p_vout ) )
00407 {
00408 msg_Err( p_vout, "cannot reinit framebuffer screen" );
00409 return VLC_EGENERIC;
00410 }
00411
00412
00413 memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size );
00414
00415 #if 0
00416
00417
00418 p_vout->i_changes |= VOUT_YUV_CHANGE;
00419 #endif
00420 }
00421
00422 return VLC_SUCCESS;
00423 }
00424
00425
00426
00427
00428
00429
00430
00431 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
00432 {
00433 static int panned=0;
00434
00435 if( p_vout->p_sys->b_pan )
00436 {
00437 p_vout->p_sys->var_info.yoffset = 0;
00438
00439
00440
00441
00442 p_vout->p_sys->var_info.xoffset = 0;
00443
00444 if(panned < 0) {
00445 ioctl( p_vout->p_sys->i_fd,
00446 FBIOPAN_DISPLAY, &p_vout->p_sys->var_info );
00447 panned++;
00448 }
00449 }
00450 }
00451
00452 #if 0
00453 static void SetPalette( vout_thread_t *p_vout, uint16_t *red, uint16_t *green,
00454 uint16_t *blue, uint16_t *transp )
00455 {
00456 struct fb_cmap cmap = { 0, 256, red, green, blue, transp };
00457
00458 ioctl( p_vout->p_sys->i_fd, FBIOPUTCMAP, &cmap );
00459 }
00460 #endif
00461
00462
00463
00464
00465
00466
00467 static int OpenDisplay( vout_thread_t *p_vout )
00468 {
00469 char *psz_device;
00470 struct fb_fix_screeninfo fix_info;
00471
00472
00473 if( !(psz_device = config_GetPsz( p_vout, FB_DEV_VAR )) )
00474 {
00475 msg_Err( p_vout, "don't know which fb device to open" );
00476 return VLC_EGENERIC;
00477 }
00478
00479 p_vout->p_sys->i_fd = open( psz_device, O_RDWR);
00480
00481 if( p_vout->p_sys->i_fd == -1 )
00482 {
00483 msg_Err( p_vout, "cannot open %s (%s)", psz_device, strerror(errno) );
00484 free( psz_device );
00485 return VLC_EGENERIC;
00486 }
00487 free( psz_device );
00488
00489
00490 if( ioctl( p_vout->p_sys->i_fd,
00491 FBIOGET_VSCREENINFO, &p_vout->p_sys->var_info ) )
00492 {
00493 msg_Err( p_vout, "cannot get fb info (%s)", strerror(errno) );
00494 close( p_vout->p_sys->i_fd );
00495 return VLC_EGENERIC;
00496 }
00497
00498 memcpy( &p_vout->p_sys->old_info, &p_vout->p_sys->var_info,
00499 sizeof( struct fb_var_screeninfo ) );
00500
00501
00502 p_vout->p_sys->var_info.activate = FB_ACTIVATE_NXTOPEN;
00503 p_vout->p_sys->var_info.xoffset = 0;
00504 p_vout->p_sys->var_info.yoffset = 0;
00505
00506 if( ioctl( p_vout->p_sys->i_fd,
00507 FBIOPUT_VSCREENINFO, &p_vout->p_sys->var_info ) )
00508 {
00509 msg_Err( p_vout, "cannot set fb info (%s)", strerror(errno) );
00510 close( p_vout->p_sys->i_fd );
00511 return VLC_EGENERIC;
00512 }
00513
00514
00515 if( ioctl( p_vout->p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info )
00516 || ioctl( p_vout->p_sys->i_fd,
00517 FBIOGET_VSCREENINFO, &p_vout->p_sys->var_info ) )
00518 {
00519 msg_Err( p_vout, "cannot get additional fb info (%s)",
00520 strerror(errno) );
00521
00522
00523 ioctl( p_vout->p_sys->i_fd,
00524 FBIOPUT_VSCREENINFO, &p_vout->p_sys->old_info );
00525
00526 close( p_vout->p_sys->i_fd );
00527 return VLC_EGENERIC;
00528 }
00529
00530
00531
00532 msg_Dbg( p_vout, "%ix%i (virtual %ix%i)",
00533 p_vout->p_sys->var_info.xres, p_vout->p_sys->var_info.yres,
00534 p_vout->p_sys->var_info.xres_virtual,
00535 p_vout->p_sys->var_info.yres_virtual );
00536
00537 p_vout->p_sys->i_height = p_vout->p_sys->var_info.yres;
00538 p_vout->p_sys->i_width = p_vout->p_sys->var_info.xres_virtual
00539 ? p_vout->p_sys->var_info.xres_virtual
00540 : p_vout->p_sys->var_info.xres;
00541
00542 p_vout->p_sys->p_palette = NULL;
00543 p_vout->p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
00544
00545 switch( p_vout->p_sys->var_info.bits_per_pixel )
00546 {
00547 case 8:
00548 p_vout->p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
00549 p_vout->p_sys->fb_cmap.start = 0;
00550 p_vout->p_sys->fb_cmap.len = 256;
00551 p_vout->p_sys->fb_cmap.red = p_vout->p_sys->p_palette;
00552 p_vout->p_sys->fb_cmap.green = p_vout->p_sys->p_palette + 256 * sizeof( uint16_t );
00553 p_vout->p_sys->fb_cmap.blue = p_vout->p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
00554 p_vout->p_sys->fb_cmap.transp = p_vout->p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
00555
00556
00557 ioctl( p_vout->p_sys->i_fd, FBIOGETCMAP, &p_vout->p_sys->fb_cmap );
00558
00559 p_vout->p_sys->i_bytes_per_pixel = 1;
00560 break;
00561
00562 case 15:
00563 case 16:
00564 p_vout->p_sys->i_bytes_per_pixel = 2;
00565 break;
00566
00567 case 24:
00568 p_vout->p_sys->i_bytes_per_pixel = 3;
00569 break;
00570
00571 case 32:
00572 p_vout->p_sys->i_bytes_per_pixel = 4;
00573 break;
00574
00575 default:
00576 msg_Err( p_vout, "screen depth %d is not supported",
00577 p_vout->p_sys->var_info.bits_per_pixel );
00578
00579
00580 ioctl( p_vout->p_sys->i_fd,
00581 FBIOPUT_VSCREENINFO, &p_vout->p_sys->old_info );
00582
00583 close( p_vout->p_sys->i_fd );
00584 return VLC_EGENERIC;
00585 }
00586
00587 p_vout->p_sys->i_page_size = p_vout->p_sys->i_width *
00588 p_vout->p_sys->i_height * p_vout->p_sys->i_bytes_per_pixel;
00589
00590
00591 p_vout->p_sys->p_video = mmap( 0, p_vout->p_sys->i_page_size,
00592 PROT_READ | PROT_WRITE, MAP_SHARED,
00593 p_vout->p_sys->i_fd, 0 );
00594
00595 if( p_vout->p_sys->p_video == ((void*)-1) )
00596 {
00597 msg_Err( p_vout, "cannot map video memory (%s)", strerror(errno) );
00598
00599 if( p_vout->p_sys->var_info.bits_per_pixel == 8 )
00600 {
00601 free( p_vout->p_sys->p_palette );
00602 }
00603
00604
00605 ioctl( p_vout->p_sys->i_fd,
00606 FBIOPUT_VSCREENINFO, &p_vout->p_sys->old_info );
00607
00608 close( p_vout->p_sys->i_fd );
00609 return VLC_EGENERIC;
00610 }
00611
00612 msg_Dbg( p_vout, "framebuffer type=%d, visual=%d, ypanstep=%d, "
00613 "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
00614 fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
00615 return VLC_SUCCESS;
00616 }
00617
00618
00619
00620
00621 static void CloseDisplay( vout_thread_t *p_vout )
00622 {
00623
00624 memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size );
00625
00626
00627 if( p_vout->p_sys->var_info.bits_per_pixel == 8 )
00628 {
00629 ioctl( p_vout->p_sys->i_fd,
00630 FBIOPUTCMAP, &p_vout->p_sys->fb_cmap );
00631 free( p_vout->p_sys->p_palette );
00632 }
00633
00634
00635 ioctl( p_vout->p_sys->i_fd,
00636 FBIOPUT_VSCREENINFO, &p_vout->p_sys->old_info );
00637
00638
00639 close( p_vout->p_sys->i_fd );
00640 }
00641
00642
00643
00644
00645
00646
00647
00648 static void SwitchDisplay(int i_signal)
00649 {
00650 #if 0
00651 vout_thread_t *p_vout;
00652
00653 vlc_mutex_lock( &p_vout_bank->lock );
00654
00655
00656 if( p_vout_bank->i_count )
00657 {
00658 p_vout = p_vout_bank->pp_vout[0];
00659
00660 switch( i_signal )
00661 {
00662 case SIGUSR1:
00663 p_vout->b_active = 0;
00664 ioctl( p_vout->p_sys->i_tty, VT_RELDISP, 1 );
00665 break;
00666 case SIGUSR2:
00667 p_vout->b_active = 1;
00668 ioctl( p_vout->p_sys->i_tty, VT_RELDISP, VT_ACTIVATE );
00669
00670 vlc_mutex_lock( &p_vout->change_lock );
00671 p_vout->i_changes |= VOUT_SIZE_CHANGE;
00672 vlc_mutex_unlock( &p_vout->change_lock );
00673 break;
00674 }
00675 }
00676
00677 vlc_mutex_unlock( &p_vout_bank->lock );
00678 #endif
00679 }
00680
00681
00682
00683
00684
00685
00686 static void TextMode( int i_tty )
00687 {
00688
00689 if( -1 == ioctl(i_tty, KDSETMODE, KD_TEXT) )
00690 {
00691
00692 }
00693 }
00694
00695 static void GfxMode( int i_tty )
00696 {
00697
00698 if( -1 == ioctl(i_tty, KDSETMODE, KD_GRAPHICS) )
00699 {
00700
00701 }
00702 }
00703