4 * Video driver for AAlib - 1.0
6 * by Folke Ashberg <folke@ashberg.de>
8 * Code started: Sun Aug 12 2001
9 * Version 1.0 : Thu Aug 16 2001
27 #include "video_out.h"
28 #include "video_out_internal.h"
30 #include "libswscale/swscale.h"
31 #include "libmpcodecs/vf_scale.h"
32 #include "font_load.h"
35 #include "osdep/keycodes.h"
37 #include "subopt-helper.h"
43 #define MESSAGE_DURATION 3
44 #define MESSAGE_SIZE 512
45 #define MESSAGE_DEKO " +++ %s +++ "
47 static const vo_info_t info
= {
50 "Alban Bedel <albeu@free.fr> and Folke Ashberg <folke@ashberg.de>",
54 const LIBVO_EXTERN(aa
)
56 /* aa's main context we use */
60 /* used for the sws */
61 static uint8_t * image
[3];
62 static int image_stride
[3];
65 static int image_format
;
66 static int image_width
;
67 static int image_height
;
68 static int image_x
, image_y
;
69 static int screen_x
, screen_y
;
70 static int screen_w
, screen_h
;
72 static int src_height
;
76 static int showosdmessage
= 0;
77 char osdmessagetext
[MESSAGE_SIZE
];
78 char posbar
[MESSAGE_SIZE
];
79 static int osdx
, osdy
;
80 static int osd_text_length
= 0;
82 font_desc_t
* vo_font_save
= NULL
;
83 static struct SwsContext
*sws
=NULL
;
86 int aaopt_osdcolor
= AA_SPECIAL
;
87 int aaopt_subcolor
= AA_SPECIAL
;
92 * this function is called by aa lib if windows resizes
93 * further during init, because here we have to calculate
99 aspect_save_screenres(aa_imgwidth(c
),aa_imgheight(c
));
100 image_height
= aa_imgheight(c
); //src_height;
101 image_width
= aa_imgwidth(c
); //src_width;
103 aspect(&image_width
,&image_height
,A_ZOOM
);
105 image_x
= (aa_imgwidth(c
) - image_width
) / 2;
106 image_y
= (aa_imgheight(c
) - image_height
) / 2;
107 screen_w
= image_width
* aa_scrwidth(c
) / aa_imgwidth(c
);
108 screen_h
= image_height
* aa_scrheight(c
) / aa_imgheight(c
);
109 screen_x
= (aa_scrwidth(c
) - screen_w
) / 2;
110 screen_y
= (aa_scrheight(c
) - screen_h
) / 2;
112 if(sws
) sws_freeContext(sws
);
113 sws
= sws_getContextFromCmdLine(src_width
,src_height
,image_format
,
114 image_width
,image_height
,IMGFMT_Y8
);
116 image
[0] = aa_image(c
) + image_y
* aa_imgwidth(c
) + image_x
;
120 image_stride
[0] = aa_imgwidth(c
);
129 osdmessage(int duration
, int deko
, const char *fmt
, ...)
132 * for outputting a centered string at the bottom
133 * of our window for a while
136 char m
[MESSAGE_SIZE
];
137 unsigned int old_len
= strlen(osdmessagetext
);
140 vsprintf(m
, fmt
, ar
);
142 if (deko
==1) sprintf(osdmessagetext
, MESSAGE_DEKO
, m
);
143 else strcpy(osdmessagetext
, m
);
145 if(old_len
> strlen(osdmessagetext
)) {
146 memset(c
->textbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,' ',old_len
);
147 memset(c
->attrbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,0,old_len
);
150 stoposd
= time(NULL
) + duration
;
151 osdx
=(aa_scrwidth(c
) / 2) - (strlen(osdmessagetext
) / 2 ) ;
156 osdpercent(int duration
, int deko
, int min
, int max
, int val
, const char * desc
, const char * unit
)
159 * prints a bar for setting values
166 step
=(float)aa_scrwidth(c
) /(float)(max
-min
);
167 where
=(val
-min
)*step
;
168 osdmessage(duration
,deko
,"%s: %i%s",desc
, val
, unit
);
170 posbar
[aa_scrwidth(c
)-1]='|';
171 for (i
=0;i
<aa_scrwidth(c
);i
++){
172 if (i
==where
) posbar
[i
]='#';
175 if (where
!=0) posbar
[0]='|';
176 if (where
!=(aa_scrwidth(c
)-1) ) posbar
[aa_scrwidth(c
)-1]='|';
178 posbar
[aa_scrwidth(c
)]='\0';
185 if(osd_text_length
> 0 && !vo_osd_text
) {
186 memset(c
->textbuffer
,' ',osd_text_length
);
187 memset(c
->attrbuffer
,0,osd_text_length
);
191 * places the mplayer status osd
193 if (vo_osd_text
&& vo_osd_text
[0] != 0) {
195 if(vo_osd_text
[0] < 32) {
196 len
= strlen(sub_osd_names_short
[vo_osd_text
[0]]) + strlen(vo_osd_text
+1) + 2;
197 aa_printf(c
, 0, 0 , aaopt_osdcolor
, "%s %s ", sub_osd_names_short
[vo_osd_text
[0]], vo_osd_text
+1);
199 len
= strlen(vo_osd_text
) + 1;
200 aa_printf(c
, 0, 0 , aaopt_osdcolor
, "%s ",vo_osd_text
);
203 if(len
< osd_text_length
) {
204 memset(c
->textbuffer
+ len
,' ',osd_text_length
- len
);
205 memset(c
->attrbuffer
+ len
,0,osd_text_length
- len
);
207 osd_text_length
= len
;
213 printosdprogbar(void){
214 /* print mplayer osd-progbar */
215 if (vo_osd_progbar_type
!=-1){
216 osdpercent(1,1,0,255,vo_osd_progbar_value
, sub_osd_names
[vo_osd_progbar_type
], "");
220 config(uint32_t width
, uint32_t height
, uint32_t d_width
,
221 uint32_t d_height
, uint32_t flags
, char *title
,
230 aspect_save_orig(width
,height
);
231 aspect_save_prescale(d_width
,d_height
);
235 image_format
= format
;
237 /* nothing will change its size, be we need some values initialized */
240 /* now init out own 'font' (to use vo_draw_text_sub without edit them) */
241 if(!vo_font_save
) vo_font_save
= vo_font
;
242 if(vo_font
== vo_font_save
) {
243 vo_font
=malloc(sizeof(font_desc_t
));//if(!desc) return NULL;
244 memset(vo_font
,0,sizeof(font_desc_t
));
245 vo_font
->pic_a
[0]=malloc(sizeof(raw_file
));
246 memset(vo_font
->pic_a
[0],0,sizeof(raw_file
));
247 vo_font
->pic_b
[0]=malloc(sizeof(raw_file
));
248 memset(vo_font
->pic_b
[0],0,sizeof(raw_file
));
251 vo_font
->dynamic
= 0;
254 vo_font
->spacewidth
=1;
255 vo_font
->charspace
=0;
257 vo_font
->pic_a
[0]->bmp
=malloc(255);
258 vo_font
->pic_a
[0]->pal
=NULL
;
259 vo_font
->pic_b
[0]->bmp
=malloc(255);
260 vo_font
->pic_b
[0]->pal
=NULL
;
261 vo_font
->pic_a
[0]->w
=1;
262 vo_font
->pic_a
[0]->h
=1;
263 for (i
=0; i
<255; i
++){
267 vo_font
->pic_a
[0]->bmp
[i
]=i
;
268 vo_font
->pic_b
[0]->bmp
[i
]=i
;
273 osdmessage(5, 1, "Welcome to ASCII ART MPlayer");
275 mp_msg(MSGT_VO
,MSGL_V
,"VO: [aa] screendriver: %s\n", c
->driver
->name
);
276 mp_msg(MSGT_VO
,MSGL_V
,"VO: [aa] keyboarddriver: %s\n", c
->kbddriver
->name
);
278 mp_msg(MSGT_VO
,MSGL_INFO
,
280 "Important suboptions\n"
281 "\textended use use all 256 characters\n"
282 "\teight use eight bit ascii\n"
283 "\tdriver set recommended aalib driver (X11,curses,linux)\n"
284 "\thelp to see all options provided by aalib\n"
289 "\t3 : brightness -\n"
290 "\t4 : brightness +\n"
291 "\t5 : fast rendering\n"
293 "\t7 : invert image\n"
294 "\ta : toggles between aa and mplayer control\n"
297 "All other keys are MPlayer defaults.\n"
306 query_format(uint32_t format
) {
308 * ...are we able to... ?
310 * All input format supported by the sws
325 return VFCAP_CSP_SUPPORTED
| VFCAP_SWSCALE
| VFCAP_OSD
;
331 draw_frame(uint8_t *src
[]) {
332 int stride
[3] = { 0 , 0 , 0 };
334 switch(image_format
) {
337 stride
[0] = src_width
*2;
341 stride
[0] = src_width
*3;
344 stride
[0] = src_width
*4;
348 sws_scale_ordered(sws
,src
,stride
,0,src_height
,image
,image_stride
);
350 /* Now 'ASCIInate' the image */
352 aa_fastrender(c
, screen_x
, screen_y
, screen_w
+ screen_x
, screen_h
+ screen_y
);
354 aa_render(c
, p
,screen_x
, screen_y
, screen_w
+ screen_x
, screen_h
+ screen_y
);
360 draw_slice(uint8_t *src
[], int stride
[],
361 int w
, int h
, int x
, int y
) {
363 int dx1
= screen_x
+ (x
* screen_w
/ src_width
);
364 int dy1
= screen_y
+ (y
* screen_h
/ src_height
);
365 int dx2
= screen_x
+ ((x
+w
) * screen_w
/ src_width
);
366 int dy2
= screen_y
+ ((y
+h
) * screen_h
/ src_height
);
368 sws_scale_ordered(sws
,src
,stride
,y
,h
,image
,image_stride
);
370 /* Now 'ASCIInate' the image */
372 aa_fastrender(c
, dx1
, dy1
, dx2
, dy2
);
374 aa_render(c
, p
,dx1
, dy1
, dx2
, dy2
);
383 /* do we have to put *our* (messages, progbar) osd to aa's txtbuf ? */
386 if (time(NULL
)>=stoposd
) {
388 if(*osdmessagetext
) {
389 memset(c
->textbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,' ',strlen(osdmessagetext
));
390 memset(c
->attrbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,0,strlen(osdmessagetext
));
391 osdmessagetext
[0] = '\0';
394 memset(c
->textbuffer
+ (osdy
+1) * aa_scrwidth(c
),' ',strlen(posbar
));
395 memset(c
->attrbuffer
+ (osdy
+1) * aa_scrwidth(c
),0,strlen(posbar
));
399 aa_puts(c
, osdx
, osdy
, AA_SPECIAL
, osdmessagetext
);
402 aa_puts(c
, 0, osdy
+ 1, AA_SPECIAL
, posbar
);
405 /* OSD time & playmode , subtitles */
417 * called by show_image and mplayer
420 while ((key
=aa_getevent(c
,0))!=AA_NONE
){
422 /* some conversations */
425 mplayer_put_key(KEY_UP
);
428 mplayer_put_key(KEY_DOWN
);
431 mplayer_put_key(KEY_LEFT
);
434 mplayer_put_key(KEY_RIGHT
);
437 mplayer_put_key(KEY_ESC
);
440 mplayer_put_key(KEY_PAGE_UP
);
443 mplayer_put_key(KEY_PAGE_DOWN
);
446 continue; /* aa lib special key */
450 if (key
=='a' || key
=='A'){
451 aaconfigmode
=!aaconfigmode
;
452 osdmessage(MESSAGE_DURATION
, 1, "aa config mode is now %s",
453 aaconfigmode
==1 ? "on. use keys 5-7" : "off");
455 if (aaconfigmode
==1) {
457 /* AA image controls */
460 osdmessage(MESSAGE_DURATION
, 1, "Fast mode is now %s", fast
==1 ? "on" : "off");
463 if (p
->dither
==AA_FLOYD_S
){
465 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Off");
466 }else if (p
->dither
==AA_NONE
){
467 p
->dither
=AA_ERRORDISTRIB
;
468 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Error Distribution");
469 }else if (p
->dither
==AA_ERRORDISTRIB
){
470 p
->dither
=AA_FLOYD_S
;
471 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Floyd Steinberg");
475 p
->inversion
=!p
->inversion
;
476 osdmessage(MESSAGE_DURATION
, 1, "Invert mode is now %s",
477 p
->inversion
==1 ? "on" : "off");
481 /* nothing if we're interested in?
482 * the mplayer should handle it!
484 mplayer_put_key(key
);
488 else mplayer_put_key(key
);
498 if (strstr(c
->driver
->name
,"Curses") || strstr(c
->driver
->name
,"Linux")){
499 freopen("/dev/tty", "w", stderr
);
502 free(vo_font
->pic_a
[0]->bmp
);
503 free(vo_font
->pic_a
[0]);
504 free(vo_font
->pic_b
[0]->bmp
);
505 free(vo_font
->pic_b
[0]);
507 vo_font
= vo_font_save
;
513 static void draw_alpha(int x
,int y
, int w
,int h
, unsigned char* src
, unsigned char *srca
, int stride
){
515 for (i
= 0; i
< h
; i
++) {
516 for (j
= 0; j
< w
; j
++) {
517 if (src
[i
*stride
+j
] > 0) {
518 c
->textbuffer
[x
+ j
+ (y
+i
)*aa_scrwidth(c
)] = src
[i
*stride
+j
];
519 c
->attrbuffer
[x
+ j
+ (y
+i
)*aa_scrwidth(c
)] = aaopt_subcolor
;
525 static void clear_alpha(int x0
,int y0
, int w
,int h
) {
528 for(l
= 0 ; l
< h
; l
++) {
529 memset(c
->textbuffer
+ (y0
+ l
) * aa_scrwidth(c
) + x0
,' ',w
);
530 memset(c
->attrbuffer
+ (y0
+ l
) * aa_scrwidth(c
) + x0
,0,w
);
537 char * vo_osd_text_save
;
538 int vo_osd_progbar_type_save
;
541 /* let vo_draw_text only write subtitle */
542 vo_osd_text_save
=vo_osd_text
; /* we have to save the osd_text */
544 vo_osd_progbar_type_save
=vo_osd_progbar_type
;
545 vo_osd_progbar_type
=-1;
546 vo_remove_text(aa_scrwidth(c
), aa_scrheight(c
),clear_alpha
);
547 vo_draw_text(aa_scrwidth(c
), aa_scrheight(c
), draw_alpha
);
548 vo_osd_text
=vo_osd_text_save
;
549 vo_osd_progbar_type
=vo_osd_progbar_type_save
;
556 if (s
==NULL
) return -1;
557 i
=strtol(s
, &rest
, 10);
558 if ((rest
==NULL
|| strlen(rest
)==0) && i
>=0 && i
<=5) return i
;
559 if (!strcasecmp(s
, "normal")) return AA_NORMAL
;
560 else if (!strcasecmp(s
, "dim")) return AA_DIM
;
561 else if (!strcasecmp(s
, "bold")) return AA_BOLD
;
562 else if (!strcasecmp(s
, "boldfont")) return AA_BOLDFONT
;
563 else if (!strcasecmp(s
, "special")) return AA_SPECIAL
;
567 static int parse_suboptions(const char *arg
) {
568 char *pseudoargv
[4], *osdcolor
= NULL
, *subcolor
= NULL
, **strings
,
570 int pseudoargc
, displayhelp
= 0, *booleans
;
571 opt_t extra_opts
[] = {
572 {"osdcolor", OPT_ARG_MSTRZ
, &osdcolor
, NULL
, 0},
573 {"subcolor", OPT_ARG_MSTRZ
, &subcolor
, NULL
, 0},
574 {"help", OPT_ARG_BOOL
, &displayhelp
, NULL
, 0} };
575 opt_t
*subopts
= NULL
, *p
;
576 char *strings_list
[] = {"-driver", "-kbddriver", "-mousedriver", "-font",
577 "-width", "-height", "-minwidth", "-minheight", "-maxwidth",
578 "-maxheight", "-recwidth", "-recheight", "-bright", "-contrast",
579 "-gamma", "-dimmul", "-boldmul", "-random" };
580 char *booleans_list
[] = {"-dim", "-bold", "-reverse", "-normal",
581 "-boldfont", "-inverse", "-extended", "-eight", "-dither",
582 "-floyd_steinberg", "-error_distribution"};
583 char *nobooleans_list
[] = {"-nodim", "-nobold", "-noreverse", "-nonormal",
584 "-noboldfont", "-noinverse", "-noextended", "-noeight", "-nodither",
585 "-nofloyd_steinberg", "-noerror_distribution"};
586 const int nstrings
= sizeof(strings_list
) / sizeof(char*);
587 const int nbooleans
= sizeof(booleans_list
) / sizeof(char*);
588 const int nextra_opts
= sizeof(extra_opts
) / sizeof(opt_t
);
589 const int nsubopts
= nstrings
+ nbooleans
+ nextra_opts
;
592 subopts
= calloc(nsubopts
+ 1, sizeof(opt_t
));
593 strings
= calloc(nstrings
, sizeof(char*));
594 booleans
= calloc(nbooleans
, sizeof(int));
597 for (i
=0; i
<nstrings
; i
++, p
++) {
598 p
->name
= strings_list
[i
] + 1; // skip '-'
599 p
->type
= OPT_ARG_MSTRZ
;
600 p
->valp
= &strings
[i
];
602 for (i
=0; i
<nbooleans
; i
++, p
++) {
603 p
->name
= booleans_list
[i
] + 1;
604 p
->type
= OPT_ARG_BOOL
;
605 p
->valp
= &booleans
[i
];
607 memcpy(p
, extra_opts
, sizeof(extra_opts
));
609 retval
= subopt_parse(arg
, subopts
);
611 if (retval
== 0 && displayhelp
) {
612 helpmsg
= strdup(aa_help
);
613 for (i
=0; i
<(signed)strlen(helpmsg
); i
++)
614 if (helpmsg
[i
] == '-') helpmsg
[i
] = ' ';
615 mp_msg(MSGT_VO
, MSGL_INFO
, MSGTR_VO_AA_HelpHeader
);
616 mp_msg(MSGT_VO
, MSGL_INFO
, "%s\n\n", helpmsg
);
617 mp_msg(MSGT_VO
, MSGL_INFO
, MSGTR_VO_AA_AdditionalOptions
);
621 pseudoargv
[3] = NULL
;
622 for (i
=0; i
<nstrings
; i
++) {
623 pseudoargc
= 3; // inside loop because aalib changes it
624 if (strings
[i
] != NULL
) {
625 pseudoargv
[1] = strings_list
[i
];
626 pseudoargv
[2] = strings
[i
];
627 aa_parseoptions(&aa_defparams
, &aa_defrenderparams
,
628 &pseudoargc
, pseudoargv
);
631 pseudoargv
[2] = NULL
;
632 for (i
=0; i
<nbooleans
; i
++) {
634 if (booleans
[i
]) pseudoargv
[1] = booleans_list
[i
];
635 else pseudoargv
[1] = nobooleans_list
[i
];
636 aa_parseoptions(&aa_defparams
, &aa_defrenderparams
,
637 &pseudoargc
, pseudoargv
);
639 if (osdcolor
) aaopt_osdcolor
= getcolor(osdcolor
);
640 if (subcolor
) aaopt_subcolor
= getcolor(subcolor
);
643 if (subopts
) free(subopts
);
644 if (booleans
) free(booleans
);
646 for (i
=0; i
<nstrings
; i
++)
651 if (osdcolor
) free(osdcolor
);
652 if (subcolor
) free(subcolor
);
653 if (helpmsg
) free(helpmsg
);
657 static int preinit(const char *arg
)
661 int fd
, vt
, major
, minor
;
667 if (parse_suboptions(arg
) != 0)
671 /* initializing of aalib */
673 hidis
=aa_getfirst(&aa_displayrecommended
);
675 /* check /dev/vcsa<vt> */
676 /* check only, if no driver is explicit set */
677 fd
= dup (fileno (stderr
));
679 major
= sbuf
.st_rdev
>> 8;
680 vt
= minor
= sbuf
.st_rdev
& 0xff;
682 sprintf (fname
, "/dev/vcsa%2.2i", vt
);
683 fp
= fopen (fname
, "w+");
685 fprintf(stderr
,"VO: [aa] cannot open %s for writing,"
686 "so we'll not use linux driver\n", fname
);
687 aa_recommendlowdisplay("linux");
688 aa_recommendhidisplay("curses");
689 aa_recommendhidisplay("X11");
691 } else aa_recommendhidisplay(hidis
);
692 c
= aa_autoinit(&aa_defparams
);
695 mp_msg(MSGT_VO
,MSGL_ERR
,"Cannot initialize aalib\n");
698 if (!aa_autoinitkbd(c
,0)) {
699 mp_msg(MSGT_VO
,MSGL_ERR
,"Cannot initialize keyboard\n");
704 aa_resizehandler(c
, (void *)resize
);
706 p
= aa_getrenderparams();
708 if ((strstr(c
->driver
->name
,"Curses")) || (strstr(c
->driver
->name
,"Linux"))){
709 freopen("/dev/null", "w", stderr
);
710 /* disable console blanking */
714 memset(image
,0,3*sizeof(uint8_t));
715 osdmessagetext
[0] = '\0';
721 static int control(uint32_t request
, void *data
, ...)
724 case VOCTRL_QUERY_FORMAT
:
725 return query_format(*((uint32_t*)data
));
726 case VOCTRL_SET_EQUALIZER
: {
731 val
= va_arg(ap
, int);
734 if(strcmp((char*)data
,"contrast") == 0)
735 p
->contrast
= ( val
+ 100 ) * 64 / 100;
736 else if(strcmp((char*)data
,"brightness") == 0)
737 p
->bright
= ( val
+ 100) * 128 / 100;
740 case VOCTRL_GET_EQUALIZER
: {
745 val
= va_arg(ap
, int*);
748 if(strcmp((char*)data
,"contrast") == 0)
749 *val
= (p
->contrast
- 64) * 100 / 64;
750 else if(strcmp((char*)data
,"brightness") == 0)
751 *val
= (p
->bright
- 128) * 100 / 128;