2002-01-17 01:32:29 +00:00
/*
* vo_zr . c - playback on zoran cards
* Copyright ( C ) Rik Snel 2001 , 2002 , License GNU GPL v2
*/
2002-04-17 21:28:51 +00:00
/* $Id$ */
2002-01-17 01:32:29 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <fcntl.h>
2002-02-17 08:24:43 +00:00
# include <errno.h>
2002-01-17 01:32:29 +00:00
# include <sys/stat.h>
# include <sys/types.h>
# include <sys/time.h>
# include <sys/mman.h>
# include <sys/ioctl.h>
# include <linux/types.h>
# include <linux/videodev.h>
2002-02-12 15:53:40 +00:00
# include "videodev_mjpeg.h"
2002-01-17 01:32:29 +00:00
# include "config.h"
# include "video_out.h"
# include "video_out_internal.h"
2004-10-28 01:15:53 +00:00
# include "mp_msg.h"
# include "m_option.h"
2002-01-18 09:58:18 +00:00
# include "fastmemcpy.h"
2002-01-17 01:32:29 +00:00
2002-02-11 01:24:56 +00:00
# include "jpeg_enc.h"
2002-11-11 15:22:10 +00:00
static vo_info_t info =
2002-01-17 01:32:29 +00:00
{
" Zoran ZR360[56]7/ZR36060 Driver (DC10(+)/buz/lml33/MatroxRR) " ,
" zr " ,
2005-01-08 14:32:26 +00:00
" Rik Snel <rsnel@cube.dyndns.org> " ,
2002-01-17 01:32:29 +00:00
" "
} ;
2002-11-11 15:22:10 +00:00
LIBVO_EXTERN ( zr )
2002-08-05 20:03:22 +00:00
# define ZR_MAX_DEVICES 4
2002-01-17 01:32:29 +00:00
/* General variables */
typedef struct {
int width ;
int height ;
int xoff ;
int yoff ;
int set ;
2002-08-05 20:03:22 +00:00
} geo_t ;
static int zr_count = 1 ;
static int zr_parsing = 0 ;
static int framenum ;
typedef struct {
/* commandline args given for this device (and defaults) */
int vdec , hdec ; /* requested decimation 1,2,4 */
int fd ; /* force decimation */
int xdoff , ydoff ; /* offset from upperleft of screen
* default is ' centered ' */
int quality ; /* jpeg quality 1=best, 20=bad */
geo_t g ; /* view window (zrcrop) */
char * device ; /* /dev/video1 */
int bw ; /* if bw == 1, display in black&white */
int norm ; /* PAL/NTSC */
/* buffers + pointers + info */
unsigned char * image ;
int image_width , image_height , size ;
int off_y , off_c , stride ; /* for use by 'draw slice/frame' */
unsigned char * buf ; /* the jpeg images will be placed here */
jpeg_enc_t * j ;
unsigned char * y_data , * u_data , * v_data ; /* used by the jpeg encoder */
int y_stride , u_stride , v_stride ; /* these point somewhere in image */
/* information for (and about) the zoran card */
int vdes ; /* file descriptor of card */
int frame , synco , queue ; /* buffer management */
struct mjpeg_sync zs ; /* state information */
struct mjpeg_params p ;
2003-11-03 17:38:50 +00:00
struct mjpeg_requestbuffers zrq ;
2002-08-05 20:03:22 +00:00
struct video_capability vc ; /* max resolution and so on */
int fields , stretchy ; /* must the *image be interlaced
or stretched to fit on the screen ? */
} zr_info_t ;
static zr_info_t zr_info [ ZR_MAX_DEVICES ] = {
2003-11-03 17:38:50 +00:00
{ 1 , 1 , 1 , - 1 , - 1 , 2 , { 0 , 0 , 0 , 0 , 0 } , NULL , 0 , VIDEO_MODE_AUTO , NULL , 0 , 0 , 0 , 0 , 0 ,
0 , NULL , NULL , NULL , NULL , NULL , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ 1 , 1 , 1 , - 1 , - 1 , 2 , { 0 , 0 , 0 , 0 , 0 } , NULL , 0 , VIDEO_MODE_AUTO , NULL , 0 , 0 , 0 , 0 , 0 ,
0 , NULL , NULL , NULL , NULL , NULL , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ 1 , 1 , 1 , - 1 , - 1 , 2 , { 0 , 0 , 0 , 0 , 0 } , NULL , 0 , VIDEO_MODE_AUTO , NULL , 0 , 0 , 0 , 0 , 0 ,
0 , NULL , NULL , NULL , NULL , NULL , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ 1 , 1 , 1 , - 1 , - 1 , 2 , { 0 , 0 , 0 , 0 , 0 } , NULL , 0 , VIDEO_MODE_AUTO , NULL , 0 , 0 , 0 , 0 , 0 ,
0 , NULL , NULL , NULL , NULL , NULL , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } } ;
2002-01-17 01:32:29 +00:00
# define MJPEG_NBUFFERS 2
2003-11-03 17:38:50 +00:00
# define MJPEG_SIZE 1024*256
2002-01-17 01:32:29 +00:00
2002-08-05 20:03:22 +00:00
int zoran_getcap ( zr_info_t * zr ) {
2003-06-21 23:22:38 +00:00
char * dev = NULL ;
2002-04-17 21:28:51 +00:00
2002-08-05 20:03:22 +00:00
if ( zr - > device )
dev = zr - > device ;
2003-06-21 23:22:38 +00:00
else {
2002-04-17 21:28:51 +00:00
struct stat vstat ;
2003-06-21 23:22:38 +00:00
const char * devs [ ] = {
" /dev/video " ,
" /dev/video0 " ,
" /dev/v4l/video0 " ,
" /dev/v4l0 " ,
" /dev/v4l " ,
NULL
} ;
int i = 0 ;
do
{
if ( ( stat ( devs [ i ] , & vstat ) = = 0 ) & & S_ISCHR ( vstat . st_mode ) )
{
dev = devs [ i ] ;
mp_msg ( MSGT_VO , MSGL_V , " zr: found video device %s \n " , dev ) ;
break ;
}
} while ( devs [ + + i ] ! = NULL ) ;
if ( ! dev )
{
mp_msg ( MSGT_VO , MSGL_ERR , " zr: unable to find video device \n " ) ;
return 1 ;
2002-04-17 21:28:51 +00:00
}
}
2002-08-05 20:03:22 +00:00
zr - > vdes = open ( dev , O_RDWR ) ;
2002-04-17 21:28:51 +00:00
2002-08-05 20:03:22 +00:00
if ( zr - > vdes < 0 ) {
2002-04-17 21:28:51 +00:00
mp_msg ( MSGT_VO , MSGL_ERR , " zr: error opening %s: %s \n " ,
dev , strerror ( errno ) ) ;
return 1 ;
}
2002-01-17 01:32:29 +00:00
/* before we can ask for the maximum resolution, we must set
* the correct tv norm */
2002-08-05 20:03:22 +00:00
if ( ioctl ( zr - > vdes , MJPIOC_G_PARAMS , & zr - > p ) < 0 ) {
2002-04-17 21:28:51 +00:00
mp_msg ( MSGT_VO , MSGL_ERR , " zr: device at %s is probably not a DC10(+)/buz/lml33 \n " , dev ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
2002-08-05 20:03:22 +00:00
if ( zr - > p . norm ! = zr - > norm & & zr - > norm ! = VIDEO_MODE_AUTO ) {
2002-01-17 01:32:29 +00:00
/* attempt to set requested norm */
2002-08-05 20:03:22 +00:00
zr - > p . norm = zr - > norm ;
if ( ioctl ( zr - > vdes , MJPIOC_S_PARAMS , & zr - > p ) < 0 ) {
2002-01-17 01:32:29 +00:00
mp_msg ( MSGT_VO , MSGL_ERR ,
2002-04-17 21:28:51 +00:00
" zr: unable to change video norm, use another program to change it (XawTV) \n " ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
2002-08-05 20:03:22 +00:00
ioctl ( zr - > vdes , MJPIOC_G_PARAMS , & zr - > p ) ;
if ( zr - > norm ! = zr - > p . norm ) {
2002-01-17 01:32:29 +00:00
mp_msg ( MSGT_VO , MSGL_ERR ,
2002-04-17 21:28:51 +00:00
" zr: unable to change video norm, use another program to change it (XawTV) \n " ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
}
2002-08-05 20:03:22 +00:00
if ( ioctl ( zr - > vdes , VIDIOCGCAP , & zr - > vc ) < 0 ) {
2003-08-29 21:42:51 +00:00
mp_msg ( MSGT_VO , MSGL_ERR , " zr: error getting video capabilities from %s \n " , dev ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
2002-08-05 20:03:22 +00:00
mp_msg ( MSGT_VO , MSGL_V , " zr: MJPEG card reports maxwidth=%d, maxheight=%d \n " , zr - > vc . maxwidth , zr - > vc . maxheight ) ;
2002-01-17 01:32:29 +00:00
return 0 ;
}
2002-08-05 20:03:22 +00:00
int init_zoran ( zr_info_t * zr , int stretchx , int stretchy ) {
2002-01-17 01:32:29 +00:00
/* center the image, and stretch it as far as possible (try to keep
* aspect ) and check if it fits */
2002-08-05 20:03:22 +00:00
if ( zr - > image_width > zr - > vc . maxwidth ) {
mp_msg ( MSGT_VO , MSGL_ERR , " zr: movie to be played is too wide, max width currenty %d \n " , zr - > vc . maxwidth ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
2002-08-05 20:03:22 +00:00
if ( zr - > image_height > zr - > vc . maxheight ) {
mp_msg ( MSGT_VO , MSGL_ERR , " zr: movie to be played is too high, max height currenty %d \n " , zr - > vc . maxheight ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
2002-08-05 20:03:22 +00:00
zr - > p . decimation = 0 ;
zr - > p . HorDcm = stretchx ;
zr - > p . VerDcm = stretchy ;
zr - > p . TmpDcm = 1 ;
zr - > p . field_per_buff = zr - > fields ;
if ( zr - > xdoff = = - 1 ) {
zr - > p . img_x = ( zr - > vc . maxwidth -
zr - > p . HorDcm * ( int ) zr - > image_width / zr - > hdec ) / 2 ;
} else {
zr - > p . img_x = zr - > xdoff ;
}
if ( zr - > ydoff = = - 1 ) {
zr - > p . img_y = ( zr - > vc . maxheight - zr - > p . VerDcm *
( 3 - zr - > fields ) * ( int ) zr - > image_height ) / 4 ;
} else {
zr - > p . img_y = zr - > ydoff ;
}
zr - > p . img_width = zr - > p . HorDcm * zr - > image_width / zr - > hdec ;
zr - > p . img_height = zr - > p . VerDcm * zr - > image_height / zr - > fields ;
mp_msg ( MSGT_VO , MSGL_V , " zr: geometry (after 'scaling'): %dx%d+%d+%d fields=%d, w=%d, h=%d \n " , zr - > p . img_width , ( 3 - zr - > fields ) * zr - > p . img_height , zr - > p . img_x , zr - > p . img_y , zr - > fields , zr - > image_width / zr - > hdec , zr - > image_height ) ;
if ( ioctl ( zr - > vdes , MJPIOC_S_PARAMS , & zr - > p ) < 0 ) {
2002-04-17 21:28:51 +00:00
mp_msg ( MSGT_VO , MSGL_ERR , " zr: error setting display parameters \n " ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
2003-11-03 17:38:50 +00:00
zr - > zrq . count = MJPEG_NBUFFERS ;
zr - > zrq . size = MJPEG_SIZE ;
2002-01-17 01:32:29 +00:00
2003-11-03 17:38:50 +00:00
if ( ioctl ( zr - > vdes , MJPIOC_REQBUFS , & zr - > zrq ) ) {
mp_msg ( MSGT_VO , MSGL_ERR , " zr: error requesting %d buffers of size %d \n " , zr - > zrq . count , zr - > zrq . size ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
2003-04-24 17:17:57 +00:00
/* the buffer count allocated may be different to the request */
2003-11-03 17:38:50 +00:00
zr - > buf = ( unsigned char * ) mmap ( 0 , zr - > zrq . count * zr - > zrq . size ,
2002-08-05 20:03:22 +00:00
PROT_READ | PROT_WRITE , MAP_SHARED , zr - > vdes , 0 ) ;
2002-01-17 01:32:29 +00:00
2002-08-05 20:03:22 +00:00
if ( zr - > buf = = MAP_FAILED ) {
2003-11-03 17:38:50 +00:00
mp_msg ( MSGT_VO , MSGL_ERR , " zr: error requesting %d buffers of size %d \n " , zr - > zrq . count , zr - > zrq . size ) ;
return 1 ;
}
mp_msg ( MSGT_VO , MSGL_V , " zr: got %d buffers of size %d (wanted %d buffers of size %d) \n " , zr - > zrq . count , zr - > zrq . size , MJPEG_NBUFFERS , MJPEG_SIZE ) ;
if ( zr - > zrq . count < MJPEG_NBUFFERS ) {
mp_msg ( MSGT_VO , MSGL_V , " zr: got not enough buffers \n " ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
2003-04-24 17:17:57 +00:00
zr - > queue = 0 ;
zr - > synco = 0 ;
2002-01-17 01:32:29 +00:00
return 0 ;
}
2002-08-05 20:03:22 +00:00
void uninit_zoran ( zr_info_t * zr ) {
if ( zr - > image ) {
free ( zr - > image ) ;
zr - > image = NULL ;
2002-01-17 01:32:29 +00:00
}
2002-08-05 20:03:22 +00:00
while ( zr - > queue > zr - > synco + 1 ) {
if ( ioctl ( zr - > vdes , MJPIOC_SYNC , & zr - > zs ) < 0 )
2002-04-17 21:28:51 +00:00
mp_msg ( MSGT_VO , MSGL_ERR , " zr: error waiting for buffers to become free \n " ) ;
2002-08-05 20:03:22 +00:00
zr - > synco + + ;
2002-01-17 01:32:29 +00:00
}
/* stop streaming */
2002-08-05 20:03:22 +00:00
zr - > frame = - 1 ;
if ( ioctl ( zr - > vdes , MJPIOC_QBUF_PLAY , & zr - > frame ) < 0 )
2002-04-17 21:28:51 +00:00
mp_msg ( MSGT_VO , MSGL_ERR , " zr: error stopping playback of last frame \n " ) ;
2003-11-03 17:38:50 +00:00
if ( munmap ( zr - > buf , zr - > zrq . count * zr - > zrq . size ) )
2003-04-24 17:17:57 +00:00
mp_msg ( MSGT_VO , MSGL_ERR , " zr: error unmapping buffer \n " ) ;
2002-08-05 20:03:22 +00:00
close ( zr - > vdes ) ;
2002-01-17 01:32:29 +00:00
}
2002-08-05 20:03:22 +00:00
int zr_geometry_sane ( geo_t * g , unsigned int width , unsigned int height ) {
if ( g - > set ) {
if ( g - > width % 2 ! = 0 | | g - > height % 2 ! = 0 | |
g - > xoff % 2 ! = 0 | | g - > yoff % 2 ! = 0 ) {
2002-04-17 21:28:51 +00:00
mp_msg ( MSGT_VO , MSGL_ERR , " zr: arguments in -zrcrop must be multiples of 2 \n " ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
2002-08-05 20:03:22 +00:00
if ( g - > width < = 0 | | g - > height < = 0 | |
g - > xoff < 0 | | g - > yoff < 0 ) {
2002-04-17 21:28:51 +00:00
mp_msg ( MSGT_VO , MSGL_ERR , " zr: width and height must be positive and offset nonnegative \n " ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
2002-08-05 20:03:22 +00:00
if ( g - > width + g - > xoff > width ) {
mp_msg ( MSGT_VO , MSGL_ERR , " zr: width+xoffset (%d+%d>%d) is too big \n " , g - > width , g - > xoff , width ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
2002-08-05 20:03:22 +00:00
if ( g - > height + g - > yoff > height ) {
mp_msg ( MSGT_VO , MSGL_ERR , " zr: height+yoffset (%d+%d>%d) is too big \n " , g - > height , g - > yoff , height ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
}
} else {
2002-08-05 20:03:22 +00:00
g - > width = width ;
g - > height = height ;
g - > xoff = 0 ;
g - > yoff = 0 ;
g - > set = 1 ;
2002-01-17 01:32:29 +00:00
}
2002-08-05 20:03:22 +00:00
return 0 ;
}
static uint32_t config ( uint32_t width , uint32_t height , uint32_t d_width ,
2005-04-18 15:52:38 +00:00
uint32_t d_height , uint32_t flags , char * title , uint32_t format )
2002-08-05 20:03:22 +00:00
{
int i , tmp , stretchx , stretchy ;
framenum = 0 ;
if ( format ! = IMGFMT_YV12 & & format ! = IMGFMT_YUY2 ) {
printf ( " vo_zr called with wrong format " ) ;
2003-08-14 01:25:42 +00:00
return 1 ;
2002-08-05 20:03:22 +00:00
}
for ( i = 0 ; i < zr_count ; i + + ) {
zr_info_t * zr = & zr_info [ i ] ;
geo_t * g = & zr - > g ;
zr - > stride = 2 * width ;
if ( zr_geometry_sane ( g , width , height ) ) return 1 ;
/* we must know the maximum resolution of the device
* it differs for DC10 + and buz for example */
zoran_getcap ( zr ) ; /*must be called before init_zoran */
/* make the scaling decision
* we are capable of stretching the image in the horizontal
* direction by factors 1 , 2 and 4
* we can stretch the image in the vertical direction by a
* factor of 1 and 2 AND we must decide about interlacing */
if ( g - > width > zr - > vc . maxwidth / 2 | |
g - > height > zr - > vc . maxheight / 2 ) {
stretchx = 1 ;
stretchy = 1 ;
zr - > fields = 2 ;
if ( zr - > vdec = = 2 ) {
zr - > fields = 1 ;
} else if ( zr - > vdec = = 4 ) {
zr - > fields = 1 ;
stretchy = 2 ;
2002-01-26 00:53:29 +00:00
}
2002-08-05 20:03:22 +00:00
stretchx = zr - > hdec ;
} else if ( g - > width > zr - > vc . maxwidth / 4 | |
g - > height > zr - > vc . maxheight / 4 ) {
stretchx = 2 ;
stretchy = 1 ;
zr - > fields = 1 ;
if ( zr - > vdec = = 2 ) {
stretchy = 2 ;
} else if ( zr - > vdec = = 4 ) {
if ( ! zr - > fd ) {
mp_msg ( MSGT_VO , MSGL_WARN , " zr: vertical decimation too high, changing to 2 (use -zrfd to keep vdec=4) \n " ) ;
zr - > vdec = 2 ;
}
stretchy = 2 ;
}
if ( zr - > hdec = = 2 ) {
stretchx = 4 ;
} else if ( zr - > hdec = = 4 ) {
if ( ! zr - > fd ) {
mp_msg ( MSGT_VO , MSGL_WARN , " zr: horizontal decimation too high, changing to 2 (use -zrfd to keep hdec=4) \n " ) ;
zr - > hdec = 2 ;
}
stretchx = 4 ;
2002-01-26 00:53:29 +00:00
}
2002-08-05 20:03:22 +00:00
} else {
/* output image is maximally stretched */
2002-01-26 00:53:29 +00:00
stretchx = 4 ;
2002-08-05 20:03:22 +00:00
stretchy = 2 ;
zr - > fields = 1 ;
if ( zr - > vdec ! = 1 & & ! zr - > fd ) {
mp_msg ( MSGT_VO , MSGL_WARN , " zr: vertical decimation too high, changing to 1 (use -zrfd to keep vdec=%d) \n " , zr - > vdec ) ;
zr - > vdec = 1 ;
}
if ( zr - > hdec ! = 1 & & ! zr - > fd ) {
mp_msg ( MSGT_VO , MSGL_WARN , " zr: vertical decimation too high, changing to 1 (use -zrfd to keep hdec=%d) \n " , zr - > hdec ) ;
zr - > hdec = 1 ;
}
2002-01-26 00:53:29 +00:00
}
2002-08-05 20:03:22 +00:00
/* It can be that the original frame was too big for display,
* or that the width of the decimated image ( for example ) after
* padding up to a multiple of 16 has become too big . ( orig
* width 720 ( exactly right for the Buz ) after decimation 360 ,
* after padding up to a multiple of 16 368 , display 736 - > too
* large ) . In these situations we auto ( re ) crop . */
tmp = 16 * ( ( g - > width - 1 ) / ( zr - > hdec * 16 ) + 1 ) ;
if ( stretchx * tmp > zr - > vc . maxwidth ) {
g - > xoff + = 2 * ( ( g - > width - zr - > hdec * ( tmp - 16 ) ) / 4 ) ;
/* g->off must be a multiple of 2 */
g - > width = zr - > hdec * ( tmp - 16 ) ;
g - > set = 0 ; /* we abuse this field to
report that g has changed */
2002-01-26 00:53:29 +00:00
}
2002-08-05 20:03:22 +00:00
tmp = 8 * zr - > fields * ( ( g - > height - 1 ) / ( zr - > vdec * zr - > fields * 8 ) + 1 ) ;
if ( stretchy * tmp > zr - > vc . maxheight ) {
g - > yoff + = 2 * ( ( g - > height - zr - > vdec *
( tmp - 8 * zr - > fields ) ) / 4 ) ;
g - > height = zr - > vdec * ( tmp - 8 * zr - > fields ) ;
g - > set = 0 ;
}
if ( ! g - > set )
mp_msg ( MSGT_VO , MSGL_V , " zr: auto(re)cropping %dx%d+%d+%d to make the image fit on the screen \n " , g - > width , g - > height , g - > xoff , g - > yoff ) ;
/* the height must be a multiple of fields*8 and the width
* must be a multiple of 16 */
/* add some black borders to make it so, and center the image*/
zr - > image_height = zr - > fields * 8 * ( ( g - > height / zr - > vdec - 1 ) /
( zr - > fields * 8 ) + 1 ) ;
zr - > image_width = ( zr - > hdec * 16 ) * ( ( g - > width - 1 ) / ( zr - > hdec * 16 ) + 1 ) ;
zr - > off_y = ( zr - > image_height - g - > height / zr - > vdec ) / 2 ;
if ( zr - > off_y % 2 ! = 0 ) zr - > off_y + + ;
zr - > off_y * = zr - > image_width ;
zr - > off_c = zr - > off_y / 4 ;
zr - > off_y + = ( zr - > image_width - g - > width ) / 2 ;
if ( zr - > off_y % 2 ! = 0 ) zr - > off_y - - ;
zr - > off_c + = ( zr - > image_width - g - > width ) / 4 ;
zr - > size = zr - > image_width * zr - > image_height ;
mp_msg ( MSGT_VO , MSGL_V , " zr: input: %dx%d, cropped: %dx%d, output: %dx%d, off_y=%d, off_c=%d \n " , width , height , g - > width , g - > height , zr - > image_width , zr - > image_height , zr - > off_y , zr - > off_c ) ;
zr - > image = malloc ( 2 * zr - > size ) ; /* this buffer allows for YUV422 data,
* so it is a bit too big for YUV420 */
if ( ! zr - > image ) {
mp_msg ( MSGT_VO , MSGL_ERR , " zr: Memory exhausted \n " ) ;
return 1 ;
}
/* and make sure that the borders are _really_ black */
switch ( format ) {
case IMGFMT_YV12 :
memset ( zr - > image , 0 , zr - > size ) ;
memset ( zr - > image + zr - > size , 0x80 , zr - > size / 4 ) ;
memset ( zr - > image + 3 * zr - > size / 2 , 0x80 , zr - > size / 4 ) ;
zr - > y_data = zr - > image ;
zr - > u_data = zr - > image + zr - > size ;
zr - > v_data = zr - > image + 3 * zr - > size / 2 ;
zr - > y_stride = zr - > image_width ;
zr - > u_stride = zr - > image_width / 2 ;
zr - > v_stride = zr - > image_width / 2 ;
zr - > j = jpeg_enc_init ( zr - > image_width / zr - > hdec ,
zr - > image_height / zr - > fields ,
zr - > hdec , zr - > y_stride * zr - > fields ,
zr - > hdec , zr - > u_stride * zr - > fields ,
zr - > hdec , zr - > v_stride * zr - > fields ,
1 , zr - > quality , zr - > bw ) ;
break ;
case IMGFMT_YUY2 :
for ( tmp = 0 ; tmp < 2 * zr - > size ; tmp + = 4 ) {
zr - > image [ tmp ] = 0 ;
zr - > image [ tmp + 1 ] = 0x80 ;
zr - > image [ tmp + 2 ] = 0 ;
zr - > image [ tmp + 3 ] = 0x80 ;
}
zr - > y_data = zr - > image ;
zr - > u_data = zr - > image + 1 ;
zr - > v_data = zr - > image + 3 ;
zr - > y_stride = 2 * zr - > image_width ;
zr - > u_stride = 2 * zr - > image_width ;
zr - > v_stride = 2 * zr - > image_width ;
zr - > j = jpeg_enc_init ( zr - > image_width / zr - > hdec ,
zr - > image_height / zr - > fields ,
zr - > hdec * 2 ,
zr - > y_stride * zr - > fields ,
zr - > hdec * 4 ,
zr - > u_stride * zr - > fields ,
zr - > hdec * 4 ,
zr - > v_stride * zr - > fields ,
0 , zr - > quality , zr - > bw ) ;
break ;
default :
mp_msg ( MSGT_VO , MSGL_FATAL , " zr: internal inconsistency in vo_zr \n " ) ;
}
if ( zr - > j = = NULL ) {
mp_msg ( MSGT_VO , MSGL_ERR , " zr: error initializing the jpeg encoder \n " ) ;
return 1 ;
}
if ( init_zoran ( zr , stretchx , stretchy ) ) {
return 1 ;
2002-01-26 00:53:29 +00:00
}
2002-01-17 01:32:29 +00:00
}
return 0 ;
}
static void draw_osd ( void ) {
}
static void flip_page ( void ) {
2002-08-05 20:03:22 +00:00
int i , j , k ;
2002-02-11 01:24:56 +00:00
//FILE *fp;
//char filename[100];
2002-01-17 01:32:29 +00:00
/* do we have a free buffer? */
2002-08-05 20:03:22 +00:00
for ( j = 0 ; j < zr_count ; j + + ) {
zr_info_t * zr = & zr_info [ j ] ;
2003-11-03 17:38:50 +00:00
/* using MJPEG_NBUFFERS here, using the real number of
* buffers may give sync issues ( real number of buffers
* is always sufficient ) */
2002-08-05 20:03:22 +00:00
if ( zr - > queue - zr - > synco < MJPEG_NBUFFERS ) {
zr - > frame = zr - > queue ;
} else {
if ( ioctl ( zr - > vdes , MJPIOC_SYNC , & zr - > zs ) < 0 )
mp_msg ( MSGT_VO , MSGL_ERR , " zr: error waiting for buffers to become free \n " ) ;
zr - > frame = zr - > zs . frame ;
zr - > synco + + ;
}
k = 0 ;
for ( i = 0 ; i < zr - > fields ; i + + )
k + = jpeg_enc_frame ( zr - > j , zr - > y_data + i * zr - > y_stride ,
zr - > u_data + i * zr - > u_stride ,
zr - > v_data + i * zr - > v_stride ,
2003-11-03 17:38:50 +00:00
zr - > buf + zr - > frame * zr - > zrq . size + k ) ;
if ( k > zr - > zrq . size ) mp_msg ( MSGT_VO , MSGL_WARN , " zr: jpeg image too large for maximum buffer size. Lower the jpeg encoding \n quality or the resolution of the movie. \n " ) ;
2002-01-17 01:32:29 +00:00
}
2002-02-11 01:24:56 +00:00
/* Warning: Only the first jpeg image contains huffman- and
* quantisation tables , so don ' t expect files other than
* test0001 . jpg to be readable */
2002-01-26 00:53:29 +00:00
/*sprintf(filename, "test%04d.jpg", framenum);
fp = fopen ( filename , " w " ) ;
if ( ! fp ) exit ( 1 ) ;
fwrite ( buf + frame * zrq . size , 1 , k , fp ) ;
fclose ( fp ) ; */
/*fp = fopen("test1.jpg", "r");
fread ( buf + frame * zrq . size , 1 , 2126 , fp ) ;
fclose ( fp ) ; */
2002-08-05 20:03:22 +00:00
for ( j = 0 ; j < zr_count ; j + + ) {
zr_info_t * zr = & zr_info [ j ] ;
if ( ioctl ( zr - > vdes , MJPIOC_QBUF_PLAY , & zr - > frame ) < 0 )
mp_msg ( MSGT_VO , MSGL_ERR , " zr: error queueing buffer for playback \n " ) ;
zr - > queue + + ;
}
2002-01-17 01:32:29 +00:00
framenum + + ;
return ;
}
static uint32_t draw_frame ( uint8_t * src [ ] ) {
2002-08-05 20:03:22 +00:00
int i , j ;
2002-01-26 00:53:29 +00:00
char * source , * dest ;
//printf("draw frame called\n");
2002-08-05 20:03:22 +00:00
for ( j = 0 ; j < zr_count ; j + + ) {
zr_info_t * zr = & zr_info [ j ] ;
geo_t * g = & zr - > g ;
source = src [ 0 ] + 2 * g - > yoff * zr - > vdec * zr - > stride + 2 * g - > xoff ;
dest = zr - > image + 2 * zr - > off_y ;
for ( i = 0 ; i < g - > height / zr - > vdec ; i + + ) {
memcpy ( dest , source , zr - > image_width * 2 ) ;
dest + = 2 * zr - > image_width ;
source + = zr - > vdec * zr - > stride ;
}
2002-01-26 00:53:29 +00:00
}
2002-01-17 01:32:29 +00:00
return 0 ;
}
static uint32_t query_format ( uint32_t format ) {
2002-05-27 18:05:26 +00:00
if ( format = = IMGFMT_YV12 | | format = = IMGFMT_YUY2 )
2002-05-30 11:20:58 +00:00
return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW ;
2002-01-17 01:32:29 +00:00
return 0 ;
}
static void uninit ( void ) {
2002-08-05 20:03:22 +00:00
int j ;
mp_msg ( MSGT_VO , MSGL_V , " zr: uninit called \n " ) ;
for ( j = 0 ; j < zr_count ; j + + ) {
jpeg_enc_uninit ( zr_info [ j ] . j ) ;
uninit_zoran ( & zr_info [ j ] ) ;
}
2002-01-17 01:32:29 +00:00
}
static void check_events ( void ) {
}
static uint32_t draw_slice ( uint8_t * srcimg [ ] , int stride [ ] ,
2002-08-05 20:03:22 +00:00
int wf , int hf , int xf , int yf ) {
int i , j , w , h , x , y ;
2002-01-17 01:32:29 +00:00
/* Apply 'geometry', crop unwanted parts */
uint8_t * dst ;
//printf("before: w=%d, h=%d, x=%d, y=%d, src0=%p, src1=%p, src2=%p\n", w, h, x, y, srcimg[0], srcimg[1], srcimg[2]);
2002-08-05 20:03:22 +00:00
for ( j = 0 ; j < zr_count ; j + + ) {
uint8_t * src = srcimg [ 0 ] ;
2002-01-17 01:32:29 +00:00
uint8_t * src1 = srcimg [ 1 ] ;
uint8_t * src2 = srcimg [ 2 ] ;
2002-08-05 20:03:22 +00:00
zr_info_t * zr = & zr_info [ j ] ;
geo_t * g = & zr - > g ;
w = wf ; h = hf ; x = xf ; y = yf ;
if ( x < g - > xoff ) {
src + = g - > xoff - x ;
src1 + = ( g - > xoff - x ) / 2 ;
src2 + = ( g - > xoff - x ) / 2 ;
w - = g - > xoff - x ;
if ( w < 0 ) break ; //return 0;
x = 0 /*g.xoff*/ ;
} else {
x - = g - > xoff ;
}
if ( x + w > g - > width ) {
w = g - > width - x ;
if ( w < 0 ) break ; //return 0;
}
if ( y < g - > yoff ) {
src + = ( g - > yoff - y ) * stride [ 0 ] ;
src1 + = ( ( g - > yoff - y ) / 2 ) * stride [ 1 ] ;
src2 + = ( ( g - > yoff - y ) / 2 ) * stride [ 2 ] ;
h - = g - > yoff - y ;
if ( h < 0 ) break ; //return 0;
y = 0 ;
} else {
y - = g - > yoff ;
}
if ( y + h > g - > height ) {
h = g - > height - y ;
if ( h < 0 ) break ; //return 0;
}
//printf("after: w=%d, h=%d, x=%d, y=%d, src0=%p, src1=%p, src2=%p\n", w, h, x, y, srcimg[0], srcimg[1], srcimg[2]);
dst = zr - > image + zr - > off_y + zr - > image_width * ( y / zr - > vdec ) + x ;
// copy Y:
for ( i = 0 ; i < h ; i + + ) {
if ( ( i + x ) % zr - > vdec = = 0 ) {
memcpy ( dst , src , w ) ;
dst + = zr - > image_width ;
2002-01-17 01:32:29 +00:00
}
2002-08-05 20:03:22 +00:00
src + = stride [ 0 ] ;
2002-01-17 01:32:29 +00:00
}
2002-08-05 20:03:22 +00:00
if ( ! zr - > bw ) {
// copy U+V:
uint8_t * dst1 = zr - > image + zr - > size + zr - > off_c + ( y / ( zr - > vdec * 2 ) ) * zr - > image_width / 2 + ( x / 2 ) ;
uint8_t * dst2 = zr - > image + 3 * zr - > size / 2 + zr - > off_c +
( y / ( zr - > vdec * 2 ) ) *
zr - > image_width / 2 + ( x / 2 ) ;
for ( i = 0 ; i < h / 2 ; i + + ) {
if ( ( i + x / 2 ) % zr - > vdec = = 0 ) {
memcpy ( dst1 , src1 , w / 2 ) ;
memcpy ( dst2 , src2 , w / 2 ) ;
dst1 + = zr - > image_width / 2 ;
dst2 + = zr - > image_width / 2 ;
}
src1 + = stride [ 1 ] ;
src2 + = stride [ 2 ] ;
}
}
}
2002-01-17 01:32:29 +00:00
return 0 ;
}
/* copied and adapted from vo_aa_parseoption */
int
2003-08-13 16:29:32 +00:00
vo_zr_parseoption ( m_option_t * conf , char * opt , char * param ) {
2002-01-17 01:32:29 +00:00
/* got an option starting with zr */
2002-08-05 20:03:22 +00:00
zr_info_t * zr = & zr_info [ zr_parsing ] ;
2002-01-17 01:32:29 +00:00
int i ;
/* do WE need it ?, always */
if ( ! strcasecmp ( opt , " zrdev " ) ) {
if ( param = = NULL ) return ERR_MISSING_PARAM ;
//if ((i=getcolor(param))==-1) return ERR_OUT_OF_RANGE;
//aaopt_osdcolor=i;
2002-08-05 20:03:22 +00:00
free ( zr - > device ) ;
zr - > device = malloc ( strlen ( param ) + 1 ) ;
strcpy ( zr - > device , param ) ;
mp_msg ( MSGT_VO , MSGL_V , " zr: using device %s \n " , zr - > device ) ;
2002-01-17 01:32:29 +00:00
return 1 ;
2002-01-26 00:53:29 +00:00
} else if ( ! strcasecmp ( opt , " zrbw " ) ) {
2002-01-17 01:32:29 +00:00
if ( param ! = NULL ) {
return ERR_OUT_OF_RANGE ;
}
2002-08-05 20:03:22 +00:00
zr - > bw = 1 ;
2002-01-26 00:53:29 +00:00
return 1 ;
} else if ( ! strcasecmp ( opt , " zrfd " ) ) {
if ( param ! = NULL ) {
return ERR_OUT_OF_RANGE ;
}
2002-08-05 20:03:22 +00:00
zr - > fd = 1 ;
2002-01-17 01:32:29 +00:00
return 1 ;
} else if ( ! strcasecmp ( opt , " zrcrop " ) ) {
2002-08-05 20:03:22 +00:00
geo_t * g = & zr - > g ;
if ( g - > set = = 1 ) {
zr_parsing + + ;
zr_count + + ;
zr = & zr_info [ zr_parsing ] ;
g = & zr - > g ;
if ( zr_count > 4 ) {
mp_msg ( MSGT_VO , MSGL_ERR , " zr: too many simultaneus display devices requested (max. is 4) \n " ) ;
return ERR_OUT_OF_RANGE ;
}
}
2002-01-17 01:32:29 +00:00
if ( param = = NULL ) return ERR_MISSING_PARAM ;
2002-08-05 20:03:22 +00:00
if ( sscanf ( param , " %dx%d+%d+%d " , & g - > width , & g - > height ,
& g - > xoff , & g - > yoff ) ! = 4 ) {
g - > xoff = 0 ; g - > yoff = 0 ;
if ( sscanf ( param , " %dx%d " , & g - > width , & g - > height ) ! = 2 ) {
2002-04-17 21:28:51 +00:00
mp_msg ( MSGT_VO , MSGL_ERR , " zr: argument to -zrcrop must be of the form 352x288+16+0 \n " ) ;
2002-01-17 01:32:29 +00:00
return ERR_OUT_OF_RANGE ;
}
}
2002-08-05 20:03:22 +00:00
g - > set = 1 ;
2002-01-17 01:32:29 +00:00
mp_msg ( MSGT_VO , MSGL_V , " zr: cropping %s \n " , param ) ;
return 1 ;
2002-01-26 00:53:29 +00:00
} else if ( ! strcasecmp ( opt , " zrhdec " ) ) {
i = atoi ( param ) ;
if ( i ! = 1 & & i ! = 2 & & i ! = 4 ) return ERR_OUT_OF_RANGE ;
2002-08-05 20:03:22 +00:00
zr - > hdec = i ;
2002-01-26 00:53:29 +00:00
return 1 ;
2002-01-17 01:32:29 +00:00
} else if ( ! strcasecmp ( opt , " zrvdec " ) ) {
i = atoi ( param ) ;
if ( i ! = 1 & & i ! = 2 & & i ! = 4 ) return ERR_OUT_OF_RANGE ;
2002-08-05 20:03:22 +00:00
zr - > vdec = i ;
return 1 ;
} else if ( ! strcasecmp ( opt , " zrxdoff " ) ) {
i = atoi ( param ) ;
zr - > xdoff = i ;
return 1 ;
} else if ( ! strcasecmp ( opt , " zrydoff " ) ) {
i = atoi ( param ) ;
zr - > ydoff = i ;
2002-01-17 01:32:29 +00:00
return 1 ;
} else if ( ! strcasecmp ( opt , " zrquality " ) ) {
i = atoi ( param ) ;
2002-04-27 18:24:12 +00:00
if ( i < 1 | | i > 20 ) return ERR_OUT_OF_RANGE ;
2002-08-05 20:03:22 +00:00
zr - > quality = i ;
2002-01-17 01:32:29 +00:00
return 1 ;
} else if ( ! strcasecmp ( opt , " zrnorm " ) ) {
if ( param = = NULL ) return ERR_MISSING_PARAM ;
if ( ! strcasecmp ( param , " NTSC " ) ) {
mp_msg ( MSGT_VO , MSGL_V , " zr: Norm set to NTSC \n " ) ;
2002-08-05 20:03:22 +00:00
zr - > norm = VIDEO_MODE_NTSC ;
2002-01-17 01:32:29 +00:00
return 1 ;
} else if ( ! strcasecmp ( param , " PAL " ) ) {
mp_msg ( MSGT_VO , MSGL_V , " zr: Norm set to PAL \n " ) ;
2002-08-05 20:03:22 +00:00
zr - > norm = VIDEO_MODE_PAL ;
2002-01-17 01:32:29 +00:00
return 1 ;
} else {
return ERR_OUT_OF_RANGE ;
}
} else if ( ! strcasecmp ( opt , " zrhelp " ) ) {
printf ( " Help for -vo zr: Zoran ZR360[56]7/ZR36060 based MJPEG capture/playback cards \n " ) ;
printf ( " \n " ) ;
printf ( " Here are the zr options: \n " ) ;
printf (
" \n "
" -zrcrop specify part of the input image that \n "
" you want to see as an x-style geometry string \n "
" example: -zrcrop 352x288+16+0 \n "
" -zrvdec vertical decimation 1, 2 or 4 \n "
2002-01-26 00:53:29 +00:00
" -zrhdec horizontal decimation 1, 2 or 4 \n "
" -zrfd decimation is only done if the primitive \n "
" hardware upscaler can correct for the decimation, \n "
" this switch allows you to see the effects \n "
" of too much decimation \n "
" -zrbw display in black&white (speed increase) \n "
2002-08-05 20:03:22 +00:00
" -zrxdoff x offset from upper-left of TV screen (default is 'centered') \n "
" -zrydoff y offset from upper-left of TV screen (default is 'centered') \n "
2002-04-27 18:24:12 +00:00
" -zrquality jpeg compression quality [BEST] 1 - 20 [VERY BAD] \n "
2002-08-05 20:03:22 +00:00
" -zrdev playback device (example -zrdev /dev/video1) \n "
" -zrnorm specify norm PAL/NTSC (default: leave at current setting) \n "
2002-01-17 01:32:29 +00:00
" \n "
2002-08-05 20:03:22 +00:00
" Cinerama support: additional occurances of -zrcrop activate cinerama mode, \n "
" suppose you have a 704x272 movie, two DC10+ cards and two beamers (or tv's), \n "
" then you would issue the following command: \n \n "
" mplayer -vo zr -zrcrop 352x272+0+0 -zrdev /dev/video0 -zrcrop 352x272+352+0 \\ \n "
" -zrdev /dev/video1 movie.avi \n \n "
" Options appearing after the second -zrcrop apply to the second card, it is \n "
" possible to dispay at a different jpeg quality or at different decimations. \n \n "
2002-10-24 21:34:54 +00:00
" The parameters -zrxdoff and -zrydoff can be used to align the two images. \n "
2002-08-05 20:03:22 +00:00
" The maximum number of zoran cards participating in cinerama is 4, so you can \n "
" build a 2x2 vidiwall. (untested for obvious reasons, the setup wit a buz and \n "
" a DC10+ (and no beamers) is tested, however) \n "
2002-01-17 01:32:29 +00:00
) ;
exit ( 0 ) ;
}
return ERR_NOT_AN_OPTION ;
}
2002-01-19 17:02:50 +00:00
2003-08-13 16:29:32 +00:00
void vo_zr_revertoption ( m_option_t * opt , char * param ) {
2002-01-19 17:02:50 +00:00
2002-08-05 20:03:22 +00:00
zr_info_t * zr = & zr_info [ 1 ] ;
zr_count = 1 ;
zr_parsing = 0 ;
2002-01-19 17:02:50 +00:00
if ( ! strcasecmp ( param , " zrdev " ) ) {
2002-08-05 20:03:22 +00:00
if ( zr - > device )
free ( zr - > device ) ;
zr - > device = NULL ;
2002-01-26 00:53:29 +00:00
} else if ( ! strcasecmp ( param , " zrbw " ) )
2002-08-05 20:03:22 +00:00
zr - > bw = 0 ;
2002-01-26 00:53:29 +00:00
else if ( ! strcasecmp ( param , " zrfd " ) )
2002-08-05 20:03:22 +00:00
zr - > fd = 0 ;
2002-01-19 17:02:50 +00:00
else if ( ! strcasecmp ( param , " zrcrop " ) )
2002-08-05 20:03:22 +00:00
zr - > g . set = zr - > g . xoff = zr - > g . yoff = 0 ;
2002-01-26 00:53:29 +00:00
else if ( ! strcasecmp ( param , " zrhdec " ) )
2002-08-05 20:03:22 +00:00
zr - > hdec = 1 ;
2002-01-19 17:02:50 +00:00
else if ( ! strcasecmp ( param , " zrvdec " ) )
2002-08-05 20:03:22 +00:00
zr - > vdec = 1 ;
else if ( ! strcasecmp ( param , " zrxdoff " ) )
2002-08-05 20:49:58 +00:00
zr - > xdoff = - 1 ;
2002-08-05 20:03:22 +00:00
else if ( ! strcasecmp ( param , " zrydoff " ) )
2002-08-05 20:49:58 +00:00
zr - > ydoff = - 1 ;
2002-01-19 17:02:50 +00:00
else if ( ! strcasecmp ( param , " zrquality " ) )
2002-08-05 20:03:22 +00:00
zr - > quality = 2 ;
2002-01-19 17:02:50 +00:00
else if ( ! strcasecmp ( param , " zrnorm " ) )
2002-08-05 20:03:22 +00:00
zr - > norm = VIDEO_MODE_AUTO ;
2002-01-19 17:02:50 +00:00
}
2002-01-26 16:01:26 +00:00
static uint32_t preinit ( const char * arg )
{
2002-02-17 08:24:43 +00:00
if ( arg )
{
printf ( " vo_zr: Unknown subdevice: %s \n " , arg ) ;
return ENOSYS ;
}
return 0 ;
2002-01-26 16:01:26 +00:00
}
2002-02-09 01:21:48 +00:00
static uint32_t control ( uint32_t request , void * data , . . . )
2002-01-26 16:01:26 +00:00
{
2002-02-09 00:47:26 +00:00
switch ( request ) {
case VOCTRL_QUERY_FORMAT :
return query_format ( * ( ( uint32_t * ) data ) ) ;
}
return VO_NOTIMPL ;
2002-01-26 16:01:26 +00:00
}