1 /*****************************************************************************
2 * omapfb.c : omap framebuffer plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2008-2009 the VideoLAN team
7 * Authors: Antoine Lejeune <phytos @ videolan.org>
8 * Based on fb.c and work of Siarhei Siamashka on mplayer for Maemo
9 * Needs a recent omapfb.h to compile
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
30 #include <fcntl.h> /* open() */
31 #include <unistd.h> /* close() */
33 #include <sys/ioctl.h>
34 #include <sys/mman.h> /* mmap() */
37 #include <asm/arch-omap/omapfb.h>
39 /* Embedded window handling */
41 #include <X11/Xutil.h>
43 #include <vlc_common.h>
44 #include <vlc_plugin.h>
46 #include <vlc_vout_window.h>
47 #include <vlc_playlist.h>
50 /*****************************************************************************
52 *****************************************************************************/
53 static int Create ( vlc_object_t
* );
54 static void Destroy ( vlc_object_t
* );
56 static int Init ( vout_thread_t
* );
57 static void End ( vout_thread_t
* );
58 static int Manage ( vout_thread_t
* );
59 static void DisplayVideo ( vout_thread_t
*, picture_t
* );
60 static int Control ( vout_thread_t
*, int, va_list );
62 static void FreePicture ( vout_thread_t
*, picture_t
* );
64 static int OpenDisplay ( vout_thread_t
* );
65 static void CloseDisplay ( vout_thread_t
* );
66 static void UpdateScreen ( vout_thread_t
*,
67 int, int, int, int, int, int, int );
69 static int InitWindow ( vout_thread_t
* );
70 static void CreateWindow ( vout_sys_t
* );
71 static void ToggleFullScreen ( vout_thread_t
* );
73 /*****************************************************************************
75 *****************************************************************************/
76 #define FB_DEV_VAR "omapfbdev"
78 #define DEVICE_TEXT N_("OMAP Framebuffer device")
79 #define DEVICE_LONGTEXT N_( \
80 "OMAP Framebuffer device to use for rendering (usually /dev/fb0).")
82 #define CHROMA_TEXT N_("Chroma used")
83 #define CHROMA_LONGTEXT N_( \
84 "Force use of a specific chroma for output. Default is Y420 (specific to N770/N8xx hardware)." )
86 #define OVERLAY_TEXT N_("Embed the overlay")
87 #define OVERLAY_LONGTEXT N_( \
88 "Embed the framebuffer overlay into a X11 window" )
91 set_shortname( N_("OMAP framebuffer") );
92 set_category( CAT_VIDEO
);
93 set_subcategory( SUBCAT_VIDEO_VOUT
);
94 add_file( FB_DEV_VAR
, "/dev/fb0", NULL
, DEVICE_TEXT
, DEVICE_LONGTEXT
,
96 add_string( "omap-chroma", NULL
, NULL
, CHROMA_TEXT
, CHROMA_LONGTEXT
,
98 add_bool( "omap-embedded", true, NULL
, OVERLAY_TEXT
, OVERLAY_LONGTEXT
,
100 set_description( N_("OMAP framebuffer video output") );
101 set_capability( "video output", 200 );
102 set_callbacks( Create
, Destroy
);
105 /*****************************************************************************
106 * omap_window_t: simple structure with the geometry of a window
107 *****************************************************************************/
116 /*****************************************************************************
117 * vout_sys_t: video output omap framebuffer method descriptor
118 *****************************************************************************
119 * This structure is part of the video output thread descriptor.
120 * It describes the FB specific properties of an output thread.
121 *****************************************************************************/
124 /* Framebuffer information */
125 int i_fd
; /* device handle */
126 struct fb_var_screeninfo fb_vinfo
; /* current mode information */
127 struct fb_fix_screeninfo fb_finfo
;
128 struct omapfb_caps caps
;
131 /* Window information */
132 struct omap_window_t output_window
; /* Size of the real output window */
133 struct omap_window_t main_window
; /* Size of the area we got to display */
134 struct omap_window_t embedded_window
; /* Size of the embedded window */
136 /* Video information */
137 uint32_t i_video_width
; /* video width */
138 uint32_t i_video_height
; /* video height */
139 vlc_fourcc_t i_chroma
;
140 int i_color_format
; /* OMAPFB_COLOR_* */
141 bool b_video_enabled
; /* Video must be displayed? */
142 picture_t
*p_output_picture
;
145 uint8_t *p_video
; /* base adress */
146 uint8_t *p_center
; /* output adress */
147 size_t i_page_size
; /* page size */
148 int i_bytes_per_pixel
; /* Bytes used by one pixel */
149 int i_line_len
; /* Length of one line in bytes */
153 vout_window_t
*owner_window
;
155 mtime_t i_time_button_last_pressed
; /* To detect double click */
162 /*****************************************************************************
163 * Create: allocates omapfb video thread output method
164 *****************************************************************************
165 * This function allocates and initializes a FB vout method.
166 *****************************************************************************/
167 static int Create( vlc_object_t
*p_this
)
169 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
172 if( p_vout
->fmt_in
.i_chroma
!= VLC_CODEC_I420
&&
173 p_vout
->fmt_in
.i_chroma
!= VLC_CODEC_YV12
)
176 /* Allocate instance and initialize some members */
177 p_vout
->p_sys
= p_sys
= calloc( 1, sizeof( vout_sys_t
) );
178 if( p_vout
->p_sys
== NULL
)
181 p_vout
->pf_init
= Init
;
182 p_vout
->pf_end
= End
;
183 p_vout
->pf_manage
= Manage
;
184 p_vout
->pf_render
= NULL
;
185 p_vout
->pf_display
= DisplayVideo
;
186 p_vout
->pf_control
= Control
;
187 p_sys
->b_video_enabled
= true;
189 if( OpenDisplay( p_vout
) )
191 free( p_vout
->p_sys
);
195 if( InitWindow( p_vout
) )
197 free( p_vout
->p_sys
);
204 /*****************************************************************************
205 * Destroy: destroy omapfb video thread output method
206 *****************************************************************************
207 * Terminate an output method created by Create
208 *****************************************************************************/
209 static void Destroy( vlc_object_t
*p_this
)
211 vout_thread_t
*p_vout
= (vout_thread_t
*)p_this
;
213 CloseDisplay( p_vout
);
215 if( p_vout
->p_sys
->owner_window
)
217 vout_window_Delete( p_vout
->p_sys
->owner_window
);
218 XCloseDisplay( p_vout
->p_sys
->p_display
);
221 /* Destroy structure */
222 free( p_vout
->p_sys
);
226 /*****************************************************************************
227 * Init: initialize omap framebuffer video thread output method
228 *****************************************************************************/
229 static int Init( vout_thread_t
*p_vout
)
231 vout_sys_t
*p_sys
= (vout_sys_t
*)p_vout
->p_sys
;
233 // We want to keep the same aspect
234 p_vout
->output
.i_aspect
= p_vout
->render
.i_aspect
;
235 p_vout
->fmt_out
.i_sar_num
= p_vout
->render
.i_aspect
* p_vout
->render
.i_height
;
236 p_vout
->fmt_out
.i_sar_den
= VOUT_ASPECT_FACTOR
* p_vout
->render
.i_width
;
237 // We ask where the video should be displayed in the video area
238 vout_PlacePicture( p_vout
, p_sys
->main_window
.i_width
,
239 p_sys
->main_window
.i_height
,
240 &p_sys
->output_window
.i_x
,
241 &p_sys
->output_window
.i_y
,
242 &p_sys
->output_window
.i_width
,
243 &p_sys
->output_window
.i_height
);
244 p_sys
->output_window
.i_x
= ( p_sys
->output_window
.i_x
+
245 p_sys
->main_window
.i_x
) & ~1;
246 p_sys
->output_window
.i_y
= ( p_sys
->output_window
.i_y
+
247 p_sys
->main_window
.i_y
) & ~1;
249 // Hardware upscaling better than software
250 if( p_vout
->fmt_render
.i_width
<= p_sys
->main_window
.i_width
&&
251 p_vout
->fmt_render
.i_height
<= p_sys
->main_window
.i_height
)
253 p_sys
->i_video_width
=
254 p_vout
->output
.i_width
=
255 p_vout
->fmt_out
.i_width
=
256 p_vout
->fmt_out
.i_visible_width
= p_vout
->fmt_render
.i_width
;
257 p_sys
->i_video_height
=
258 p_vout
->output
.i_height
=
259 p_vout
->fmt_out
.i_height
=
260 p_vout
->fmt_out
.i_visible_height
= p_vout
->fmt_render
.i_height
;
264 p_sys
->i_video_width
=
265 p_vout
->output
.i_width
=
266 p_vout
->fmt_out
.i_width
=
267 p_vout
->fmt_out
.i_visible_width
= p_sys
->output_window
.i_width
;
268 p_sys
->i_video_height
=
269 p_vout
->output
.i_height
=
270 p_vout
->fmt_out
.i_height
=
271 p_vout
->fmt_out
.i_visible_height
= p_sys
->output_window
.i_height
;
274 p_vout
->output
.i_chroma
=
275 p_vout
->fmt_out
.i_chroma
= VLC_FOURCC( 'Y','4','2','0' );
276 p_sys
->i_color_format
= OMAPFB_COLOR_YUV420
;
278 // place in the framebuffer where we have to write
279 p_sys
->p_center
= p_sys
->p_video
+ p_sys
->output_window
.i_x
*p_sys
->i_bytes_per_pixel
280 + p_sys
->output_window
.i_y
*p_sys
->i_line_len
;
282 // We get and set a direct render vlc picture
283 I_OUTPUTPICTURES
= 0;
284 picture_t
*p_pic
= NULL
;
287 for( i_index
= 0 ; i_index
< VOUT_MAX_PICTURES
; i_index
++ )
289 if( p_vout
->p_picture
[ i_index
].i_status
== FREE_PICTURE
)
291 p_pic
= p_vout
->p_picture
+ i_index
;
296 /* Allocate the picture */
302 p_sys
->p_output_picture
= p_pic
;
303 p_pic
->p
->p_pixels
= p_vout
->p_sys
->p_center
;
304 p_pic
->p
->i_pixel_pitch
= p_vout
->p_sys
->i_bytes_per_pixel
;
305 p_pic
->p
->i_lines
= p_sys
->i_video_height
;
306 p_pic
->p
->i_visible_lines
= p_sys
->i_video_height
;
307 p_pic
->p
->i_pitch
= p_sys
->i_line_len
;
308 p_pic
->p
->i_visible_pitch
= p_sys
->i_line_len
;
310 p_pic
->i_status
= DESTROYED_PICTURE
;
311 p_pic
->i_type
= DIRECT_PICTURE
;
313 PP_OUTPUTPICTURE
[ I_OUTPUTPICTURES
] = p_pic
;
320 /*****************************************************************************
321 * End: terminate omap framebuffer video thread output method
322 *****************************************************************************/
323 static void End( vout_thread_t
*p_vout
)
325 /* Clear the screen */
326 UpdateScreen( p_vout
, 0, 0,
327 p_vout
->p_sys
->fb_vinfo
.xres
,
328 p_vout
->p_sys
->fb_vinfo
.yres
,
329 p_vout
->p_sys
->fb_vinfo
.xres
,
330 p_vout
->p_sys
->fb_vinfo
.yres
,
331 OMAPFB_COLOR_RGB565
);
334 /*****************************************************************************
335 * Control: control facility for the vout
336 *****************************************************************************/
337 static int Control( vout_thread_t
*p_vout
, int i_query
, va_list args
)
342 /*****************************************************************************
343 * FreePicture: Destroy the picture and make it free again
344 ******************************************************************************/
345 static void FreePicture( vout_thread_t
*p_vout
, picture_t
*p_pic
)
347 p_pic
->p
->p_pixels
= NULL
;
348 p_pic
->i_status
= FREE_PICTURE
;
351 /*****************************************************************************
352 * Manage: handle omapfb events
353 *****************************************************************************
354 * This function should be called regularly by video output thread.
355 *****************************************************************************/
356 static int Manage( vout_thread_t
*p_vout
)
360 while( XPending( p_vout
->p_sys
->p_display
) )
362 XNextEvent( p_vout
->p_sys
->p_display
, &xevent
);
364 if( xevent
.type
== ButtonPress
&&
365 ((XButtonEvent
*)&xevent
)->button
== Button1
)
367 /* detect double-clicks */
368 if( ( ((XButtonEvent
*)&xevent
)->time
-
369 p_vout
->p_sys
->i_time_button_last_pressed
) < 300 )
371 p_vout
->i_changes
|= VOUT_FULLSCREEN_CHANGE
;
374 p_vout
->p_sys
->i_time_button_last_pressed
=
375 ((XButtonEvent
*)&xevent
)->time
;
377 else if( ( xevent
.type
== VisibilityNotify
&&
378 xevent
.xvisibility
.state
!= VisibilityUnobscured
) ||
379 xevent
.type
== UnmapNotify
)
381 UpdateScreen( p_vout
, 0, 0,
382 p_vout
->p_sys
->fb_vinfo
.xres
,
383 p_vout
->p_sys
->fb_vinfo
.yres
,
384 p_vout
->p_sys
->fb_vinfo
.xres
,
385 p_vout
->p_sys
->fb_vinfo
.yres
,
386 OMAPFB_COLOR_RGB565
);
387 p_vout
->p_sys
->b_video_enabled
= false;
388 p_vout
->p_sys
->p_output_picture
->p
->p_pixels
=
389 p_vout
->p_sys
->p_null
;
393 if( p_vout
->i_changes
& VOUT_FULLSCREEN_CHANGE
)
395 /* Update the object variable and trigger callback */
396 p_vout
->b_fullscreen
= !p_vout
->b_fullscreen
;
397 var_SetBool( p_vout
, "fullscreen", p_vout
->b_fullscreen
);
399 if( p_vout
->p_sys
->owner_window
)
400 vout_window_SetFullscreen( p_vout
->p_sys
->owner_window
,
401 p_vout
->b_fullscreen
);
402 p_vout
->i_changes
&= ~VOUT_FULLSCREEN_CHANGE
;
405 if( p_vout
->i_changes
& VOUT_SIZE_CHANGE
)
407 FreePicture( p_vout
, p_vout
->p_sys
->p_output_picture
);
410 msg_Err( p_vout
, "cannot reinit framebuffer screen" );
417 /*****************************************************************************
418 * DisplayVideo: displays previously rendered output
419 *****************************************************************************
420 * This function update the screen resulting in the display of the last
422 *****************************************************************************/
423 static void DisplayVideo( vout_thread_t
*p_vout
, picture_t
*p_pic
)
427 if( !p_vout
->p_sys
->b_video_enabled
)
430 UpdateScreen( p_vout
,
431 p_vout
->p_sys
->output_window
.i_x
,
432 p_vout
->p_sys
->output_window
.i_y
,
433 p_vout
->p_sys
->i_video_width
,
434 p_vout
->p_sys
->i_video_height
,
435 p_vout
->p_sys
->output_window
.i_width
,
436 p_vout
->p_sys
->output_window
.i_height
,
437 p_vout
->p_sys
->i_color_format
);
439 // Wait for the window to be fully displayed
440 ioctl( p_vout
->p_sys
->i_fd
, OMAPFB_SYNC_GFX
);
443 /*****************************************************************************
444 * OpenDisplay: initialize framebuffer
445 *****************************************************************************/
446 static int OpenDisplay( vout_thread_t
*p_vout
)
448 vout_sys_t
*p_sys
= (vout_sys_t
*) p_vout
->p_sys
;
449 char *psz_device
; /* framebuffer device path */
451 /* Open framebuffer device */
452 if( !(psz_device
= var_CreateGetNonEmptyString( p_vout
, FB_DEV_VAR
)) )
454 msg_Err( p_vout
, "don't know which fb device to open" );
458 p_sys
->i_fd
= vlc_open( psz_device
, O_RDWR
);
459 if( p_sys
->i_fd
== -1 )
461 msg_Err( p_vout
, "cannot open %s (%m)", psz_device
);
467 // Get caps, try older interface if needed
468 if( ioctl( p_sys
->i_fd
, OMAPFB_GET_CAPS
, &p_sys
->caps
) != 0 )
470 if( ioctl( p_sys
->i_fd
, OMAP_IOR( 42, unsigned long ), &p_sys
->caps
) != 0 )
472 msg_Err( p_vout
, "OMAPFB_GET_CAPS ioctl failed" );
473 close( p_sys
->i_fd
);
478 if( ( p_sys
->caps
.ctrl
& OMAPFB_CAPS_TEARSYNC
) != 0 )
479 p_sys
->b_tearsync
= true;
481 if( ioctl( p_sys
->i_fd
, FBIOGET_VSCREENINFO
, &p_sys
->fb_vinfo
) )
483 msg_Err( p_vout
, "Can't get VSCREENINFO: %m" );
484 close( p_sys
->i_fd
);
487 if( ioctl( p_sys
->i_fd
, FBIOGET_FSCREENINFO
, &p_sys
->fb_finfo
) )
489 msg_Err( p_vout
, "Can't get FSCREENINFO: %m" );
490 close( p_sys
->i_fd
);
494 p_sys
->i_bytes_per_pixel
= 2;
495 p_sys
->i_page_size
= p_sys
->fb_finfo
.smem_len
;
496 p_sys
->i_line_len
= p_sys
->fb_finfo
.line_length
;
498 if( (p_sys
->p_video
= (uint8_t *)mmap( 0, p_sys
->i_page_size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
,
499 p_sys
->i_fd
, 0 )) == MAP_FAILED
)
501 msg_Err( p_vout
, "Can't mmap: %m" );
502 close( p_sys
->i_fd
);
506 p_sys
->p_display
= XOpenDisplay( NULL
);
508 /* Open /dev/null and map it */
509 p_sys
->i_null_fd
= vlc_open( "/dev/zero", O_RDWR
);
510 if( p_sys
->i_null_fd
== -1 )
512 msg_Err( p_vout
, "cannot open /dev/zero (%m)" );
513 munmap( p_sys
->p_video
, p_sys
->i_page_size
);
514 close( p_sys
->i_fd
);
518 if( (p_sys
->p_null
= (uint8_t *)mmap( 0, p_sys
->i_page_size
, PROT_READ
| PROT_WRITE
,
519 MAP_PRIVATE
, p_sys
->i_null_fd
, 0 )) == MAP_FAILED
)
521 msg_Err( p_vout
, "Can't mmap 2: %m" );
522 munmap( p_sys
->p_video
, p_sys
->i_page_size
);
523 close( p_sys
->i_null_fd
);
524 close( p_sys
->i_fd
);
531 /*****************************************************************************
532 * CloseDisplay: terminate FB video thread output method
533 *****************************************************************************/
534 static void CloseDisplay( vout_thread_t
*p_vout
)
536 munmap( p_vout
->p_sys
->p_video
, p_vout
->p_sys
->i_page_size
);
537 munmap( p_vout
->p_sys
->p_null
, p_vout
->p_sys
->i_page_size
);
539 close( p_vout
->p_sys
->i_fd
);
540 close( p_vout
->p_sys
->i_null_fd
);
543 /*****************************************************************************
544 * UpdateScreen: update the screen of the omapfb
545 *****************************************************************************/
546 static void UpdateScreen( vout_thread_t
*p_vout
, int i_x
, int i_y
,
547 int i_width
, int i_height
,
548 int i_out_width
, int i_out_height
, int i_format
)
550 struct omapfb_update_window update
;
553 update
.width
= i_width
;
554 update
.height
= i_height
;
557 update
.out_width
= i_out_width
;
558 update
.out_height
= i_out_height
;
559 update
.format
= i_format
;
560 if( p_vout
->p_sys
->b_tearsync
)
561 update
.format
|= OMAPFB_FORMAT_FLAG_TEARSYNC
;
562 ioctl( p_vout
->p_sys
->i_fd
, OMAPFB_UPDATE_WINDOW
, &update
);
565 /*****************************************************************************
566 * InitWindow: get embedded window and init X11
567 *****************************************************************************/
568 static int InitWindow( vout_thread_t
*p_vout
)
570 vout_sys_t
*p_sys
= (vout_sys_t
*)p_vout
->p_sys
;
572 if( var_CreateGetBool( p_vout
, "omap-embedded" ) )
574 p_sys
->p_display
= XOpenDisplay( NULL
);
576 // Request window from interface
577 vout_window_cfg_t wnd_cfg
;
579 memset( &wnd_cfg
, 0, sizeof(wnd_cfg
) );
580 wnd_cfg
.type
= VOUT_WINDOW_TYPE_XID
;
581 wnd_cfg
.x
= p_sys
->embedded_window
.i_x
;
582 wnd_cfg
.y
= p_sys
->embedded_window
.i_y
;
583 wnd_cfg
.width
= p_sys
->embedded_window
.i_width
;
584 wnd_cfg
.height
= p_sys
->embedded_window
.i_height
;
586 p_sys
->owner_window
= vout_window_New( VLC_OBJECT(p_vout
), NULL
, &wnd_cfg
);
587 p_sys
->main_window
= p_sys
->embedded_window
;
589 // We have to create a new window to get some events
590 CreateWindow( p_sys
);
594 // No embedding, fullscreen framebuffer overlay with no events handling
595 p_sys
->main_window
.i_x
= p_sys
->main_window
.i_y
= 0;
596 p_sys
->main_window
.i_width
= p_sys
->fb_vinfo
.xres
;
597 p_sys
->main_window
.i_height
= p_sys
->fb_vinfo
.yres
;
598 p_vout
->b_fullscreen
= true;
599 var_SetBool( p_vout
, "fullscreen", p_vout
->b_fullscreen
);
605 static void CreateWindow( vout_sys_t
*p_sys
)
607 XSetWindowAttributes xwindow_attributes
;
608 xwindow_attributes
.backing_store
= Always
;
609 xwindow_attributes
.background_pixel
=
610 BlackPixel( p_sys
->p_display
, DefaultScreen(p_sys
->p_display
) );
611 xwindow_attributes
.event_mask
= ExposureMask
| StructureNotifyMask
612 | VisibilityChangeMask
;
613 p_sys
->window
= XCreateWindow( p_sys
->p_display
,
614 p_sys
->owner_window
->handle
.xid
,
616 p_sys
->main_window
.i_width
,
617 p_sys
->main_window
.i_height
,
620 CWBackingStore
| CWBackPixel
| CWEventMask
,
621 &xwindow_attributes
);
623 XMapWindow( p_sys
->p_display
, p_sys
->window
);
624 XSelectInput( p_sys
->p_display
, p_sys
->owner_window
->handle
.xid
,
625 StructureNotifyMask
);