4 * Copyright (C) Alban Bedel - 03/2003
6 * This file is part of MPlayer, a free movie player.
8 * MPlayer is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
13 * MPlayer is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with GNU Make; see the file COPYING. If not, write to
20 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <sys/types.h>
32 #include <sys/ioctl.h>
35 #include "video_out.h"
36 #include "video_out_internal.h"
41 #include "fastmemcpy.h"
42 #include "drivers/tdfx_vid.h"
45 static vo_info_t info
=
55 LIBVO_EXTERN(tdfx_vid
)
57 static tdfx_vid_config_t tdfx_cfg
;
59 static unsigned char* agp_mem
= NULL
;
60 static int tdfx_fd
= -1;
62 static uint32_t img_fmt
; // The real input format
63 static uint32_t src_width
, src_height
, src_fmt
, src_bpp
, src_stride
;
64 static uint32_t dst_width
, dst_height
, dst_fmt
, dst_bpp
, dst_stride
;
66 static uint32_t tdfx_page
;
67 static uint32_t front_buffer
;
68 static uint32_t back_buffer
;
69 static uint8_t num_buffer
= 3;
70 static uint32_t buffer_size
; // Max size
71 static uint8_t current_buffer
= 0;
72 static uint8_t current_ip_buf
= 0;
73 static uint32_t buffer_stride
[3];
75 static int use_overlay
= 1;
76 static tdfx_vid_overlay_t tdfx_ov
;
79 static void clear_screen(void) {
80 tdfx_vid_agp_move_t mov
;
82 memset(agp_mem
,0,tdfx_cfg
.screen_width
*dst_bpp
*tdfx_cfg
.screen_height
);
84 mov
.move2
= TDFX_VID_MOVE_2_PACKED
;
85 mov
.width
= tdfx_cfg
.screen_width
*dst_bpp
;
86 mov
.height
= tdfx_cfg
.screen_height
;
88 mov
.src_stride
= tdfx_cfg
.screen_width
*dst_bpp
;
89 mov
.dst
= front_buffer
;
90 mov
.dst_stride
= tdfx_cfg
.screen_stride
;
92 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXVID_Move
, mov
.width
,mov
.src_stride
,mov
.height
,mov
.dst_stride
);
94 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
))
95 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_AGPMoveFailedToClearTheScreen
);
99 static int draw_slice(uint8_t *image
[], int stride
[], int w
,int h
,int x
,int y
)
104 printf("Draw slices %d\n",current_buffer
);
113 // copy :( to agp_mem
114 // still faster than tdfxfb wich directly copy to the video mem :)
115 mem2agpcpy_pic(agp_mem
+ current_buffer
* buffer_size
+
116 y
*buffer_stride
[0] + x
* src_bpp
,
118 src_bpp
*w
,h
,buffer_stride
[0],stride
[0]);
124 ptr
[0] = agp_mem
+ current_buffer
* buffer_size
;
125 mem2agpcpy_pic(ptr
[0] + y
* buffer_stride
[0] + x
,image
[0],w
,h
,
126 buffer_stride
[0],stride
[0]);
127 ptr
[1] = ptr
[0] + (src_height
*src_width
);
128 mem2agpcpy_pic(ptr
[1] + y
/2 * buffer_stride
[1] + x
/2,image
[1],w
/2,h
/2,
129 buffer_stride
[1],stride
[1]);
130 ptr
[2] = ptr
[1] + (src_height
*src_width
/4);
131 mem2agpcpy_pic(ptr
[2] + y
/2 * buffer_stride
[2] + x
/2,image
[2],w
/2,h
/2,
132 buffer_stride
[2],stride
[2]);
139 static void draw_osd(void)
146 tdfx_vid_blit_t blit
;
153 // TDFX_VID_OVERLAY_ON does nothing if the overlay is alredy on
154 if(!ioctl(tdfx_fd
,TDFX_VID_OVERLAY_ON
)) { // X11 killed the overlay :(
155 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
156 mp_msg(MSGT_VO
, MSGL_ERR
, "tdfx_vid: set_overlay failed\n");
158 // These formats need conversion
163 memset(&blit
,0,sizeof(tdfx_vid_blit_t
));
164 blit
.src
= back_buffer
;
165 blit
.src_stride
= src_stride
;
168 blit
.src_w
= src_width
;
169 blit
.src_h
= src_height
;
170 blit
.src_format
= src_fmt
;
172 blit
.dst
= front_buffer
;
173 blit
.dst_stride
= dst_stride
;
176 blit
.dst_w
= src_width
;
177 blit
.dst_h
= src_height
;
178 blit
.dst_format
= IMGFMT_BGR16
;
179 if(ioctl(tdfx_fd
,TDFX_VID_BLIT
,&blit
))
180 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_BlitFailed
);
184 memset(&blit
,0,sizeof(tdfx_vid_blit_t
));
185 blit
.src
= back_buffer
;
186 blit
.src_stride
= src_stride
;
189 blit
.src_w
= src_width
;
190 blit
.src_h
= src_height
;
191 blit
.src_format
= src_fmt
;
193 blit
.dst
= front_buffer
;
194 blit
.dst_stride
= dst_stride
;
197 blit
.dst_w
= dst_width
;
198 blit
.dst_h
= dst_height
;
199 blit
.dst_format
= dst_fmt
;
201 if(ioctl(tdfx_fd
,TDFX_VID_BLIT
,&blit
))
202 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_BlitFailed
);
206 draw_frame(uint8_t *src
[])
208 int stride
[] = { src_stride
, 0, 0};
209 return draw_slice(src
,stride
,src_width
, src_height
,0,0);
213 query_format(uint32_t format
)
217 if(tdfx_cfg
.screen_format
== TDFX_VID_FORMAT_BGR8
)
218 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
228 if(tdfx_cfg
.screen_format
== TDFX_VID_FORMAT_BGR8
)
230 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
236 config(uint32_t width
, uint32_t height
, uint32_t d_width
, uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
)
241 // When we are run as sub vo we must follow the size gaven to us
242 if(!(flags
& VOFLAG_XOVERLAY_SUB_VO
)) {
244 vo_screenwidth
= tdfx_cfg
.screen_width
;
246 vo_screenheight
= tdfx_cfg
.screen_height
;
248 aspect_save_orig(width
,height
);
249 aspect_save_prescale(d_width
,d_height
);
250 aspect_save_screenres(vo_screenwidth
,vo_screenheight
);
252 if(flags
&VOFLAG_FULLSCREEN
) { /* -fs */
253 aspect(&d_width
,&d_height
,A_ZOOM
);
256 aspect(&d_width
,&d_height
,A_NOZOOM
);
263 buffer_stride
[0] = 0;
270 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXVID_NonNativeOverlayFormatNeedConversion
);
273 src_bpp
= ((format
& 0x3F)+7)/8;
277 buffer_size
= src_width
* src_height
* 3 / 2;
278 buffer_stride
[0] = ((src_width
+1)/2)*2;
279 buffer_stride
[1] = buffer_stride
[2] = buffer_stride
[0]/2;
280 src_fmt
= TDFX_VID_FORMAT_YUY2
;
286 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_UnsupportedInputFormat
,format
);
291 src_stride
= src_width
*src_bpp
;
292 // The overlay need a 4 bytes aligned stride
294 src_stride
= ((src_stride
+3)/4)*4;
298 buffer_size
= src_stride
*src_height
;
299 if(!buffer_stride
[0])
300 buffer_stride
[0] = src_stride
;
302 dst_fmt
= tdfx_cfg
.screen_format
;
303 dst_bpp
= ((dst_fmt
& 0x3F)+7)/8;
305 dst_height
= d_height
;
306 dst_stride
= tdfx_cfg
.screen_stride
;
308 tdfx_page
= tdfx_cfg
.screen_stride
*tdfx_cfg
.screen_height
;
309 front_buffer
= tdfx_cfg
.screen_start
;
310 back_buffer
= front_buffer
+ tdfx_page
;
313 tdfx_vid_overlay_t ov
;
314 uint32_t ov_fmt
= src_fmt
, ov_stride
= src_stride
;
316 back_buffer
= (((back_buffer
+3)/4)*4);
317 // With the overlay the front buffer is not on the screen
318 // so we take the back buffer
319 front_buffer
= back_buffer
;
324 back_buffer
= front_buffer
+ 2*(src_stride
*src_height
);
325 ov_stride
= dst_stride
= src_width
<<1;
326 ov_fmt
= IMGFMT_BGR16
;
329 ov
.src
[0] = front_buffer
;
330 ov
.src
[1] = front_buffer
+ (src_stride
*src_height
);
331 ov
.src_width
= src_width
;
332 ov
.src_height
= src_height
;
333 ov
.src_stride
= ov_stride
;
335 ov
.dst_width
= dst_width
;
336 ov
.dst_height
= dst_height
;
341 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&ov
)) {
342 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_OverlaySetupFailed
);
347 if(use_overlay
== 1) {
348 if(ioctl(tdfx_fd
,TDFX_VID_OVERLAY_ON
)) {
349 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_OverlayOnFailed
);
356 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXVID_OverlayReady
,
357 src_width
,src_stride
,src_height
,src_bpp
,
358 dst_width
,dst_stride
,dst_height
,dst_bpp
);
363 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXVID_TextureBlitReady
,
364 src_width
,src_stride
,src_height
,src_bpp
,
365 dst_width
,dst_stride
,dst_height
,dst_bpp
);
373 if(use_overlay
== 2) {
374 if(ioctl(tdfx_fd
,TDFX_VID_OVERLAY_OFF
))
375 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_OverlayOffFailed
);
383 static void check_events(void)
387 static int preinit(const char *arg
)
390 tdfx_fd
= open(arg
? arg
: "/dev/tdfx_vid", O_RDWR
);
392 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_CantOpen
,arg
? arg
: "/dev/tdfx_vid",
397 if(ioctl(tdfx_fd
,TDFX_VID_GET_CONFIG
,&tdfx_cfg
)) {
398 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_CantGetCurrentCfg
,strerror(errno
));
402 mp_msg(MSGT_VO
,MSGL_INFO
, "tdfx_vid version %d\n"
405 " Format: %c%c%c%d\n",
408 tdfx_cfg
.screen_width
, tdfx_cfg
.screen_height
,
409 tdfx_cfg
.screen_format
>>24,(tdfx_cfg
.screen_format
>>16)&0xFF,
410 (tdfx_cfg
.screen_format
>>8)&0xFF,tdfx_cfg
.screen_format
&0xFF);
412 // For now just allocate more than i ever need
413 agp_mem
= mmap( NULL
, 1024*768*4, PROT_READ
| PROT_WRITE
, MAP_SHARED
,
416 if(agp_mem
== MAP_FAILED
) {
417 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_MemmapFailed
);
421 memset(agp_mem
,0,1024*768*4);
426 static uint32_t get_image(mp_image_t
*mpi
) {
430 printf("Get image %d\n",buf
);
433 // Currently read are too slow bcs we read from the
434 // agp aperture and not the memory directly
435 //if(mpi->flags&MP_IMGFLAG_READABLE) return VO_FALSE;
437 if(mpi
->flags
&MP_IMGFLAG_READABLE
&&
438 (mpi
->type
==MP_IMGTYPE_IPB
|| mpi
->type
==MP_IMGTYPE_IP
)){
439 // reference (I/P) frame of IP or IPB:
440 if(num_buffer
<2) return VO_FALSE
; // not enough
442 // for IPB with 2 buffers we can DR only one of the 2 P frames:
443 if(mpi
->type
==MP_IMGTYPE_IPB
&& num_buffer
<3 && current_ip_buf
) return VO_FALSE
;
445 if(mpi
->type
==MP_IMGTYPE_IPB
) ++buf
; // preserve space for B
448 switch(mpi
->imgfmt
) {
456 mpi
->planes
[0] = agp_mem
+ buf
* buffer_size
;
457 mpi
->stride
[0] = src_stride
;
461 mpi
->planes
[0] = agp_mem
+ buf
* buffer_size
;
462 mpi
->stride
[0] = mpi
->width
;
463 mpi
->planes
[1] = mpi
->planes
[0] + mpi
->stride
[0]*mpi
->height
;
464 mpi
->stride
[1] = mpi
->chroma_width
;
465 mpi
->planes
[2] = mpi
->planes
[1] + mpi
->stride
[1]*mpi
->chroma_height
;
466 mpi
->stride
[2] = mpi
->chroma_width
;
469 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_GetImageTodo
);
472 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
473 mpi
->priv
= (void*)buf
;
478 static uint32_t start_slice(mp_image_t
*mpi
){
482 printf("Start slices %d\n",buf
);
485 if(mpi
->flags
& MP_IMGFLAG_DIRECT
)
486 buf
= (int)mpi
->priv
;
487 current_buffer
= buf
;
492 static uint32_t draw_image(mp_image_t
*mpi
){
494 tdfx_vid_agp_move_t mov
;
501 printf("Draw image %d\n",buf
);
504 if(mpi
->flags
& MP_IMGFLAG_DIRECT
)
505 buf
= (int)mpi
->priv
;
507 switch(mpi
->imgfmt
) {
515 if(!(mpi
->flags
&(MP_IMGFLAG_DIRECT
|MP_IMGFLAG_DRAW_CALLBACK
))) {
520 planes
[0] = agp_mem
+ buf
* buffer_size
;
521 mem2agpcpy_pic(planes
[0],mpi
->planes
[0],src_bpp
*mpi
->width
,mpi
->height
,
522 buffer_stride
[0],mpi
->stride
[0]);
524 planes
[0] = agp_mem
+ buf
* buffer_size
;
526 mov
.move2
= TDFX_VID_MOVE_2_PACKED
;
527 mov
.width
= mpi
->width
*((mpi
->bpp
+7)/8);
528 mov
.height
= mpi
->height
;
529 mov
.src
= planes
[0] - agp_mem
;
530 mov
.src_stride
= buffer_stride
[0];
532 mov
.dst
= back_buffer
;
533 mov
.dst_stride
= src_stride
;
535 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
))
536 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_AgpMoveFailed
);
541 if(!(mpi
->flags
&(MP_IMGFLAG_DIRECT
|MP_IMGFLAG_DRAW_CALLBACK
))) {
546 planes
[0] = agp_mem
+ buf
* buffer_size
;
547 memcpy_pic(planes
[0],mpi
->planes
[0],mpi
->width
,mpi
->height
,
548 buffer_stride
[0],mpi
->stride
[0]);
549 planes
[1] = planes
[0] + (mpi
->height
*mpi
->stride
[0]);
550 memcpy_pic(planes
[1],mpi
->planes
[1],mpi
->chroma_width
,mpi
->chroma_height
,
551 buffer_stride
[1],mpi
->stride
[1]);
552 planes
[2] = planes
[1] + (mpi
->chroma_height
*mpi
->stride
[1]);
553 memcpy_pic(planes
[2],mpi
->planes
[2],mpi
->chroma_width
,mpi
->chroma_height
,
554 buffer_stride
[2],mpi
->stride
[2]);
556 planes
[0] = agp_mem
+ buf
* buffer_size
;
557 planes
[1] = planes
[0] + buffer_stride
[0] * src_height
;
558 planes
[2] = planes
[1] + buffer_stride
[1] * src_height
/2;
561 // Setup the yuv thing
562 yuv
.base
= back_buffer
;
563 yuv
.stride
= src_stride
;
564 if(ioctl(tdfx_fd
,TDFX_VID_SET_YUV
,&yuv
)) {
565 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_SetYuvFailed
);
572 mov
.move2
= TDFX_VID_MOVE_2_YUV
;
573 mov
.width
= mpi
->width
;
574 mov
.height
= mpi
->height
;
575 mov
.src
= planes
[0] - agp_mem
;
576 mov
.src_stride
= buffer_stride
[0];
578 mov
.dst_stride
= TDFX_VID_YUV_STRIDE
;
580 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
581 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_AgpMoveFailedOnYPlane
);
586 p
= mpi
->imgfmt
== IMGFMT_YV12
? 1 : 2;
587 mov
.width
= mpi
->chroma_width
;
588 mov
.height
= mpi
->chroma_height
;
589 mov
.src
= planes
[p
] - agp_mem
;
590 mov
.src_stride
= buffer_stride
[p
];
591 mov
.dst
+= TDFX_VID_YUV_PLANE_SIZE
;
592 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
593 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_AgpMoveFailedOnUPlane
);
597 p
= mpi
->imgfmt
== IMGFMT_YV12
? 2 : 1;
598 mov
.src
= planes
[p
] - agp_mem
;
599 mov
.src_stride
= buffer_stride
[p
];
600 mov
.dst
+= TDFX_VID_YUV_PLANE_SIZE
;
601 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
602 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_AgpMoveFailedOnVPlane
);
607 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_UnknownFormat
,mpi
->imgfmt
);
614 static uint32_t fullscreen(void) {
616 aspect(&dst_width
,&dst_height
,vo_fs
? A_ZOOM
: A_NOZOOM
);
617 // This does not work :((
622 static uint32_t set_window(mp_win_t
* w
) {
623 if(!use_overlay
) return VO_FALSE
;
625 tdfx_ov
.dst_x
= w
->x
;
626 tdfx_ov
.dst_y
= w
->y
;
627 tdfx_ov
.dst_width
= w
->w
;
628 tdfx_ov
.dst_height
= w
->h
;
630 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
631 mp_msg(MSGT_VO
, MSGL_V
, "tdfx_vid: set window failed\n");
636 static uint32_t set_colorkey(mp_colorkey_t
* colork
) {
637 if(!use_overlay
) return VO_FALSE
;
639 tdfx_ov
.colorkey
[0] = tdfx_ov
.colorkey
[1] = colork
->x11
;
640 tdfx_ov
.use_colorkey
= 1;
641 tdfx_ov
.invert_colorkey
= 0;
643 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
644 mp_msg(MSGT_VO
, MSGL_V
, "tdfx_vid: set colorkey failed\n");
649 static int control(uint32_t request
, void *data
, ...)
652 case VOCTRL_QUERY_FORMAT
:
653 return query_format(*((uint32_t*)data
));
654 case VOCTRL_GET_IMAGE
:
655 return get_image(data
);
656 case VOCTRL_DRAW_IMAGE
:
657 return draw_image(data
);
658 case VOCTRL_START_SLICE
:
659 return start_slice(data
);
660 case VOCTRL_FULLSCREEN
:
662 case VOCTRL_XOVERLAY_SUPPORT
:
664 case VOCTRL_XOVERLAY_SET_COLORKEY
:
665 return set_colorkey(data
);
666 case VOCTRL_XOVERLAY_SET_WIN
:
667 return set_window(data
);