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"
38 #include "fastmemcpy.h"
39 #include "drivers/tdfx_vid.h"
42 static const vo_info_t info
=
52 const LIBVO_EXTERN(tdfx_vid
)
54 static tdfx_vid_config_t tdfx_cfg
;
56 static unsigned char* agp_mem
= NULL
;
57 static int tdfx_fd
= -1;
59 static uint32_t img_fmt
; // The real input format
60 static uint32_t src_width
, src_height
, src_fmt
, src_bpp
, src_stride
;
61 static uint32_t dst_width
, dst_height
, dst_fmt
, dst_bpp
, dst_stride
;
63 static uint32_t tdfx_page
;
64 static uint32_t front_buffer
;
65 static uint32_t back_buffer
;
66 static uint8_t num_buffer
= 3;
67 static uint32_t buffer_size
; // Max size
68 static uint8_t current_buffer
= 0;
69 static uint8_t current_ip_buf
= 0;
70 static uint32_t buffer_stride
[3];
72 static int use_overlay
= 1;
73 static tdfx_vid_overlay_t tdfx_ov
;
77 static void clear_screen(void) {
78 tdfx_vid_agp_move_t mov
;
80 memset(agp_mem
,0,tdfx_cfg
.screen_width
*dst_bpp
*tdfx_cfg
.screen_height
);
82 mov
.move2
= TDFX_VID_MOVE_2_PACKED
;
83 mov
.width
= tdfx_cfg
.screen_width
*dst_bpp
;
84 mov
.height
= tdfx_cfg
.screen_height
;
86 mov
.src_stride
= tdfx_cfg
.screen_width
*dst_bpp
;
87 mov
.dst
= front_buffer
;
88 mov
.dst_stride
= tdfx_cfg
.screen_stride
;
90 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXVID_Move
, mov
.width
,mov
.src_stride
,mov
.height
,mov
.dst_stride
);
92 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
))
93 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_AGPMoveFailedToClearTheScreen
);
98 static int draw_slice(uint8_t *image
[], int stride
[], int w
,int h
,int x
,int y
)
103 printf("Draw slices %d\n",current_buffer
);
112 // copy :( to agp_mem
113 // still faster than tdfxfb wich directly copy to the video mem :)
114 mem2agpcpy_pic(agp_mem
+ current_buffer
* buffer_size
+
115 y
*buffer_stride
[0] + x
* src_bpp
,
117 src_bpp
*w
,h
,buffer_stride
[0],stride
[0]);
123 ptr
[0] = agp_mem
+ current_buffer
* buffer_size
;
124 mem2agpcpy_pic(ptr
[0] + y
* buffer_stride
[0] + x
,image
[0],w
,h
,
125 buffer_stride
[0],stride
[0]);
126 ptr
[1] = ptr
[0] + (src_height
*src_width
);
127 mem2agpcpy_pic(ptr
[1] + y
/2 * buffer_stride
[1] + x
/2,image
[1],w
/2,h
/2,
128 buffer_stride
[1],stride
[1]);
129 ptr
[2] = ptr
[1] + (src_height
*src_width
/4);
130 mem2agpcpy_pic(ptr
[2] + y
/2 * buffer_stride
[2] + x
/2,image
[2],w
/2,h
/2,
131 buffer_stride
[2],stride
[2]);
138 static void draw_osd(void)
145 tdfx_vid_blit_t blit
;
152 // TDFX_VID_OVERLAY_ON does nothing if the overlay is already on
153 if(!ioctl(tdfx_fd
,TDFX_VID_OVERLAY_ON
)) { // X11 killed the overlay :(
154 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
155 mp_msg(MSGT_VO
, MSGL_ERR
, "tdfx_vid: set_overlay failed\n");
157 // These formats need conversion
162 memset(&blit
,0,sizeof(tdfx_vid_blit_t
));
163 blit
.src
= back_buffer
;
164 blit
.src_stride
= src_stride
;
167 blit
.src_w
= src_width
;
168 blit
.src_h
= src_height
;
169 blit
.src_format
= src_fmt
;
171 blit
.dst
= front_buffer
;
172 blit
.dst_stride
= dst_stride
;
175 blit
.dst_w
= src_width
;
176 blit
.dst_h
= src_height
;
177 blit
.dst_format
= IMGFMT_BGR16
;
178 if(ioctl(tdfx_fd
,TDFX_VID_BLIT
,&blit
))
179 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_BlitFailed
);
183 memset(&blit
,0,sizeof(tdfx_vid_blit_t
));
184 blit
.src
= back_buffer
;
185 blit
.src_stride
= src_stride
;
188 blit
.src_w
= src_width
;
189 blit
.src_h
= src_height
;
190 blit
.src_format
= src_fmt
;
192 blit
.dst
= front_buffer
;
193 blit
.dst_stride
= dst_stride
;
196 blit
.dst_w
= dst_width
;
197 blit
.dst_h
= dst_height
;
198 blit
.dst_format
= dst_fmt
;
200 if(ioctl(tdfx_fd
,TDFX_VID_BLIT
,&blit
))
201 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_BlitFailed
);
205 draw_frame(uint8_t *src
[])
207 int stride
[] = { src_stride
, 0, 0};
208 return draw_slice(src
,stride
,src_width
, src_height
,0,0);
212 query_format(uint32_t format
)
216 if(tdfx_cfg
.screen_format
== TDFX_VID_FORMAT_BGR8
)
217 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
227 if(tdfx_cfg
.screen_format
== TDFX_VID_FORMAT_BGR8
)
229 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
235 config(uint32_t width
, uint32_t height
, uint32_t d_width
, uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
)
240 // When we are run as sub vo we must follow the size gaven to us
241 if(!(flags
& VOFLAG_XOVERLAY_SUB_VO
)) {
243 vo_screenwidth
= tdfx_cfg
.screen_width
;
245 vo_screenheight
= tdfx_cfg
.screen_height
;
247 aspect_save_orig(width
,height
);
248 aspect_save_prescale(d_width
,d_height
);
249 aspect_save_screenres(vo_screenwidth
,vo_screenheight
);
251 if(flags
&VOFLAG_FULLSCREEN
) { /* -fs */
252 aspect(&d_width
,&d_height
,A_ZOOM
);
255 aspect(&d_width
,&d_height
,A_NOZOOM
);
262 buffer_stride
[0] = 0;
269 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXVID_NonNativeOverlayFormatNeedConversion
);
272 src_bpp
= ((format
& 0x3F)+7)/8;
276 buffer_size
= src_width
* src_height
* 3 / 2;
277 buffer_stride
[0] = ((src_width
+1)/2)*2;
278 buffer_stride
[1] = buffer_stride
[2] = buffer_stride
[0]/2;
279 src_fmt
= TDFX_VID_FORMAT_YUY2
;
285 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_UnsupportedInputFormat
,format
);
290 src_stride
= src_width
*src_bpp
;
291 // The overlay need a 4 bytes aligned stride
293 src_stride
= ((src_stride
+3)/4)*4;
297 buffer_size
= src_stride
*src_height
;
298 if(!buffer_stride
[0])
299 buffer_stride
[0] = src_stride
;
301 dst_fmt
= tdfx_cfg
.screen_format
;
302 dst_bpp
= ((dst_fmt
& 0x3F)+7)/8;
304 dst_height
= d_height
;
305 dst_stride
= tdfx_cfg
.screen_stride
;
307 tdfx_page
= tdfx_cfg
.screen_stride
*tdfx_cfg
.screen_height
;
308 front_buffer
= tdfx_cfg
.screen_start
;
309 back_buffer
= front_buffer
+ tdfx_page
;
312 tdfx_vid_overlay_t ov
;
313 uint32_t ov_fmt
= src_fmt
, ov_stride
= src_stride
;
315 back_buffer
= (((back_buffer
+3)/4)*4);
316 // With the overlay the front buffer is not on the screen
317 // so we take the back buffer
318 front_buffer
= back_buffer
;
323 back_buffer
= front_buffer
+ 2*(src_stride
*src_height
);
324 ov_stride
= dst_stride
= src_width
<<1;
325 ov_fmt
= IMGFMT_BGR16
;
328 ov
.src
[0] = front_buffer
;
329 ov
.src
[1] = front_buffer
+ (src_stride
*src_height
);
330 ov
.src_width
= src_width
;
331 ov
.src_height
= src_height
;
332 ov
.src_stride
= ov_stride
;
334 ov
.dst_width
= dst_width
;
335 ov
.dst_height
= dst_height
;
340 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&ov
)) {
341 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_OverlaySetupFailed
);
346 if(use_overlay
== 1) {
347 if(ioctl(tdfx_fd
,TDFX_VID_OVERLAY_ON
)) {
348 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_OverlayOnFailed
);
355 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXVID_OverlayReady
,
356 src_width
,src_stride
,src_height
,src_bpp
,
357 dst_width
,dst_stride
,dst_height
,dst_bpp
);
362 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXVID_TextureBlitReady
,
363 src_width
,src_stride
,src_height
,src_bpp
,
364 dst_width
,dst_stride
,dst_height
,dst_bpp
);
372 if(use_overlay
== 2) {
373 if(ioctl(tdfx_fd
,TDFX_VID_OVERLAY_OFF
))
374 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_OverlayOffFailed
);
382 static void check_events(void)
386 static int preinit(const char *arg
)
389 tdfx_fd
= open(arg
? arg
: "/dev/tdfx_vid", O_RDWR
);
391 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_CantOpen
,arg
? arg
: "/dev/tdfx_vid",
396 if(ioctl(tdfx_fd
,TDFX_VID_GET_CONFIG
,&tdfx_cfg
)) {
397 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_CantGetCurrentCfg
,strerror(errno
));
401 mp_msg(MSGT_VO
,MSGL_INFO
, "tdfx_vid version %d\n"
404 " Format: %c%c%c%d\n",
407 tdfx_cfg
.screen_width
, tdfx_cfg
.screen_height
,
408 tdfx_cfg
.screen_format
>>24,(tdfx_cfg
.screen_format
>>16)&0xFF,
409 (tdfx_cfg
.screen_format
>>8)&0xFF,tdfx_cfg
.screen_format
&0xFF);
411 // For now just allocate more than i ever need
412 agp_mem
= mmap( NULL
, 1024*768*4, PROT_READ
| PROT_WRITE
, MAP_SHARED
,
415 if(agp_mem
== MAP_FAILED
) {
416 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_MemmapFailed
);
420 memset(agp_mem
,0,1024*768*4);
425 static uint32_t get_image(mp_image_t
*mpi
) {
429 printf("Get image %d\n",buf
);
432 // Currently read are too slow bcs we read from the
433 // agp aperture and not the memory directly
434 //if(mpi->flags&MP_IMGFLAG_READABLE) return VO_FALSE;
436 if(mpi
->flags
&MP_IMGFLAG_READABLE
&&
437 (mpi
->type
==MP_IMGTYPE_IPB
|| mpi
->type
==MP_IMGTYPE_IP
)){
438 // reference (I/P) frame of IP or IPB:
439 if(num_buffer
<2) return VO_FALSE
; // not enough
441 // for IPB with 2 buffers we can DR only one of the 2 P frames:
442 if(mpi
->type
==MP_IMGTYPE_IPB
&& num_buffer
<3 && current_ip_buf
) return VO_FALSE
;
444 if(mpi
->type
==MP_IMGTYPE_IPB
) ++buf
; // preserve space for B
447 switch(mpi
->imgfmt
) {
455 mpi
->planes
[0] = agp_mem
+ buf
* buffer_size
;
456 mpi
->stride
[0] = src_stride
;
460 mpi
->planes
[0] = agp_mem
+ buf
* buffer_size
;
461 mpi
->stride
[0] = mpi
->width
;
462 mpi
->planes
[1] = mpi
->planes
[0] + mpi
->stride
[0]*mpi
->height
;
463 mpi
->stride
[1] = mpi
->chroma_width
;
464 mpi
->planes
[2] = mpi
->planes
[1] + mpi
->stride
[1]*mpi
->chroma_height
;
465 mpi
->stride
[2] = mpi
->chroma_width
;
468 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_GetImageTodo
);
471 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
472 mpi
->priv
= (void*)buf
;
477 static uint32_t start_slice(mp_image_t
*mpi
){
481 printf("Start slices %d\n",buf
);
484 if(mpi
->flags
& MP_IMGFLAG_DIRECT
)
485 buf
= (int)mpi
->priv
;
486 current_buffer
= buf
;
491 static uint32_t draw_image(mp_image_t
*mpi
){
493 tdfx_vid_agp_move_t mov
;
499 printf("Draw image %d\n",buf
);
502 if(mpi
->flags
& MP_IMGFLAG_DIRECT
)
503 buf
= (int)mpi
->priv
;
505 switch(mpi
->imgfmt
) {
513 if(!(mpi
->flags
&(MP_IMGFLAG_DIRECT
|MP_IMGFLAG_DRAW_CALLBACK
))) {
518 planes
[0] = agp_mem
+ buf
* buffer_size
;
519 mem2agpcpy_pic(planes
[0],mpi
->planes
[0],src_bpp
*mpi
->width
,mpi
->height
,
520 buffer_stride
[0],mpi
->stride
[0]);
522 planes
[0] = agp_mem
+ buf
* buffer_size
;
524 mov
.move2
= TDFX_VID_MOVE_2_PACKED
;
525 mov
.width
= mpi
->width
*((mpi
->bpp
+7)/8);
526 mov
.height
= mpi
->height
;
527 mov
.src
= planes
[0] - agp_mem
;
528 mov
.src_stride
= buffer_stride
[0];
530 mov
.dst
= back_buffer
;
531 mov
.dst_stride
= src_stride
;
533 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
))
534 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_AgpMoveFailed
);
539 if(!(mpi
->flags
&(MP_IMGFLAG_DIRECT
|MP_IMGFLAG_DRAW_CALLBACK
))) {
544 planes
[0] = agp_mem
+ buf
* buffer_size
;
545 memcpy_pic(planes
[0],mpi
->planes
[0],mpi
->width
,mpi
->height
,
546 buffer_stride
[0],mpi
->stride
[0]);
547 planes
[1] = planes
[0] + (mpi
->height
*mpi
->stride
[0]);
548 memcpy_pic(planes
[1],mpi
->planes
[1],mpi
->chroma_width
,mpi
->chroma_height
,
549 buffer_stride
[1],mpi
->stride
[1]);
550 planes
[2] = planes
[1] + (mpi
->chroma_height
*mpi
->stride
[1]);
551 memcpy_pic(planes
[2],mpi
->planes
[2],mpi
->chroma_width
,mpi
->chroma_height
,
552 buffer_stride
[2],mpi
->stride
[2]);
554 planes
[0] = agp_mem
+ buf
* buffer_size
;
555 planes
[1] = planes
[0] + buffer_stride
[0] * src_height
;
556 planes
[2] = planes
[1] + buffer_stride
[1] * src_height
/2;
559 // Setup the yuv thing
560 yuv
.base
= back_buffer
;
561 yuv
.stride
= src_stride
;
562 if(ioctl(tdfx_fd
,TDFX_VID_SET_YUV
,&yuv
)) {
563 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_SetYuvFailed
);
570 mov
.move2
= TDFX_VID_MOVE_2_YUV
;
571 mov
.width
= mpi
->width
;
572 mov
.height
= mpi
->height
;
573 mov
.src
= planes
[0] - agp_mem
;
574 mov
.src_stride
= buffer_stride
[0];
576 mov
.dst_stride
= TDFX_VID_YUV_STRIDE
;
578 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
579 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_AgpMoveFailedOnYPlane
);
584 p
= mpi
->imgfmt
== IMGFMT_YV12
? 1 : 2;
585 mov
.width
= mpi
->chroma_width
;
586 mov
.height
= mpi
->chroma_height
;
587 mov
.src
= planes
[p
] - agp_mem
;
588 mov
.src_stride
= buffer_stride
[p
];
589 mov
.dst
+= TDFX_VID_YUV_PLANE_SIZE
;
590 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
591 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_AgpMoveFailedOnUPlane
);
595 p
= mpi
->imgfmt
== IMGFMT_YV12
? 2 : 1;
596 mov
.src
= planes
[p
] - agp_mem
;
597 mov
.src_stride
= buffer_stride
[p
];
598 mov
.dst
+= TDFX_VID_YUV_PLANE_SIZE
;
599 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
600 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_AgpMoveFailedOnVPlane
);
605 mp_msg(MSGT_VO
,MSGL_WARN
, MSGTR_LIBVO_TDFXVID_UnknownFormat
,mpi
->imgfmt
);
612 static uint32_t fullscreen(void) {
614 aspect(&dst_width
,&dst_height
,vo_fs
? A_ZOOM
: A_NOZOOM
);
615 // This does not work :((
620 static uint32_t set_window(mp_win_t
* w
) {
621 if(!use_overlay
) return VO_FALSE
;
623 tdfx_ov
.dst_x
= w
->x
;
624 tdfx_ov
.dst_y
= w
->y
;
625 tdfx_ov
.dst_width
= w
->w
;
626 tdfx_ov
.dst_height
= w
->h
;
628 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
629 mp_msg(MSGT_VO
, MSGL_V
, "tdfx_vid: set window failed\n");
634 static uint32_t set_colorkey(mp_colorkey_t
* colork
) {
635 if(!use_overlay
) return VO_FALSE
;
637 tdfx_ov
.colorkey
[0] = tdfx_ov
.colorkey
[1] = colork
->x11
;
638 tdfx_ov
.use_colorkey
= 1;
639 tdfx_ov
.invert_colorkey
= 0;
641 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
642 mp_msg(MSGT_VO
, MSGL_V
, "tdfx_vid: set colorkey failed\n");
647 static int control(uint32_t request
, void *data
)
650 case VOCTRL_QUERY_FORMAT
:
651 return query_format(*((uint32_t*)data
));
652 case VOCTRL_GET_IMAGE
:
653 return get_image(data
);
654 case VOCTRL_DRAW_IMAGE
:
655 return draw_image(data
);
656 case VOCTRL_START_SLICE
:
657 return start_slice(data
);
658 case VOCTRL_FULLSCREEN
:
660 case VOCTRL_XOVERLAY_SUPPORT
:
662 case VOCTRL_XOVERLAY_SET_COLORKEY
:
663 return set_colorkey(data
);
664 case VOCTRL_XOVERLAY_SET_WIN
:
665 return set_window(data
);