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 along
19 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include <sys/types.h>
31 #include <sys/ioctl.h>
34 #include "video_out.h"
35 #include "video_out_internal.h"
40 #include "fastmemcpy.h"
41 #include "drivers/tdfx_vid.h"
44 static const vo_info_t info
=
54 const LIBVO_EXTERN(tdfx_vid
)
56 static tdfx_vid_config_t tdfx_cfg
;
58 static unsigned char* agp_mem
= NULL
;
59 static int tdfx_fd
= -1;
61 static uint32_t img_fmt
; // The real input format
62 static uint32_t src_width
, src_height
, src_fmt
, src_bpp
, src_stride
;
63 static uint32_t dst_width
, dst_height
, dst_fmt
, dst_bpp
, dst_stride
;
65 static uint32_t tdfx_page
;
66 static uint32_t front_buffer
;
67 static uint32_t back_buffer
;
68 static uint8_t num_buffer
= 3;
69 static uint32_t buffer_size
; // Max size
70 static uint8_t current_buffer
= 0;
71 static uint8_t current_ip_buf
= 0;
72 static uint32_t buffer_stride
[3];
74 static int use_overlay
= 1;
75 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
);
100 static int draw_slice(uint8_t *image
[], int stride
[], int w
,int h
,int x
,int y
)
105 printf("Draw slices %d\n",current_buffer
);
114 // copy :( to agp_mem
115 // still faster than tdfxfb wich directly copy to the video mem :)
116 mem2agpcpy_pic(agp_mem
+ current_buffer
* buffer_size
+
117 y
*buffer_stride
[0] + x
* src_bpp
,
119 src_bpp
*w
,h
,buffer_stride
[0],stride
[0]);
125 ptr
[0] = agp_mem
+ current_buffer
* buffer_size
;
126 mem2agpcpy_pic(ptr
[0] + y
* buffer_stride
[0] + x
,image
[0],w
,h
,
127 buffer_stride
[0],stride
[0]);
128 ptr
[1] = ptr
[0] + (src_height
*src_width
);
129 mem2agpcpy_pic(ptr
[1] + y
/2 * buffer_stride
[1] + x
/2,image
[1],w
/2,h
/2,
130 buffer_stride
[1],stride
[1]);
131 ptr
[2] = ptr
[1] + (src_height
*src_width
/4);
132 mem2agpcpy_pic(ptr
[2] + y
/2 * buffer_stride
[2] + x
/2,image
[2],w
/2,h
/2,
133 buffer_stride
[2],stride
[2]);
140 static void draw_osd(void)
147 tdfx_vid_blit_t blit
;
154 // TDFX_VID_OVERLAY_ON does nothing if the overlay is already on
155 if(!ioctl(tdfx_fd
,TDFX_VID_OVERLAY_ON
)) { // X11 killed the overlay :(
156 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
157 mp_msg(MSGT_VO
, MSGL_ERR
, "tdfx_vid: set_overlay failed\n");
159 // These formats need conversion
164 memset(&blit
,0,sizeof(tdfx_vid_blit_t
));
165 blit
.src
= back_buffer
;
166 blit
.src_stride
= src_stride
;
169 blit
.src_w
= src_width
;
170 blit
.src_h
= src_height
;
171 blit
.src_format
= src_fmt
;
173 blit
.dst
= front_buffer
;
174 blit
.dst_stride
= dst_stride
;
177 blit
.dst_w
= src_width
;
178 blit
.dst_h
= src_height
;
179 blit
.dst_format
= IMGFMT_BGR16
;
180 if(ioctl(tdfx_fd
,TDFX_VID_BLIT
,&blit
))
181 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_BlitFailed
);
185 memset(&blit
,0,sizeof(tdfx_vid_blit_t
));
186 blit
.src
= back_buffer
;
187 blit
.src_stride
= src_stride
;
190 blit
.src_w
= src_width
;
191 blit
.src_h
= src_height
;
192 blit
.src_format
= src_fmt
;
194 blit
.dst
= front_buffer
;
195 blit
.dst_stride
= dst_stride
;
198 blit
.dst_w
= dst_width
;
199 blit
.dst_h
= dst_height
;
200 blit
.dst_format
= dst_fmt
;
202 if(ioctl(tdfx_fd
,TDFX_VID_BLIT
,&blit
))
203 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_BlitFailed
);
207 draw_frame(uint8_t *src
[])
209 int stride
[] = { src_stride
, 0, 0};
210 return draw_slice(src
,stride
,src_width
, src_height
,0,0);
214 query_format(uint32_t format
)
218 if(tdfx_cfg
.screen_format
== TDFX_VID_FORMAT_BGR8
)
219 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
229 if(tdfx_cfg
.screen_format
== TDFX_VID_FORMAT_BGR8
)
231 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
237 config(uint32_t width
, uint32_t height
, uint32_t d_width
, uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
)
242 // When we are run as sub vo we must follow the size gaven to us
243 if(!(flags
& VOFLAG_XOVERLAY_SUB_VO
)) {
245 vo_screenwidth
= tdfx_cfg
.screen_width
;
247 vo_screenheight
= tdfx_cfg
.screen_height
;
249 aspect_save_orig(width
,height
);
250 aspect_save_prescale(d_width
,d_height
);
251 aspect_save_screenres(vo_screenwidth
,vo_screenheight
);
253 if(flags
&VOFLAG_FULLSCREEN
) { /* -fs */
254 aspect(&d_width
,&d_height
,A_ZOOM
);
257 aspect(&d_width
,&d_height
,A_NOZOOM
);
264 buffer_stride
[0] = 0;
271 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXVID_NonNativeOverlayFormatNeedConversion
);
274 src_bpp
= ((format
& 0x3F)+7)/8;
278 buffer_size
= src_width
* src_height
* 3 / 2;
279 buffer_stride
[0] = ((src_width
+1)/2)*2;
280 buffer_stride
[1] = buffer_stride
[2] = buffer_stride
[0]/2;
281 src_fmt
= TDFX_VID_FORMAT_YUY2
;
287 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_UnsupportedInputFormat
,format
);
292 src_stride
= src_width
*src_bpp
;
293 // The overlay need a 4 bytes aligned stride
295 src_stride
= ((src_stride
+3)/4)*4;
299 buffer_size
= src_stride
*src_height
;
300 if(!buffer_stride
[0])
301 buffer_stride
[0] = src_stride
;
303 dst_fmt
= tdfx_cfg
.screen_format
;
304 dst_bpp
= ((dst_fmt
& 0x3F)+7)/8;
306 dst_height
= d_height
;
307 dst_stride
= tdfx_cfg
.screen_stride
;
309 tdfx_page
= tdfx_cfg
.screen_stride
*tdfx_cfg
.screen_height
;
310 front_buffer
= tdfx_cfg
.screen_start
;
311 back_buffer
= front_buffer
+ tdfx_page
;
314 tdfx_vid_overlay_t ov
;
315 uint32_t ov_fmt
= src_fmt
, ov_stride
= src_stride
;
317 back_buffer
= (((back_buffer
+3)/4)*4);
318 // With the overlay the front buffer is not on the screen
319 // so we take the back buffer
320 front_buffer
= back_buffer
;
325 back_buffer
= front_buffer
+ 2*(src_stride
*src_height
);
326 ov_stride
= dst_stride
= src_width
<<1;
327 ov_fmt
= IMGFMT_BGR16
;
330 ov
.src
[0] = front_buffer
;
331 ov
.src
[1] = front_buffer
+ (src_stride
*src_height
);
332 ov
.src_width
= src_width
;
333 ov
.src_height
= src_height
;
334 ov
.src_stride
= ov_stride
;
336 ov
.dst_width
= dst_width
;
337 ov
.dst_height
= dst_height
;
342 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&ov
)) {
343 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_OverlaySetupFailed
);
348 if(use_overlay
== 1) {
349 if(ioctl(tdfx_fd
,TDFX_VID_OVERLAY_ON
)) {
350 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_OverlayOnFailed
);
357 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXVID_OverlayReady
,
358 src_width
,src_stride
,src_height
,src_bpp
,
359 dst_width
,dst_stride
,dst_height
,dst_bpp
);
364 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXVID_TextureBlitReady
,
365 src_width
,src_stride
,src_height
,src_bpp
,
366 dst_width
,dst_stride
,dst_height
,dst_bpp
);
374 if(use_overlay
== 2) {
375 if(ioctl(tdfx_fd
,TDFX_VID_OVERLAY_OFF
))
376 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_OverlayOffFailed
);
384 static void check_events(void)
388 static int preinit(const char *arg
)
391 tdfx_fd
= open(arg
? arg
: "/dev/tdfx_vid", O_RDWR
);
393 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_CantOpen
,arg
? arg
: "/dev/tdfx_vid",
398 if(ioctl(tdfx_fd
,TDFX_VID_GET_CONFIG
,&tdfx_cfg
)) {
399 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_CantGetCurrentCfg
,strerror(errno
));
403 mp_msg(MSGT_VO
,MSGL_INFO
, "tdfx_vid version %d\n"
406 " Format: %c%c%c%d\n",
409 tdfx_cfg
.screen_width
, tdfx_cfg
.screen_height
,
410 tdfx_cfg
.screen_format
>>24,(tdfx_cfg
.screen_format
>>16)&0xFF,
411 (tdfx_cfg
.screen_format
>>8)&0xFF,tdfx_cfg
.screen_format
&0xFF);
413 // For now just allocate more than i ever need
414 agp_mem
= mmap( NULL
, 1024*768*4, PROT_READ
| PROT_WRITE
, MAP_SHARED
,
417 if(agp_mem
== MAP_FAILED
) {
418 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_MemmapFailed
);
422 memset(agp_mem
,0,1024*768*4);
427 static uint32_t get_image(mp_image_t
*mpi
) {
431 printf("Get image %d\n",buf
);
434 // Currently read are too slow bcs we read from the
435 // agp aperture and not the memory directly
436 //if(mpi->flags&MP_IMGFLAG_READABLE) return VO_FALSE;
438 if(mpi
->flags
&MP_IMGFLAG_READABLE
&&
439 (mpi
->type
==MP_IMGTYPE_IPB
|| mpi
->type
==MP_IMGTYPE_IP
)){
440 // reference (I/P) frame of IP or IPB:
441 if(num_buffer
<2) return VO_FALSE
; // not enough
443 // for IPB with 2 buffers we can DR only one of the 2 P frames:
444 if(mpi
->type
==MP_IMGTYPE_IPB
&& num_buffer
<3 && current_ip_buf
) return VO_FALSE
;
446 if(mpi
->type
==MP_IMGTYPE_IPB
) ++buf
; // preserve space for B
449 switch(mpi
->imgfmt
) {
457 mpi
->planes
[0] = agp_mem
+ buf
* buffer_size
;
458 mpi
->stride
[0] = src_stride
;
462 mpi
->planes
[0] = agp_mem
+ buf
* buffer_size
;
463 mpi
->stride
[0] = mpi
->width
;
464 mpi
->planes
[1] = mpi
->planes
[0] + mpi
->stride
[0]*mpi
->height
;
465 mpi
->stride
[1] = mpi
->chroma_width
;
466 mpi
->planes
[2] = mpi
->planes
[1] + mpi
->stride
[1]*mpi
->chroma_height
;
467 mpi
->stride
[2] = mpi
->chroma_width
;
470 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_GetImageTodo
);
473 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
474 mpi
->priv
= (void*)buf
;
479 static uint32_t start_slice(mp_image_t
*mpi
){
483 printf("Start slices %d\n",buf
);
486 if(mpi
->flags
& MP_IMGFLAG_DIRECT
)
487 buf
= (int)mpi
->priv
;
488 current_buffer
= buf
;
493 static uint32_t draw_image(mp_image_t
*mpi
){
495 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
);