2 * copyright (C) 2003 Alban Bedel
4 * This file is part of MPlayer.
6 * MPlayer is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * MPlayer is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include <sys/types.h>
29 #include <sys/ioctl.h>
32 #include "video_out.h"
33 #include "video_out_internal.h"
37 #include "fastmemcpy.h"
38 #include "drivers/tdfx_vid.h"
41 static const vo_info_t info
=
51 const LIBVO_EXTERN(tdfx_vid
)
53 static tdfx_vid_config_t tdfx_cfg
;
55 static unsigned char* agp_mem
= NULL
;
56 static int tdfx_fd
= -1;
58 static uint32_t img_fmt
; // The real input format
59 static uint32_t src_width
, src_height
, src_fmt
, src_bpp
, src_stride
;
60 static uint32_t dst_width
, dst_height
, dst_fmt
, dst_bpp
, dst_stride
;
62 static uint32_t tdfx_page
;
63 static uint32_t front_buffer
;
64 static uint32_t back_buffer
;
65 static uint8_t num_buffer
= 3;
66 static uint32_t buffer_size
; // Max size
67 static uint8_t current_buffer
= 0;
68 static uint8_t current_ip_buf
= 0;
69 static uint32_t buffer_stride
[3];
71 static int use_overlay
= 1;
72 static tdfx_vid_overlay_t tdfx_ov
;
76 static void clear_screen(void) {
77 tdfx_vid_agp_move_t mov
;
79 memset(agp_mem
,0,tdfx_cfg
.screen_width
*dst_bpp
*tdfx_cfg
.screen_height
);
81 mov
.move2
= TDFX_VID_MOVE_2_PACKED
;
82 mov
.width
= tdfx_cfg
.screen_width
*dst_bpp
;
83 mov
.height
= tdfx_cfg
.screen_height
;
85 mov
.src_stride
= tdfx_cfg
.screen_width
*dst_bpp
;
86 mov
.dst
= front_buffer
;
87 mov
.dst_stride
= tdfx_cfg
.screen_stride
;
89 mp_tmsg(MSGT_VO
,MSGL_INFO
, "[VO_TDXVID] Move %d(%d) x %d => %d.\n", mov
.width
,mov
.src_stride
,mov
.height
,mov
.dst_stride
);
91 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
))
92 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] AGP move failed to clear the screen.\n");
97 static int draw_slice(uint8_t *image
[], int stride
[], int w
,int h
,int x
,int y
)
102 printf("Draw slices %d\n",current_buffer
);
111 // copy :( to agp_mem
112 // still faster than tdfxfb wich directly copy to the video mem :)
113 mem2agpcpy_pic(agp_mem
+ current_buffer
* buffer_size
+
114 y
*buffer_stride
[0] + x
* src_bpp
,
116 src_bpp
*w
,h
,buffer_stride
[0],stride
[0]);
122 ptr
[0] = agp_mem
+ current_buffer
* buffer_size
;
123 mem2agpcpy_pic(ptr
[0] + y
* buffer_stride
[0] + x
,image
[0],w
,h
,
124 buffer_stride
[0],stride
[0]);
125 ptr
[1] = ptr
[0] + (src_height
*src_width
);
126 mem2agpcpy_pic(ptr
[1] + y
/2 * buffer_stride
[1] + x
/2,image
[1],w
/2,h
/2,
127 buffer_stride
[1],stride
[1]);
128 ptr
[2] = ptr
[1] + (src_height
*src_width
/4);
129 mem2agpcpy_pic(ptr
[2] + y
/2 * buffer_stride
[2] + x
/2,image
[2],w
/2,h
/2,
130 buffer_stride
[2],stride
[2]);
137 static void draw_osd(void)
144 tdfx_vid_blit_t blit
;
151 // TDFX_VID_OVERLAY_ON does nothing if the overlay is already on
152 if(!ioctl(tdfx_fd
,TDFX_VID_OVERLAY_ON
)) { // X11 killed the overlay :(
153 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
154 mp_msg(MSGT_VO
, MSGL_ERR
, "tdfx_vid: set_overlay failed\n");
156 // These formats need conversion
161 memset(&blit
,0,sizeof(tdfx_vid_blit_t
));
162 blit
.src
= back_buffer
;
163 blit
.src_stride
= src_stride
;
166 blit
.src_w
= src_width
;
167 blit
.src_h
= src_height
;
168 blit
.src_format
= src_fmt
;
170 blit
.dst
= front_buffer
;
171 blit
.dst_stride
= dst_stride
;
174 blit
.dst_w
= src_width
;
175 blit
.dst_h
= src_height
;
176 blit
.dst_format
= IMGFMT_BGR16
;
177 if(ioctl(tdfx_fd
,TDFX_VID_BLIT
,&blit
))
178 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] Blit failed.\n");
182 memset(&blit
,0,sizeof(tdfx_vid_blit_t
));
183 blit
.src
= back_buffer
;
184 blit
.src_stride
= src_stride
;
187 blit
.src_w
= src_width
;
188 blit
.src_h
= src_height
;
189 blit
.src_format
= src_fmt
;
191 blit
.dst
= front_buffer
;
192 blit
.dst_stride
= dst_stride
;
195 blit
.dst_w
= dst_width
;
196 blit
.dst_h
= dst_height
;
197 blit
.dst_format
= dst_fmt
;
199 if(ioctl(tdfx_fd
,TDFX_VID_BLIT
,&blit
))
200 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] Blit failed.\n");
204 draw_frame(uint8_t *src
[])
206 int stride
[] = { src_stride
, 0, 0};
207 return draw_slice(src
,stride
,src_width
, src_height
,0,0);
211 query_format(uint32_t format
)
215 if(tdfx_cfg
.screen_format
== TDFX_VID_FORMAT_BGR8
)
216 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
226 if(tdfx_cfg
.screen_format
== TDFX_VID_FORMAT_BGR8
)
228 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
234 config(uint32_t width
, uint32_t height
, uint32_t d_width
, uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
)
239 // When we are run as sub vo we must follow the size gaven to us
240 if(!(flags
& VOFLAG_XOVERLAY_SUB_VO
)) {
242 vo_screenwidth
= tdfx_cfg
.screen_width
;
244 vo_screenheight
= tdfx_cfg
.screen_height
;
246 aspect_save_orig(width
,height
);
247 aspect_save_prescale(d_width
,d_height
);
248 aspect_save_screenres(vo_screenwidth
,vo_screenheight
);
250 if(flags
&VOFLAG_FULLSCREEN
) { /* -fs */
251 aspect(&d_width
,&d_height
,A_ZOOM
);
254 aspect(&d_width
,&d_height
,A_NOZOOM
);
261 buffer_stride
[0] = 0;
268 mp_tmsg(MSGT_VO
,MSGL_INFO
, "[VO_TDFXVID] Non-native overlay format needs conversion.\n");
271 src_bpp
= ((format
& 0x3F)+7)/8;
275 buffer_size
= src_width
* src_height
* 3 / 2;
276 buffer_stride
[0] = ((src_width
+1)/2)*2;
277 buffer_stride
[1] = buffer_stride
[2] = buffer_stride
[0]/2;
278 src_fmt
= TDFX_VID_FORMAT_YUY2
;
284 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] Unsupported input format 0x%x.\n",format
);
289 src_stride
= src_width
*src_bpp
;
290 // The overlay need a 4 bytes aligned stride
292 src_stride
= ((src_stride
+3)/4)*4;
296 buffer_size
= src_stride
*src_height
;
297 if(!buffer_stride
[0])
298 buffer_stride
[0] = src_stride
;
300 dst_fmt
= tdfx_cfg
.screen_format
;
301 dst_bpp
= ((dst_fmt
& 0x3F)+7)/8;
303 dst_height
= d_height
;
304 dst_stride
= tdfx_cfg
.screen_stride
;
306 tdfx_page
= tdfx_cfg
.screen_stride
*tdfx_cfg
.screen_height
;
307 front_buffer
= tdfx_cfg
.screen_start
;
308 back_buffer
= front_buffer
+ tdfx_page
;
311 tdfx_vid_overlay_t ov
;
312 uint32_t ov_fmt
= src_fmt
, ov_stride
= src_stride
;
314 back_buffer
= (((back_buffer
+3)/4)*4);
315 // With the overlay the front buffer is not on the screen
316 // so we take the back buffer
317 front_buffer
= back_buffer
;
322 back_buffer
= front_buffer
+ 2*(src_stride
*src_height
);
323 ov_stride
= dst_stride
= src_width
<<1;
324 ov_fmt
= IMGFMT_BGR16
;
327 ov
.src
[0] = front_buffer
;
328 ov
.src
[1] = front_buffer
+ (src_stride
*src_height
);
329 ov
.src_width
= src_width
;
330 ov
.src_height
= src_height
;
331 ov
.src_stride
= ov_stride
;
333 ov
.dst_width
= dst_width
;
334 ov
.dst_height
= dst_height
;
339 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&ov
)) {
340 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] Overlay setup failed.\n");
345 if(use_overlay
== 1) {
346 if(ioctl(tdfx_fd
,TDFX_VID_OVERLAY_ON
)) {
347 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] Overlay on failed.\n");
354 mp_tmsg(MSGT_VO
,MSGL_INFO
, "[VO_TDFXVID] Overlay ready: %d(%d) x %d @ %d => %d(%d) x %d @ %d.\n",
355 src_width
,src_stride
,src_height
,src_bpp
,
356 dst_width
,dst_stride
,dst_height
,dst_bpp
);
361 mp_tmsg(MSGT_VO
,MSGL_INFO
, "[VO_TDFXVID] Texture blit ready: %d(%d) x %d @ %d => %d(%d) x %d @ %d.\n",
362 src_width
,src_stride
,src_height
,src_bpp
,
363 dst_width
,dst_stride
,dst_height
,dst_bpp
);
371 if(use_overlay
== 2) {
372 if(ioctl(tdfx_fd
,TDFX_VID_OVERLAY_OFF
))
373 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] Overlay off failed\n");
381 static void check_events(void)
385 static int preinit(const char *arg
)
388 tdfx_fd
= open(arg
? arg
: "/dev/tdfx_vid", O_RDWR
);
390 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] Can't open %s: %s.\n",arg
? arg
: "/dev/tdfx_vid",
395 if(ioctl(tdfx_fd
,TDFX_VID_GET_CONFIG
,&tdfx_cfg
)) {
396 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] Can't get current configuration: %s.\n",strerror(errno
));
400 mp_msg(MSGT_VO
,MSGL_INFO
, "tdfx_vid version %d\n"
403 " Format: %c%c%c%d\n",
406 tdfx_cfg
.screen_width
, tdfx_cfg
.screen_height
,
407 tdfx_cfg
.screen_format
>>24,(tdfx_cfg
.screen_format
>>16)&0xFF,
408 (tdfx_cfg
.screen_format
>>8)&0xFF,tdfx_cfg
.screen_format
&0xFF);
410 // For now just allocate more than i ever need
411 agp_mem
= mmap( NULL
, 1024*768*4, PROT_READ
| PROT_WRITE
, MAP_SHARED
,
414 if(agp_mem
== MAP_FAILED
) {
415 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] Memmap failed !!!!!\n");
419 memset(agp_mem
,0,1024*768*4);
424 static uint32_t get_image(mp_image_t
*mpi
) {
428 printf("Get image %d\n",buf
);
431 // Currently read are too slow bcs we read from the
432 // agp aperture and not the memory directly
433 //if(mpi->flags&MP_IMGFLAG_READABLE) return VO_FALSE;
435 if(mpi
->flags
&MP_IMGFLAG_READABLE
&&
436 (mpi
->type
==MP_IMGTYPE_IPB
|| mpi
->type
==MP_IMGTYPE_IP
)){
437 // reference (I/P) frame of IP or IPB:
438 if(num_buffer
<2) return VO_FALSE
; // not enough
440 // for IPB with 2 buffers we can DR only one of the 2 P frames:
441 if(mpi
->type
==MP_IMGTYPE_IPB
&& num_buffer
<3 && current_ip_buf
) return VO_FALSE
;
443 if(mpi
->type
==MP_IMGTYPE_IPB
) ++buf
; // preserve space for B
446 switch(mpi
->imgfmt
) {
454 mpi
->planes
[0] = agp_mem
+ buf
* buffer_size
;
455 mpi
->stride
[0] = src_stride
;
459 mpi
->planes
[0] = agp_mem
+ buf
* buffer_size
;
460 mpi
->stride
[0] = mpi
->width
;
461 mpi
->planes
[1] = mpi
->planes
[0] + mpi
->stride
[0]*mpi
->height
;
462 mpi
->stride
[1] = mpi
->chroma_width
;
463 mpi
->planes
[2] = mpi
->planes
[1] + mpi
->stride
[1]*mpi
->chroma_height
;
464 mpi
->stride
[2] = mpi
->chroma_width
;
467 mp_tmsg(MSGT_VO
,MSGL_WARN
, "Get image todo.\n");
470 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
471 mpi
->priv
= (void*)buf
;
476 static uint32_t start_slice(mp_image_t
*mpi
){
480 printf("Start slices %d\n",buf
);
483 if(mpi
->flags
& MP_IMGFLAG_DIRECT
)
484 buf
= (int)mpi
->priv
;
485 current_buffer
= buf
;
490 static uint32_t draw_image(mp_image_t
*mpi
){
492 tdfx_vid_agp_move_t mov
;
498 printf("Draw image %d\n",buf
);
501 if(mpi
->flags
& MP_IMGFLAG_DIRECT
)
502 buf
= (int)mpi
->priv
;
504 switch(mpi
->imgfmt
) {
512 if(!(mpi
->flags
&(MP_IMGFLAG_DIRECT
|MP_IMGFLAG_DRAW_CALLBACK
))) {
517 planes
[0] = agp_mem
+ buf
* buffer_size
;
518 mem2agpcpy_pic(planes
[0],mpi
->planes
[0],src_bpp
*mpi
->width
,mpi
->height
,
519 buffer_stride
[0],mpi
->stride
[0]);
521 planes
[0] = agp_mem
+ buf
* buffer_size
;
523 mov
.move2
= TDFX_VID_MOVE_2_PACKED
;
524 mov
.width
= mpi
->width
*((mpi
->bpp
+7)/8);
525 mov
.height
= mpi
->height
;
526 mov
.src
= planes
[0] - agp_mem
;
527 mov
.src_stride
= buffer_stride
[0];
529 mov
.dst
= back_buffer
;
530 mov
.dst_stride
= src_stride
;
532 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
))
533 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] AGP move failed.\n");
538 if(!(mpi
->flags
&(MP_IMGFLAG_DIRECT
|MP_IMGFLAG_DRAW_CALLBACK
))) {
543 planes
[0] = agp_mem
+ buf
* buffer_size
;
544 memcpy_pic(planes
[0],mpi
->planes
[0],mpi
->width
,mpi
->height
,
545 buffer_stride
[0],mpi
->stride
[0]);
546 planes
[1] = planes
[0] + (mpi
->height
*mpi
->stride
[0]);
547 memcpy_pic(planes
[1],mpi
->planes
[1],mpi
->chroma_width
,mpi
->chroma_height
,
548 buffer_stride
[1],mpi
->stride
[1]);
549 planes
[2] = planes
[1] + (mpi
->chroma_height
*mpi
->stride
[1]);
550 memcpy_pic(planes
[2],mpi
->planes
[2],mpi
->chroma_width
,mpi
->chroma_height
,
551 buffer_stride
[2],mpi
->stride
[2]);
553 planes
[0] = agp_mem
+ buf
* buffer_size
;
554 planes
[1] = planes
[0] + buffer_stride
[0] * src_height
;
555 planes
[2] = planes
[1] + buffer_stride
[1] * src_height
/2;
558 // Setup the yuv thing
559 yuv
.base
= back_buffer
;
560 yuv
.stride
= src_stride
;
561 if(ioctl(tdfx_fd
,TDFX_VID_SET_YUV
,&yuv
)) {
562 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] Set YUV failed.\n");
569 mov
.move2
= TDFX_VID_MOVE_2_YUV
;
570 mov
.width
= mpi
->width
;
571 mov
.height
= mpi
->height
;
572 mov
.src
= planes
[0] - agp_mem
;
573 mov
.src_stride
= buffer_stride
[0];
575 mov
.dst_stride
= TDFX_VID_YUV_STRIDE
;
577 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
578 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] AGP move failed on Y plane.\n");
583 p
= mpi
->imgfmt
== IMGFMT_YV12
? 1 : 2;
584 mov
.width
= mpi
->chroma_width
;
585 mov
.height
= mpi
->chroma_height
;
586 mov
.src
= planes
[p
] - agp_mem
;
587 mov
.src_stride
= buffer_stride
[p
];
588 mov
.dst
+= TDFX_VID_YUV_PLANE_SIZE
;
589 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
590 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] AGP move failed on U plane.\n");
594 p
= mpi
->imgfmt
== IMGFMT_YV12
? 2 : 1;
595 mov
.src
= planes
[p
] - agp_mem
;
596 mov
.src_stride
= buffer_stride
[p
];
597 mov
.dst
+= TDFX_VID_YUV_PLANE_SIZE
;
598 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
599 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] AGP move failed on V plane.\n");
604 mp_tmsg(MSGT_VO
,MSGL_WARN
, "[VO_TDFXVID] unknown format: 0x%x.\n",mpi
->imgfmt
);
611 static uint32_t fullscreen(void) {
613 aspect(&dst_width
,&dst_height
,vo_fs
? A_ZOOM
: A_NOZOOM
);
614 // This does not work :((
619 static uint32_t set_window(mp_win_t
* w
) {
620 if(!use_overlay
) return VO_FALSE
;
622 tdfx_ov
.dst_x
= w
->x
;
623 tdfx_ov
.dst_y
= w
->y
;
624 tdfx_ov
.dst_width
= w
->w
;
625 tdfx_ov
.dst_height
= w
->h
;
627 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
628 mp_msg(MSGT_VO
, MSGL_V
, "tdfx_vid: set window failed\n");
633 static uint32_t set_colorkey(mp_colorkey_t
* colork
) {
634 if(!use_overlay
) return VO_FALSE
;
636 tdfx_ov
.colorkey
[0] = tdfx_ov
.colorkey
[1] = colork
->x11
;
637 tdfx_ov
.use_colorkey
= 1;
638 tdfx_ov
.invert_colorkey
= 0;
640 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
641 mp_msg(MSGT_VO
, MSGL_V
, "tdfx_vid: set colorkey failed\n");
646 static int control(uint32_t request
, void *data
)
649 case VOCTRL_QUERY_FORMAT
:
650 return query_format(*((uint32_t*)data
));
651 case VOCTRL_GET_IMAGE
:
652 return get_image(data
);
653 case VOCTRL_DRAW_IMAGE
:
654 return draw_image(data
);
655 case VOCTRL_START_SLICE
:
656 return start_slice(data
);
657 case VOCTRL_FULLSCREEN
:
659 case VOCTRL_XOVERLAY_SUPPORT
:
661 case VOCTRL_XOVERLAY_SET_COLORKEY
:
662 return set_colorkey(data
);
663 case VOCTRL_XOVERLAY_SET_WIN
:
664 return set_window(data
);